Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / arch / i386 / firmware / pcbios / hidemem.c
diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c
new file mode 100644 (file)
index 0000000..8f3069e
--- /dev/null
@@ -0,0 +1,231 @@
+/* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <assert.h>
+#include <realmode.h>
+#include <biosint.h>
+#include <basemem.h>
+#include <fakee820.h>
+#include <ipxe/init.h>
+#include <ipxe/io.h>
+#include <ipxe/hidemem.h>
+
+/** Set to true if you want to test a fake E820 map */
+#define FAKE_E820 0
+
+/** Alignment for hidden memory regions */
+#define ALIGN_HIDDEN 4096   /* 4kB page alignment should be enough */
+
+/**
+ * A hidden region of iPXE
+ *
+ * This represents a region that will be edited out of the system's
+ * memory map.
+ *
+ * This structure is accessed by assembly code, so must not be
+ * changed.
+ */
+struct hidden_region {
+       /** Physical start address */
+       uint64_t start;
+       /** Physical end address */
+       uint64_t end;
+};
+
+/** Hidden base memory */
+extern struct hidden_region __data16 ( hidemem_base );
+#define hidemem_base __use_data16 ( hidemem_base )
+
+/** Hidden umalloc memory */
+extern struct hidden_region __data16 ( hidemem_umalloc );
+#define hidemem_umalloc __use_data16 ( hidemem_umalloc )
+
+/** Hidden text memory */
+extern struct hidden_region __data16 ( hidemem_textdata );
+#define hidemem_textdata __use_data16 ( hidemem_textdata )
+
+/** Assembly routine in e820mangler.S */
+extern void int15();
+
+/** Vector for storing original INT 15 handler */
+extern struct segoff __text16 ( int15_vector );
+#define int15_vector __use_text16 ( int15_vector )
+
+/* The linker defines these symbols for us */
+extern char _textdata[];
+extern char _etextdata[];
+extern char _text16_memsz[];
+#define _text16_memsz ( ( unsigned int ) _text16_memsz )
+extern char _data16_memsz[];
+#define _data16_memsz ( ( unsigned int ) _data16_memsz )
+
+/**
+ * Hide region of memory from system memory map
+ *
+ * @v region           Hidden memory region
+ * @v start            Start of region
+ * @v end              End of region
+ */
+static void hide_region ( struct hidden_region *region,
+                         physaddr_t start, physaddr_t end ) {
+
+       /* Some operating systems get a nasty shock if a region of the
+        * E820 map seems to start on a non-page boundary.  Make life
+        * safer by rounding out our edited region.
+        */
+       region->start = ( start & ~( ALIGN_HIDDEN - 1 ) );
+       region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) );
+
+       DBG ( "Hiding region [%llx,%llx)\n", region->start, region->end );
+}
+
+/**
+ * Hide used base memory
+ *
+ */
+void hide_basemem ( void ) {
+       /* Hide from the top of free base memory to 640kB.  Don't use
+        * hide_region(), because we don't want this rounded to the
+        * nearest page boundary.
+        */
+       hidemem_base.start = ( get_fbms() * 1024 );
+}
+
+/**
+ * Hide umalloc() region
+ *
+ */
+void hide_umalloc ( physaddr_t start, physaddr_t end ) {
+       assert ( end <= virt_to_phys ( _textdata ) );
+       hide_region ( &hidemem_umalloc, start, end );
+}
+
+/**
+ * Hide .text and .data
+ *
+ */
+void hide_textdata ( void ) {
+       hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ),
+                     virt_to_phys ( _etextdata ) );
+}
+
+/**
+ * Hide Etherboot
+ *
+ * Installs an INT 15 handler to edit Etherboot out of the memory map
+ * returned by the BIOS.
+ */
+static void hide_etherboot ( void ) {
+       struct memory_map memmap;
+       unsigned int rm_ds_top;
+       unsigned int rm_cs_top;
+       unsigned int fbms;
+
+       /* Dump memory map before mangling */
+       DBG ( "Hiding iPXE from system memory map\n" );
+       get_memmap ( &memmap );
+
+       /* Hook in fake E820 map, if we're testing one */
+       if ( FAKE_E820 ) {
+               DBG ( "Hooking in fake E820 map\n" );
+               fake_e820();
+               get_memmap ( &memmap );
+       }
+
+       /* Initialise the hidden regions */
+       hide_basemem();
+       hide_umalloc ( virt_to_phys ( _textdata ), virt_to_phys ( _textdata ) );
+       hide_textdata();
+
+       /* Some really moronic BIOSes bring up the PXE stack via the
+        * UNDI loader entry point and then don't bother to unload it
+        * before overwriting the code and data segments.  If this
+        * happens, we really don't want to leave INT 15 hooked,
+        * because that will cause any loaded OS to die horribly as
+        * soon as it attempts to fetch the system memory map.
+        *
+        * We use a heuristic to guess whether or not we are being
+        * loaded sensibly.
+        */
+       rm_cs_top = ( ( ( rm_cs << 4 ) + _text16_memsz + 1024 - 1 ) >> 10 );
+       rm_ds_top = ( ( ( rm_ds << 4 ) + _data16_memsz + 1024 - 1 ) >> 10 );
+       fbms = get_fbms();
+       if ( ( rm_cs_top < fbms ) && ( rm_ds_top < fbms ) ) {
+               DBG ( "Detected potentially unsafe UNDI load at CS=%04x "
+                     "DS=%04x FBMS=%dkB\n", rm_cs, rm_ds, fbms );
+               DBG ( "Disabling INT 15 memory hiding\n" );
+               return;
+       }
+
+       /* Hook INT 15 */
+       hook_bios_interrupt ( 0x15, ( unsigned int ) int15,
+                             &int15_vector );
+
+       /* Dump memory map after mangling */
+       DBG ( "Hidden iPXE from system memory map\n" );
+       get_memmap ( &memmap );
+}
+
+/**
+ * Unhide Etherboot
+ *
+ * Uninstalls the INT 15 handler installed by hide_etherboot(), if
+ * possible.
+ */
+static void unhide_etherboot ( int flags __unused ) {
+       struct memory_map memmap;
+       int rc;
+
+       /* 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 ( ( rc = unhook_bios_interrupt ( 0x15, ( unsigned int ) int15,
+                                           &int15_vector ) ) != 0 ) {
+               DBG ( "Cannot unhook INT15: %s\n", strerror ( rc ) );
+               /* Leave it hooked; there's nothing else we can do,
+                * and it should be intrinsically safe (though
+                * wasteful of RAM).
+                */
+       }
+
+       /* Unhook fake E820 map, if used */
+       if ( FAKE_E820 )
+               unfake_e820();
+
+       /* Dump memory map after unhiding */
+       DBG ( "Unhidden iPXE from system memory map\n" );
+       get_memmap ( &memmap );
+}
+
+/** Hide Etherboot startup function */
+struct startup_fn hide_etherboot_startup_fn __startup_fn ( STARTUP_EARLY ) = {
+       .startup = hide_etherboot,
+       .shutdown = unhide_etherboot,
+};