Browse Source

Make prot_call() able to transparently return via the newly installed copy

of librm.
tags/v0.9.3
Michael Brown 19 years ago
parent
commit
04a99841e6
2 changed files with 73 additions and 33 deletions
  1. 54
    16
      src/arch/i386/transitions/librm.S
  2. 19
    17
      src/arch/i386/transitions/librm_mgmt.c

+ 54
- 16
src/arch/i386/transitions/librm.S View File

@@ -144,7 +144,17 @@ _librm_start:
144 144
  ****************************************************************************
145 145
  */
146 146
 	.fill FREE_BASEMEM_HEADER_SIZE, 1, 0
147
-	
147
+
148
+/****************************************************************************
149
+ * Record of the current physical location of the installed copy.
150
+ * Used by prot_call in order to return via the current installed copy
151
+ * even if Etherboot has been relocated during the protected-mode
152
+ * call.
153
+ ****************************************************************************
154
+ */
155
+EXPORT(librm_base):
156
+librm_base:	.long 0
157
+		
148 158
 /****************************************************************************
149 159
  * GDT for initial transition to protected mode
150 160
  *
@@ -266,6 +276,9 @@ EXPORT(real_to_prot):
266 276
 	xorl	%ebx, %ebx
267 277
 	movw	%cs, %bx
268 278
 	shll	$4, %ebx
279
+
280
+	/* Record physical base address of librm */
281
+	movl	%ebx, %ds:OFFSET(librm_base)
269 282
 		
270 283
 	/* Check base address of stored protected-mode GDT.  If it's
271 284
 	 * zero, set it up to use our internal GDT (with physical
@@ -360,6 +373,9 @@ EXPORT(prot_to_real):
360 373
 	popl	OFFSET(save_ebx)(%ebx)
361 374
 	movl	%eax, OFFSET(save_eax)(%ebx)
362 375
 
376
+	/* Record physical base address of librm */
377
+	movl	%ebx, OFFSET(librm_base)(%ebx)
378
+
363 379
 	/* Extract return address from the stack, convert to offset
364 380
 	 * within librm and save in save_retaddr
365 381
 	 */
@@ -445,22 +461,32 @@ p2r_ljmp:
445 461
  *
446 462
  * Call a specific C function in the protected-mode code.  The
447 463
  * prototype of the C function must be
448
- *   void function ( struct real_mode_regs *rm_regs,
449
- *		     void (*retaddr) (void) ); 
464
+ *   void function ( struct real_mode_regs *rm_regs ); 
450 465
  * rm_regs will point to a struct containing the real-mode registers
451
- * at entry to prot_call.  retaddr will point to the (virtual) return
452
- * address from "function".  This return address will point into
453
- * librm.  It is included so that "function" may, if desired, relocate
454
- * librm and return via the new copy.  It must not be directly called
455
- * as a function, i.e. you may not do "*retaddr()"; you must instead
456
- * do something like:
457
- *	*retaddr += ( new_librm_location - old_librm_location );
458
- *	return;
466
+ * at entry to prot_call.  
459 467
  *
460 468
  * All registers will be preserved across prot_call(), unless the C
461 469
  * function explicitly overwrites values in rm_regs.  Interrupt status
462 470
  * will also be preserved.  Gate A20 will be enabled.
463 471
  *
472
+ * The protected-mode code may install librm to a new location.  If it
473
+ * does so, it must update librm_base in *this* copy of librm to point
474
+ * to the new physical location.  prot_call will then return via the
475
+ * newly installed copy.
476
+ *
477
+ * Note that when Etherboot performs its initial relocation, "*this*"
478
+ * copy in the above paragraph will refer to the "master" copy, since
479
+ * that is the initial installed copy.  Etherboot will return to
480
+ * prot_call using a virtual address, so will return to the master
481
+ * copy in high memory (rather than the original copy in base memory).
482
+ * The master copy in high memory will have the physical address of
483
+ * the newly installed copy in librm_base, since install_librm()
484
+ * writes it there.  Thus, Etherboot's initialise() function will
485
+ * return to the master copy of prot_call(), which will then jump to
486
+ * the installed copy.
487
+ *
488
+ * It works, trust me.
489
+ *
464 490
  * Parameters:
465 491
  *   function : virtual address of protected-mode function to call
466 492
  *
@@ -529,14 +555,10 @@ EXPORT(prot_call):
529 555
 	popl	%eax	/* discard */
530 556
 	popal
531 557
 
532
-	/* Push &rm_regs and &retaddr on the stack, and call function */
533
-	movl	%esp, %ebp
558
+	/* Push &rm_regs on the stack, and call function */
534 559
 	pushl	%esp
