From dca369ddc33d5a7b068182a3ef6f1ca71fbf4960 Mon Sep 17 00:00:00 2001
From: Michael Brown <mcb30@etherboot.org>
Date: Sun, 14 Jan 2007 00:53:56 +0000
Subject: [PATCH] Call hide_etherboot() from startup(), rather than requiring
 the prefix to do it.

---
 src/arch/i386/firmware/pcbios/hidemem.c | 16 ++++++++++
 src/arch/i386/prefix/libprefix.S        | 42 +++++++++++++------------
 src/core/main.c                         |  7 +++--
 src/include/gpxe/hidemem.h              |  2 ++
 4 files changed, 45 insertions(+), 22 deletions(-)

diff --git a/src/arch/i386/firmware/pcbios/hidemem.c b/src/arch/i386/firmware/pcbios/hidemem.c
index fa58135e..c372246c 100644
--- a/src/arch/i386/firmware/pcbios/hidemem.c
+++ b/src/arch/i386/firmware/pcbios/hidemem.c
@@ -128,6 +128,22 @@ void hide_etherboot ( void ) {
  * possible.
  */
 void unhide_etherboot ( void ) {
+
+	/* If we have more than one hooked interrupt at this point, it
+	 * means that some other vector is still hooked, in which case
+	 * we can't safely unhook INT 15 because we need to keep our
+	 * memory protected.  (We expect there to be at least one
+	 * hooked interrupt, because INT 15 itself is still hooked).
+	 */
+	if ( hooked_bios_interrupts > 1 ) {
+		DBG ( "Cannot unhide: %d interrupt vectors still hooked\n",
+		      hooked_bios_interrupts );
+		return;
+	}
+
+	/* Try to unhook INT 15.  If it fails, then just leave it
+	 * hooked; it takes care of protecting itself.  :)
+	 */
 	unhook_bios_interrupt ( 0x15, ( unsigned int ) int15,
 				&int15_vector );
 }
diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/i386/prefix/libprefix.S
index d1da0b00..02531211 100644
--- a/src/arch/i386/prefix/libprefix.S
+++ b/src/arch/i386/prefix/libprefix.S
@@ -230,7 +230,7 @@ install_highmem:
  * GDT for flat real mode
  *
  * We only ever use this GDT to set segment limits; the bases are
- * unused.  Also, we only flatten data segments, so we don't need to
+ * unused.  Also, we only change data segments, so we don't need to
  * worry about the code or stack segments.  This makes everything much
  * simpler.
  ****************************************************************************
@@ -250,6 +250,11 @@ flat_ds:	/* Flat real mode data segment */
 	.word	0xffff, 0
 	.byte	0, 0x93, 0xcf, 0
 
+real_ds:	/* Normal real mode data segment */
+	.equ	REAL_DS, real_ds - gdt
+	.word	0xffff, 0
+	.byte	0, 0x93, 0x00, 0
+
 gdt_end:
 	.equ	gdt_length, gdt_end - gdt
 	.size gdt, . - gdt
@@ -257,12 +262,12 @@ gdt_end:
 #endif /* KEEP_IT_REAL */
 
 /****************************************************************************
- * flatten_real_mode (real-mode near call)
+ * set_real_mode_limits (real-mode near call)
  *
- * Sets 4GB limits on the data segments %ds and %es.
+ * Sets limits on the data segments %ds and %es.
  *
- * Parameters: 
- *   none
+ * Parameters:
+ *   %cx : segment type (FLAT_DS for 4GB or REAL_DS for 64kB)
  ****************************************************************************
  */
 
@@ -270,7 +275,7 @@ gdt_end:
 	
 	.section ".prefix.lib"
 	.code16
-flatten_real_mode:
+set_real_mode_limits:
 	/* Preserve real-mode segment values and temporary registers */
 	pushw	%es
 	pushw	%ds
@@ -294,9 +299,8 @@ flatten_real_mode:
 	movl	%eax, %cr0
 
 	/* Set flat segment limits */
-	movw	$FLAT_DS, %ax
-	movw	%ax, %ds
-	movw	%ax, %es
+	movw	%cx, %ds
+	movw	%cx, %es
 
 	/* Switch back to real mode */
 	movl	%cr0, %eax
@@ -309,7 +313,7 @@ flatten_real_mode:
 	popw	%ds
 	popw	%es
 	ret
-	.size flatten_real_mode, . - flatten_real_mode
+	.size set_real_mode_limits, . - set_real_mode_limits
 	
 #endif /* KEEP_IT_REAL */
 	
@@ -369,7 +373,8 @@ install_prealloc:
 	 * prior to reading the E820 memory map and relocating
 	 * properly.
 	 */
-	call	flatten_real_mode
+	movw	$FLAT_DS, %cx
+	call	set_real_mode_limits
 	movl	$HIGHMEM_LOADPOINT, %edi
 	call	install_highmem
 
@@ -383,19 +388,16 @@ install_prealloc:
 	addw	$4, %sp
 
 	/* Move code to new location, set up new protected-mode GDT */
-	call	flatten_real_mode
+	movw	$FLAT_DS, %cx
+	call	set_real_mode_limits
 	pushl	%edi
 	es rep addr32 movsb
 	popl	%edi
 	lcall	*init_librm_vector
-	
-	/* Hide Etherboot from BIOS memory map.  Note that making this
-	 * protected-mode call will also restore normal (non-flat)
-	 * real mode, as part of the protected-to-real transition.
-	 */
-	pushl	$hide_etherboot
-	lcall	*prot_call_vector
-	addw	$4, %sp
+
+	/* Restore real-mode segment limits */
+	movw	$REAL_DS, %cx
+	call	set_real_mode_limits
 
 	/* Restore registers and interrupt status */
 	popl	%ecx
diff --git a/src/core/main.c b/src/core/main.c
index 185e44b2..64e098ca 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -20,6 +20,7 @@ Literature dealing with the network protocols:
 #include <gpxe/shell.h>
 #include <gpxe/shell_banner.h>
 #include <gpxe/shutdown.h>
+#include <gpxe/hidemem.h>
 #include <usr/autoboot.h>
 
 /**
@@ -28,8 +29,9 @@ Literature dealing with the network protocols:
  * Call this function only once, before doing (almost) anything else.
  */
 static void startup ( void ) {
+	hide_etherboot();
 	init_heap();
-	call_init_fns ();
+	call_init_fns();
 	probe_devices();
 }
 
@@ -41,7 +43,8 @@ static void startup ( void ) {
  */
 void shutdown ( void ) {
 	remove_devices();
-	call_exit_fns ();
+	call_exit_fns();
+	unhide_etherboot();
 }
 
 /**
diff --git a/src/include/gpxe/hidemem.h b/src/include/gpxe/hidemem.h
index 547f8881..db867f1c 100644
--- a/src/include/gpxe/hidemem.h
+++ b/src/include/gpxe/hidemem.h
@@ -17,6 +17,8 @@ enum hidemem_region_id {
 	EXTMEM,
 };
 
+extern void hide_etherboot();
+extern void unhide_etherboot();
 extern void hide_region ( unsigned int region_id, physaddr_t start,
 			  physaddr_t end );