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 | |
---|
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 | }; |
---|