535
-	subl	$12, 0(%esp)
536
-	pushl	%ebp
537 560
 	call	*%ebx
538 561
 	popl	%eax /* discard */
539
-	popl	%eax /* discard */
540 562
 
541 563
 	/* Switch to physical addresses, discard PM register store */
542 564
 	lcall	$VIRTUAL_CS, $_virt_to_phys
@@ -553,6 +575,22 @@ EXPORT(prot_call):
553 575
 	rep movsb
554 576
 	movl	%esi, %esp	/* remove rm_regs from PM stack */
555 577
 
578
+	/* Obtain physical base address of installed copy of librm in
579
+	 * %ebx.  (It's possible that this *isn't* the physical base
580
+	 * address of the copy we're currently executing in, because
581
+	 * the protected-mode call could have moved librm.  If it does
582
+	 * so, it must update librm_base in our copy to reflect the
583
+	 * new location.
584
+	 */
585
+	call	1f
586
+1:	popl	%ebp
587
+	movl	OFFSET(librm_base-1b)(%ebp), %ebx
588
+	
589
+	/* Jump to running in installed copy of librm */
590
+	addl	$OFFSET(1f), %ebx
591
+	jmp	*%ebx
592
+1:	
593
+	
556 594
 	/* Switch to real mode */
557 595
 	call	prot_to_real
558 596
 	.code16

+ 19
- 17
src/arch/i386/transitions/librm_mgmt.c View File

@@ -23,7 +23,6 @@
23 23
 
24 24
 /* Current location of librm in base memory */
25 25
 char *installed_librm = librm;
26
-static uint32_t installed_librm_phys;
27 26
 
28 27
 /* Whether or not we have base memory currently allocated for librm.
29 28
  * Note that we *can* have a working librm present in unallocated base
@@ -67,7 +66,8 @@ void remove_from_rm_stack ( void *data, size_t size ) {
67 66
  * Install librm to base memory
68 67
  *
69 68
  */
70
-static inline void install_librm ( char *addr ) {
69
+static void install_librm ( char *addr ) {
70
+	librm_base = virt_to_phys ( addr );
71 71
 	memcpy ( addr, librm, librm_size );
72 72
 	installed_librm = addr;
73 73
 }
@@ -79,21 +79,25 @@ static inline void install_librm ( char *addr ) {
79 79
  * copy.
80 80
  *
81 81
  */
82
-static inline void uninstall_librm ( void ) {
82
+static void uninstall_librm ( void ) {
83 83
 	memcpy ( librm, installed_librm, librm_size );
84
+	librm_base = 0;
84 85
 }
85 86
 
86 87
 /*
87
- * On entry, record the physical location of librm.  Do this so that
88
- * we can update installed_librm after relocation.
89
- *
90
- * Doing this is probably more efficient than making installed_librm
91
- * be a physical address, because of the number of times that
92
- * installed_librm gets referenced in the remainder of the code.
88
+ * If librm isn't installed (i.e. if we have librm, but weren't
89
+ * entered via it), then install librm and a real-mode stack to a
90
+ * fixed temporary location, just so that we can e.g. issue printf()
93 91
  *
92
+ * [ If we were entered via librm, then the real_to_prot call will
93
+ * have filled in librm_base. ]
94 94
  */
95 95
 static void librm_init ( void ) {
96
-	installed_librm_phys = virt_to_phys ( installed_librm );
96
+	if ( ! librm_base ) {
97
+		install_librm ( phys_to_virt ( 0x7c00 ) );
98
+		inst_rm_stack.segment = 0x7c0;
99
+		inst_rm_stack.offset = 0x1000;
100
+	}
97 101
 }
98 102
 
99 103
 /*
@@ -112,19 +116,17 @@ static void librm_exit ( void ) {
112 116
 }
113 117
 
114 118
 /*
115
- * On reset, we want to free up our old installed copy of librm, if
116
- * any, then allocate a new base memory block and install there.
119
+ * Reset gets called immediately after relocation.
117 120
  *
118 121
  */
119 122
 
120 123
 static void librm_reset ( void ) {
121 124
 	char *new_librm;
122 125
 
123
-	/* Point installed_librm back at last known physical location */
124
-	installed_librm = phys_to_virt ( installed_librm_phys );
125
-
126
-	/* Uninstall old librm */
127
-	uninstall_librm();
126
+	/* Point installed_librm back at last known physical location.
127
+	 * Do this in case we have just relocated and the virtual
128
+	 * address has therefore changed. */
129
+	installed_librm = phys_to_virt ( librm_base );
128 130
 
129 131
 	/* Free allocated base memory, if applicable */
130 132
 	librm_exit();

Loading…
Cancel
Save