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., 675 Mass Ave, Cambridge, MA 02139, USA.
00016  */
00017 
00018 FILE_LICENCE ( GPL2_OR_LATER );
00019 
00020 #include <assert.h>
00021 #include <realmode.h>
00022 #include <biosint.h>
00023 #include <basemem.h>
00024 #include <fakee820.h>
00025 #include <gpxe/init.h>
00026 #include <gpxe/memmap.h>
00027 #include <gpxe/hidemem.h>
00028 
00029 /** Set to true if you want to test a fake E820 map */
00030 #define FAKE_E820 0
00031 
00032 /** Alignment for hidden memory regions */
00033 #define ALIGN_HIDDEN 4096   /* 4kB page alignment should be enough */
00034 
00035 /**
00036  * A hidden region of gPXE
00037  *
00038  * This represents a region that will be edited out of the system's
00039  * memory map.
00040  *
00041  * This structure is accessed by assembly code, so must not be
00042  * changed.
00043  */
00044 struct hidden_region {
00045         /** Physical start address */
00046         uint64_t start;
00047         /** Physical end address */
00048         uint64_t end;
00049 };
00050 
00051 /** Hidden base memory */
00052 extern struct hidden_region __data16 ( hidemem_base );
00053 #define hidemem_base __use_data16 ( hidemem_base )
00054 
00055 /** Hidden umalloc memory */
00056 extern struct hidden_region __data16 ( hidemem_umalloc );
00057 #define hidemem_umalloc __use_data16 ( hidemem_umalloc )
00058 
00059 /** Hidden text memory */
00060 extern struct hidden_region __data16 ( hidemem_textdata );
00061 #define hidemem_textdata __use_data16 ( hidemem_textdata )
00062 
00063 /** Assembly routine in e820mangler.S */
00064 extern void int15();
00065 
00066 /** Vector for storing original INT 15 handler */
00067 extern struct segoff __text16 ( int15_vector );
00068 #define int15_vector __use_text16 ( int15_vector )
00069 
00070 /* The linker defines these symbols for us */
00071 extern char _textdata[];
00072 extern char _etextdata[];
00073 extern char _text16_memsz[];
00074 #define _text16_memsz ( ( unsigned int ) _text16_memsz )
00075 extern char _data16_memsz[];
00076 #define _data16_memsz ( ( unsigned int ) _data16_memsz )
00077 
00078 /**
00079  * Hide region of memory from system memory map
00080  *
00081  * @v region            Hidden memory region
00082  * @v start             Start of region
00083  * @v end               End of region
00084  */
00085 static void hide_region ( struct hidden_region *region,
00086                           physaddr_t start, physaddr_t end ) {
00087 
00088         /* Some operating systems get a nasty shock if a region of the
00089          * E820 map seems to start on a non-page boundary.  Make life
00090          * safer by rounding out our edited region.
00091          */
00092         region->start = ( start & ~( ALIGN_HIDDEN - 1 ) );
00093         region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) );
00094 
00095         DBG ( "Hiding region [%llx,%llx)\n", region->start, region->end );
00096 }
00097 
00098 /**
00099  * Hide used base memory
00100  *
00101  */
00102 void hide_basemem ( void ) {
00103         /* Hide from the top of free base memory to 640kB.  Don't use
00104          * hide_region(), because we don't want this rounded to the
00105          * nearest page boundary.
00106          */
00107         hidemem_base.start = ( get_fbms() * 1024 );
00108 }
00109 
00110 /**
00111  * Hide umalloc() region
00112  *
00113  */
00114 void hide_umalloc ( physaddr_t start, physaddr_t end ) {
00115         assert ( end <= virt_to_phys ( _textdata ) );
00116         hide_region ( &hidemem_umalloc, start, end );
00117 }
00118 
00119 /**
00120  * Hide .text and .data
00121  *
00122  */
00123 void hide_textdata ( void ) {
00124         hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ),
00125                       virt_to_phys ( _etextdata ) );
00126 }
00127 
00128 /**
00129  * Hide Etherboot
00130  *
00131  * Installs an INT 15 handler to edit Etherboot out of the memory map
00132  * returned by the BIOS.
00133  */
00134 static void hide_etherboot ( void ) {
00135         struct memory_map memmap;
00136         unsigned int rm_ds_top;
00137         unsigned int rm_cs_top;
00138         unsigned int fbms;
00139 
00140         /* Dump memory map before mangling */
00141         DBG ( "Hiding gPXE from system memory map\n" );
00142         get_memmap ( &memmap );
00143 
00144         /* Hook in fake E820 map, if we're testing one */
00145         if ( FAKE_E820 ) {
00146                 DBG ( "Hooking in fake E820 map\n" );
00147                 fake_e820();
00148                 get_memmap ( &memmap );
00149         }
00150 
00151         /* Initialise the hidden regions */
00152         hide_basemem();
00153         hide_umalloc ( virt_to_phys ( _textdata ), virt_to_phys ( _textdata ) );
00154         hide_textdata();
00155 
00156         /* Some really moronic BIOSes bring up the PXE stack via the
00157          * UNDI loader entry point and then don't bother to unload it
00158          * before overwriting the code and data segments.  If this
00159          * happens, we really don't want to leave INT 15 hooked,
00160          * because that will cause any loaded OS to die horribly as
00161          * soon as it attempts to fetch the system memory map.
00162          *
00163          * We use a heuristic to guess whether or not we are being
00164          * loaded sensibly.
00165          */
00166         rm_cs_top = ( ( ( rm_cs << 4 ) + _text16_memsz + 1024 - 1 ) >> 10 );
00167         rm_ds_top = ( ( ( rm_ds << 4 ) + _data16_memsz + 1024 - 1 ) >> 10 );
00168         fbms = get_fbms();
00169         if ( ( rm_cs_top < fbms ) && ( rm_ds_top < fbms ) ) {
00170                 DBG ( "Detected potentially unsafe UNDI load at CS=%04x "
00171                       "DS=%04x FBMS=%dkB\n", rm_cs, rm_ds, fbms );
00172                 DBG ( "Disabling INT 15 memory hiding\n" );
00173                 return;
00174         }
00175 
00176         /* Hook INT 15 */
00177         hook_bios_interrupt ( 0x15, ( unsigned int ) int15,
00178                               &int15_vector );
00179 
00180         /* Dump memory map after mangling */
00181         DBG ( "Hidden gPXE from system memory map\n" );
00182         get_memmap ( &memmap );
00183 }
00184 
00185 /**
00186  * Unhide Etherboot
00187  *
00188  * Uninstalls the INT 15 handler installed by hide_etherboot(), if
00189  * possible.
00190  */
00191 static void unhide_etherboot ( int flags __unused ) {
00192 
00193         /* If we have more than one hooked interrupt at this point, it
00194          * means that some other vector is still hooked, in which case
00195          * we can't safely unhook INT 15 because we need to keep our
00196          * memory protected.  (We expect there to be at least one
00197          * hooked interrupt, because INT 15 itself is still hooked).
00198          */
00199         if ( hooked_bios_interrupts > 1 ) {
00200                 DBG ( "Cannot unhide: %d interrupt vectors still hooked\n",
00201                       hooked_bios_interrupts );
00202                 return;
00203         }
00204 
00205         /* Try to unhook INT 15.  If it fails, then just leave it
00206          * hooked; it takes care of protecting itself.  :)
00207          */
00208         unhook_bios_interrupt ( 0x15, ( unsigned int ) int15,
00209                                 &int15_vector );
00210 
00211         /* Unhook fake E820 map, if used */
00212         if ( FAKE_E820 )
00213                 unfake_e820();
00214 }
00215 
00216 /** Hide Etherboot startup function */
00217 struct startup_fn hide_etherboot_startup_fn __startup_fn ( STARTUP_EARLY ) = {
00218         .startup = hide_etherboot,
00219         .shutdown = unhide_etherboot,
00220 };

Generated on Tue Apr 6 20:00:49 2010 for gPXE by  doxygen 1.5.7.1