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., 51 Franklin Street, Fifth Floor, Boston, MA
20 FILE_LICENCE ( GPL2_OR_LATER )
26 #define SMAP 0x534d4150
28 /* Most documentation refers to the E820 buffer as being 20 bytes, and
29 * the API makes it perfectly legitimate to pass only a 20-byte buffer
30 * and expect to get valid data. However, some morons at ACPI decided
31 * to extend the data structure by adding an extra "extended
32 * attributes" field and by including critical information within this
33 * field, such as whether or not the region is enabled. A caller who
34 * passes in only a 20-byte buffer therefore risks getting very, very
35 * misleading information.
37 * I have personally witnessed an HP BIOS that returns a value of
38 * 0x0009 in the extended attributes field. If we don't pass this
39 * value through to the caller, 32-bit WinPE will die, usually with a
40 * PAGE_FAULT_IN_NONPAGED_AREA blue screen of death.
42 * Allow a ridiculously large maximum value (64 bytes) for the E820
43 * buffer as a guard against insufficiently creative idiots in the
46 #define E820MAXSIZE 64
48 /****************************************************************************
50 * Allowed memory windows
52 * There are two ways to view this list. The first is as a list of
53 * (non-overlapping) allowed memory regions, sorted by increasing
54 * address. The second is as a list of (non-overlapping) hidden
55 * memory regions, again sorted by increasing address. The second
56 * view is offset by half an entry from the first: think about this
57 * for a moment and it should make sense.
59 * xxx_memory_window is used to indicate an "allowed region"
60 * structure, hidden_xxx_memory is used to indicate a "hidden region"
61 * structure. Each structure is 16 bytes in length.
63 ****************************************************************************
65 .section ".data16", "aw", @progbits
68 .globl hidemem_umalloc
69 .globl hidemem_textdata
71 base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */
73 hidemem_base: .long 0x000a0000, 0x00000000 /* Changes at runtime */
74 ext_memory_window: .long 0x000a0000, 0x00000000 /* 640kB mark */
76 hidemem_umalloc: .long 0xffffffff, 0xffffffff /* Changes at runtime */
77 .long 0xffffffff, 0xffffffff /* Changes at runtime */
79 hidemem_textdata: .long 0xffffffff, 0xffffffff /* Changes at runtime */
80 .long 0xffffffff, 0xffffffff /* Changes at runtime */
82 .long 0xffffffff, 0xffffffff /* End of memory */
85 /****************************************************************************
86 * Truncate region to memory window
89 * %edx:%eax Start of region
90 * %ecx:%ebx Length of region
93 * %edx:%eax Start of windowed region
94 * %ecx:%ebx Length of windowed region
95 ****************************************************************************
97 .section ".text16", "ax", @progbits
99 /* Convert (start,len) to (start, end) */
102 /* Truncate to window start */
109 2: /* Truncate to window end */
116 2: /* Convert (start, end) back to (start, len) */
119 /* If length is <0, set length to 0 */
124 .size window_region, . - window_region
126 /****************************************************************************
127 * Patch "memory above 1MB" figure
130 * %ax Memory above 1MB, in 1kB blocks
132 * %ax Modified memory above 1M in 1kB blocks
133 ****************************************************************************
135 .section ".text16", "ax", @progbits
138 /* Convert to (start,len) format and call truncate */
144 movw $ext_memory_window, %si
146 /* Convert back to "memory above 1MB" format and return via %ax */
154 .size patch_1m, . - patch_1m
156 /****************************************************************************
157 * Patch "memory above 16MB" figure
160 * %bx Memory above 16MB, in 64kB blocks
162 * %bx Modified memory above 16M in 64kB blocks
163 ****************************************************************************
165 .section ".text16", "ax", @progbits
168 /* Convert to (start,len) format and call truncate */
172 movl $0x1000000, %eax
173 movw $ext_memory_window, %si
175 /* Convert back to "memory above 16MB" format and return via %bx */
183 .size patch_16m, . - patch_16m
185 /****************************************************************************
186 * Patch "memory between 1MB and 16MB" and "memory above 16MB" figures
189 * %ax Memory between 1MB and 16MB, in 1kB blocks
190 * %bx Memory above 16MB, in 64kB blocks
192 * %ax Modified memory between 1MB and 16MB, in 1kB blocks
193 * %bx Modified memory above 16MB, in 64kB blocks
194 ****************************************************************************
196 .section ".text16", "ax", @progbits
200 /* If 1M region is no longer full-length, kill off the 16M region */
201 cmpw $( 15 * 1024 ), %ax
205 .size patch_1m_16m, . - patch_1m_16m
207 /****************************************************************************
208 * Get underlying e820 memory region to underlying_e820 buffer
215 * Wraps the underlying INT 15,e820 call so that the continuation
216 * value (%ebx) is a 16-bit simple sequence counter (with the high 16
217 * bits ignored), and termination is always via CF=1 rather than
220 ****************************************************************************
222 .section ".text16", "ax", @progbits
225 /* If the requested region is in the cache, return it */
226 cmpw %bx, underlying_e820_index
230 movw $underlying_e820_cache, %si
231 cmpl underlying_e820_cache_size, %ecx
233 movl underlying_e820_cache_size, %ecx
244 /* If the requested region is earlier than the cached region,
245 * invalidate the cache.
247 cmpw %bx, underlying_e820_index
249 movw $0xffff, underlying_e820_index
251 /* If the cache is invalid, reset the underlying %ebx */
252 cmpw $0xffff, underlying_e820_index
254 andl $0, underlying_e820_ebx
256 /* If the cache is valid but the continuation value is zero,
257 * this means that the previous underlying call returned with
258 * %ebx=0. Return with CF=1 in this case.
260 cmpw $0xffff, underlying_e820_index
262 cmpl $0, underlying_e820_ebx
267 /* Get the next region into the cache */
272 pushl %esi /* Some implementations corrupt %esi, so we */
273 pushl %edi /* preserve %esi, %edi and %ebp to be paranoid */
278 movw $underlying_e820_cache, %di
279 cmpl $E820MAXSIZE, %ecx
281 movl $E820MAXSIZE, %ecx
282 1: movl underlying_e820_ebx, %ebx
285 lcall *%cs:int15_vector
290 /* Check for error return from underlying e820 call */
291 jc 2f /* CF set: error */
293 je 3f /* 'SMAP' missing: error */
294 2: /* An error occurred: return values returned by underlying e820 call */
295 stc /* Force CF set if SMAP was missing */
296 addr32 leal 16(%esp), %esp /* avoid changing other flags */
298 3: /* No error occurred */
299 movl %ebx, underlying_e820_ebx
300 movl %ecx, underlying_e820_cache_size
305 /* Mark cache as containing this result */
306 incw underlying_e820_index
308 /* Loop until found */
309 jmp get_underlying_e820
310 .size get_underlying_e820, . - get_underlying_e820
312 .section ".data16", "aw", @progbits
313 underlying_e820_index:
314 .word 0xffff /* Initialise to an invalid value */
315 .size underlying_e820_index, . - underlying_e820_index
317 .section ".bss16", "aw", @nobits
320 .size underlying_e820_ebx, . - underlying_e820_ebx
322 .section ".bss16", "aw", @nobits
323 underlying_e820_cache:
325 .size underlying_e820_cache, . - underlying_e820_cache
327 .section ".bss16", "aw", @nobits
328 underlying_e820_cache_size:
330 .size underlying_e820_cache_size, . - underlying_e820_cache_size
332 /****************************************************************************
333 * Get windowed e820 region, without empty region stripping
340 * Wraps the underlying INT 15,e820 call so that each underlying
341 * region is returned N times, windowed to fit within N visible-memory
342 * windows. Termination is always via CF=1.
344 ****************************************************************************
346 .section ".text16", "ax", @progbits
349 /* Preserve registers */
353 /* Split %ebx into %si:%bx, store original %bx in %bp */
358 /* %si == 0 => start of memory_windows list */
361 movw $memory_windows, %si
363 /* Get (cached) underlying e820 region to buffer */
364 call get_underlying_e820
365 jc 99f /* Abort on error */
367 /* Preserve registers */
369 /* start => %edx:%eax, len => %ecx:%ebx */
370 movl %es:0(%di), %eax
371 movl %es:4(%di), %edx
372 movl %es:8(%di), %ebx
373 movl %es:12(%di), %ecx
374 /* Truncate region to current window */
376 1: /* Store modified values in e820 map entry */
377 movl %eax, %es:0(%di)
378 movl %edx, %es:4(%di)
379 movl %ebx, %es:8(%di)
380 movl %ecx, %es:12(%di)
381 /* Restore registers */
384 /* Derive continuation value for next call */
386 cmpw $memory_windows_end, %si
388 /* End of memory windows: reset %si and allow %bx to continue */
391 1: /* More memory windows to go: restore original %bx */
393 2: /* Construct %ebx from %si:%bx */
400 99: /* Restore registers and return */
404 .size get_windowed_e820, . - get_windowed_e820
406 /****************************************************************************
407 * Get windowed e820 region, with empty region stripping
414 * Wraps the underlying INT 15,e820 call so that each underlying
415 * region is returned up to N times, windowed to fit within N
416 * visible-memory windows. Empty windows are never returned.
417 * Termination is always via CF=1.
419 ****************************************************************************
421 .section ".text16", "ax", @progbits
424 /* Record entry parameters */
429 /* Get next windowed region */
430 call get_windowed_e820
431 jc 99f /* abort on error */
433 /* If region is non-empty, finish here */
439 /* Region was empty: restore entry parameters and go to next region */
443 jmp get_nonempty_e820
447 99: /* Return values from underlying call */
448 addr32 leal 12(%esp), %esp /* avoid changing flags */
450 .size get_nonempty_e820, . - get_nonempty_e820
452 /****************************************************************************
453 * Get mangled e820 region, with empty region stripping
460 * Wraps the underlying INT 15,e820 call so that underlying regions
461 * are windowed to the allowed memory regions. Empty regions are
462 * stripped from the map. Termination is always via %ebx=0.
464 ****************************************************************************
466 .section ".text16", "ax", @progbits
469 /* Get a nonempty region */
470 call get_nonempty_e820
471 jc 99f /* Abort on error */
473 /* Peek ahead to see if there are any further nonempty regions */
483 call get_nonempty_e820
487 jnc 99f /* There are further nonempty regions */
489 /* No futher nonempty regions: zero %ebx and clear CF */
494 .size get_mangled_e820, . - get_mangled_e820
496 /****************************************************************************
497 * INT 15,e820 handler
498 ****************************************************************************
500 .section ".text16", "ax", @progbits
505 call get_mangled_e820
509 .size int15_e820, . - int15_e820
511 /****************************************************************************
512 * INT 15,e801 handler
513 ****************************************************************************
515 .section ".text16", "ax", @progbits
517 /* Call previous handler */
519 lcall *%cs:int15_vector
533 .size int15_e801, . - int15_e801
535 /****************************************************************************
537 ****************************************************************************
539 .section ".text16", "ax", @progbits
541 /* Call previous handler */
543 lcall *%cs:int15_vector
552 .size int15_88, . - int15_88
554 /****************************************************************************
556 ****************************************************************************
558 .section ".text16", "ax", @progbits
561 /* See if we want to intercept this call */
578 ljmp *%cs:int15_vector
579 .size int15, . - int15
581 .section ".text16.data", "aw", @progbits
585 .size int15_vector, . - int15_vector