iPXE
hidemem.c
Go to the documentation of this file.
00001 /* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
00002  *
00003  * This program is free software; you can redistribute it and/or
00004  * modify it under the terms of the GNU General Public License as
00005  * published by the Free Software Foundation; either version 2 of the
00006  * License, or any later version.
00007  *
00008  * This program is distributed in the hope that it will be useful, but
00009  * WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011  * General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program; if not, write to the Free Software
00015  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00016  * 02110-1301, USA.
00017  *
00018  * You can also choose to distribute this program under the terms of
00019  * the Unmodified Binary Distribution Licence (as given in the file
00020  * COPYING.UBDL), provided that you have satisfied its requirements.
00021  */
00022 
00023 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00024 
00025 #include <assert.h>
00026 #include <realmode.h>
00027 #include <biosint.h>
00028 #include <basemem.h>
00029 #include <fakee820.h>
00030 #include <ipxe/init.h>
00031 #include <ipxe/io.h>
00032 #include <ipxe/hidemem.h>
00033 
00034 /** Set to true if you want to test a fake E820 map */
00035 #define FAKE_E820 0
00036 
00037 /** Alignment for hidden memory regions */
00038 #define ALIGN_HIDDEN 4096   /* 4kB page alignment should be enough */
00039 
00040 /**
00041  * A hidden region of iPXE
00042  *
00043  * This represents a region that will be edited out of the system's
00044  * memory map.
00045  *
00046  * This structure is accessed by assembly code, so must not be
00047  * changed.
00048  */
00049 struct hidden_region {
00050         /** Physical start address */
00051         uint64_t start;
00052         /** Physical end address */
00053         uint64_t end;
00054 };
00055 
00056 /** Hidden base memory */
00057 extern struct hidden_region __data16 ( hidemem_base );
00058 #define hidemem_base __use_data16 ( hidemem_base )
00059 
00060 /** Hidden umalloc memory */
00061 extern struct hidden_region __data16 ( hidemem_umalloc );
00062 #define hidemem_umalloc __use_data16 ( hidemem_umalloc )
00063 
00064 /** Hidden text memory */
00065 extern struct hidden_region __data16 ( hidemem_textdata );
00066 #define hidemem_textdata __use_data16 ( hidemem_textdata )
00067 
00068 /** Assembly routine in e820mangler.S */
00069 extern void int15();
00070 
00071 /** Vector for storing original INT 15 handler */
00072 extern struct segoff __text16 ( int15_vector );
00073 #define int15_vector __use_text16 ( int15_vector )
00074 
00075 /* The linker defines these symbols for us */
00076 extern char _textdata[];
00077 extern char _etextdata[];
00078 extern char _text16_memsz[];
00079 #define _text16_memsz ( ( size_t ) _text16_memsz )
00080 extern char _data16_memsz[];
00081 #define _data16_memsz ( ( size_t ) _data16_memsz )
00082 
00083 /**
00084  * Hide region of memory from system memory map
00085  *
00086  * @v region            Hidden memory region
00087  * @v start             Start of region
00088  * @v end               End of region
00089  */
00090 static void hide_region ( struct hidden_region *region,
00091                           physaddr_t start, physaddr_t end ) {
00092 
00093         /* Some operating systems get a nasty shock if a region of the
00094          * E820 map seems to start on a non-page boundary.  Make life
00095          * safer by rounding out our edited region.
00096          */
00097         region->start = ( start & ~( ALIGN_HIDDEN - 1 ) );
00098         region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) );
00099 
00100         DBG ( "Hiding region [%llx,%llx)\n", region->start, region->end );
00101 }
00102 
00103 /**
00104  * Hide used base memory
00105  *
00106  */
00107 void hide_basemem ( void ) {
00108         /* Hide from the top of free base memory to 640kB.  Don't use
00109          * hide_region(), because we don't want this rounded to the
00110          * nearest page boundary.
00111          */
00112         hidemem_base.start = ( get_fbms() * 1024 );
00113 }
00114 
00115 /**
00116  * Hide umalloc() region
00117  *
00118  */
00119 void hide_umalloc ( physaddr_t start, physaddr_t end ) {
00120         assert ( end <= virt_to_phys ( _textdata ) );
00121         hide_region ( &hidemem_umalloc, start, end );
00122 }
00123 
00124 /**
00125  * Hide .text and .data
00126  *
00127  */
00128 void hide_textdata ( void ) {
00129         hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ),
00130                       virt_to_phys ( _etextdata ) );
00131 }
00132 
00133 /**
00134  * Hide Etherboot
00135  *
00136  * Installs an INT 15 handler to edit Etherboot out of the memory map
00137  * returned by the BIOS.
00138  */
00139 static void hide_etherboot ( void ) {
00140         struct memory_map memmap;
00141         unsigned int rm_ds_top;
00142         unsigned int rm_cs_top;
00143         unsigned int fbms;
00144 
00145         /* Dump memory map before mangling */
00146         DBG ( "Hiding iPXE from system memory map\n" );
00147         get_memmap ( &memmap );
00148 
00149         /* Hook in fake E820 map, if we're testing one */
00150         if ( FAKE_E820 ) {
00151                 DBG ( "Hooking in fake E820 map\n" );
00152                 fake_e820();
00153                 get_memmap ( &memmap );
00154         }
00155 
00156         /* Initialise the hidden regions */
00157         hide_basemem();
00158         hide_umalloc ( virt_to_phys ( _textdata ), virt_to_phys ( _textdata ) );
00159         hide_textdata();
00160 
00161         /* Some really moronic BIOSes bring up the PXE stack via the
00162          * UNDI loader entry point and then don't bother to unload it
00163          * before overwriting the code and data segments.  If this
00164          * happens, we really don't want to leave INT 15 hooked,
00165          * because that will cause any loaded OS to die horribly as
00166          * soon as it attempts to fetch the system memory map.
00167          *
00168          * We use a heuristic to guess whether or not we are being
00169          * loaded sensibly.
00170          */
00171         rm_cs_top = ( ( ( rm_cs << 4 ) + _text16_memsz + 1024 - 1 ) >> 10 );
00172         rm_ds_top = ( ( ( rm_ds << 4 ) + _data16_memsz + 1024 - 1 ) >> 10 );
00173         fbms = get_fbms();
00174         if ( ( rm_cs_top < fbms ) && ( rm_ds_top < fbms ) ) {
00175                 DBG ( "Detected potentially unsafe UNDI load at CS=%04x "
00176                       "DS=%04x FBMS=%dkB\n", rm_cs, rm_ds, fbms );
00177                 DBG ( "Disabling INT 15 memory hiding\n" );
00178                 return;
00179         }
00180 
00181         /* Hook INT 15 */
00182         hook_bios_interrupt ( 0x15, ( intptr_t ) int15, &int15_vector );
00183 
00184         /* Dump memory map after mangling */
00185         DBG ( "Hidden iPXE from system memory map\n" );
00186         get_memmap ( &memmap );
00187 }
00188 
00189 /**
00190  * Unhide Etherboot
00191  *
00192  * Uninstalls the INT 15 handler installed by hide_etherboot(), if
00193  * possible.
00194  */
00195 static void unhide_etherboot ( int flags __unused ) {
00196         struct memory_map memmap;
00197         int rc;
00198 
00199         /* If we have more than one hooked interrupt at this point, it
00200          * means that some other vector is still hooked, in which case
00201          * we can't safely unhook INT 15 because we need to keep our
00202          * memory protected.  (We expect there to be at least one
00203          * hooked interrupt, because INT 15 itself is still hooked).
00204          */
00205         if ( hooked_bios_interrupts > 1 ) {
00206                 DBG ( "Cannot unhide: %d interrupt vectors still hooked\n",
00207                       hooked_bios_interrupts );
00208                 return;
00209         }
00210 
00211         /* Try to unhook INT 15 */
00212         if ( ( rc = unhook_bios_interrupt ( 0x15, ( intptr_t ) int15,
00213                                             &int15_vector ) ) != 0 ) {
00214                 DBG ( "Cannot unhook INT15: %s\n", strerror ( rc ) );
00215                 /* Leave it hooked; there's nothing else we can do,
00216                  * and it should be intrinsically safe (though
00217                  * wasteful of RAM).
00218                  */
00219         }
00220 
00221         /* Unhook fake E820 map, if used */
00222         if ( FAKE_E820 )
00223                 unfake_e820();
00224 
00225         /* Dump memory map after unhiding */
00226         DBG ( "Unhidden iPXE from system memory map\n" );
00227         get_memmap ( &memmap );
00228 }
00229 
00230 /** Hide Etherboot startup function */
00231 struct startup_fn hide_etherboot_startup_fn __startup_fn ( STARTUP_EARLY ) = {
00232         .startup = hide_etherboot,
00233         .shutdown = unhide_etherboot,
00234 };