diff options
author | 2015-08-28 09:58:54 +0800 | |
---|---|---|
committer | 2015-09-01 12:44:00 +0800 | |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/arch/i386/firmware | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/arch/i386/firmware')
7 files changed, 1791 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c new file mode 100644 index 000000000..b23f2c356 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <realmode.h> +#include <bios.h> +#include <basemem.h> +#include <ipxe/hidemem.h> + +/** @file + * + * Base memory allocation + * + */ + +/** + * Set the BIOS free base memory counter + * + * @v new_fbms New free base memory counter (in kB) + */ +void set_fbms ( unsigned int new_fbms ) { + uint16_t fbms = new_fbms; + + /* Update the BIOS memory counter */ + put_real ( fbms, BDA_SEG, BDA_FBMS ); + + /* Update our hidden memory region map */ + hide_basemem(); +} diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c new file mode 100644 index 000000000..bd73838b5 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <assert.h> +#include <realmode.h> +#include <bios.h> +#include <ipxe/console.h> +#include <ipxe/ansiesc.h> +#include <ipxe/keymap.h> +#include <config/console.h> + +#define ATTR_BOLD 0x08 + +#define ATTR_FCOL_MASK 0x07 +#define ATTR_FCOL_BLACK 0x00 +#define ATTR_FCOL_BLUE 0x01 +#define ATTR_FCOL_GREEN 0x02 +#define ATTR_FCOL_CYAN 0x03 +#define ATTR_FCOL_RED 0x04 +#define ATTR_FCOL_MAGENTA 0x05 +#define ATTR_FCOL_YELLOW 0x06 +#define ATTR_FCOL_WHITE 0x07 + +#define ATTR_BCOL_MASK 0x70 +#define ATTR_BCOL_BLACK 0x00 +#define ATTR_BCOL_BLUE 0x10 +#define ATTR_BCOL_GREEN 0x20 +#define ATTR_BCOL_CYAN 0x30 +#define ATTR_BCOL_RED 0x40 +#define ATTR_BCOL_MAGENTA 0x50 +#define ATTR_BCOL_YELLOW 0x60 +#define ATTR_BCOL_WHITE 0x70 + +#define ATTR_DEFAULT ATTR_FCOL_WHITE + +/* Set default console usage if applicable */ +#if ! ( defined ( CONSOLE_PCBIOS ) && CONSOLE_EXPLICIT ( CONSOLE_PCBIOS ) ) +#undef CONSOLE_PCBIOS +#define CONSOLE_PCBIOS ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG ) +#endif + +/** Current character attribute */ +static unsigned int bios_attr = ATTR_DEFAULT; + +/** + * Handle ANSI CUP (cursor position) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params[0] Row (1 is top) + * @v params[1] Column (1 is left) + */ +static void bios_handle_cup ( struct ansiesc_context *ctx __unused, + unsigned int count __unused, int params[] ) { + int cx = ( params[1] - 1 ); + int cy = ( params[0] - 1 ); + + if ( cx < 0 ) + cx = 0; + if ( cy < 0 ) + cy = 0; + + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "int $0x10\n\t" + "cli\n\t" ) + : : "a" ( 0x0200 ), "b" ( 1 ), + "d" ( ( cy << 8 ) | cx ) ); +} + +/** + * Handle ANSI ED (erase in page) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params[0] Region to erase + */ +static void bios_handle_ed ( struct ansiesc_context *ctx __unused, + unsigned int count __unused, + int params[] __unused ) { + /* We assume that we always clear the whole screen */ + assert ( params[0] == ANSIESC_ED_ALL ); + + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "int $0x10\n\t" + "cli\n\t" ) + : : "a" ( 0x0600 ), "b" ( bios_attr << 8 ), + "c" ( 0 ), + "d" ( ( ( console_height - 1 ) << 8 ) | + ( console_width - 1 ) ) ); +} + +/** + * Handle ANSI SGR (set graphics rendition) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params List of graphic rendition aspects + */ +static void bios_handle_sgr ( struct ansiesc_context *ctx __unused, + unsigned int count, int params[] ) { + static const uint8_t bios_attr_fcols[10] = { + ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN, + ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA, + ATTR_FCOL_CYAN, ATTR_FCOL_WHITE, + ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */ + }; + static const uint8_t bios_attr_bcols[10] = { + ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN, + ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA, + ATTR_BCOL_CYAN, ATTR_BCOL_WHITE, + ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */ + }; + unsigned int i; + int aspect; + + for ( i = 0 ; i < count ; i++ ) { + aspect = params[i]; + if ( aspect == 0 ) { + bios_attr = ATTR_DEFAULT; + } else if ( aspect == 1 ) { + bios_attr |= ATTR_BOLD; + } else if ( aspect == 22 ) { + bios_attr &= ~ATTR_BOLD; + } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) { + bios_attr &= ~ATTR_FCOL_MASK; + bios_attr |= bios_attr_fcols[ aspect - 30 ]; + } else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) { + bios_attr &= ~ATTR_BCOL_MASK; + bios_attr |= bios_attr_bcols[ aspect - 40 ]; + } + } +} + +/** + * Handle ANSI DECTCEM set (show cursor) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params List of graphic rendition aspects + */ +static void bios_handle_dectcem_set ( struct ansiesc_context *ctx __unused, + unsigned int count __unused, + int params[] __unused ) { + uint8_t height; + + /* Get character height */ + get_real ( height, BDA_SEG, BDA_CHAR_HEIGHT ); + + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "int $0x10\n\t" + "cli\n\t" ) + : : "a" ( 0x0100 ), + "c" ( ( ( height - 2 ) << 8 ) | + ( height - 1 ) ) ); +} + +/** + * Handle ANSI DECTCEM reset (hide cursor) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params List of graphic rendition aspects + */ +static void bios_handle_dectcem_reset ( struct ansiesc_context *ctx __unused, + unsigned int count __unused, + int params[] __unused ) { + + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "int $0x10\n\t" + "cli\n\t" ) + : : "a" ( 0x0100 ), "c" ( 0x2000 ) ); +} + +/** BIOS console ANSI escape sequence handlers */ +static struct ansiesc_handler bios_ansiesc_handlers[] = { + { ANSIESC_CUP, bios_handle_cup }, + { ANSIESC_ED, bios_handle_ed }, + { ANSIESC_SGR, bios_handle_sgr }, + { ANSIESC_DECTCEM_SET, bios_handle_dectcem_set }, + { ANSIESC_DECTCEM_RESET, bios_handle_dectcem_reset }, + { 0, NULL } +}; + +/** BIOS console ANSI escape sequence context */ +static struct ansiesc_context bios_ansiesc_ctx = { + .handlers = bios_ansiesc_handlers, +}; + +/** + * Print a character to BIOS console + * + * @v character Character to be printed + */ +static void bios_putchar ( int character ) { + int discard_a, discard_b, discard_c; + + /* Intercept ANSI escape sequences */ + character = ansiesc_process ( &bios_ansiesc_ctx, character ); + if ( character < 0 ) + return; + + /* Print character with attribute */ + __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */ + "sti\n\t" + /* Skip non-printable characters */ + "cmpb $0x20, %%al\n\t" + "jb 1f\n\t" + /* Read attribute */ + "movb %%al, %%cl\n\t" + "movb $0x08, %%ah\n\t" + "int $0x10\n\t" + "xchgb %%al, %%cl\n\t" + /* Skip if attribute matches */ + "cmpb %%ah, %%bl\n\t" + "je 1f\n\t" + /* Set attribute */ + "movw $0x0001, %%cx\n\t" + "movb $0x09, %%ah\n\t" + "int $0x10\n\t" + "\n1:\n\t" + /* Print character */ + "xorw %%bx, %%bx\n\t" + "movb $0x0e, %%ah\n\t" + "int $0x10\n\t" + "cli\n\t" + "popl %%ebp\n\t" /* gcc bug */ ) + : "=a" ( discard_a ), "=b" ( discard_b ), + "=c" ( discard_c ) + : "a" ( character ), "b" ( bios_attr ) ); +} + +/** + * Pointer to current ANSI output sequence + * + * While we are in the middle of returning an ANSI sequence for a + * special key, this will point to the next character to return. When + * not in the middle of such a sequence, this will point to a NUL + * (note: not "will be NULL"). + */ +static const char *ansi_input = ""; + +/** A mapping from a BIOS scan code to an ANSI escape sequence */ +#define BIOS_KEY( key, ansi ) key ansi "\0" + +/** Mapping from BIOS scan codes to ANSI escape sequences */ +static const char ansi_sequences[] = { + BIOS_KEY ( "\x53", "[3~" ) /* Delete */ + BIOS_KEY ( "\x48", "[A" ) /* Up arrow */ + BIOS_KEY ( "\x50", "[B" ) /* Down arrow */ + BIOS_KEY ( "\x4b", "[D" ) /* Left arrow */ + BIOS_KEY ( "\x4d", "[C" ) /* Right arrow */ + BIOS_KEY ( "\x47", "[H" ) /* Home */ + BIOS_KEY ( "\x4f", "[F" ) /* End */ + BIOS_KEY ( "\x49", "[5~" ) /* Page up */ + BIOS_KEY ( "\x51", "[6~" ) /* Page down */ + BIOS_KEY ( "\x3f", "[15~" ) /* F5 */ + BIOS_KEY ( "\x40", "[17~" ) /* F6 */ + BIOS_KEY ( "\x41", "[18~" ) /* F7 */ + BIOS_KEY ( "\x42", "[19~" ) /* F8 (required for PXE) */ + BIOS_KEY ( "\x43", "[20~" ) /* F9 */ + BIOS_KEY ( "\x44", "[21~" ) /* F10 */ + BIOS_KEY ( "\x85", "[23~" ) /* F11 */ + BIOS_KEY ( "\x86", "[24~" ) /* F12 */ +}; + +/** + * Get ANSI escape sequence corresponding to BIOS scancode + * + * @v scancode BIOS scancode + * @ret ansi_seq ANSI escape sequence, if any, otherwise NULL + */ +static const char * scancode_to_ansi_seq ( unsigned int scancode ) { + const char *seq = ansi_sequences; + + while ( *seq ) { + if ( *(seq++) == ( ( char ) scancode ) ) + return seq; + seq += ( strlen ( seq ) + 1 ); + } + DBG ( "Unrecognised BIOS scancode %02x\n", scancode ); + return NULL; +} + +/** + * Map a key + * + * @v character Character read from console + * @ret character Mapped character + */ +static int bios_keymap ( unsigned int character ) { + struct key_mapping *mapping; + + for_each_table_entry ( mapping, KEYMAP ) { + if ( mapping->from == character ) + return mapping->to; + } + return character; +} + +/** + * Get character from BIOS console + * + * @ret character Character read from console + */ +static int bios_getchar ( void ) { + uint16_t keypress; + unsigned int character; + const char *ansi_seq; + + /* If we are mid-sequence, pass out the next byte */ + if ( ( character = *ansi_input ) ) { + ansi_input++; + return character; + } + + /* Read character from real BIOS console */ + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "int $0x16\n\t" + "cli\n\t" ) + : "=a" ( keypress ) : "a" ( 0x1000 ) ); + character = ( keypress & 0xff ); + + /* If it's a normal character, just map and return it */ + if ( character && ( character < 0x80 ) ) + return bios_keymap ( character ); + + /* Otherwise, check for a special key that we know about */ + if ( ( ansi_seq = scancode_to_ansi_seq ( keypress >> 8 ) ) ) { + /* Start of escape sequence: return ESC (0x1b) */ + ansi_input = ansi_seq; + return 0x1b; + } + + return 0; +} + +/** + * Check for character ready to read from BIOS console + * + * @ret True Character available to read + * @ret False No character available to read + */ +static int bios_iskey ( void ) { + unsigned int discard_a; + unsigned int flags; + + /* If we are mid-sequence, we are always ready */ + if ( *ansi_input ) + return 1; + + /* Otherwise check the real BIOS console */ + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "int $0x16\n\t" + "pushfw\n\t" + "popw %w0\n\t" + "cli\n\t" ) + : "=r" ( flags ), "=a" ( discard_a ) + : "a" ( 0x1100 ) ); + return ( ! ( flags & ZF ) ); +} + +struct console_driver bios_console __console_driver = { + .putchar = bios_putchar, + .getchar = bios_getchar, + .iskey = bios_iskey, + .usage = CONSOLE_PCBIOS, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S new file mode 100644 index 000000000..cea17ef8e --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + + .text + .arch i386 + .code16 + +#define SMAP 0x534d4150 + +/* Most documentation refers to the E820 buffer as being 20 bytes, and + * the API makes it perfectly legitimate to pass only a 20-byte buffer + * and expect to get valid data. However, some morons at ACPI decided + * to extend the data structure by adding an extra "extended + * attributes" field and by including critical information within this + * field, such as whether or not the region is enabled. A caller who + * passes in only a 20-byte buffer therefore risks getting very, very + * misleading information. + * + * I have personally witnessed an HP BIOS that returns a value of + * 0x0009 in the extended attributes field. If we don't pass this + * value through to the caller, 32-bit WinPE will die, usually with a + * PAGE_FAULT_IN_NONPAGED_AREA blue screen of death. + * + * Allow a ridiculously large maximum value (64 bytes) for the E820 + * buffer as a guard against insufficiently creative idiots in the + * future. + */ +#define E820MAXSIZE 64 + +/**************************************************************************** + * + * Allowed memory windows + * + * There are two ways to view this list. The first is as a list of + * (non-overlapping) allowed memory regions, sorted by increasing + * address. The second is as a list of (non-overlapping) hidden + * memory regions, again sorted by increasing address. The second + * view is offset by half an entry from the first: think about this + * for a moment and it should make sense. + * + * xxx_memory_window is used to indicate an "allowed region" + * structure, hidden_xxx_memory is used to indicate a "hidden region" + * structure. Each structure is 16 bytes in length. + * + **************************************************************************** + */ + .section ".data16", "aw", @progbits + .align 16 + .globl hidemem_base + .globl hidemem_umalloc + .globl hidemem_textdata +memory_windows: +base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */ + +hidemem_base: .long 0x000a0000, 0x00000000 /* Changes at runtime */ +ext_memory_window: .long 0x000a0000, 0x00000000 /* 640kB mark */ + +hidemem_umalloc: .long 0xffffffff, 0xffffffff /* Changes at runtime */ + .long 0xffffffff, 0xffffffff /* Changes at runtime */ + +hidemem_textdata: .long 0xffffffff, 0xffffffff /* Changes at runtime */ + .long 0xffffffff, 0xffffffff /* Changes at runtime */ + + .long 0xffffffff, 0xffffffff /* End of memory */ +memory_windows_end: + +/**************************************************************************** + * Truncate region to memory window + * + * Parameters: + * %edx:%eax Start of region + * %ecx:%ebx Length of region + * %si Memory window + * Returns: + * %edx:%eax Start of windowed region + * %ecx:%ebx Length of windowed region + **************************************************************************** + */ + .section ".text16", "ax", @progbits +window_region: + /* Convert (start,len) to (start, end) */ + addl %eax, %ebx + adcl %edx, %ecx + /* Truncate to window start */ + cmpl 4(%si), %edx + jne 1f + cmpl 0(%si), %eax +1: jae 2f + movl 4(%si), %edx + movl 0(%si), %eax +2: /* Truncate to window end */ + cmpl 12(%si), %ecx + jne 1f + cmpl 8(%si), %ebx +1: jbe 2f + movl 12(%si), %ecx + movl 8(%si), %ebx +2: /* Convert (start, end) back to (start, len) */ + subl %eax, %ebx + sbbl %edx, %ecx + /* If length is <0, set length to 0 */ + jae 1f + xorl %ebx, %ebx + xorl %ecx, %ecx + ret + .size window_region, . - window_region + +/**************************************************************************** + * Patch "memory above 1MB" figure + * + * Parameters: + * %ax Memory above 1MB, in 1kB blocks + * Returns: + * %ax Modified memory above 1M in 1kB blocks + **************************************************************************** + */ + .section ".text16", "ax", @progbits +patch_1m: + pushal + /* Convert to (start,len) format and call truncate */ + xorl %ecx, %ecx + movzwl %ax, %ebx + shll $10, %ebx + xorl %edx, %edx + movl $0x100000, %eax + movw $ext_memory_window, %si + call window_region + /* Convert back to "memory above 1MB" format and return via %ax */ + pushfw + shrl $10, %ebx + popfw + movw %sp, %bp + movw %bx, 28(%bp) + popal + ret + .size patch_1m, . - patch_1m + +/**************************************************************************** + * Patch "memory above 16MB" figure + * + * Parameters: + * %bx Memory above 16MB, in 64kB blocks + * Returns: + * %bx Modified memory above 16M in 64kB blocks + **************************************************************************** + */ + .section ".text16", "ax", @progbits +patch_16m: + pushal + /* Convert to (start,len) format and call truncate */ + xorl %ecx, %ecx + shll $16, %ebx + xorl %edx, %edx + movl $0x1000000, %eax + movw $ext_memory_window, %si + call window_region + /* Convert back to "memory above 16MB" format and return via %bx */ + pushfw + shrl $16, %ebx + popfw + movw %sp, %bp + movw %bx, 16(%bp) + popal + ret + .size patch_16m, . - patch_16m + +/**************************************************************************** + * Patch "memory between 1MB and 16MB" and "memory above 16MB" figures + * + * Parameters: + * %ax Memory between 1MB and 16MB, in 1kB blocks + * %bx Memory above 16MB, in 64kB blocks + * Returns: + * %ax Modified memory between 1MB and 16MB, in 1kB blocks + * %bx Modified memory above 16MB, in 64kB blocks + **************************************************************************** + */ + .section ".text16", "ax", @progbits +patch_1m_16m: + call patch_1m + call patch_16m + /* If 1M region is no longer full-length, kill off the 16M region */ + cmpw $( 15 * 1024 ), %ax + je 1f + xorw %bx, %bx +1: ret + .size patch_1m_16m, . - patch_1m_16m + +/**************************************************************************** + * Get underlying e820 memory region to underlying_e820 buffer + * + * Parameters: + * As for INT 15,e820 + * Returns: + * As for INT 15,e820 + * + * Wraps the underlying INT 15,e820 call so that the continuation + * value (%ebx) is a 16-bit simple sequence counter (with the high 16 + * bits ignored), and termination is always via CF=1 rather than + * %ebx=0. + * + **************************************************************************** + */ + .section ".text16", "ax", @progbits +get_underlying_e820: + + /* If the requested region is in the cache, return it */ + cmpw %bx, underlying_e820_index + jne 2f + pushw %di + pushw %si + movw $underlying_e820_cache, %si + cmpl underlying_e820_cache_size, %ecx + jbe 1f + movl underlying_e820_cache_size, %ecx +1: pushl %ecx + rep movsb + popl %ecx + popw %si + popw %di + incw %bx + movl %edx, %eax + clc + ret +2: + /* If the requested region is earlier than the cached region, + * invalidate the cache. + */ + cmpw %bx, underlying_e820_index + jbe 1f + movw $0xffff, underlying_e820_index +1: + /* If the cache is invalid, reset the underlying %ebx */ + cmpw $0xffff, underlying_e820_index + jne 1f + andl $0, underlying_e820_ebx +1: + /* If the cache is valid but the continuation value is zero, + * this means that the previous underlying call returned with + * %ebx=0. Return with CF=1 in this case. + */ + cmpw $0xffff, underlying_e820_index + je 1f + cmpl $0, underlying_e820_ebx + jne 1f + stc + ret +1: + /* Get the next region into the cache */ + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + pushl %esi /* Some implementations corrupt %esi, so we */ + pushl %edi /* preserve %esi, %edi and %ebp to be paranoid */ + pushl %ebp + pushw %es + pushw %ds + popw %es + movw $underlying_e820_cache, %di + cmpl $E820MAXSIZE, %ecx + jbe 1f + movl $E820MAXSIZE, %ecx +1: movl underlying_e820_ebx, %ebx + stc + pushfw + lcall *%cs:int15_vector + popw %es + popl %ebp + popl %edi + popl %esi + /* Check for error return from underlying e820 call */ + jc 2f /* CF set: error */ + cmpl $SMAP, %eax + je 3f /* 'SMAP' missing: error */ +2: /* An error occurred: return values returned by underlying e820 call */ + stc /* Force CF set if SMAP was missing */ + addr32 leal 16(%esp), %esp /* avoid changing other flags */ + ret +3: /* No error occurred */ + movl %ebx, underlying_e820_ebx + movl %ecx, underlying_e820_cache_size + popl %edx + popl %ecx + popl %ebx + popl %eax + /* Mark cache as containing this result */ + incw underlying_e820_index + + /* Loop until found */ + jmp get_underlying_e820 + .size get_underlying_e820, . - get_underlying_e820 + + .section ".data16", "aw", @progbits +underlying_e820_index: + .word 0xffff /* Initialise to an invalid value */ + .size underlying_e820_index, . - underlying_e820_index + + .section ".bss16", "aw", @nobits +underlying_e820_ebx: + .long 0 + .size underlying_e820_ebx, . - underlying_e820_ebx + + .section ".bss16", "aw", @nobits +underlying_e820_cache: + .space E820MAXSIZE + .size underlying_e820_cache, . - underlying_e820_cache + + .section ".bss16", "aw", @nobits +underlying_e820_cache_size: + .long 0 + .size underlying_e820_cache_size, . - underlying_e820_cache_size + +/**************************************************************************** + * Get windowed e820 region, without empty region stripping + * + * Parameters: + * As for INT 15,e820 + * Returns: + * As for INT 15,e820 + * + * Wraps the underlying INT 15,e820 call so that each underlying + * region is returned N times, windowed to fit within N visible-memory + * windows. Termination is always via CF=1. + * + **************************************************************************** + */ + .section ".text16", "ax", @progbits +get_windowed_e820: + + /* Preserve registers */ + pushl %esi + pushw %bp + + /* Split %ebx into %si:%bx, store original %bx in %bp */ + pushl %ebx + popw %bp + popw %si + + /* %si == 0 => start of memory_windows list */ + testw %si, %si + jne 1f + movw $memory_windows, %si +1: + /* Get (cached) underlying e820 region to buffer */ + call get_underlying_e820 + jc 99f /* Abort on error */ + + /* Preserve registers */ + pushal + /* start => %edx:%eax, len => %ecx:%ebx */ + movl %es:0(%di), %eax + movl %es:4(%di), %edx + movl %es:8(%di), %ebx + movl %es:12(%di), %ecx + /* Truncate region to current window */ + call window_region +1: /* Store modified values in e820 map entry */ + movl %eax, %es:0(%di) + movl %edx, %es:4(%di) + movl %ebx, %es:8(%di) + movl %ecx, %es:12(%di) + /* Restore registers */ + popal + + /* Derive continuation value for next call */ + addw $16, %si + cmpw $memory_windows_end, %si + jne 1f + /* End of memory windows: reset %si and allow %bx to continue */ + xorw %si, %si + jmp 2f +1: /* More memory windows to go: restore original %bx */ + movw %bp, %bx +2: /* Construct %ebx from %si:%bx */ + pushw %si + pushw %bx + popl %ebx + +98: /* Clear CF */ + clc +99: /* Restore registers and return */ + popw %bp + popl %esi + ret + .size get_windowed_e820, . - get_windowed_e820 + +/**************************************************************************** + * Get windowed e820 region, with empty region stripping + * + * Parameters: + * As for INT 15,e820 + * Returns: + * As for INT 15,e820 + * + * Wraps the underlying INT 15,e820 call so that each underlying + * region is returned up to N times, windowed to fit within N + * visible-memory windows. Empty windows are never returned. + * Termination is always via CF=1. + * + **************************************************************************** + */ + .section ".text16", "ax", @progbits +get_nonempty_e820: + + /* Record entry parameters */ + pushl %eax + pushl %ecx + pushl %edx + + /* Get next windowed region */ + call get_windowed_e820 + jc 99f /* abort on error */ + + /* If region is non-empty, finish here */ + cmpl $0, %es:8(%di) + jne 98f + cmpl $0, %es:12(%di) + jne 98f + + /* Region was empty: restore entry parameters and go to next region */ + popl %edx + popl %ecx + popl %eax + jmp get_nonempty_e820 + +98: /* Clear CF */ + clc +99: /* Return values from underlying call */ + addr32 leal 12(%esp), %esp /* avoid changing flags */ + ret + .size get_nonempty_e820, . - get_nonempty_e820 + +/**************************************************************************** + * Get mangled e820 region, with empty region stripping + * + * Parameters: + * As for INT 15,e820 + * Returns: + * As for INT 15,e820 + * + * Wraps the underlying INT 15,e820 call so that underlying regions + * are windowed to the allowed memory regions. Empty regions are + * stripped from the map. Termination is always via %ebx=0. + * + **************************************************************************** + */ + .section ".text16", "ax", @progbits +get_mangled_e820: + + /* Get a nonempty region */ + call get_nonempty_e820 + jc 99f /* Abort on error */ + + /* Peek ahead to see if there are any further nonempty regions */ + pushal + pushw %es + movw %sp, %bp + subw %cx, %sp + movl $0xe820, %eax + movl $SMAP, %edx + pushw %ss + popw %es + movw %sp, %di + call get_nonempty_e820 + movw %bp, %sp + popw %es + popal + jnc 99f /* There are further nonempty regions */ + + /* No futher nonempty regions: zero %ebx and clear CF */ + xorl %ebx, %ebx + +99: /* Return */ + ret + .size get_mangled_e820, . - get_mangled_e820 + +/**************************************************************************** + * INT 15,e820 handler + **************************************************************************** + */ + .section ".text16", "ax", @progbits +int15_e820: + pushw %ds + pushw %cs:rm_ds + popw %ds + call get_mangled_e820 + popw %ds + call patch_cf + iret + .size int15_e820, . - int15_e820 + +/**************************************************************************** + * INT 15,e801 handler + **************************************************************************** + */ + .section ".text16", "ax", @progbits +int15_e801: + /* Call previous handler */ + pushfw + lcall *%cs:int15_vector + call patch_cf + /* Edit result */ + pushw %ds + pushw %cs:rm_ds + popw %ds + call patch_1m_16m + xchgw %ax, %cx + xchgw %bx, %dx + call patch_1m_16m + xchgw %ax, %cx + xchgw %bx, %dx + popw %ds + iret + .size int15_e801, . - int15_e801 + +/**************************************************************************** + * INT 15,88 handler + **************************************************************************** + */ + .section ".text16", "ax", @progbits +int15_88: + /* Call previous handler */ + pushfw + lcall *%cs:int15_vector + call patch_cf + /* Edit result */ + pushw %ds + pushw %cs:rm_ds + popw %ds + call patch_1m + popw %ds + iret + .size int15_88, . - int15_88 + +/**************************************************************************** + * INT 15 handler + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .globl int15 +int15: + /* See if we want to intercept this call */ + pushfw + cmpw $0xe820, %ax + jne 1f + cmpl $SMAP, %edx + jne 1f + popfw + jmp int15_e820 +1: cmpw $0xe801, %ax + jne 2f + popfw + jmp int15_e801 +2: cmpb $0x88, %ah + jne 3f + popfw + jmp int15_88 +3: popfw + ljmp *%cs:int15_vector + .size int15, . - int15 + + .section ".text16.data", "aw", @progbits + .globl int15_vector +int15_vector: + .long 0 + .size int15_vector, . - int15_vector diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c new file mode 100644 index 000000000..e5f713728 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c @@ -0,0 +1,94 @@ +/* Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <realmode.h> +#include <biosint.h> + +/** Assembly routine in inline asm */ +extern void int15_fakee820(); + +/** Original INT 15 handler */ +static struct segoff __text16 ( real_int15_vector ); +#define real_int15_vector __use_text16 ( real_int15_vector ) + +/** An INT 15,e820 memory map entry */ +struct e820_entry { + /** Start of region */ + uint64_t start; + /** Length of region */ + uint64_t len; + /** Type of region */ + uint32_t type; +} __attribute__ (( packed )); + +#define E820_TYPE_RAM 1 /**< Normal memory */ +#define E820_TYPE_RSVD 2 /**< Reserved and unavailable */ +#define E820_TYPE_ACPI 3 /**< ACPI reclaim memory */ +#define E820_TYPE_NVS 4 /**< ACPI NVS memory */ + +/** Fake e820 map */ +static struct e820_entry __text16_array ( e820map, [] ) __used = { + { 0x00000000ULL, ( 0x000a0000ULL - 0x00000000ULL ), E820_TYPE_RAM }, + { 0x00100000ULL, ( 0xcfb50000ULL - 0x00100000ULL ), E820_TYPE_RAM }, + { 0xcfb50000ULL, ( 0xcfb64000ULL - 0xcfb50000ULL ), E820_TYPE_RSVD }, + { 0xcfb64000ULL, ( 0xcfb66000ULL - 0xcfb64000ULL ), E820_TYPE_RSVD }, + { 0xcfb66000ULL, ( 0xcfb85c00ULL - 0xcfb66000ULL ), E820_TYPE_ACPI }, + { 0xcfb85c00ULL, ( 0xd0000000ULL - 0xcfb85c00ULL ), E820_TYPE_RSVD }, + { 0xe0000000ULL, ( 0xf0000000ULL - 0xe0000000ULL ), E820_TYPE_RSVD }, + { 0xfe000000ULL, (0x100000000ULL - 0xfe000000ULL ), E820_TYPE_RSVD }, + {0x100000000ULL, (0x230000000ULL -0x100000000ULL ), E820_TYPE_RAM }, +}; +#define e820map __use_text16 ( e820map ) + +void fake_e820 ( void ) { + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint15_fakee820:\n\t" + "pushfw\n\t" + "cmpl $0xe820, %%eax\n\t" + "jne 99f\n\t" + "cmpl $0x534d4150, %%edx\n\t" + "jne 99f\n\t" + "pushaw\n\t" + "movw %%sp, %%bp\n\t" + "andb $~0x01, 22(%%bp)\n\t" /* Clear return CF */ + "leaw e820map(%%bx), %%si\n\t" + "cs rep movsb\n\t" + "popaw\n\t" + "movl %%edx, %%eax\n\t" + "addl $20, %%ebx\n\t" + "cmpl %0, %%ebx\n\t" + "jne 1f\n\t" + "xorl %%ebx,%%ebx\n\t" + "\n1:\n\t" + "popfw\n\t" + "iret\n\t" + "\n99:\n\t" + "popfw\n\t" + "ljmp *%%cs:real_int15_vector\n\t" ) + : : "i" ( sizeof ( e820map ) ) ); + + hook_bios_interrupt ( 0x15, ( unsigned int ) int15_fakee820, + &real_int15_vector ); +} + +void unfake_e820 ( void ) { + unhook_bios_interrupt ( 0x15, ( unsigned int ) int15_fakee820, + &real_int15_vector ); +} diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c new file mode 100644 index 000000000..8f3069e18 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c @@ -0,0 +1,231 @@ +/* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <assert.h> +#include <realmode.h> +#include <biosint.h> +#include <basemem.h> +#include <fakee820.h> +#include <ipxe/init.h> +#include <ipxe/io.h> +#include <ipxe/hidemem.h> + +/** Set to true if you want to test a fake E820 map */ +#define FAKE_E820 0 + +/** Alignment for hidden memory regions */ +#define ALIGN_HIDDEN 4096 /* 4kB page alignment should be enough */ + +/** + * A hidden region of iPXE + * + * This represents a region that will be edited out of the system's + * memory map. + * + * This structure is accessed by assembly code, so must not be + * changed. + */ +struct hidden_region { + /** Physical start address */ + uint64_t start; + /** Physical end address */ + uint64_t end; +}; + +/** Hidden base memory */ +extern struct hidden_region __data16 ( hidemem_base ); +#define hidemem_base __use_data16 ( hidemem_base ) + +/** Hidden umalloc memory */ +extern struct hidden_region __data16 ( hidemem_umalloc ); +#define hidemem_umalloc __use_data16 ( hidemem_umalloc ) + +/** Hidden text memory */ +extern struct hidden_region __data16 ( hidemem_textdata ); +#define hidemem_textdata __use_data16 ( hidemem_textdata ) + +/** Assembly routine in e820mangler.S */ +extern void int15(); + +/** Vector for storing original INT 15 handler */ +extern struct segoff __text16 ( int15_vector ); +#define int15_vector __use_text16 ( int15_vector ) + +/* The linker defines these symbols for us */ +extern char _textdata[]; +extern char _etextdata[]; +extern char _text16_memsz[]; +#define _text16_memsz ( ( unsigned int ) _text16_memsz ) +extern char _data16_memsz[]; +#define _data16_memsz ( ( unsigned int ) _data16_memsz ) + +/** + * Hide region of memory from system memory map + * + * @v region Hidden memory region + * @v start Start of region + * @v end End of region + */ +static void hide_region ( struct hidden_region *region, + physaddr_t start, physaddr_t end ) { + + /* Some operating systems get a nasty shock if a region of the + * E820 map seems to start on a non-page boundary. Make life + * safer by rounding out our edited region. + */ + region->start = ( start & ~( ALIGN_HIDDEN - 1 ) ); + region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) ); + + DBG ( "Hiding region [%llx,%llx)\n", region->start, region->end ); +} + +/** + * Hide used base memory + * + */ +void hide_basemem ( void ) { + /* Hide from the top of free base memory to 640kB. Don't use + * hide_region(), because we don't want this rounded to the + * nearest page boundary. + */ + hidemem_base.start = ( get_fbms() * 1024 ); +} + +/** + * Hide umalloc() region + * + */ +void hide_umalloc ( physaddr_t start, physaddr_t end ) { + assert ( end <= virt_to_phys ( _textdata ) ); + hide_region ( &hidemem_umalloc, start, end ); +} + +/** + * Hide .text and .data + * + */ +void hide_textdata ( void ) { + hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ), + virt_to_phys ( _etextdata ) ); +} + +/** + * Hide Etherboot + * + * Installs an INT 15 handler to edit Etherboot out of the memory map + * returned by the BIOS. + */ +static void hide_etherboot ( void ) { + struct memory_map memmap; + unsigned int rm_ds_top; + unsigned int rm_cs_top; + unsigned int fbms; + + /* Dump memory map before mangling */ + DBG ( "Hiding iPXE from system memory map\n" ); + get_memmap ( &memmap ); + + /* Hook in fake E820 map, if we're testing one */ + if ( FAKE_E820 ) { + DBG ( "Hooking in fake E820 map\n" ); + fake_e820(); + get_memmap ( &memmap ); + } + + /* Initialise the hidden regions */ + hide_basemem(); + hide_umalloc ( virt_to_phys ( _textdata ), virt_to_phys ( _textdata ) ); + hide_textdata(); + + /* Some really moronic BIOSes bring up the PXE stack via the + * UNDI loader entry point and then don't bother to unload it + * before overwriting the code and data segments. If this + * happens, we really don't want to leave INT 15 hooked, + * because that will cause any loaded OS to die horribly as + * soon as it attempts to fetch the system memory map. + * + * We use a heuristic to guess whether or not we are being + * loaded sensibly. + */ + rm_cs_top = ( ( ( rm_cs << 4 ) + _text16_memsz + 1024 - 1 ) >> 10 ); + rm_ds_top = ( ( ( rm_ds << 4 ) + _data16_memsz + 1024 - 1 ) >> 10 ); + fbms = get_fbms(); + if ( ( rm_cs_top < fbms ) && ( rm_ds_top < fbms ) ) { + DBG ( "Detected potentially unsafe UNDI load at CS=%04x " + "DS=%04x FBMS=%dkB\n", rm_cs, rm_ds, fbms ); + DBG ( "Disabling INT 15 memory hiding\n" ); + return; + } + + /* Hook INT 15 */ + hook_bios_interrupt ( 0x15, ( unsigned int ) int15, + &int15_vector ); + + /* Dump memory map after mangling */ + DBG ( "Hidden iPXE from system memory map\n" ); + get_memmap ( &memmap ); +} + +/** + * Unhide Etherboot + * + * Uninstalls the INT 15 handler installed by hide_etherboot(), if + * possible. + */ +static void unhide_etherboot ( int flags __unused ) { + struct memory_map memmap; + int rc; + + /* If we have more than one hooked interrupt at this point, it + * means that some other vector is still hooked, in which case + * we can't safely unhook INT 15 because we need to keep our + * memory protected. (We expect there to be at least one + * hooked interrupt, because INT 15 itself is still hooked). + */ + if ( hooked_bios_interrupts > 1 ) { + DBG ( "Cannot unhide: %d interrupt vectors still hooked\n", + hooked_bios_interrupts ); + return; + } + + /* Try to unhook INT 15 */ + if ( ( rc = unhook_bios_interrupt ( 0x15, ( unsigned int ) int15, + &int15_vector ) ) != 0 ) { + DBG ( "Cannot unhook INT15: %s\n", strerror ( rc ) ); + /* Leave it hooked; there's nothing else we can do, + * and it should be intrinsically safe (though + * wasteful of RAM). + */ + } + + /* Unhook fake E820 map, if used */ + if ( FAKE_E820 ) + unfake_e820(); + + /* Dump memory map after unhiding */ + DBG ( "Unhidden iPXE from system memory map\n" ); + get_memmap ( &memmap ); +} + +/** Hide Etherboot startup function */ +struct startup_fn hide_etherboot_startup_fn __startup_fn ( STARTUP_EARLY ) = { + .startup = hide_etherboot, + .shutdown = unhide_etherboot, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c new file mode 100644 index 000000000..0937a7ce2 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <errno.h> +#include <realmode.h> +#include <bios.h> +#include <memsizes.h> +#include <ipxe/io.h> + +/** + * @file + * + * Memory mapping + * + */ + +/** Magic value for INT 15,e820 calls */ +#define SMAP ( 0x534d4150 ) + +/** An INT 15,e820 memory map entry */ +struct e820_entry { + /** Start of region */ + uint64_t start; + /** Length of region */ + uint64_t len; + /** Type of region */ + uint32_t type; + /** Extended attributes (optional) */ + uint32_t attrs; +} __attribute__ (( packed )); + +#define E820_TYPE_RAM 1 /**< Normal memory */ +#define E820_TYPE_RESERVED 2 /**< Reserved and unavailable */ +#define E820_TYPE_ACPI 3 /**< ACPI reclaim memory */ +#define E820_TYPE_NVS 4 /**< ACPI NVS memory */ + +#define E820_ATTR_ENABLED 0x00000001UL +#define E820_ATTR_NONVOLATILE 0x00000002UL +#define E820_ATTR_UNKNOWN 0xfffffffcUL + +#define E820_MIN_SIZE 20 + +/** Buffer for INT 15,e820 calls */ +static struct e820_entry __bss16 ( e820buf ); +#define e820buf __use_data16 ( e820buf ) + +/** We are running during POST; inhibit INT 15,e820 and INT 15,e801 */ +uint8_t __bss16 ( memmap_post ); +#define memmap_post __use_data16 ( memmap_post ) + +/** + * Get size of extended memory via INT 15,e801 + * + * @ret extmem Extended memory size, in kB, or 0 + */ +static unsigned int extmemsize_e801 ( void ) { + uint16_t extmem_1m_to_16m_k, extmem_16m_plus_64k; + uint16_t confmem_1m_to_16m_k, confmem_16m_plus_64k; + unsigned int flags; + unsigned int extmem; + + /* Inhibit INT 15,e801 during POST */ + if ( memmap_post ) { + DBG ( "INT 15,e801 not available during POST\n" ); + return 0; + } + + __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" + "int $0x15\n\t" + "pushfw\n\t" + "popw %w0\n\t" ) + : "=r" ( flags ), + "=a" ( extmem_1m_to_16m_k ), + "=b" ( extmem_16m_plus_64k ), + "=c" ( confmem_1m_to_16m_k ), + "=d" ( confmem_16m_plus_64k ) + : "a" ( 0xe801 ) ); + + if ( flags & CF ) { + DBG ( "INT 15,e801 failed with CF set\n" ); + return 0; + } + + if ( ! ( extmem_1m_to_16m_k | extmem_16m_plus_64k ) ) { + DBG ( "INT 15,e801 extmem=0, using confmem\n" ); + extmem_1m_to_16m_k = confmem_1m_to_16m_k; + extmem_16m_plus_64k = confmem_16m_plus_64k; + } + + extmem = ( extmem_1m_to_16m_k + ( extmem_16m_plus_64k * 64 ) ); + DBG ( "INT 15,e801 extended memory size %d+64*%d=%d kB " + "[100000,%llx)\n", extmem_1m_to_16m_k, extmem_16m_plus_64k, + extmem, ( 0x100000 + ( ( ( uint64_t ) extmem ) * 1024 ) ) ); + + /* Sanity check. Some BIOSes report the entire 4GB address + * space as available, which cannot be correct (since that + * would leave no address space available for 32-bit PCI + * BARs). + */ + if ( extmem == ( 0x400000 - 0x400 ) ) { + DBG ( "INT 15,e801 reported whole 4GB; assuming insane\n" ); + return 0; + } + + return extmem; +} + +/** + * Get size of extended memory via INT 15,88 + * + * @ret extmem Extended memory size, in kB + */ +static unsigned int extmemsize_88 ( void ) { + uint16_t extmem; + + /* Ignore CF; it is not reliable for this call */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x15" ) + : "=a" ( extmem ) : "a" ( 0x8800 ) ); + + DBG ( "INT 15,88 extended memory size %d kB [100000, %x)\n", + extmem, ( 0x100000 + ( extmem * 1024 ) ) ); + return extmem; +} + +/** + * Get size of extended memory + * + * @ret extmem Extended memory size, in kB + * + * Note that this is only an approximation; for an accurate picture, + * use the E820 memory map obtained via get_memmap(); + */ +unsigned int extmemsize ( void ) { + unsigned int extmem_e801; + unsigned int extmem_88; + + /* Try INT 15,e801 first, then fall back to INT 15,88 */ + extmem_88 = extmemsize_88(); + extmem_e801 = extmemsize_e801(); + return ( extmem_e801 ? extmem_e801 : extmem_88 ); +} + +/** + * Get e820 memory map + * + * @v memmap Memory map to fill in + * @ret rc Return status code + */ +static int meme820 ( struct memory_map *memmap ) { + struct memory_region *region = memmap->regions; + struct memory_region *prev_region = NULL; + uint32_t next = 0; + uint32_t smap; + size_t size; + unsigned int flags; + unsigned int discard_D; + + /* Inhibit INT 15,e820 during POST */ + if ( memmap_post ) { + DBG ( "INT 15,e820 not available during POST\n" ); + return -ENOTTY; + } + + /* Clear the E820 buffer. Do this once before starting, + * rather than on each call; some BIOSes rely on the contents + * being preserved between calls. + */ + memset ( &e820buf, 0, sizeof ( e820buf ) ); + + do { + /* Some BIOSes corrupt %esi for fun. Guard against + * this by telling gcc that all non-output registers + * may be corrupted. + */ + __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" + "stc\n\t" + "int $0x15\n\t" + "pushfw\n\t" + "popw %%dx\n\t" + "popl %%ebp\n\t" ) + : "=a" ( smap ), "=b" ( next ), + "=c" ( size ), "=d" ( flags ), + "=D" ( discard_D ) + : "a" ( 0xe820 ), "b" ( next ), + "D" ( __from_data16 ( &e820buf ) ), + "c" ( sizeof ( e820buf ) ), + "d" ( SMAP ) + : "esi", "memory" ); + + if ( smap != SMAP ) { + DBG ( "INT 15,e820 failed SMAP signature check\n" ); + return -ENOTSUP; + } + + if ( size < E820_MIN_SIZE ) { + DBG ( "INT 15,e820 returned only %zd bytes\n", size ); + return -EINVAL; + } + + if ( flags & CF ) { + DBG ( "INT 15,e820 terminated on CF set\n" ); + break; + } + + /* If first region is not RAM, assume map is invalid */ + if ( ( memmap->count == 0 ) && + ( e820buf.type != E820_TYPE_RAM ) ) { + DBG ( "INT 15,e820 failed, first entry not RAM\n" ); + return -EINVAL; + } + + DBG ( "INT 15,e820 region [%llx,%llx) type %d", + e820buf.start, ( e820buf.start + e820buf.len ), + ( int ) e820buf.type ); + if ( size > offsetof ( typeof ( e820buf ), attrs ) ) { + DBG ( " (%s", ( ( e820buf.attrs & E820_ATTR_ENABLED ) + ? "enabled" : "disabled" ) ); + if ( e820buf.attrs & E820_ATTR_NONVOLATILE ) + DBG ( ", non-volatile" ); + if ( e820buf.attrs & E820_ATTR_UNKNOWN ) + DBG ( ", other [%08x]", e820buf.attrs ); + DBG ( ")" ); + } + DBG ( "\n" ); + + /* Discard non-RAM regions */ + if ( e820buf.type != E820_TYPE_RAM ) + continue; + + /* Check extended attributes, if present */ + if ( size > offsetof ( typeof ( e820buf ), attrs ) ) { + if ( ! ( e820buf.attrs & E820_ATTR_ENABLED ) ) + continue; + if ( e820buf.attrs & E820_ATTR_NONVOLATILE ) + continue; + } + + region->start = e820buf.start; + region->end = e820buf.start + e820buf.len; + + /* Check for adjacent regions and merge them */ + if ( prev_region && ( region->start == prev_region->end ) ) { + prev_region->end = region->end; + } else { + prev_region = region; + region++; + memmap->count++; + } + + if ( memmap->count >= ( sizeof ( memmap->regions ) / + sizeof ( memmap->regions[0] ) ) ) { + DBG ( "INT 15,e820 too many regions returned\n" ); + /* Not a fatal error; what we've got so far at + * least represents valid regions of memory, + * even if we couldn't get them all. + */ + break; + } + } while ( next != 0 ); + + /* Sanity checks. Some BIOSes report complete garbage via INT + * 15,e820 (especially at POST time), despite passing the + * signature checks. We currently check for a base memory + * region (starting at 0) and at least one high memory region + * (starting at 0x100000). + */ + if ( memmap->count < 2 ) { + DBG ( "INT 15,e820 returned only %d regions; assuming " + "insane\n", memmap->count ); + return -EINVAL; + } + if ( memmap->regions[0].start != 0 ) { + DBG ( "INT 15,e820 region 0 starts at %llx (expected 0); " + "assuming insane\n", memmap->regions[0].start ); + return -EINVAL; + } + if ( memmap->regions[1].start != 0x100000 ) { + DBG ( "INT 15,e820 region 1 starts at %llx (expected 100000); " + "assuming insane\n", memmap->regions[0].start ); + return -EINVAL; + } + + return 0; +} + +/** + * Get memory map + * + * @v memmap Memory map to fill in + */ +void x86_get_memmap ( struct memory_map *memmap ) { + unsigned int basemem, extmem; + int rc; + + DBG ( "Fetching system memory map\n" ); + + /* Clear memory map */ + memset ( memmap, 0, sizeof ( *memmap ) ); + + /* Get base and extended memory sizes */ + basemem = basememsize(); + DBG ( "FBMS base memory size %d kB [0,%x)\n", + basemem, ( basemem * 1024 ) ); + extmem = extmemsize(); + + /* Try INT 15,e820 first */ + if ( ( rc = meme820 ( memmap ) ) == 0 ) { + DBG ( "Obtained system memory map via INT 15,e820\n" ); + return; + } + + /* Fall back to constructing a map from basemem and extmem sizes */ + DBG ( "INT 15,e820 failed; constructing map\n" ); + memmap->regions[0].end = ( basemem * 1024 ); + memmap->regions[1].start = 0x100000; + memmap->regions[1].end = 0x100000 + ( extmem * 1024 ); + memmap->count = 2; +} + +PROVIDE_IOAPI ( x86, get_memmap, x86_get_memmap ); diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c new file mode 100644 index 000000000..5c74b0431 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <realmode.h> +#include <pnpbios.h> + +/** @file + * + * PnP BIOS + * + */ + +/** PnP BIOS structure */ +struct pnp_bios { + /** Signature + * + * Must be equal to @c PNP_BIOS_SIGNATURE + */ + uint32_t signature; + /** Version as BCD (e.g. 1.0 is 0x10) */ + uint8_t version; + /** Length of this structure */ + uint8_t length; + /** System capabilities */ + uint16_t control; + /** Checksum */ + uint8_t checksum; +} __attribute__ (( packed )); + +/** Signature for a PnP BIOS structure */ +#define PNP_BIOS_SIGNATURE \ + ( ( '$' << 0 ) + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) ) + +/** + * Test address for PnP BIOS structure + * + * @v offset Offset within BIOS segment to test + * @ret rc Return status code + */ +static int is_pnp_bios ( unsigned int offset ) { + union { + struct pnp_bios pnp_bios; + uint8_t bytes[256]; /* 256 is maximum length possible */ + } u; + size_t len; + unsigned int i; + uint8_t sum = 0; + + /* Read start of header and verify signature */ + copy_from_real ( &u.pnp_bios, BIOS_SEG, offset, sizeof ( u.pnp_bios )); + if ( u.pnp_bios.signature != PNP_BIOS_SIGNATURE ) + return -EINVAL; + + /* Read whole header and verify checksum */ + len = u.pnp_bios.length; + copy_from_real ( &u.bytes, BIOS_SEG, offset, len ); + for ( i = 0 ; i < len ; i++ ) { + sum += u.bytes[i]; + } + if ( sum != 0 ) + return -EINVAL; + + DBG ( "Found PnP BIOS at %04x:%04x\n", BIOS_SEG, offset ); + + return 0; +} + +/** + * Locate Plug-and-Play BIOS + * + * @ret pnp_offset Offset of PnP BIOS structure within BIOS segment + * + * The PnP BIOS structure will be at BIOS_SEG:pnp_offset. If no PnP + * BIOS is found, -1 is returned. + */ +int find_pnp_bios ( void ) { + static int pnp_offset = 0; + + if ( pnp_offset ) + return pnp_offset; + + for ( pnp_offset = 0 ; pnp_offset < 0x10000 ; pnp_offset += 0x10 ) { + if ( is_pnp_bios ( pnp_offset ) == 0 ) + return pnp_offset; + } + + pnp_offset = -1; + return pnp_offset; +} |