Home | History | Annotate | Download | only in pcbios
      1 /* Copyright (C) 2006 Michael Brown <mbrown (at) fensystems.co.uk>.
      2  *
      3  * This program is free software; you can redistribute it and/or
      4  * modify it under the terms of the GNU General Public License as
      5  * published by the Free Software Foundation; either version 2 of the
      6  * License, or any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful, but
      9  * WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     11  * General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program; if not, write to the Free Software
     15  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     16  */
     17 
     18 FILE_LICENCE ( GPL2_OR_LATER );
     19 
     20 #include <assert.h>
     21 #include <realmode.h>
     22 #include <biosint.h>
     23 #include <basemem.h>
     24 #include <fakee820.h>
     25 #include <gpxe/init.h>
     26 #include <gpxe/memmap.h>
     27 #include <gpxe/hidemem.h>
     28 
     29 /** Set to true if you want to test a fake E820 map */
     30 #define FAKE_E820 0
     31 
     32 /** Alignment for hidden memory regions */
     33 #define ALIGN_HIDDEN 4096   /* 4kB page alignment should be enough */
     34 
     35 /**
     36  * A hidden region of gPXE
     37  *
     38  * This represents a region that will be edited out of the system's
     39  * memory map.
     40  *
     41  * This structure is accessed by assembly code, so must not be
     42  * changed.
     43  */
     44 struct hidden_region {
     45 	/** Physical start address */
     46 	uint64_t start;
     47 	/** Physical end address */
     48 	uint64_t end;
     49 };
     50 
     51 /** Hidden base memory */
     52 extern struct hidden_region __data16 ( hidemem_base );
     53 #define hidemem_base __use_data16 ( hidemem_base )
     54 
     55 /** Hidden umalloc memory */
     56 extern struct hidden_region __data16 ( hidemem_umalloc );
     57 #define hidemem_umalloc __use_data16 ( hidemem_umalloc )
     58 
     59 /** Hidden text memory */
     60 extern struct hidden_region __data16 ( hidemem_textdata );
     61 #define hidemem_textdata __use_data16 ( hidemem_textdata )
     62 
     63 /** Assembly routine in e820mangler.S */
     64 extern void int15();
     65 
     66 /** Vector for storing original INT 15 handler */
     67 extern struct segoff __text16 ( int15_vector );
     68 #define int15_vector __use_text16 ( int15_vector )
     69 
     70 /* The linker defines these symbols for us */
     71 extern char _textdata[];
     72 extern char _etextdata[];
     73 extern char _text16_memsz[];
     74 #define _text16_memsz ( ( unsigned int ) _text16_memsz )
     75 extern char _data16_memsz[];
     76 #define _data16_memsz ( ( unsigned int ) _data16_memsz )
     77 
     78 /**
     79  * Hide region of memory from system memory map
     80  *
     81  * @v region		Hidden memory region
     82  * @v start		Start of region
     83  * @v end		End of region
     84  */
     85 static void hide_region ( struct hidden_region *region,
     86 			  physaddr_t start, physaddr_t end ) {
     87 
     88 	/* Some operating systems get a nasty shock if a region of the
     89 	 * E820 map seems to start on a non-page boundary.  Make life
     90 	 * safer by rounding out our edited region.
     91 	 */
     92 	region->start = ( start & ~( ALIGN_HIDDEN - 1 ) );
     93 	region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) );
     94 
     95 	DBG ( "Hiding region [%llx,%llx)\n", region->start, region->end );
     96 }
     97 
     98 /**
     99  * Hide used base memory
    100  *
    101  */
    102 void hide_basemem ( void ) {
    103 	/* Hide from the top of free base memory to 640kB.  Don't use
    104 	 * hide_region(), because we don't want this rounded to the
    105 	 * nearest page boundary.
    106 	 */
    107 	hidemem_base.start = ( get_fbms() * 1024 );
    108 }
    109 
    110 /**
    111  * Hide umalloc() region
    112  *
    113  */
    114 void hide_umalloc ( physaddr_t start, physaddr_t end ) {
    115 	assert ( end <= virt_to_phys ( _textdata ) );
    116 	hide_region ( &hidemem_umalloc, start, end );
    117 }
    118 
    119 /**
    120  * Hide .text and .data
    121  *
    122  */
    123 void hide_textdata ( void ) {
    124 	hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ),
    125 		      virt_to_phys ( _etextdata ) );
    126 }
    127 
    128 /**
    129  * Hide Etherboot
    130  *
    131  * Installs an INT 15 handler to edit Etherboot out of the memory map
    132  * returned by the BIOS.
    133  */
    134 static void hide_etherboot ( void ) {
    135 	struct memory_map memmap;
    136 	unsigned int rm_ds_top;
    137 	unsigned int rm_cs_top;
    138 	unsigned int fbms;
    139 
    140 	/* Dump memory map before mangling */
    141 	DBG ( "Hiding gPXE from system memory map\n" );
    142 	get_memmap ( &memmap );
    143 
    144 	/* Hook in fake E820 map, if we're testing one */
    145 	if ( FAKE_E820 ) {
    146 		DBG ( "Hooking in fake E820 map\n" );
    147 		fake_e820();
    148 		get_memmap ( &memmap );
    149 	}
    150 
    151 	/* Initialise the hidden regions */
    152 	hide_basemem();
    153 	hide_umalloc ( virt_to_phys ( _textdata ), virt_to_phys ( _textdata ) );
    154 	hide_textdata();
    155 
    156 	/* Some really moronic BIOSes bring up the PXE stack via the
    157 	 * UNDI loader entry point and then don't bother to unload it
    158 	 * before overwriting the code and data segments.  If this
    159 	 * happens, we really don't want to leave INT 15 hooked,
    160 	 * because that will cause any loaded OS to die horribly as
    161 	 * soon as it attempts to fetch the system memory map.
    162 	 *
    163 	 * We use a heuristic to guess whether or not we are being
    164 	 * loaded sensibly.
    165 	 */
    166 	rm_cs_top = ( ( ( rm_cs << 4 ) + _text16_memsz + 1024 - 1 ) >> 10 );
    167 	rm_ds_top = ( ( ( rm_ds << 4 ) + _data16_memsz + 1024 - 1 ) >> 10 );
    168 	fbms = get_fbms();
    169 	if ( ( rm_cs_top < fbms ) && ( rm_ds_top < fbms ) ) {
    170 		DBG ( "Detected potentially unsafe UNDI load at CS=%04x "
    171 		      "DS=%04x FBMS=%dkB\n", rm_cs, rm_ds, fbms );
    172 		DBG ( "Disabling INT 15 memory hiding\n" );
    173 		return;
    174 	}
    175 
    176 	/* Hook INT 15 */
    177 	hook_bios_interrupt ( 0x15, ( unsigned int ) int15,
    178 			      &int15_vector );
    179 
    180 	/* Dump memory map after mangling */
    181 	DBG ( "Hidden gPXE from system memory map\n" );
    182 	get_memmap ( &memmap );
    183 }
    184 
    185 /**
    186  * Unhide Etherboot
    187  *
    188  * Uninstalls the INT 15 handler installed by hide_etherboot(), if
    189  * possible.
    190  */
    191 static void unhide_etherboot ( int flags __unused ) {
    192 
    193 	/* If we have more than one hooked interrupt at this point, it
    194 	 * means that some other vector is still hooked, in which case
    195 	 * we can't safely unhook INT 15 because we need to keep our
    196 	 * memory protected.  (We expect there to be at least one
    197 	 * hooked interrupt, because INT 15 itself is still hooked).
    198 	 */
    199 	if ( hooked_bios_interrupts > 1 ) {
    200 		DBG ( "Cannot unhide: %d interrupt vectors still hooked\n",
    201 		      hooked_bios_interrupts );
    202 		return;
    203 	}
    204 
    205 	/* Try to unhook INT 15.  If it fails, then just leave it
    206 	 * hooked; it takes care of protecting itself.  :)
    207 	 */
    208 	unhook_bios_interrupt ( 0x15, ( unsigned int ) int15,
    209 				&int15_vector );
    210 
    211 	/* Unhook fake E820 map, if used */
    212 	if ( FAKE_E820 )
    213 		unfake_e820();
    214 }
    215 
    216 /** Hide Etherboot startup function */
    217 struct startup_fn hide_etherboot_startup_fn __startup_fn ( STARTUP_EARLY ) = {
    218 	.startup = hide_etherboot,
    219 	.shutdown = unhide_etherboot,
    220 };
    221