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
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
30 #define SMAP 0x534d4150
32 /* Most documentation refers to the E820 buffer as being 20 bytes, and
33 * the API makes it perfectly legitimate to pass only a 20-byte buffer
34 * and expect to get valid data. However, some morons at ACPI decided
35 * to extend the data structure by adding an extra "extended
36 * attributes" field and by including critical information within this
37 * field, such as whether or not the region is enabled. A caller who
38 * passes in only a 20-byte buffer therefore risks getting very, very
39 * misleading information.
41 * I have personally witnessed an HP BIOS that returns a value of
42 * 0x0009 in the extended attributes field. If we don't pass this
43 * value through to the caller, 32-bit WinPE will die, usually with a
44 * PAGE_FAULT_IN_NONPAGED_AREA blue screen of death.
46 * Allow a ridiculously large maximum value (64 bytes) for the E820
47 * buffer as a guard against insufficiently creative idiots in the
50 #define E820MAXSIZE 64
52 /****************************************************************************
54 * Allowed memory windows
56 * There are two ways to view this list. The first is as a list of
57 * (non-overlapping) allowed memory regions, sorted by increasing
58 * address. The second is as a list of (non-overlapping) hidden
59 * memory regions, again sorted by increasing address. The second
60 * view is offset by half an entry from the first: think about this
61 * for a moment and it should make sense.
63 * xxx_memory_window is used to indicate an "allowed region"
64 * structure, hidden_xxx_memory is used to indicate a "hidden region"
65 * structure. Each structure is 16 bytes in length.
67 ****************************************************************************
69 .section ".data16", "aw", @progbits
72 .globl hidemem_umalloc
73 .globl hidemem_textdata
75 base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */
77 hidemem_base: .long 0x000a0000, 0x00000000 /* Changes at runtime */
78 ext_memory_window: .long 0x000a0000, 0x00000000 /* 640kB mark */
80 hidemem_umalloc: .long 0xffffffff, 0xffffffff /* Changes at runtime */
81 .long 0xffffffff, 0xffffffff /* Changes at runtime */
83 hidemem_textdata: .long 0xffffffff, 0xffffffff /* Changes at runtime */
84 .long 0xffffffff, 0xffffffff /* Changes at runtime */
86 .long 0xffffffff, 0xffffffff /* End of memory */
89 /****************************************************************************
90 * Truncate region to memory window
93 * %edx:%eax Start of region
94 * %ecx:%ebx Length of region
97 * %edx:%eax Start of windowed region
98 * %ecx:%ebx Length of windowed region
99 ****************************************************************************
101 .section ".text16", "ax", @progbits
103 /* Convert (start,len) to (start, end) */
106 /* Truncate to window start */
113 2: /* Truncate to window end */
120 2: /* Convert (start, end) back to (start, len) */
123 /* If length is <0, set length to 0 */
128 .size window_region, . - window_region
130 /****************************************************************************
131 * Patch "memory above 1MB" figure
134 * %ax Memory above 1MB, in 1kB blocks
136 * %ax Modified memory above 1M in 1kB blocks
137 ****************************************************************************
139 .section ".text16", "ax", @progbits
142 /* Convert to (start,len) format and call truncate */
148 movw $ext_memory_window, %si
150 /* Convert back to "memory above 1MB" format and return via %ax */
158 .size patch_1m, . - patch_1m
160 /****************************************************************************
161 * Patch "memory above 16MB" figure
164 * %bx Memory above 16MB, in 64kB blocks
166 * %bx Modified memory above 16M in 64kB blocks
167 ****************************************************************************
169 .section ".text16", "ax", @progbits
172 /* Convert to (start,len) format and call truncate */
176 movl $0x1000000, %eax
177 movw $ext_memory_window, %si
179 /* Convert back to "memory above 16MB" format and return via %bx */
187 .size patch_16m, . - patch_16m
189 /****************************************************************************
190 * Patch "memory between 1MB and 16MB" and "memory above 16MB" figures
193 * %ax Memory between 1MB and 16MB, in 1kB blocks
194 * %bx Memory above 16MB, in 64kB blocks
196 * %ax Modified memory between 1MB and 16MB, in 1kB blocks
197 * %bx Modified memory above 16MB, in 64kB blocks
198 ****************************************************************************
200 .section ".text16", "ax", @progbits
204 /* If 1M region is no longer full-length, kill off the 16M region */
205 cmpw $( 15 * 1024 ), %ax
209 .size patch_1m_16m, . - patch_1m_16m
211 /****************************************************************************
212 * Get underlying e820 memory region to underlying_e820 buffer
219 * Wraps the underlying INT 15,e820 call so that the continuation
220 * value (%ebx) is a 16-bit simple sequence counter (with the high 16
221 * bits ignored), and termination is always via CF=1 rather than
224 ****************************************************************************
226 .section ".text16", "ax", @progbits
229 /* If the requested region is in the cache, return it */
230 cmpw %bx, underlying_e820_index
234 movw $underlying_e820_cache, %si
235 cmpl underlying_e820_cache_size, %ecx
237 movl underlying_e820_cache_size, %ecx
248 /* If the requested region is earlier than the cached region,
249 * invalidate the cache.
251 cmpw %bx, underlying_e820_index
253 movw $0xffff, underlying_e820_index
255 /* If the cache is invalid, reset the underlying %ebx */
256 cmpw $0xffff, underlying_e820_index
258 andl $0, underlying_e820_ebx
260 /* If the cache is valid but the continuation value is zero,
261 * this means that the previous underlying call returned with
262 * %ebx=0. Return with CF=1 in this case.
264 cmpw $0xffff, underlying_e820_index
266 cmpl $0, underlying_e820_ebx
271 /* Get the next region into the cache */
276 pushl %esi /* Some implementations corrupt %esi, so we */
277 pushl %edi /* preserve %esi, %edi and %ebp to be paranoid */
282 movw $underlying_e820_cache, %di
283 cmpl $E820MAXSIZE, %ecx
285 movl $E820MAXSIZE, %ecx
286 1: movl underlying_e820_ebx, %ebx
289 lcall *%cs:int15_vector
294 /* Check for error return from underlying e820 call */
295 jc 2f /* CF set: error */
297 je 3f /* 'SMAP' missing: error */
298 2: /* An error occurred: return values returned by underlying e820 call */
299 stc /* Force CF set if SMAP was missing */
300 addr32 leal 16(%esp), %esp /* avoid changing other flags */
302 3: /* No error occurred */
303 movl %ebx, underlying_e820_ebx
304 movl %ecx, underlying_e820_cache_size
309 /* Mark cache as containing this result */
310 incw underlying_e820_index
312 /* Loop until found */
313 jmp get_underlying_e820
314 .size get_underlying_e820, . - get_underlying_e820
316 .section ".data16", "aw", @progbits
317 underlying_e820_index:
318 .word 0xffff /* Initialise to an invalid value */
319 .size underlying_e820_index, . - underlying_e820_index
321 .section ".bss16", "aw", @nobits
324 .size underlying_e820_ebx, . - underlying_e820_ebx
326 .section ".bss16", "aw", @nobits
327 underlying_e820_cache:
329 .size underlying_e820_cache, . - underlying_e820_cache
331 .section ".bss16", "aw", @nobits
332 underlying_e820_cache_size:
334 .size underlying_e820_cache_size, . - underlying_e820_cache_size
336 /****************************************************************************
337 * Get windowed e820 region, without empty region stripping
344 * Wraps the underlying INT 15,e820 call so that each underlying
345 * region is returned N times, windowed to fit within N visible-memory
346 * windows. Termination is always via CF=1.
348 ****************************************************************************
350 .section ".text16", "ax", @progbits
353 /* Preserve registers */
357 /* Split %ebx into %si:%bx, store original %bx in %bp */
362 /* %si == 0 => start of memory_windows list */
365 movw $memory_windows, %si
367 /* Get (cached) underlying e820 region to buffer */
368 call get_underlying_e820
369 jc 99f /* Abort on error */
371 /* Preserve registers */
373 /* start => %edx:%eax, len => %ecx:%ebx */
374 movl %es:0(%di), %eax
375 movl %es:4(%di), %edx
376 movl %es:8(%di), %ebx
377 movl %es:12(%di), %ecx
378 /* Truncate region to current window */
380 1: /* Store modified values in e820 map entry */
381 movl %eax, %es:0(%di)
382 movl %edx, %es:4(%di)
383 movl %ebx, %es:8(%di)
384 movl %ecx, %es:12(%di)
385 /* Restore registers */
388 /* Derive continuation value for next call */
390 cmpw $memory_windows_end, %si
392 /* End of memory windows: reset %si and allow %bx to continue */
395 1: /* More memory windows to go: restore original %bx */
397 2: /* Construct %ebx from %si:%bx */
404 99: /* Restore registers and return */
408 .size get_windowed_e820, . - get_windowed_e820
410 /****************************************************************************
411 * Get windowed e820 region, with empty region stripping
418 * Wraps the underlying INT 15,e820 call so that each underlying
419 * region is returned up to N times, windowed to fit within N
420 * visible-memory windows. Empty windows are never returned.
421 * Termination is always via CF=1.
423 ****************************************************************************
425 .section ".text16", "ax", @progbits
428 /* Record entry parameters */
433 /* Get next windowed region */
434 call get_windowed_e820
435 jc 99f /* abort on error */
437 /* If region is non-empty, finish here */
443 /* Region was empty: restore entry parameters and go to next region */
447 jmp get_nonempty_e820
451 99: /* Return values from underlying call */
452 addr32 leal 12(%esp), %esp /* avoid changing flags */
454 .size get_nonempty_e820, . - get_nonempty_e820
456 /****************************************************************************
457 * Get mangled e820 region, with empty region stripping
464 * Wraps the underlying INT 15,e820 call so that underlying regions
465 * are windowed to the allowed memory regions. Empty regions are
466 * stripped from the map. Termination is always via %ebx=0.
468 ****************************************************************************
470 .section ".text16", "ax", @progbits
473 /* Get a nonempty region */
474 call get_nonempty_e820
475 jc 99f /* Abort on error */
477 /* Peek ahead to see if there are any further nonempty regions */
487 call get_nonempty_e820
491 jnc 99f /* There are further nonempty regions */
493 /* No futher nonempty regions: zero %ebx and clear CF */
498 .size get_mangled_e820, . - get_mangled_e820
500 /****************************************************************************
501 * INT 15,e820 handler
502 ****************************************************************************
504 .section ".text16", "ax", @progbits
509 call get_mangled_e820
513 .size int15_e820, . - int15_e820
515 /****************************************************************************
516 * INT 15,e801 handler
517 ****************************************************************************
519 .section ".text16", "ax", @progbits
521 /* Call previous handler */
523 lcall *%cs:int15_vector
537 .size int15_e801, . - int15_e801
539 /****************************************************************************
541 ****************************************************************************
543 .section ".text16", "ax", @progbits
545 /* Call previous handler */
547 lcall *%cs:int15_vector
556 .size int15_88, . - int15_88
558 /****************************************************************************
560 ****************************************************************************
562 .section ".text16", "ax", @progbits
565 /* See if we want to intercept this call */
582 ljmp *%cs:int15_vector
583 .size int15, . - int15
585 .section ".text16.data", "aw", @progbits
589 .size int15_vector, . - int15_vector