2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #define SMAP 0x534d4150
25 /* Most documentation refers to the E820 buffer as being 20 bytes, and
26 * the API makes it perfectly legitimate to pass only a 20-byte buffer
27 * and expect to get valid data. However, some morons at ACPI decided
28 * to extend the data structure by adding an extra "extended
29 * attributes" field and by including critical information within this
30 * field, such as whether or not the region is enabled. A caller who
31 * passes in only a 20-byte buffer therefore risks getting very, very
32 * misleading information.
34 * I have personally witnessed an HP BIOS that returns a value of
35 * 0x0009 in the extended attributes field. If we don't pass this
36 * value through to the caller, 32-bit WinPE will die, usually with a
37 * PAGE_FAULT_IN_NONPAGED_AREA blue screen of death.
39 * Allow a ridiculously large maximum value (64 bytes) for the E820
40 * buffer as a guard against insufficiently creative idiots in the
43 #define E820MAXSIZE 64
45 /****************************************************************************
47 * Allowed memory windows
49 * There are two ways to view this list. The first is as a list of
50 * (non-overlapping) allowed memory regions, sorted by increasing
51 * address. The second is as a list of (non-overlapping) hidden
52 * memory regions, again sorted by increasing address. The second
53 * view is offset by half an entry from the first: think about this
54 * for a moment and it should make sense.
56 * xxx_memory_window is used to indicate an "allowed region"
57 * structure, hidden_xxx_memory is used to indicate a "hidden region"
58 * structure. Each structure is 16 bytes in length.
60 ****************************************************************************
62 .section ".data16", "aw", @progbits
65 .globl hidemem_umalloc
66 .globl hidemem_textdata
68 base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */
70 hidemem_base: .long 0x000a0000, 0x00000000 /* Changes at runtime */
71 ext_memory_window: .long 0x000a0000, 0x00000000 /* 640kB mark */
73 hidemem_umalloc: .long 0xffffffff, 0xffffffff /* Changes at runtime */
74 .long 0xffffffff, 0xffffffff /* Changes at runtime */
76 hidemem_textdata: .long 0xffffffff, 0xffffffff /* Changes at runtime */
77 .long 0xffffffff, 0xffffffff /* Changes at runtime */
79 .long 0xffffffff, 0xffffffff /* End of memory */
82 /****************************************************************************
83 * Truncate region to memory window
86 * %edx:%eax Start of region
87 * %ecx:%ebx Length of region
90 * %edx:%eax Start of windowed region
91 * %ecx:%ebx Length of windowed region
92 ****************************************************************************
94 .section ".text16", "ax", @progbits
96 /* Convert (start,len) to (start, end) */
99 /* Truncate to window start */
106 2: /* Truncate to window end */
113 2: /* Convert (start, end) back to (start, len) */
116 /* If length is <0, set length to 0 */
121 .size window_region, . - window_region
123 /****************************************************************************
124 * Patch "memory above 1MB" figure
127 * %ax Memory above 1MB, in 1kB blocks
129 * %ax Modified memory above 1M in 1kB blocks
130 ****************************************************************************
132 .section ".text16", "ax", @progbits
135 /* Convert to (start,len) format and call truncate */
141 movw $ext_memory_window, %si
143 /* Convert back to "memory above 1MB" format and return via %ax */
151 .size patch_1m, . - patch_1m
153 /****************************************************************************
154 * Patch "memory above 16MB" figure
157 * %bx Memory above 16MB, in 64kB blocks
159 * %bx Modified memory above 16M in 64kB blocks
160 ****************************************************************************
162 .section ".text16", "ax", @progbits
165 /* Convert to (start,len) format and call truncate */
169 movl $0x1000000, %eax
170 movw $ext_memory_window, %si
172 /* Convert back to "memory above 16MB" format and return via %bx */
180 .size patch_16m, . - patch_16m
182 /****************************************************************************
183 * Patch "memory between 1MB and 16MB" and "memory above 16MB" figures
186 * %ax Memory between 1MB and 16MB, in 1kB blocks
187 * %bx Memory above 16MB, in 64kB blocks
189 * %ax Modified memory between 1MB and 16MB, in 1kB blocks
190 * %bx Modified memory above 16MB, in 64kB blocks
191 ****************************************************************************
193 .section ".text16", "ax", @progbits
197 /* If 1M region is no longer full-length, kill off the 16M region */
198 cmpw $( 15 * 1024 ), %ax
202 .size patch_1m_16m, . - patch_1m_16m
204 /****************************************************************************
205 * Get underlying e820 memory region to underlying_e820 buffer
212 * Wraps the underlying INT 15,e820 call so that the continuation
213 * value (%ebx) is a 16-bit simple sequence counter (with the high 16
214 * bits ignored), and termination is always via CF=1 rather than
217 ****************************************************************************
219 .section ".text16", "ax", @progbits
222 /* If the requested region is in the cache, return it */
223 cmpw %bx, underlying_e820_index
227 movw $underlying_e820_cache, %si
228 cmpl underlying_e820_cache_size, %ecx
230 movl underlying_e820_cache_size, %ecx
240 /* If the requested region is earlier than the cached region,
241 * invalidate the cache.
243 cmpw %bx, underlying_e820_index
245 movw $0xffff, underlying_e820_index
247 /* If the cache is invalid, reset the underlying %ebx */
248 cmpw $0xffff, underlying_e820_index
250 andl $0, underlying_e820_ebx
252 /* If the cache is valid but the continuation value is zero,
253 * this means that the previous underlying call returned with
254 * %ebx=0. Return with CF=1 in this case.
256 cmpw $0xffff, underlying_e820_index
258 cmpl $0, underlying_e820_ebx
263 /* Get the next region into the cache */
268 pushl %esi /* Some implementations corrupt %esi, so we */
269 pushl %edi /* preserve %esi, %edi and %ebp to be paranoid */
274 movw $underlying_e820_cache, %di
275 cmpl $E820MAXSIZE, %ecx
277 movl $E820MAXSIZE, %ecx
278 1: movl underlying_e820_ebx, %ebx
281 lcall *%cs:int15_vector
286 /* Check for error return from underlying e820 call */
287 jc 2f /* CF set: error */
289 je 3f /* 'SMAP' missing: error */
290 2: /* An error occurred: return values returned by underlying e820 call */
291 stc /* Force CF set if SMAP was missing */
292 addr32 leal 16(%esp), %esp /* avoid changing other flags */
294 3: /* No error occurred */
295 movl %ebx, underlying_e820_ebx
296 movl %ecx, underlying_e820_cache_size
301 /* Mark cache as containing this result */
302 incw underlying_e820_index
304 /* Loop until found */
305 jmp get_underlying_e820
306 .size get_underlying_e820, . - get_underlying_e820
308 .section ".data16", "aw", @progbits
309 underlying_e820_index:
310 .word 0xffff /* Initialise to an invalid value */
311 .size underlying_e820_index, . - underlying_e820_index
313 .section ".bss16", "aw", @nobits
316 .size underlying_e820_ebx, . - underlying_e820_ebx
318 .section ".bss16", "aw", @nobits
319 underlying_e820_cache:
321 .size underlying_e820_cache, . - underlying_e820_cache
323 .section ".bss16", "aw", @nobits
324 underlying_e820_cache_size:
326 .size underlying_e820_cache_size, . - underlying_e820_cache_size
328 /****************************************************************************
329 * Get windowed e820 region, without empty region stripping
336 * Wraps the underlying INT 15,e820 call so that each underlying
337 * region is returned N times, windowed to fit within N visible-memory
338 * windows. Termination is always via CF=1.
340 ****************************************************************************
342 .section ".text16", "ax", @progbits
345 /* Preserve registers */
349 /* Split %ebx into %si:%bx, store original %bx in %bp */
354 /* %si == 0 => start of memory_windows list */
357 movw $memory_windows, %si
359 /* Get (cached) underlying e820 region to buffer */
360 call get_underlying_e820
361 jc 99f /* Abort on error */
363 /* Preserve registers */
365 /* start => %edx:%eax, len => %ecx:%ebx */
366 movl %es:0(%di), %eax
367 movl %es:4(%di), %edx
368 movl %es:8(%di), %ebx
369 movl %es:12(%di), %ecx
370 /* Truncate region to current window */
372 1: /* Store modified values in e820 map entry */
373 movl %eax, %es:0(%di)
374 movl %edx, %es:4(%di)
375 movl %ebx, %es:8(%di)
376 movl %ecx, %es:12(%di)
377 /* Restore registers */
380 /* Derive continuation value for next call */
382 cmpw $memory_windows_end, %si
384 /* End of memory windows: reset %si and allow %bx to continue */
387 1: /* More memory windows to go: restore original %bx */
389 2: /* Construct %ebx from %si:%bx */
396 99: /* Restore registers and return */
400 .size get_windowed_e820, . - get_windowed_e820
402 /****************************************************************************
403 * Get windowed e820 region, with empty region stripping
410 * Wraps the underlying INT 15,e820 call so that each underlying
411 * region is returned up to N times, windowed to fit within N
412 * visible-memory windows. Empty windows are never returned.
413 * Termination is always via CF=1.
415 ****************************************************************************
417 .section ".text16", "ax", @progbits
420 /* Record entry parameters */
425 /* Get next windowed region */
426 call get_windowed_e820
427 jc 99f /* abort on error */
429 /* If region is non-empty, finish here */
435 /* Region was empty: restore entry parameters and go to next region */
439 jmp get_nonempty_e820
443 99: /* Return values from underlying call */
444 addr32 leal 12(%esp), %esp /* avoid changing flags */
446 .size get_nonempty_e820, . - get_nonempty_e820
448 /****************************************************************************
449 * Get mangled e820 region, with empty region stripping
456 * Wraps the underlying INT 15,e820 call so that underlying regions
457 * are windowed to the allowed memory regions. Empty regions are
458 * stripped from the map. Termination is always via %ebx=0.
460 ****************************************************************************
462 .section ".text16", "ax", @progbits
465 /* Get a nonempty region */
466 call get_nonempty_e820
467 jc 99f /* Abort on error */
469 /* Peek ahead to see if there are any further nonempty regions */
479 call get_nonempty_e820
483 jnc 99f /* There are further nonempty regions */
485 /* No futher nonempty regions: zero %ebx and clear CF */
490 .size get_mangled_e820, . - get_mangled_e820
492 /****************************************************************************
493 * INT 15,e820 handler
494 ****************************************************************************
496 .section ".text16", "ax", @progbits
501 call get_mangled_e820
504 .size int15_e820, . - int15_e820
506 /****************************************************************************
507 * INT 15,e801 handler
508 ****************************************************************************
510 .section ".text16", "ax", @progbits
512 /* Call previous handler */
514 lcall *%cs:int15_vector
527 /* Restore flags returned by previous handler and return */
530 .size int15_e801, . - int15_e801
532 /****************************************************************************
534 ****************************************************************************
536 .section ".text16", "ax", @progbits
538 /* Call previous handler */
540 lcall *%cs:int15_vector
548 /* Restore flags returned by previous handler and return */
551 .size int15_88, . - int15_88
553 /****************************************************************************
555 ****************************************************************************
557 .section ".text16", "ax", @progbits
560 /* See if we want to intercept this call */
577 ljmp *%cs:int15_vector
578 .size int15, . - int15
580 .section ".text16.data", "aw", @progbits
584 .size int15_vector, . - int15_vector