1/* Copyright (C) 2006 Michael Brown <mbrown@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 18FILE_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 */ 44struct hidden_region { 45 /** Physical start address */ 46 uint64_t start; 47 /** Physical end address */ 48 uint64_t end; 49}; 50 51/** Hidden base memory */ 52extern struct hidden_region __data16 ( hidemem_base ); 53#define hidemem_base __use_data16 ( hidemem_base ) 54 55/** Hidden umalloc memory */ 56extern struct hidden_region __data16 ( hidemem_umalloc ); 57#define hidemem_umalloc __use_data16 ( hidemem_umalloc ) 58 59/** Hidden text memory */ 60extern struct hidden_region __data16 ( hidemem_textdata ); 61#define hidemem_textdata __use_data16 ( hidemem_textdata ) 62 63/** Assembly routine in e820mangler.S */ 64extern void int15(); 65 66/** Vector for storing original INT 15 handler */ 67extern struct segoff __text16 ( int15_vector ); 68#define int15_vector __use_text16 ( int15_vector ) 69 70/* The linker defines these symbols for us */ 71extern char _textdata[]; 72extern char _etextdata[]; 73extern char _text16_memsz[]; 74#define _text16_memsz ( ( unsigned int ) _text16_memsz ) 75extern 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 */ 85static 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 */ 102void 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 */ 114void 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 */ 123void 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 */ 134static 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 */ 191static 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 */ 217struct startup_fn hide_etherboot_startup_fn __startup_fn ( STARTUP_EARLY ) = { 218 .startup = hide_etherboot, 219 .shutdown = unhide_etherboot, 220}; 221