1/* 2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. 3 * 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. 8 * 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. 13 * 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. 17 */ 18 19FILE_LICENCE ( GPL2_OR_LATER ) 20 21 .text 22 .arch i386 23 .code16 24 25#define SMAP 0x534d4150 26 27/* Most documentation refers to the E820 buffer as being 20 bytes, and 28 * the API makes it perfectly legitimate to pass only a 20-byte buffer 29 * and expect to get valid data. However, some morons at ACPI decided 30 * to extend the data structure by adding an extra "extended 31 * attributes" field and by including critical information within this 32 * field, such as whether or not the region is enabled. A caller who 33 * passes in only a 20-byte buffer therefore risks getting very, very 34 * misleading information. 35 * 36 * I have personally witnessed an HP BIOS that returns a value of 37 * 0x0009 in the extended attributes field. If we don't pass this 38 * value through to the caller, 32-bit WinPE will die, usually with a 39 * PAGE_FAULT_IN_NONPAGED_AREA blue screen of death. 40 * 41 * Allow a ridiculously large maximum value (64 bytes) for the E820 42 * buffer as a guard against insufficiently creative idiots in the 43 * future. 44 */ 45#define E820MAXSIZE 64 46 47/**************************************************************************** 48 * 49 * Allowed memory windows 50 * 51 * There are two ways to view this list. The first is as a list of 52 * (non-overlapping) allowed memory regions, sorted by increasing 53 * address. The second is as a list of (non-overlapping) hidden 54 * memory regions, again sorted by increasing address. The second 55 * view is offset by half an entry from the first: think about this 56 * for a moment and it should make sense. 57 * 58 * xxx_memory_window is used to indicate an "allowed region" 59 * structure, hidden_xxx_memory is used to indicate a "hidden region" 60 * structure. Each structure is 16 bytes in length. 61 * 62 **************************************************************************** 63 */ 64 .section ".data16", "aw", @progbits 65 .align 16 66 .globl hidemem_base 67 .globl hidemem_umalloc 68 .globl hidemem_textdata 69memory_windows: 70base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */ 71 72hidemem_base: .long 0x000a0000, 0x00000000 /* Changes at runtime */ 73ext_memory_window: .long 0x000a0000, 0x00000000 /* 640kB mark */ 74 75hidemem_umalloc: .long 0xffffffff, 0xffffffff /* Changes at runtime */ 76 .long 0xffffffff, 0xffffffff /* Changes at runtime */ 77 78hidemem_textdata: .long 0xffffffff, 0xffffffff /* Changes at runtime */ 79 .long 0xffffffff, 0xffffffff /* Changes at runtime */ 80 81 .long 0xffffffff, 0xffffffff /* End of memory */ 82memory_windows_end: 83 84/**************************************************************************** 85 * Truncate region to memory window 86 * 87 * Parameters: 88 * %edx:%eax Start of region 89 * %ecx:%ebx Length of region 90 * %si Memory window 91 * Returns: 92 * %edx:%eax Start of windowed region 93 * %ecx:%ebx Length of windowed region 94 **************************************************************************** 95 */ 96 .section ".text16", "ax", @progbits 97window_region: 98 /* Convert (start,len) to (start, end) */ 99 addl %eax, %ebx 100 adcl %edx, %ecx 101 /* Truncate to window start */ 102 cmpl 4(%si), %edx 103 jne 1f 104 cmpl 0(%si), %eax 1051: jae 2f 106 movl 4(%si), %edx 107 movl 0(%si), %eax 1082: /* Truncate to window end */ 109 cmpl 12(%si), %ecx 110 jne 1f 111 cmpl 8(%si), %ebx 1121: jbe 2f 113 movl 12(%si), %ecx 114 movl 8(%si), %ebx 1152: /* Convert (start, end) back to (start, len) */ 116 subl %eax, %ebx 117 sbbl %edx, %ecx 118 /* If length is <0, set length to 0 */ 119 jae 1f 120 xorl %ebx, %ebx 121 xorl %ecx, %ecx 122 ret 123 .size window_region, . - window_region 124 125/**************************************************************************** 126 * Patch "memory above 1MB" figure 127 * 128 * Parameters: 129 * %ax Memory above 1MB, in 1kB blocks 130 * Returns: 131 * %ax Modified memory above 1M in 1kB blocks 132 **************************************************************************** 133 */ 134 .section ".text16", "ax", @progbits 135patch_1m: 136 pushal 137 /* Convert to (start,len) format and call truncate */ 138 xorl %ecx, %ecx 139 movzwl %ax, %ebx 140 shll $10, %ebx 141 xorl %edx, %edx 142 movl $0x100000, %eax 143 movw $ext_memory_window, %si 144 call window_region 145 /* Convert back to "memory above 1MB" format and return via %ax */ 146 pushfw 147 shrl $10, %ebx 148 popfw 149 movw %sp, %bp 150 movw %bx, 28(%bp) 151 popal 152 ret 153 .size patch_1m, . - patch_1m 154 155/**************************************************************************** 156 * Patch "memory above 16MB" figure 157 * 158 * Parameters: 159 * %bx Memory above 16MB, in 64kB blocks 160 * Returns: 161 * %bx Modified memory above 16M in 64kB blocks 162 **************************************************************************** 163 */ 164 .section ".text16", "ax", @progbits 165patch_16m: 166 pushal 167 /* Convert to (start,len) format and call truncate */ 168 xorl %ecx, %ecx 169 shll $16, %ebx 170 xorl %edx, %edx 171 movl $0x1000000, %eax 172 movw $ext_memory_window, %si 173 call window_region 174 /* Convert back to "memory above 16MB" format and return via %bx */ 175 pushfw 176 shrl $16, %ebx 177 popfw 178 movw %sp, %bp 179 movw %bx, 16(%bp) 180 popal 181 ret 182 .size patch_16m, . - patch_16m 183 184/**************************************************************************** 185 * Patch "memory between 1MB and 16MB" and "memory above 16MB" figures 186 * 187 * Parameters: 188 * %ax Memory between 1MB and 16MB, in 1kB blocks 189 * %bx Memory above 16MB, in 64kB blocks 190 * Returns: 191 * %ax Modified memory between 1MB and 16MB, in 1kB blocks 192 * %bx Modified memory above 16MB, in 64kB blocks 193 **************************************************************************** 194 */ 195 .section ".text16", "ax", @progbits 196patch_1m_16m: 197 call patch_1m 198 call patch_16m 199 /* If 1M region is no longer full-length, kill off the 16M region */ 200 cmpw $( 15 * 1024 ), %ax 201 je 1f 202 xorw %bx, %bx 2031: ret 204 .size patch_1m_16m, . - patch_1m_16m 205 206/**************************************************************************** 207 * Get underlying e820 memory region to underlying_e820 buffer 208 * 209 * Parameters: 210 * As for INT 15,e820 211 * Returns: 212 * As for INT 15,e820 213 * 214 * Wraps the underlying INT 15,e820 call so that the continuation 215 * value (%ebx) is a 16-bit simple sequence counter (with the high 16 216 * bits ignored), and termination is always via CF=1 rather than 217 * %ebx=0. 218 * 219 **************************************************************************** 220 */ 221 .section ".text16", "ax", @progbits 222get_underlying_e820: 223 224 /* If the requested region is in the cache, return it */ 225 cmpw %bx, underlying_e820_index 226 jne 2f 227 pushw %di 228 pushw %si 229 movw $underlying_e820_cache, %si 230 cmpl underlying_e820_cache_size, %ecx 231 jbe 1f 232 movl underlying_e820_cache_size, %ecx 2331: pushl %ecx 234 rep movsb 235 popl %ecx 236 popw %si 237 popw %di 238 incw %bx 239 movl %edx, %eax 240 clc 241 ret 2422: 243 /* If the requested region is earlier than the cached region, 244 * invalidate the cache. 245 */ 246 cmpw %bx, underlying_e820_index 247 jbe 1f 248 movw $0xffff, underlying_e820_index 2491: 250 /* If the cache is invalid, reset the underlying %ebx */ 251 cmpw $0xffff, underlying_e820_index 252 jne 1f 253 andl $0, underlying_e820_ebx 2541: 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. 258 */ 259 cmpw $0xffff, underlying_e820_index 260 je 1f 261 cmpl $0, underlying_e820_ebx 262 jne 1f 263 stc 264 ret 2651: 266 /* Get the next region into the cache */ 267 pushl %eax 268 pushl %ebx 269 pushl %ecx 270 pushl %edx 271 pushl %esi /* Some implementations corrupt %esi, so we */ 272 pushl %edi /* preserve %esi, %edi and %ebp to be paranoid */ 273 pushl %ebp 274 pushw %es 275 pushw %ds 276 popw %es 277 movw $underlying_e820_cache, %di 278 cmpl $E820MAXSIZE, %ecx 279 jbe 1f 280 movl $E820MAXSIZE, %ecx 2811: movl underlying_e820_ebx, %ebx 282 stc 283 pushfw 284 lcall *%cs:int15_vector 285 popw %es 286 popl %ebp 287 popl %edi 288 popl %esi 289 /* Check for error return from underlying e820 call */ 290 jc 2f /* CF set: error */ 291 cmpl $SMAP, %eax 292 je 3f /* 'SMAP' missing: error */ 2932: /* 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 */ 296 ret 2973: /* No error occurred */ 298 movl %ebx, underlying_e820_ebx 299 movl %ecx, underlying_e820_cache_size 300 popl %edx 301 popl %ecx 302 popl %ebx 303 popl %eax 304 /* Mark cache as containing this result */ 305 incw underlying_e820_index 306 307 /* Loop until found */ 308 jmp get_underlying_e820 309 .size get_underlying_e820, . - get_underlying_e820 310 311 .section ".data16", "aw", @progbits 312underlying_e820_index: 313 .word 0xffff /* Initialise to an invalid value */ 314 .size underlying_e820_index, . - underlying_e820_index 315 316 .section ".bss16", "aw", @nobits 317underlying_e820_ebx: 318 .long 0 319 .size underlying_e820_ebx, . - underlying_e820_ebx 320 321 .section ".bss16", "aw", @nobits 322underlying_e820_cache: 323 .space E820MAXSIZE 324 .size underlying_e820_cache, . - underlying_e820_cache 325 326 .section ".bss16", "aw", @nobits 327underlying_e820_cache_size: 328 .long 0 329 .size underlying_e820_cache_size, . - underlying_e820_cache_size 330 331/**************************************************************************** 332 * Get windowed e820 region, without empty region stripping 333 * 334 * Parameters: 335 * As for INT 15,e820 336 * Returns: 337 * As for INT 15,e820 338 * 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. 342 * 343 **************************************************************************** 344 */ 345 .section ".text16", "ax", @progbits 346get_windowed_e820: 347 348 /* Preserve registers */ 349 pushl %esi 350 pushw %bp 351 352 /* Split %ebx into %si:%bx, store original %bx in %bp */ 353 pushl %ebx 354 popw %bp 355 popw %si 356 357 /* %si == 0 => start of memory_windows list */ 358 testw %si, %si 359 jne 1f 360 movw $memory_windows, %si 3611: 362 /* Get (cached) underlying e820 region to buffer */ 363 call get_underlying_e820 364 jc 99f /* Abort on error */ 365 366 /* Preserve registers */ 367 pushal 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 */ 374 call window_region 3751: /* 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 */ 381 popal 382 383 /* Derive continuation value for next call */ 384 addw $16, %si 385 cmpw $memory_windows_end, %si 386 jne 1f 387 /* End of memory windows: reset %si and allow %bx to continue */ 388 xorw %si, %si 389 jmp 2f 3901: /* More memory windows to go: restore original %bx */ 391 movw %bp, %bx 3922: /* Construct %ebx from %si:%bx */ 393 pushw %si 394 pushw %bx 395 popl %ebx 396 39798: /* Clear CF */ 398 clc 39999: /* Restore registers and return */ 400 popw %bp 401 popl %esi 402 ret 403 .size get_windowed_e820, . - get_windowed_e820 404 405/**************************************************************************** 406 * Get windowed e820 region, with empty region stripping 407 * 408 * Parameters: 409 * As for INT 15,e820 410 * Returns: 411 * As for INT 15,e820 412 * 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. 417 * 418 **************************************************************************** 419 */ 420 .section ".text16", "ax", @progbits 421get_nonempty_e820: 422 423 /* Record entry parameters */ 424 pushl %eax 425 pushl %ecx 426 pushl %edx 427 428 /* Get next windowed region */ 429 call get_windowed_e820 430 jc 99f /* abort on error */ 431 432 /* If region is non-empty, finish here */ 433 cmpl $0, %es:8(%di) 434 jne 98f 435 cmpl $0, %es:12(%di) 436 jne 98f 437 438 /* Region was empty: restore entry parameters and go to next region */ 439 popl %edx 440 popl %ecx 441 popl %eax 442 jmp get_nonempty_e820 443 44498: /* Clear CF */ 445 clc 44699: /* Return values from underlying call */ 447 addr32 leal 12(%esp), %esp /* avoid changing flags */ 448 ret 449 .size get_nonempty_e820, . - get_nonempty_e820 450 451/**************************************************************************** 452 * Get mangled e820 region, with empty region stripping 453 * 454 * Parameters: 455 * As for INT 15,e820 456 * Returns: 457 * As for INT 15,e820 458 * 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. 462 * 463 **************************************************************************** 464 */ 465 .section ".text16", "ax", @progbits 466get_mangled_e820: 467 468 /* Get a nonempty region */ 469 call get_nonempty_e820 470 jc 99f /* Abort on error */ 471 472 /* Peek ahead to see if there are any further nonempty regions */ 473 pushal 474 pushw %es 475 movw %sp, %bp 476 subw %cx, %sp 477 movl $0xe820, %eax 478 movl $SMAP, %edx 479 pushw %ss 480 popw %es 481 movw %sp, %di 482 call get_nonempty_e820 483 movw %bp, %sp 484 popw %es 485 popal 486 jnc 99f /* There are further nonempty regions */ 487 488 /* No futher nonempty regions: zero %ebx and clear CF */ 489 xorl %ebx, %ebx 490 49199: /* Return */ 492 ret 493 .size get_mangled_e820, . - get_mangled_e820 494 495/**************************************************************************** 496 * Set/clear CF on the stack as appropriate, assumes stack is as it should 497 * be immediately before IRET 498 **************************************************************************** 499 */ 500patch_cf: 501 pushw %bp 502 movw %sp, %bp 503 setc 8(%bp) /* Set/reset CF; clears PF, AF, ZF, SF */ 504 popw %bp 505 ret 506 507/**************************************************************************** 508 * INT 15,e820 handler 509 **************************************************************************** 510 */ 511 .section ".text16", "ax", @progbits 512int15_e820: 513 pushw %ds 514 pushw %cs:rm_ds 515 popw %ds 516 call get_mangled_e820 517 popw %ds 518 call patch_cf 519 iret 520 .size int15_e820, . - int15_e820 521 522/**************************************************************************** 523 * INT 15,e801 handler 524 **************************************************************************** 525 */ 526 .section ".text16", "ax", @progbits 527int15_e801: 528 /* Call previous handler */ 529 pushfw 530 lcall *%cs:int15_vector 531 call patch_cf 532 /* Edit result */ 533 pushw %ds 534 pushw %cs:rm_ds 535 popw %ds 536 call patch_1m_16m 537 xchgw %ax, %cx 538 xchgw %bx, %dx 539 call patch_1m_16m 540 xchgw %ax, %cx 541 xchgw %bx, %dx 542 popw %ds 543 iret 544 .size int15_e801, . - int15_e801 545 546/**************************************************************************** 547 * INT 15,88 handler 548 **************************************************************************** 549 */ 550 .section ".text16", "ax", @progbits 551int15_88: 552 /* Call previous handler */ 553 pushfw 554 lcall *%cs:int15_vector 555 call patch_cf 556 /* Edit result */ 557 pushw %ds 558 pushw %cs:rm_ds 559 popw %ds 560 call patch_1m 561 popw %ds 562 iret 563 .size int15_88, . - int15_88 564 565/**************************************************************************** 566 * INT 15 handler 567 **************************************************************************** 568 */ 569 .section ".text16", "ax", @progbits 570 .globl int15 571int15: 572 /* See if we want to intercept this call */ 573 pushfw 574 cmpw $0xe820, %ax 575 jne 1f 576 cmpl $SMAP, %edx 577 jne 1f 578 popfw 579 jmp int15_e820 5801: cmpw $0xe801, %ax 581 jne 2f 582 popfw 583 jmp int15_e801 5842: cmpb $0x88, %ah 585 jne 3f 586 popfw 587 jmp int15_88 5883: popfw 589 ljmp *%cs:int15_vector 590 .size int15, . - int15 591 592 .section ".text16.data", "aw", @progbits 593 .globl int15_vector 594int15_vector: 595 .long 0 596 .size int15_vector, . - int15_vector 597