Home | History | Annotate | Download | only in pcbios
      1 /*
      2  * Copyright (C) 2006 Michael Brown <mbrown (at) 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 
     19 FILE_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
     69 memory_windows:
     70 base_memory_window:	.long 0x00000000, 0x00000000 /* Start of memory */
     71 
     72 hidemem_base:		.long 0x000a0000, 0x00000000 /* Changes at runtime */
     73 ext_memory_window:	.long 0x000a0000, 0x00000000 /* 640kB mark */
     74 
     75 hidemem_umalloc:	.long 0xffffffff, 0xffffffff /* Changes at runtime */
     76 			.long 0xffffffff, 0xffffffff /* Changes at runtime */
     77 
     78 hidemem_textdata:	.long 0xffffffff, 0xffffffff /* Changes at runtime */
     79 			.long 0xffffffff, 0xffffffff /* Changes at runtime */
     80 
     81 			.long 0xffffffff, 0xffffffff /* End of memory */
     82 memory_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
     97 window_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
    105 1:	jae	2f
    106 	movl	4(%si), %edx
    107 	movl	0(%si), %eax
    108 2:	/* Truncate to window end */
    109 	cmpl	12(%si), %ecx
    110 	jne	1f
    111 	cmpl	8(%si), %ebx
    112 1:	jbe	2f
    113 	movl	12(%si), %ecx
    114 	movl	8(%si), %ebx
    115 2:	/* 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
    135 patch_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
    165 patch_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
    196 patch_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
    203 1:	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
    222 get_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
    233 1:	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
    242 2:
    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
    249 1:
    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
    254 1:
    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
    265 1:
    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
    281 1:	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 */
    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 */
    296 	ret
    297 3:	/* 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
    312 underlying_e820_index:
    313 	.word	0xffff /* Initialise to an invalid value */
    314 	.size underlying_e820_index, . - underlying_e820_index
    315 
    316 	.section ".bss16", "aw", @nobits
    317 underlying_e820_ebx:
    318 	.long	0
    319 	.size underlying_e820_ebx, . - underlying_e820_ebx
    320 
    321 	.section ".bss16", "aw", @nobits
    322 underlying_e820_cache:
    323 	.space	E820MAXSIZE
    324 	.size underlying_e820_cache, . - underlying_e820_cache
    325 
    326 	.section ".bss16", "aw", @nobits
    327 underlying_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
    346 get_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
    361 1:
    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
    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 */
    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
    390 1:	/* More memory windows to go: restore original %bx */
    391 	movw	%bp, %bx
    392 2:	/* Construct %ebx from %si:%bx */
    393 	pushw	%si
    394 	pushw	%bx
    395 	popl	%ebx
    396 
    397 98:	/* Clear CF */
    398 	clc
    399 99:	/* 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
    421 get_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 
    444 98:	/* Clear CF */
    445 	clc
    446 99:	/* 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
    466 get_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 
    491 99:	/* 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  */
    500 patch_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
    512 int15_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
    527 int15_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
    551 int15_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
    571 int15:
    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
    580 1:	cmpw	$0xe801, %ax
    581 	jne	2f
    582 	popfw
    583 	jmp	int15_e801
    584 2:	cmpb	$0x88, %ah
    585 	jne	3f
    586 	popfw
    587 	jmp	int15_88
    588 3:	popfw
    589 	ljmp	*%cs:int15_vector
    590 	.size int15, . - int15
    591 
    592 	.section ".text16.data", "aw", @progbits
    593 	.globl int15_vector
    594 int15_vector:
    595 	.long 0
    596 	.size int15_vector, . - int15_vector
    597