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.
21 .section ".text16", "ax", @progbits
22 .section ".data16", "aw", @progbits
23 .section ".text16.data", "aw", @progbits
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 ****************************************************************************
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 ****************************************************************************
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 ****************************************************************************
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 ****************************************************************************
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 ****************************************************************************
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 ****************************************************************************
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
243 /* If the requested region is earlier than the cached region,
244 * invalidate the cache.
246 cmpw %bx, underlying_e820_index
248 movw $0xffff, underlying_e820_index
250 /* If the cache is invalid, reset the underlying %ebx */
251 cmpw $0xffff, underlying_e820_index
253 andl $0, underlying_e820_ebx
255 /* If the cache is valid but the continuation value is zero,
256 * this means that the previous underlying call returned with
257 * %ebx=0. Return with CF=1 in this case.
259 cmpw $0xffff, underlying_e820_index
261 cmpl $0, underlying_e820_ebx
266 /* Get the next region into the cache */
271 pushl %esi /* Some implementations corrupt %esi, so we */
272 pushl %edi /* preserve %esi, %edi and %ebp to be paranoid */
277 movw $underlying_e820_cache, %di
278 cmpl $E820MAXSIZE, %ecx
280 movl $E820MAXSIZE, %ecx
281 1: movl underlying_e820_ebx, %ebx
284 lcall *%cs:int15_vector
289 /* Check for error return from underlying e820 call */
290 jc 2f /* CF set: error */
292 je 3f /* 'SMAP' missing: error */
293 2: /* An error occurred: return values returned by underlying e820 call */
294 stc /* Force CF set if SMAP was missing */
295 addr32 leal 16(%esp), %esp /* avoid changing other flags */
297 3: /* No error occurred */
298 movl %ebx, underlying_e820_ebx
299 movl %ecx, underlying_e820_cache_size
304 /* Mark cache as containing this result */
305 incw underlying_e820_index
307 /* Loop until found */
308 jmp get_underlying_e820
309 .size get_underlying_e820, . - get_underlying_e820
312 underlying_e820_index:
313 .word 0xffff /* Initialise to an invalid value */
314 .size underlying_e820_index, . - underlying_e820_index
319 .size underlying_e820_ebx, . - underlying_e820_ebx
322 underlying_e820_cache:
324 .size underlying_e820_cache, . - underlying_e820_cache
327 underlying_e820_cache_size:
329 .size underlying_e820_cache_size, . - underlying_e820_cache_size
331 /****************************************************************************
332 * Get windowed e820 region, without empty region stripping
339 * Wraps the underlying INT 15,e820 call so that each underlying
340 * region is returned N times, windowed to fit within N visible-memory
341 * windows. Termination is always via CF=1.
343 ****************************************************************************
348 /* Preserve registers */
352 /* Split %ebx into %si:%bx, store original %bx in %bp */
357 /* %si == 0 => start of memory_windows list */
360 movw $memory_windows, %si
362 /* Get (cached) underlying e820 region to buffer */
363 call get_underlying_e820
364 jc 99f /* Abort on error */
366 /* Preserve registers */
368 /* start => %edx:%eax, len => %ecx:%ebx */
369 movl %es:0(%di), %eax
370 movl %es:4(%di), %edx
371 movl %es:8(%di), %ebx
372 movl %es:12(%di), %ecx
373 /* Truncate region to current window */
375 1: /* Store modified values in e820 map entry */
376 movl %eax, %es:0(%di)
377 movl %edx, %es:4(%di)
378 movl %ebx, %es:8(%di)
379 movl %ecx, %es:12(%di)
380 /* Restore registers */
383 /* Derive continuation value for next call */
385 cmpw $memory_windows_end, %si
387 /* End of memory windows: reset %si and allow %bx to continue */
390 1: /* More memory windows to go: restore original %bx */
392 2: /* Construct %ebx from %si:%bx */
399 99: /* Restore registers and return */
403 .size get_windowed_e820, . - get_windowed_e820
405 /****************************************************************************
406 * Get windowed e820 region, with empty region stripping
413 * Wraps the underlying INT 15,e820 call so that each underlying
414 * region is returned up to N times, windowed to fit within N
415 * visible-memory windows. Empty windows are never returned.
416 * Termination is always via CF=1.
418 ****************************************************************************
423 /* Record entry parameters */
428 /* Get next windowed region */
429 call get_windowed_e820
430 jc 99f /* abort on error */
432 /* If region is non-empty, finish here */
438 /* Region was empty: restore entry parameters and go to next region */
442 jmp get_nonempty_e820
446 99: /* Return values from underlying call */
447 addr32 leal 12(%esp), %esp /* avoid changing flags */
449 .size get_nonempty_e820, . - get_nonempty_e820
451 /****************************************************************************
452 * Get mangled e820 region, with empty region stripping
459 * Wraps the underlying INT 15,e820 call so that underlying regions
460 * are windowed to the allowed memory regions. Empty regions are
461 * stripped from the map. Termination is always via %ebx=0.
463 ****************************************************************************
468 /* Get a nonempty region */
469 call get_nonempty_e820
470 jc 99f /* Abort on error */
472 /* Peek ahead to see if there are any further nonempty regions */
482 call get_nonempty_e820
486 jnc 99f /* There are further nonempty regions */
488 /* No futher nonempty regions: zero %ebx and clear CF */
493 .size get_mangled_e820, . - get_mangled_e820
495 /****************************************************************************
496 * INT 15,e820 handler
497 ****************************************************************************
504 call get_mangled_e820
507 .size int15_e820, . - int15_e820
509 /****************************************************************************
510 * INT 15,e801 handler
511 ****************************************************************************
515 /* Call previous handler */
517 lcall *%cs:int15_vector
530 /* Restore flags returned by previous handler and return */
533 .size int15_e801, . - int15_e801
535 /****************************************************************************
537 ****************************************************************************
541 /* Call previous handler */
543 lcall *%cs:int15_vector
551 /* Restore flags returned by previous handler and return */
554 .size int15_88, . - int15_88
556 /****************************************************************************
558 ****************************************************************************
563 /* See if we want to intercept this call */
580 ljmp *%cs:int15_vector
581 .size int15, . - int15
583 .section ".text16.data"
587 .size int15_vector, . - int15_vector