diff options
| author | Michael Brown | 2016-02-16 16:19:01 +0100 |
|---|---|---|
| committer | Michael Brown | 2016-02-16 20:32:32 +0100 |
| commit | f468f12b1eca15e703aa2a79f1c82969c04c2322 (patch) | |
| tree | f1868e6cb7debaeb7aca59e4488b603fcc6481d7 /src/arch/x86/prefix | |
| parent | [bios] Move isolinux definitions to Makefile.pcbios (diff) | |
| download | ipxe-f468f12b1eca15e703aa2a79f1c82969c04c2322.tar.gz ipxe-f468f12b1eca15e703aa2a79f1c82969c04c2322.tar.xz ipxe-f468f12b1eca15e703aa2a79f1c82969c04c2322.zip | |
[bios] Add bin-x86_64-pcbios build platform
Move most arch/i386 files to arch/x86, and adjust the contents of the
Makefiles and the include/bits/*.h headers to reflect the new
locations.
This patch makes no substantive code changes, as can be seen using a
rename-aware diff (e.g. "git show -M5").
This patch does not make the pcbios platform functional for x86_64; it
merely allows it to compile without errors.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/arch/x86/prefix')
| -rw-r--r-- | src/arch/x86/prefix/bootpart.S | 218 | ||||
| -rw-r--r-- | src/arch/x86/prefix/dskprefix.S | 383 | ||||
| -rw-r--r-- | src/arch/x86/prefix/exeprefix.S | 161 | ||||
| -rw-r--r-- | src/arch/x86/prefix/hdprefix.S | 111 | ||||
| -rw-r--r-- | src/arch/x86/prefix/isaromprefix.S | 29 | ||||
| -rw-r--r-- | src/arch/x86/prefix/kkkpxeprefix.S | 17 | ||||
| -rw-r--r-- | src/arch/x86/prefix/kkpxeprefix.S | 11 | ||||
| -rw-r--r-- | src/arch/x86/prefix/kpxeprefix.S | 10 | ||||
| -rw-r--r-- | src/arch/x86/prefix/libprefix.S | 1013 | ||||
| -rw-r--r-- | src/arch/x86/prefix/lkrnprefix.S | 236 | ||||
| -rw-r--r-- | src/arch/x86/prefix/mbr.S | 15 | ||||
| -rw-r--r-- | src/arch/x86/prefix/mromprefix.S | 516 | ||||
| -rw-r--r-- | src/arch/x86/prefix/nbiprefix.S | 84 | ||||
| -rw-r--r-- | src/arch/x86/prefix/nullprefix.S | 15 | ||||
| -rw-r--r-- | src/arch/x86/prefix/pciromprefix.S | 29 | ||||
| -rw-r--r-- | src/arch/x86/prefix/pxeprefix.S | 862 | ||||
| -rw-r--r-- | src/arch/x86/prefix/romprefix.S | 907 | ||||
| -rw-r--r-- | src/arch/x86/prefix/undiloader.S | 54 | ||||
| -rw-r--r-- | src/arch/x86/prefix/unlzma.S | 942 | ||||
| -rw-r--r-- | src/arch/x86/prefix/unlzma16.S | 9 | ||||
| -rw-r--r-- | src/arch/x86/prefix/usbdisk.S | 34 |
21 files changed, 5656 insertions, 0 deletions
diff --git a/src/arch/x86/prefix/bootpart.S b/src/arch/x86/prefix/bootpart.S new file mode 100644 index 000000000..6d0c6034a --- /dev/null +++ b/src/arch/x86/prefix/bootpart.S @@ -0,0 +1,218 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +#define BOOT_SEG 0x07c0 +#define EXEC_SEG 0x0100 +#define STACK_SEG 0x0200 +#define STACK_SIZE 0x2000 + + .text + .arch i386 + .section ".prefix", "awx", @progbits + .code16 + +/* + * Find active partition + * + * Parameters: + * %dl : BIOS drive number + * %bp : Active partition handler routine + */ +find_active_partition: + /* Set up stack at STACK_SEG:STACK_SIZE */ + movw $STACK_SEG, %ax + movw %ax, %ss + movw $STACK_SIZE, %sp + + /* Relocate self to EXEC_SEG */ + pushw $BOOT_SEG + popw %ds + pushw $EXEC_SEG + popw %es + xorw %si, %si + xorw %di, %di + movw $0x200, %cx + rep movsb + ljmp $EXEC_SEG, $1f +1: pushw %ds + popw %es + pushw %cs + popw %ds + + /* Check for LBA extensions */ + movb $0x41, %ah + movw $0x55aa, %bx + stc + int $0x13 + jc 1f + cmpw $0xaa55, %bx + jne 1f + movw $read_lba, read_sectors +1: + /* Read and process root partition table */ + xorb %dh, %dh + movw $0x0001, %cx + xorl %esi, %esi + xorl %edi, %edi + call process_table + + /* Print failure message */ + movw $10f, %si + jmp boot_error +10: .asciz "Could not locate active partition\r\n" + +/* + * Print failure message and boot next device + * + * Parameters: + * %si : Failure string + */ +boot_error: + cld + movw $0x0007, %bx + movb $0x0e, %ah +1: lodsb + testb %al, %al + je 99f + int $0x10 + jmp 1b +99: /* Boot next device */ + int $0x18 + +/* + * Process partition table + * + * Parameters: + * %dl : BIOS drive number + * %dh : Head + * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7) + * %ch : Low eight bits of cylinder + * %esi:%edi : LBA address + * %bp : Active partition handler routine + * + * Returns: + * CF set on error + */ +process_table: + pushal + call read_boot_sector + jc 99f + movw $446, %bx +1: call process_partition + addw $16, %bx + cmpw $510, %bx + jne 1b +99: popal + ret + +/* + * Process partition + * + * Parameters: + * %dl : BIOS drive number + * %dh : Head + * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7) + * %ch : Low eight bits of cylinder + * %esi:%edi : LBA address + * %bx : Offset within partition table + * %bp : Active partition handler routine + */ +process_partition: + pushal + /* Load C/H/S values from partition entry */ + movb %es:1(%bx), %dh + movw %es:2(%bx), %cx + /* Update LBA address from partition entry */ + addl %es:8(%bx), %edi + adcl $0, %esi + /* Check active flag */ + testb $0x80, %es:(%bx) + jz 1f + call read_boot_sector + jc 99f + jmp *%bp +1: /* Check for extended partition */ + movb %es:4(%bx), %al + cmpb $0x05, %al + je 2f + cmpb $0x0f, %al + je 2f + cmpb $0x85, %al + jne 99f +2: call process_table +99: popal + /* Reload original partition table */ + call read_boot_sector + ret + +/* + * Read single sector to %es:0000 and verify 0x55aa signature + * + * Parameters: + * %dl : BIOS drive number + * %dh : Head + * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7) + * %ch : Low eight bits of cylinder + * %esi:%edi : LBA address + * + * Returns: + * CF set on error + */ +read_boot_sector: + pushw %ax + movw $1, %ax + call *read_sectors + jc 99f + cmpw $0xaa55, %es:(510) + je 99f + stc +99: popw %ax + ret + +/* + * Read sectors to %es:0000 + * + * Parameters: + * %dl : BIOS drive number + * %dh : Head + * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7) + * %ch : Low eight bits of cylinder + * %esi:%edi : LBA address + * %ax : Number of sectors (max 127) + * + * Returns: + * CF set on error + */ +read_sectors: .word read_chs + +read_chs: + /* Read sectors using C/H/S address */ + pushal + xorw %bx, %bx + movb $0x02, %ah + stc + int $0x13 + sti + popal + ret + +read_lba: + /* Read sectors using LBA address */ + pushal + movw %ax, (lba_desc + 2) + pushw %es + popw (lba_desc + 6) + movl %edi, (lba_desc + 8) + movl %esi, (lba_desc + 12) + movw $lba_desc, %si + movb $0x42, %ah + int $0x13 + popal + ret + +lba_desc: + .byte 0x10 + .byte 0 + .word 1 + .word 0x0000 + .word 0x0000 + .long 0, 0 diff --git a/src/arch/x86/prefix/dskprefix.S b/src/arch/x86/prefix/dskprefix.S new file mode 100644 index 000000000..7aa017ccd --- /dev/null +++ b/src/arch/x86/prefix/dskprefix.S @@ -0,0 +1,383 @@ +/* NOTE: this boot sector contains instructions that need at least an 80186. + * Yes, as86 has a bug somewhere in the valid instruction set checks. + * + */ + +/* floppyload.S Copyright (C) 1991, 1992 Linus Torvalds + * modified by Drew Eckhardt + * modified by Bruce Evans (bde) + * + * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines. + * + * It then loads the system at SYSSEG<<4, using BIOS interrupts. + * + * The loader has been made as simple as possible, and continuous read errors + * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by + * getting whole tracks at a time whenever possible. + */ + +FILE_LICENCE ( GPL2_ONLY ) + +.equ BOOTSEG, 0x07C0 /* original address of boot-sector */ + +.equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */ + + .org 0 + .arch i386 + .text + .section ".prefix", "ax", @progbits + .code16 + .globl _dsk_start +_dsk_start: + + jmp $BOOTSEG, $go /* reload cs:ip to match relocation addr */ +go: + movw $0x2000-12, %di /* 0x2000 is arbitrary value >= length */ + /* of bootsect + room for stack + 12 for */ + /* saved disk parm block */ + + movw $BOOTSEG, %ax + movw %ax,%ds + movw %ax,%es + movw %ax,%ss /* put stack at BOOTSEG:0x4000-12. */ + movw %di,%sp + +/* Many BIOS's default disk parameter tables will not recognize multi-sector + * reads beyond the maximum sector number specified in the default diskette + * parameter tables - this may mean 7 sectors in some cases. + * + * Since single sector reads are slow and out of the question, we must take care + * of this by creating new parameter tables (for the first disk) in RAM. We + * will set the maximum sector count to 36 - the most we will encounter on an + * ED 2.88. High doesn't hurt. Low does. + * + * Segments are as follows: ds=es=ss=cs - BOOTSEG + */ + + xorw %cx,%cx + movw %cx,%es /* access segment 0 */ + movw $0x78, %bx /* 0:bx is parameter table address */ + pushw %ds /* save ds */ +/* 0:bx is parameter table address */ + ldsw %es:(%bx),%si /* loads ds and si */ + + movw %ax,%es /* ax is BOOTSECT (loaded above) */ + movb $6, %cl /* copy 12 bytes */ + cld + pushw %di /* keep a copy for later */ + rep + movsw /* ds:si is source, es:di is dest */ + popw %di + + movb $36,%es:4(%di) + + movw %cx,%ds /* access segment 0 */ + xchgw %di,(%bx) + movw %es,%si + xchgw %si,2(%bx) + popw %ds /* restore ds */ + movw %di, dpoff /* save old parameters */ + movw %si, dpseg /* to restore just before finishing */ + pushw %ds + popw %es /* reload es */ + +/* Note that es is already set up. Also cx is 0 from rep movsw above. */ + + xorb %ah,%ah /* reset FDC */ + xorb %dl,%dl + int $0x13 + +/* Get disk drive parameters, specifically number of sectors/track. + * + * It seems that there is no BIOS call to get the number of sectors. Guess + * 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read, + * 15 if sector 15 can be read. Otherwise guess 9. + */ + + movw $disksizes, %si /* table of sizes to try */ + +probe_loop: + lodsb + cbtw /* extend to word */ + movw %ax, sectors + cmpw $disksizes+4, %si + jae got_sectors /* if all else fails, try 9 */ + xchgw %cx,%ax /* cx = track and sector */ + xorw %dx,%dx /* drive 0, head 0 */ + movw $0x0200, %bx /* address after boot sector */ + /* (512 bytes from origin, es = cs) */ + movw $0x0201, %ax /* service 2, 1 sector */ + int $0x13 + jc probe_loop /* try next value */ + +got_sectors: + movw $msg1end-msg1, %cx + movw $msg1, %si + call print_str + +/* ok, we've written the Loading... message, now we want to load the system */ + + movw $SYSSEG, %ax + movw %ax,%es /* segment of SYSSEG<<4 */ + pushw %es + call read_it + +/* This turns off the floppy drive motor, so that we enter the kernel in a + * known state, and don't have to worry about it later. + */ + movw $0x3f2, %dx + xorb %al,%al + outb %al,%dx + + call print_nl + pop %es /* = SYSSEG */ + +/* Restore original disk parameters */ + movw $0x78, %bx + movw dpoff, %di + movw dpseg, %si + xorw %ax,%ax + movw %ax,%ds + movw %di,(%bx) + movw %si,2(%bx) + + /* Everything now loaded. %es = SYSSEG, so %es:0000 points to + * start of loaded image. + */ + + /* Jump to loaded copy */ + ljmp $SYSSEG, $start_runtime + +endseg: .word SYSSEG + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADDW" + .long endseg + .long 16 + .long 0 + .previous + +/* This routine loads the system at address SYSSEG<<4, making sure no 64kB + * boundaries are crossed. We try to load it as fast as possible, loading whole + * tracks whenever we can. + * + * in: es - starting address segment (normally SYSSEG) + */ +read_it: + movw $0,sread /* load whole image including prefix */ + movw %es,%ax + testw $0x0fff, %ax +die: jne die /* es must be at 64kB boundary */ + xorw %bx,%bx /* bx is starting address within segment */ +rp_read: + movw %es,%ax + movw %bx,%dx + movb $4, %cl + shrw %cl,%dx /* bx is always divisible by 16 */ + addw %dx,%ax + cmpw endseg, %ax /* have we loaded all yet? */ + jb ok1_read + ret +ok1_read: + movw sectors, %ax + subw sread, %ax + movw %ax,%cx + shlw $9, %cx + addw %bx,%cx + jnc ok2_read + je ok2_read + xorw %ax,%ax + subw %bx,%ax + shrw $9, %ax +ok2_read: + call read_track + movw %ax,%cx + addw sread, %ax + cmpw sectors, %ax + jne ok3_read + movw $1, %ax + subw head, %ax + jne ok4_read + incw track +ok4_read: + movw %ax, head + xorw %ax,%ax +ok3_read: + movw %ax, sread + shlw $9, %cx + addw %cx,%bx + jnc rp_read + movw %es,%ax + addb $0x10, %ah + movw %ax,%es + xorw %bx,%bx + jmp rp_read + +read_track: + pusha + pushw %ax + pushw %bx + pushw %bp /* just in case the BIOS is buggy */ + movw $0x0e2e, %ax /* 0x2e = . */ + movw $0x0007, %bx + int $0x10 + popw %bp + popw %bx + popw %ax + + movw track, %dx + movw sread, %cx + incw %cx + movb %dl,%ch + movw head, %dx + movb %dl,%dh + andw $0x0100, %dx + movb $2, %ah + + pushw %dx /* save for error dump */ + pushw %cx + pushw %bx + pushw %ax + + int $0x13 + jc bad_rt + addw $8, %sp + popa + ret + +bad_rt: pushw %ax /* save error code */ + call print_all /* ah = error, al = read */ + + xorb %ah,%ah + xorb %dl,%dl + int $0x13 + + addw $10, %sp + popa + jmp read_track + +/* print_all is for debugging purposes. It will print out all of the registers. + * The assumption is that this is called from a routine, with a stack frame like + * dx + * cx + * bx + * ax + * error + * ret <- sp + */ + +print_all: + call print_nl /* nl for readability */ + movw $5, %cx /* error code + 4 registers */ + movw %sp,%bp + +print_loop: + pushw %cx /* save count left */ + + cmpb $5, %cl + jae no_reg /* see if register name is needed */ + + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movw $0xe05+0x41-1, %ax + subb %cl,%al + int $0x10 + + movb $0x58, %al /* 'X' */ + int $0x10 + + movb $0x3A, %al /* ':' */ + int $0x10 + +no_reg: + addw $2, %bp /* next register */ + call print_hex /* print it */ + movb $0x20, %al /* print a space */ + int $0x10 + popw %cx + loop print_loop + call print_nl /* nl for readability */ + ret + +print_str: + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ +prloop: + lodsb + int $0x10 + loop prloop + ret + +print_nl: + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movw $0xe0d, %ax /* CR */ + int $0x10 + movb $0xa, %al /* LF */ + int $0x10 + ret + +/* print_hex prints the word pointed to by ss:bp in hexadecimal. */ + +print_hex: + movw (%bp),%dx /* load word into dx */ + movb $4, %cl + movb $0x0e, %ah /* write char, tty mode */ + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + call print_digit + call print_digit + call print_digit +/* fall through */ +print_digit: + rol %cl,%dx /* rotate so that lowest 4 bits are used */ + movb $0x0f, %al /* mask for nybble */ + andb %dl,%al + addb $0x90, %al /* convert al to ascii hex (four instructions) */ + daa + adcb $0x40, %al + daa + int $0x10 + ret + +sread: .word 0 /* sectors read of current track */ +head: .word 0 /* current head */ +track: .word 0 /* current track */ + +sectors: + .word 0 + +dpseg: .word 0 +dpoff: .word 0 + +disksizes: + .byte 36,18,15,9 + +msg1: + .ascii "Loading ROM image" +msg1end: + + .org 510, 0 + .word 0xAA55 + +start_runtime: + /* Install iPXE */ + call install + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16", "awx", @progbits +1: + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall iPXE */ + call uninstall + + /* Boot next device */ + int $0x18 + diff --git a/src/arch/x86/prefix/exeprefix.S b/src/arch/x86/prefix/exeprefix.S new file mode 100644 index 000000000..5c648d51d --- /dev/null +++ b/src/arch/x86/prefix/exeprefix.S @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2011 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +/* Initial temporary stack size */ +#define EXE_STACK_SIZE 0x400 + +/* Temporary decompression area (avoid DOS high memory area) */ +#define EXE_DECOMPRESS_ADDRESS 0x110000 + +/* Fields within the Program Segment Prefix */ +#define PSP_CMDLINE_LEN 0x80 +#define PSP_CMDLINE_START 0x81 + + .text + .arch i386 + .org 0 + .code16 + .section ".prefix", "awx", @progbits + +signature: + /* "MZ" signature */ + .ascii "MZ" + +last_block: + /* Number of bytes in last block that are really used */ + .word 0 + +blocks: + /* Number of 512-byte blocks */ + .word 0 + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADDW" + .long blocks + .long 512 + .long 0 + .previous + +num_reloc: + /* Number of relocation entries stored after the header */ + .word 0 + +header_pgh: + /* Number of paragraphs in the header */ + .word ( ( _exe_start - signature ) / 16 ) + +min_bss_pgh: + /* Minimum number of paragraphs of additional (BSS) memory */ + .word ( EXE_STACK_SIZE / 16 ) + +max_bss_pgh: + /* Maximum number of paragraphs of additional (BSS) memory */ + .word ( EXE_STACK_SIZE / 16 ) + +init_ss: + /* Initial stack segment (relative to start of executable) */ + .word -( ( _exe_start - signature ) / 16 ) + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADDW" + .long init_ss + .long 16 + .long 0 + .previous + +init_sp: + /* Initial stack pointer */ + .word EXE_STACK_SIZE + +checksum: + /* Checksum (ignored) */ + .word 0 + +init_ip: + /* Initial instruction pointer */ + .word _exe_start + +init_cs: + /* Initial code segment (relative to start of executable) */ + .word -( ( _exe_start - signature ) / 16 ) + +reloc_table: + /* Relocation table offset */ + .word 0 + +overlay: + /* Overlay number */ + .word 0 + + .align 16, 0 + + .globl _exe_start +_exe_start: + /* Install iPXE. Use a fixed temporary decompression area to + * avoid trashing the DOS high memory area. + */ + call alloc_basemem + xorl %esi, %esi + movl $EXE_DECOMPRESS_ADDRESS, %edi + orl $0xffffffff, %ebp /* Allow arbitrary relocation */ + call install_prealloc + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16", "awx", @progbits +1: + /* Terminate command line with a NUL */ + movzbw PSP_CMDLINE_LEN, %si + movb $0, PSP_CMDLINE_START(%si) + + /* Calculate command line physical address */ + xorl %esi, %esi + movw %ds, %si + shll $4, %esi + addl $PSP_CMDLINE_START, %esi + + /* Set up %ds for access to .data16 */ + movw %bx, %ds + + /* Record command line address */ + movl %esi, cmdline_phys + + /* Run iPXE */ + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall iPXE */ + call uninstall + + /* Exit back to DOS. This is very unlikely to work */ + movw $0x4c00, %ax + int $0x21 diff --git a/src/arch/x86/prefix/hdprefix.S b/src/arch/x86/prefix/hdprefix.S new file mode 100644 index 000000000..1d012d80b --- /dev/null +++ b/src/arch/x86/prefix/hdprefix.S @@ -0,0 +1,111 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .arch i386 + .section ".prefix", "awx", @progbits + .code16 + .org 0 + .globl _hd_start +_hd_start: + + movw $load_image, %bp + jmp find_active_partition + +#include "bootpart.S" + +load_image: + /* Get disk geometry */ + pushal + pushw %es + movb $0x08, %ah + int $0x13 + jc load_failed + movb %cl, max_sector + movb %dh, max_head + popw %es + popal + +1: /* Read to end of current track */ + movb %cl, %al + negb %al + addb max_sector, %al + incb %al + andb $0x3f, %al + movzbl %al, %eax + call *read_sectors + jc load_failed + + /* Update %es */ + movw %es, %bx + shll $5, %eax + addw %ax, %bx + movw %bx, %es + shrl $5, %eax + + /* Update LBA address */ + addl %eax, %edi + adcl $0, %esi + + /* Update CHS address */ + andb $0xc0, %cl + orb $0x01, %cl + incb %dh + cmpb max_head, %dh + jbe 2f + xorb %dh, %dh + incb %ch + jnc 2f + addb $0xc0, %cl +2: + /* Loop until whole image is read */ + subl %eax, load_length + ja 1b + ljmp $BOOT_SEG, $start_image + +max_sector: + .byte 0 +max_head: + .byte 0 +load_length: + .long 0 + + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADDL" + .long load_length + .long 512 + .long 0 + .previous + + +load_failed: + movw $10f, %si + jmp boot_error +10: .asciz "Could not load iPXE\r\n" + + .org 510 + .byte 0x55, 0xaa + +start_image: + /* Install iPXE */ + call install + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16", "awx", @progbits +1: + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall iPXE */ + call uninstall + + /* Boot next device */ + int $0x18 diff --git a/src/arch/x86/prefix/isaromprefix.S b/src/arch/x86/prefix/isaromprefix.S new file mode 100644 index 000000000..fb49819ee --- /dev/null +++ b/src/arch/x86/prefix/isaromprefix.S @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +#define BUSTYPE "ISAR" +#define _rom_start _isarom_start +#include "romprefix.S" diff --git a/src/arch/x86/prefix/kkkpxeprefix.S b/src/arch/x86/prefix/kkkpxeprefix.S new file mode 100644 index 000000000..6e43cd26a --- /dev/null +++ b/src/arch/x86/prefix/kkkpxeprefix.S @@ -0,0 +1,17 @@ +/***************************************************************************** + * PXE prefix that keeps the whole PXE stack present and provides an exit hook + * + * This prefix is essentially intended solely for the case of ipxelinux.0 + ***************************************************************************** + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +/* Provide the PXENV_FILE_EXIT_HOOK API call */ +REQUIRING_SYMBOL ( _kkkpxe_start ) +REQUIRE_OBJECT ( pxe_exit_hook ) + +#define PXELOADER_KEEP_UNDI +#define PXELOADER_KEEP_PXE +#define _pxe_start _kkkpxe_start +#include "pxeprefix.S" diff --git a/src/arch/x86/prefix/kkpxeprefix.S b/src/arch/x86/prefix/kkpxeprefix.S new file mode 100644 index 000000000..3c17dbdb1 --- /dev/null +++ b/src/arch/x86/prefix/kkpxeprefix.S @@ -0,0 +1,11 @@ +/***************************************************************************** + * PXE prefix that keeps the whole PXE stack present + ***************************************************************************** + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +#define PXELOADER_KEEP_UNDI +#define PXELOADER_KEEP_PXE +#define _pxe_start _kkpxe_start +#include "pxeprefix.S" diff --git a/src/arch/x86/prefix/kpxeprefix.S b/src/arch/x86/prefix/kpxeprefix.S new file mode 100644 index 000000000..200006d83 --- /dev/null +++ b/src/arch/x86/prefix/kpxeprefix.S @@ -0,0 +1,10 @@ +/***************************************************************************** + * PXE prefix that keep the UNDI portion of the PXE stack present + ***************************************************************************** + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +#define PXELOADER_KEEP_UNDI +#define _pxe_start _kpxe_start +#include "pxeprefix.S" diff --git a/src/arch/x86/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S new file mode 100644 index 000000000..3cdb6ec9a --- /dev/null +++ b/src/arch/x86/prefix/libprefix.S @@ -0,0 +1,1013 @@ +/* + * 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .arch i386 + +/* Image compression enabled */ +#define COMPRESS 1 + +/* Protected mode flag */ +#define CR0_PE 1 + +/* Allow for DBG()-style messages within libprefix */ +#ifdef NDEBUG + .macro progress message + .endm +#else + .macro progress message + pushfl + pushw %ds + pushw %si + pushw %di + pushw %cs + popw %ds + xorw %di, %di + movw $progress_\@, %si + call print_message + popw %di + popw %si + popw %ds + popfl + .section ".prefix.data", "aw", @progbits +progress_\@: + .asciz "\message" + .size progress_\@, . - progress_\@ + .previous + .endm +#endif + +/***************************************************************************** + * Utility function: print character (with LF -> LF,CR translation) + * + * Parameters: + * %al : character to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl print_character +print_character: + /* Preserve registers */ + pushw %ax + pushw %bx + pushw %bp + /* If %di is non-zero, write character to buffer and exit */ + testw %di, %di + jz 1f + movb %al, %ds:(%di) + incw %di + jmp 3f +1: /* Print character */ + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ + cmpb $0x0a, %al /* '\n'? */ + jne 2f + int $0x10 + movb $0x0d, %al +2: int $0x10 + /* Restore registers and return */ +3: popw %bp + popw %bx + popw %ax + ret + .size print_character, . - print_character + +/***************************************************************************** + * Utility function: print space + * + * Parameters: + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl print_space +print_space: + /* Preserve registers */ + pushw %ax + /* Print space */ + movb $( ' ' ), %al + call print_character + /* Restore registers and return */ + popw %ax + ret + .size print_space, . - print_space + +/***************************************************************************** + * Utility function: print a NUL-terminated string + * + * Parameters: + * %ds:si : string to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:si : character after terminating NUL + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl print_message +print_message: + /* Preserve registers */ + pushw %ax + /* Print string */ +1: lodsb + testb %al, %al + je 2f + call print_character + jmp 1b +2: /* Restore registers and return */ + popw %ax + ret + .size print_message, . - print_message + +/***************************************************************************** + * Utility functions: print hex digit/byte/word/dword + * + * Parameters: + * %al (low nibble) : digit to print + * %al : byte to print + * %ax : word to print + * %eax : dword to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl print_hex_dword +print_hex_dword: + rorl $16, %eax + call print_hex_word + rorl $16, %eax + /* Fall through */ + .size print_hex_dword, . - print_hex_dword + .globl print_hex_word +print_hex_word: + xchgb %al, %ah + call print_hex_byte + xchgb %al, %ah + /* Fall through */ + .size print_hex_word, . - print_hex_word + .globl print_hex_byte +print_hex_byte: + rorb $4, %al + call print_hex_nibble + rorb $4, %al + /* Fall through */ + .size print_hex_byte, . - print_hex_byte + .globl print_hex_nibble +print_hex_nibble: + /* Preserve registers */ + pushw %ax + /* Print digit (technique by Norbert Juffa <norbert.juffa@amd.com> */ + andb $0x0f, %al + cmpb $10, %al + sbbb $0x69, %al + das + call print_character + /* Restore registers and return */ + popw %ax + ret + .size print_hex_nibble, . - print_hex_nibble + +/***************************************************************************** + * Utility function: print PCI bus:dev.fn + * + * Parameters: + * %ax : PCI bus:dev.fn to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl print_pci_busdevfn +print_pci_busdevfn: + /* Preserve registers */ + pushw %ax + /* Print bus */ + xchgb %al, %ah + call print_hex_byte + /* Print ":" */ + movb $( ':' ), %al + call print_character + /* Print device */ + movb %ah, %al + shrb $3, %al + call print_hex_byte + /* Print "." */ + movb $( '.' ), %al + call print_character + /* Print function */ + movb %ah, %al + andb $0x07, %al + call print_hex_nibble + /* Restore registers and return */ + popw %ax + ret + .size print_pci_busdevfn, . - print_pci_busdevfn + +/***************************************************************************** + * Utility function: clear current line + * + * Parameters: + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl print_kill_line +print_kill_line: + /* Preserve registers */ + pushw %ax + pushw %cx + /* Print CR */ + movb $( '\r' ), %al + call print_character + /* Print 79 spaces */ + movw $79, %cx +1: call print_space + loop 1b + /* Print CR */ + call print_character + /* Restore registers and return */ + popw %cx + popw %ax + ret + .size print_kill_line, . - print_kill_line + +/**************************************************************************** + * copy_bytes + * + * Copy bytes + * + * Parameters: + * %ds:esi : source address + * %es:edi : destination address + * %ecx : length + * Returns: + * %ds:esi : next source address + * %es:edi : next destination address + * Corrupts: + * None + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 +copy_bytes: + pushl %ecx + rep addr32 movsb + popl %ecx + ret + .size copy_bytes, . - copy_bytes + +/**************************************************************************** + * zero_bytes + * + * Zero bytes + * + * Parameters: + * %es:edi : destination address + * %ecx : length + * Returns: + * %es:edi : next destination address + * Corrupts: + * None + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 +zero_bytes: + pushl %ecx + pushw %ax + xorw %ax, %ax + rep addr32 stosb + popw %ax + popl %ecx + ret + .size zero_bytes, . - zero_bytes + +/**************************************************************************** + * process_bytes + * + * Call memcpy()-like function + * + * Parameters: + * %esi : source physical address + * %edi : destination physical address + * %ecx : length + * %bx : memcpy()-like function to call, passing parameters: + * %ds:esi : source address + * %es:edi : destination address + * %ecx : length + * and returning: + * %ds:esi : next source address + * %es:edi : next destination address + * Returns: + * %esi : next source physical address + * %edi : next destination physical address + * Corrupts: + * None + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 +process_bytes: + +#ifndef KEEP_IT_REAL + + /* Preserve registers */ + pushl %eax + pushl %ebp + + /* Construct GDT on stack (since .prefix may not be writable) */ + .equ PM_DS, 0x18 /* Flat data segment */ + pushl $0x00cf9300 + pushl $0x0000ffff + .equ PM_SS, 0x10 /* Stack segment based at %ss:0000 */ + pushl $0x008f0930 + pushw %ss + pushw $0xffff + .equ PM_CS, 0x08 /* Code segment based at %cs:0000 */ + pushl $0x008f09b0 + pushw %cs + pushw $0xffff + pushl $0 /* Base and length */ + pushw %ss + pushw $0x1f + movzwl %sp, %ebp + shll $4, 0x02(%bp) + addl %ebp, 0x02(%bp) + shll $4, 0x0a(%bp) + shll $4, 0x12(%bp) + subw $8, %sp + sgdt -8(%bp) + + /* Switch to protected mode */ + pushw %gs + pushw %fs + pushw %es + pushw %ds + pushw %ss + pushw %cs + pushw $2f + cli + data32 lgdt (%bp) + movl %cr0, %eax + orb $CR0_PE, %al + movl %eax, %cr0 + ljmp $PM_CS, $1f +1: movw $PM_SS, %ax + movw %ax, %ss + movw $PM_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + +#ifdef NDEBUG + /* Call memcpy()-like function */ + call *%bx +#endif + + /* Return to (flat) real mode */ + movl %cr0, %eax + andb $0!CR0_PE, %al + movl %eax, %cr0 + lret +2: /* lret will ljmp to here */ + popw %ss + popw %ds + popw %es + popw %fs + popw %gs + +#ifndef NDEBUG + /* Call memcpy()-like function in flat real mode (to allow for + * debug output via INT 10). + */ + pushw %ds + pushw %es + xorw %ax, %ax + movw %ax, %ds + movw %ax, %es + call *%bx + popw %es + popw %ds +#endif + + /* Restore GDT */ + data32 lgdt -8(%bp) + addw $( 8 /* saved GDT */ + ( PM_DS + 8 ) /* GDT on stack */ ), %sp + + /* Restore registers and return */ + popl %ebp + popl %eax + ret + +#else /* KEEP_IT_REAL */ + + /* Preserve registers */ + pushl %eax + pushw %ds + pushw %es + + /* Convert %esi and %edi to %ds:esi and %es:edi */ + shrl $4, %esi + movw %si, %ds + xorw %si, %si + shll $4, %esi + shrl $4, %edi + movw %di, %es + xorw %di, %di + shll $4, %edi + + /* Call memcpy()-like function */ + call *%bx + + /* Convert %ds:esi and %es:edi back to physical addresses */ + xorl %eax, %eax + movw %ds, %ax + shll $4, %eax + addl %eax, %esi + xorl %eax, %eax + movw %es, %ax + shll $4, %eax + addl %eax, %edi + + /* Restore registers and return */ + popw %es + popw %ds + popl %eax + ret + +#endif /* KEEP_IT_REAL */ + + .size process_bytes, . - process_bytes + +/**************************************************************************** + * install_block + * + * Install block to specified address + * + * Parameters: + * %esi : source physical address (must be a multiple of 16) + * %edi : destination physical address (must be a multiple of 16) + * %ecx : length of (decompressed) data + * %edx : total length of block (including any uninitialised data portion) + * Returns: + * %esi : next source physical address (will be a multiple of 16) + * %edi : next destination physical address (will be a multiple of 16) + * Corrupts: + * none + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 +install_block: + /* Preserve registers */ + pushl %ecx + pushw %bx + + /* Decompress (or copy) source to destination */ +#if COMPRESS + movw $decompress16, %bx +#else + movw $copy_bytes, %bx +#endif + call process_bytes + + /* Zero .bss portion */ + negl %ecx + addl %edx, %ecx + movw $zero_bytes, %bx + call process_bytes + + /* Round up %esi and %edi to start of next blocks */ + addl $0xf, %esi + andl $~0xf, %esi + addl $0xf, %edi + andl $~0xf, %edi + + /* Restore registers and return */ + popw %bx + popl %ecx + ret + .size install_block, . - install_block + +/**************************************************************************** + * alloc_basemem + * + * Allocate space for .text16 and .data16 from top of base memory. + * Memory is allocated using the BIOS free base memory counter at + * 0x40:13. + * + * Parameters: + * none + * Returns: + * %ax : .text16 segment address + * %bx : .data16 segment address + * Corrupts: + * none + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl alloc_basemem +alloc_basemem: + /* Preserve registers */ + pushw %fs + + /* FBMS => %ax as segment address */ + pushw $0x40 + popw %fs + movw %fs:0x13, %ax + shlw $6, %ax + + /* Calculate .data16 segment address */ + subw $_data16_memsz_ppgh, %ax + pushw %ax + + /* Calculate .text16 segment address */ + subw $_text16_memsz_ppgh, %ax + pushw %ax + + /* Update FBMS */ + shrw $6, %ax + movw %ax, %fs:0x13 + + /* Retrieve .text16 and .data16 segment addresses */ + popw %ax + popw %bx + + /* Restore registers and return */ + popw %fs + ret + .size alloc_basemem, . - alloc_basemem + +/**************************************************************************** + * free_basemem + * + * Free space allocated with alloc_basemem. + * + * Parameters: + * none (.text16 segment address is implicit in %cs) + * Returns: + * %ax : 0 if successfully freed + * Corrupts: + * none + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .code16 + .globl free_basemem +free_basemem: + /* Preserve registers */ + pushw %fs + pushw %ax + + /* Check FBMS counter */ + movw %cs, %ax + shrw $6, %ax + pushw $0x40 + popw %fs + cmpw %ax, %fs:0x13 + jne 1f + + /* Check hooked interrupt count */ + cmpw $0, %cs:hooked_bios_interrupts + jne 1f + + /* OK to free memory */ + movw %cs, %ax + addw $_text16_memsz_ppgh, %ax + addw $_data16_memsz_ppgh, %ax + shrw $6, %ax + movw %ax, %fs:0x13 + xorw %ax, %ax + +1: /* Restore registers and return */ + popw %ax + popw %fs + ret + .size free_basemem, . - free_basemem + + .section ".text16.data", "aw", @progbits + .globl hooked_bios_interrupts +hooked_bios_interrupts: + .word 0 + .size hooked_bios_interrupts, . - hooked_bios_interrupts + +/**************************************************************************** + * install + * + * Install all text and data segments. + * + * Parameters: + * none + * Returns: + * %ax : .text16 segment address + * %bx : .data16 segment address + * Corrupts: + * none + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl install +install: + progress "install:\n" + /* Preserve registers */ + pushl %esi + pushl %edi + pushl %ebp + /* Allocate space for .text16 and .data16 */ + call alloc_basemem + /* Image source = %cs:0000 */ + xorl %esi, %esi + /* Image destination = default */ + xorl %edi, %edi + /* Allow arbitrary relocation */ + orl $0xffffffff, %ebp + /* Install text and data segments */ + call install_prealloc + /* Restore registers and return */ + popl %ebp + popl %edi + popl %esi + ret + .size install, . - install + +/**************************************************************************** + * install_prealloc + * + * Install all text and data segments. + * + * Parameters: + * %ax : .text16 segment address + * %bx : .data16 segment address + * %esi : Image source physical address (or zero for %cs:0000) + * %edi : Decompression temporary area physical address (or zero for default) + * %ebp : Maximum end address for relocation + * - 0xffffffff for no maximum + * - 0x00000000 to inhibit use of INT 15,e820 and INT 15,e801 + * Corrupts: + * none + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl install_prealloc +install_prealloc: + progress "install_prealloc:\n" + /* Save registers on external stack */ + pushal + pushw %ds + pushw %es + cld /* Sanity: clear the direction flag asap */ + + /* Switch to temporary stack in .bss16 */ + pushw %ss + popw %ds + movl %esp, %ecx + movw %bx, %ss + movl $_data16_memsz, %esp + pushw %ds + pushl %ecx + + /* Set up %ds for (read-only) access to .prefix */ + pushw %cs + popw %ds + + /* Save decompression temporary area physical address */ + pushl %edi + + /* Install .text16.early and calculate %ecx as offset to next block */ + progress " .text16.early\n" + pushl %esi + xorl %esi, %esi + movw %cs, %si + shll $4, %esi + pushl %esi /* Save original %cs:0000 */ + addl $_text16_early_lma, %esi + movzwl %ax, %edi + shll $4, %edi + movl $_text16_early_filesz, %ecx + movl $_text16_early_memsz, %edx + call install_block /* .text16.early */ + popl %ecx /* Calculate offset to next block */ + subl %esi, %ecx + negl %ecx + popl %esi + +#ifndef KEEP_IT_REAL + + /* Access high memory by enabling the A20 gate. (We will + * already have 4GB segment limits as a result of calling + * install_block.) + */ + progress " access_highmem\n" + pushw %cs + pushw $1f + pushw %ax + pushw $access_highmem + lret +1: /* Die if we could not access high memory */ + jnc 3f + movw $a20_death_message, %si + xorw %di, %di + call print_message +2: jmp 2b + .section ".prefix.data", "aw", @progbits +a20_death_message: + .asciz "\nHigh memory inaccessible - cannot continue\n" + .size a20_death_message, . - a20_death_message + .previous +3: +#endif + + /* Open payload (which may not yet be in memory) */ + progress " open_payload\n" + pushw %cs + pushw $1f + pushw %ax + pushw $open_payload + lret +1: /* Die if we could not access the payload */ + jnc 3f + xorw %di, %di + movl %esi, %eax + call print_hex_dword + call print_space + movl %ecx, %eax + call print_hex_dword + movw $payload_death_message, %si + call print_message +2: /* Halt system */ + cli + hlt + jmp 2b + .section ".prefix.data", "aw", @progbits +payload_death_message: + .asciz "\nPayload inaccessible - cannot continue\n" + .size payload_death_message, . - payload_death_message + .previous +3: + + /* Calculate physical address of payload (i.e. first source) */ + testl %esi, %esi + jnz 1f + movw %cs, %si + shll $4, %esi +1: addl %ecx, %esi + + /* Install .text16.late and .data16 */ + progress " .text16.late\n" + movl $_text16_late_filesz, %ecx + movl $_text16_late_memsz, %edx + call install_block /* .text16.late */ + progress " .data16\n" + movzwl %bx, %edi + shll $4, %edi + movl $_data16_filesz, %ecx + movl $_data16_filesz, %edx /* do not zero our temporary stack */ + call install_block /* .data16 */ + + /* Set up %ds for access to .data16 */ + movw %bx, %ds + + /* Restore decompression temporary area physical address */ + popl %edi + +#ifndef KEEP_IT_REAL + + /* Find a suitable decompression temporary area, if none specified */ + pushl %eax + testl %edi, %edi + jnz 1f + /* Use INT 15,88 to find the highest available address via INT + * 15,88. This limits us to around 64MB, which should avoid + * all of the POST-time memory map failure modes. + */ + movb $0x88, %ah + int $0x15 + movw %ax, %di + addl $0x400, %edi + subl $_textdata_memsz_kb, %edi + shll $10, %edi + /* Sanity check: if we have ended up below 1MB, use 1MB */ + cmpl $0x100000, %edi + jae 1f + movl $0x100000, %edi +1: popl %eax + + /* Install .text and .data to temporary area in high memory, + * prior to reading the E820 memory map and relocating + * properly. + */ + progress " .textdata\n" + pushl %edi + movl $_textdata_filesz, %ecx + movl $_textdata_memsz, %edx + call install_block + popl %edi + +#endif /* KEEP_IT_REAL */ + + /* Switch back to original stack and zero .bss16 */ + addr32 lss %ss:(%esp), %esp + pushl %edi + pushw %es + movw %bx, %es + movl $_data16_filesz, %edi + movl $_data16_memsz, %ecx + subl %edi, %ecx + call zero_bytes + popw %es + popl %edi + +#ifndef KEEP_IT_REAL + + /* Initialise librm at current location */ + progress " init_librm\n" + movw %ax, (init_librm_vector+2) + lcall *init_librm_vector + + /* Inhibit INT 15,e820 and INT 15,e801 if applicable */ + testl %ebp, %ebp + jnz 1f + incb memmap_post + decl %ebp +1: + /* Call relocate() to determine target address for relocation. + * relocate() will return with %esi, %edi and %ecx set up + * ready for the copy to the new location. + */ + progress " relocate\n" + movw %ax, (prot_call_vector+2) + pushl $relocate + lcall *prot_call_vector + popl %edx /* discard */ + + /* Copy code to new location */ + progress " copy\n" + pushl %edi + pushw %bx + movw $copy_bytes, %bx + call process_bytes + popw %bx + popl %edi + + /* Initialise librm at new location */ + progress " init_librm\n" + lcall *init_librm_vector + +#else /* KEEP_IT_REAL */ + + /* Initialise libkir */ + movw %ax, (init_libkir_vector+2) + lcall *init_libkir_vector + +#endif /* KEEP_IT_REAL */ + + /* Close access to payload */ + progress " close_payload\n" + movw %ax, (close_payload_vector+2) + lcall *close_payload_vector + + /* Restore registers */ + popw %es + popw %ds + popal + ret + .size install_prealloc, . - install_prealloc + + /* Vectors for far calls to .text16 functions. Must be in + * .data16, since .prefix may not be writable. + */ + .section ".data16", "aw", @progbits +#ifdef KEEP_IT_REAL +init_libkir_vector: + .word init_libkir + .word 0 + .size init_libkir_vector, . - init_libkir_vector +#else +init_librm_vector: + .word init_librm + .word 0 + .size init_librm_vector, . - init_librm_vector +prot_call_vector: + .word prot_call + .word 0 + .size prot_call_vector, . - prot_call_vector +#endif +close_payload_vector: + .word close_payload + .word 0 + .size close_payload_vector, . - close_payload_vector + + /* Dummy routines to open and close payload */ + .section ".text16.early.data", "aw", @progbits + .weak open_payload + .weak close_payload +open_payload: +close_payload: + clc + lret + .size open_payload, . - open_payload + .size close_payload, . - close_payload + +/**************************************************************************** + * uninstall + * + * Uninstall all text and data segments. + * + * Parameters: + * none (.text16 segment address is implicit in %cs) + * Returns: + * none + * Corrupts: + * none + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .code16 + .globl uninstall +uninstall: + call free_basemem + ret + .size uninstall, . - uninstall + + + + /* File split information for the compressor */ +#if COMPRESS +#define PACK_OR_COPY "PACK" +#else +#define PACK_OR_COPY "COPY" +#endif + .section ".zinfo", "a", @progbits + .ascii "COPY" + .long _prefix_lma + .long _prefix_filesz + .long _max_align + .ascii PACK_OR_COPY + .long _text16_early_lma + .long _text16_early_filesz + .long _max_align + .ascii "PAYL" + .long 0 + .long 0 + .long _payload_align + .ascii "COPY" + .long _pprefix_lma + .long _pprefix_filesz + .long _max_align + .ascii PACK_OR_COPY + .long _text16_late_lma + .long _text16_late_filesz + .long _max_align + .ascii PACK_OR_COPY + .long _data16_lma + .long _data16_filesz + .long _max_align + .ascii PACK_OR_COPY + .long _textdata_lma + .long _textdata_filesz + .long _max_align + + .weak _payload_align + .equ _payload_align, 1 diff --git a/src/arch/x86/prefix/lkrnprefix.S b/src/arch/x86/prefix/lkrnprefix.S new file mode 100644 index 000000000..64135e14b --- /dev/null +++ b/src/arch/x86/prefix/lkrnprefix.S @@ -0,0 +1,236 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +#define BZI_LOAD_HIGH_ADDR 0x100000 + + .text + .arch i386 + .code16 + .section ".prefix", "ax", @progbits + .globl _lkrn_start +_lkrn_start: + +/***************************************************************************** + * + * Kernel header + * + * We place our prefix (i.e. our .prefix and .text16.early sections) + * within the bzImage real-mode portion which gets loaded at + * 1000:0000, and our payload (i.e. everything else) within the + * bzImage protected-mode portion which gets loaded at 0x100000 + * upwards. + * + */ + + .org 0x1f1 +setup_sects: + .byte -1 /* Allow for initial "boot sector" */ + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADHL" + .long setup_sects + .long 512 + .long 0 + .previous +root_flags: + .word 0 +syssize: + .long 0 + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADPL" + .long syssize + .long 16 + .long 0 + .previous +ram_size: + .word 0 +vid_mode: + .word 0 +root_dev: + .word 0 +boot_flag: + .word 0xaa55 +jump: + /* Manually specify a two-byte jmp instruction here rather + * than leaving it up to the assembler. + */ + .byte 0xeb, ( setup - header ) +header: + .byte 'H', 'd', 'r', 'S' +version: + .word 0x0207 /* 2.07 */ +realmode_swtch: + .long 0 +start_sys: + .word 0 +kernel_version: + .word version_string - 0x200 +type_of_loader: + .byte 0 +loadflags: + .byte 0x01 /* LOADED_HIGH */ +setup_move_size: + .word 0 +code32_start: + .long 0 +ramdisk_image: + .long 0 +ramdisk_size: + .long 0 +bootsect_kludge: + .long 0 +heap_end_ptr: + .word 0 +ext_loader_ver: + .byte 0 +ext_loader_type: + .byte 0 +cmd_line_ptr: + .long 0 +initrd_addr_max: + .long 0xffffffff +kernel_alignment: + .long 0 +relocatable_kernel: + .byte 0 +min_alignment: + .byte 0 +xloadflags: + .word 0 +cmdline_size: + .long 0x7ff +hardware_subarch: + .long 0 +hardware_subarch_data: + .byte 0, 0, 0, 0, 0, 0, 0, 0 + +version_string: + .asciz VERSION + +/***************************************************************************** + * + * Setup code + * + */ + +setup: + /* Fix up code segment */ + pushw %ds + pushw $1f + lret +1: + /* Set up stack just below 0x7c00 and clear direction flag */ + xorw %ax, %ax + movw %ax, %ss + movw $0x7c00, %sp + cld + + /* Retrieve command-line pointer */ + movl cmd_line_ptr, %edx + testl %edx, %edx + jz no_cmd_line + + /* Set up %es:%di to point to command line */ + movl %edx, %edi + andl $0xf, %edi + rorl $4, %edx + movw %dx, %es + + /* Find length of command line */ + pushw %di + movw $0xffff, %cx + repnz scasb + notw %cx + popw %si + + /* Make space for command line on stack */ + movw %sp, %di + subw %cx, %di + andw $~0xf, %di + movw %di, %sp + + /* Copy command line to stack */ + pushw %ds + pushw %es + popw %ds + pushw %ss + popw %es + rep movsb + popw %ds + + /* Store new command-line pointer */ + movzwl %sp, %edx +no_cmd_line: + + /* Calculate maximum relocation address */ + movl ramdisk_image, %ebp + testl %ebp, %ebp + jnz 1f + orl $0xffffffff, %ebp /* Allow arbitrary relocation if no initrd */ +1: + /* Install iPXE */ + call alloc_basemem + xorl %esi, %esi + xorl %edi, %edi + call install_prealloc + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16", "awx", @progbits +1: + /* Retrieve initrd pointer and size */ + movl ramdisk_image, %ebp + movl ramdisk_size, %ecx + + /* Set up %ds for access to .data16 */ + movw %bx, %ds + + /* Store command-line pointer */ + movl %edx, cmdline_phys + + /* Store initrd pointer and size */ + movl %ebp, initrd_phys + movl %ecx, initrd_len + + /* Run iPXE */ + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall iPXE */ + call uninstall + + /* Boot next device */ + int $0x18 + +/***************************************************************************** + * + * Open payload (called by libprefix) + * + * Parameters: + * %ds:0000 : Prefix + * %esi : Buffer for copy of image source (or zero if no buffer available) + * %ecx : Expected offset within buffer of first payload block + * Returns: + * %esi : Valid image source address (buffered or unbuffered) + * %ecx : Actual offset within buffer of first payload block + * CF set on error + */ + + .section ".text16.early", "awx", @progbits + .globl open_payload +open_payload: + + /* Our payload will always end up at BZI_LOAD_HIGH_ADDR */ + movl $BZI_LOAD_HIGH_ADDR, %esi + xorl %ecx, %ecx + lret + + /* Payload must be aligned to a whole number of setup sectors */ + .globl _payload_align + .equ _payload_align, 512 diff --git a/src/arch/x86/prefix/mbr.S b/src/arch/x86/prefix/mbr.S new file mode 100644 index 000000000..a1e237de8 --- /dev/null +++ b/src/arch/x86/prefix/mbr.S @@ -0,0 +1,15 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .arch i386 + .section ".prefix", "awx", @progbits + .code16 + .org 0 + +mbr: + movw $exec_sector, %bp + jmp find_active_partition +exec_sector: + ljmp $0x0000, $0x7c00 + +#include "bootpart.S" diff --git a/src/arch/x86/prefix/mromprefix.S b/src/arch/x86/prefix/mromprefix.S new file mode 100644 index 000000000..b636b92af --- /dev/null +++ b/src/arch/x86/prefix/mromprefix.S @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2010 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +#define PCIBIOS_READ_CONFIG_WORD 0xb109 +#define PCIBIOS_READ_CONFIG_DWORD 0xb10a +#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c +#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d +#define PCI_COMMAND 0x04 +#define PCI_COMMAND_MEM 0x02 +#define PCI_BAR_0 0x10 +#define PCI_BAR_5 0x24 +#define PCI_BAR_EXPROM 0x30 + +#define PCIR_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) ) + +#define ROMPREFIX_EXCLUDE_PAYLOAD 1 +#define ROMPREFIX_MORE_IMAGES 1 +#define _pcirom_start _mrom_start +#include "pciromprefix.S" + + .text + .arch i386 + .code16 + +/* Obtain access to payload by exposing the expansion ROM BAR at the + * address currently used by a suitably large memory BAR on the same + * device. The memory BAR is temporarily disabled. Using a memory + * BAR on the same device means that we don't have to worry about the + * configuration of any intermediate PCI bridges. + * + * Parameters: + * %ds:0000 : Prefix + * %esi : Buffer for copy of image source (or zero if no buffer available) + * %ecx : Expected offset within buffer of first payload block + * Returns: + * %esi : Valid image source address (buffered or unbuffered) + * %ecx : Actual offset within buffer of first payload block + * CF set on error + */ + .section ".text16.early", "awx", @progbits + .globl open_payload +open_payload: + /* Preserve registers */ + pushl %eax + pushw %bx + pushl %edx + pushl %edi + pushw %bp + pushw %es + pushw %ds + + /* Retrieve bus:dev.fn from .prefix */ + movw init_pci_busdevfn, %bx + + /* Set up %ds for access to .text16.early */ + pushw %cs + popw %ds + + /* Set up %es for access to flat address space */ + xorw %ax, %ax + movw %ax, %es + + /* Store bus:dev.fn to .text16.early */ + movw %bx, payload_pci_busdevfn + + /* Get expansion ROM BAR current value */ + movw $PCI_BAR_EXPROM, %di + call pci_read_bar + movl %eax, rom_bar_orig_value + + /* Get expansion ROM BAR size */ + call pci_size_mem_bar_low + movl %ecx, rom_bar_size + + /* Find a suitable memory BAR to use */ + movw $PCI_BAR_0, %di /* %di is PCI BAR register */ + xorw %bp, %bp /* %bp is increment */ +find_mem_bar: + /* Move to next BAR */ + addw %bp, %di + cmpw $PCI_BAR_5, %di + jle 1f + stc + movl $0xbabababa, %esi /* Report "No suitable BAR" */ + movl rom_bar_size, %ecx + jmp 99f +1: movw $4, %bp + + /* Get BAR current value */ + call pci_read_bar + + /* Skip non-existent BARs */ + notl %eax + testl %eax, %eax + notl %eax + jz find_mem_bar + + /* Skip I/O BARs */ + testb $0x01, %al + jnz find_mem_bar + + /* Set increment to 8 for 64-bit BARs */ + testb $0x04, %al + jz 1f + movw $8, %bp +1: + /* Skip 64-bit BARs with high dword set; we couldn't use this + * address for the (32-bit) expansion ROM BAR anyway + */ + testl %edx, %edx + jnz find_mem_bar + + /* Get low dword of BAR size */ + call pci_size_mem_bar_low + + /* Skip BARs smaller than the expansion ROM BAR */ + cmpl %ecx, rom_bar_size + ja find_mem_bar + + /* We have a memory BAR with a 32-bit address that is large + * enough to use. Store BAR number and original value. + */ + movw %di, stolen_bar_register + movl %eax, stolen_bar_orig_value + + /* Remove flags from BAR address */ + xorb %al, %al + + /* Write zero to our stolen BAR. This doesn't technically + * disable it, but it's a pretty safe bet that the PCI bridge + * won't pass through accesses to this region anyway. Note + * that the high dword (if any) must already be zero. + */ + xorl %ecx, %ecx + call pci_write_config_dword + + /* Enable expansion ROM BAR at stolen BAR's address */ + movl %eax, %ecx + orb $0x1, %cl + movw $PCI_BAR_EXPROM, %di + call pci_write_config_dword + + /* Locate our ROM image */ +1: movl $0xaa55, %ecx /* 55aa signature */ + addr32 es cmpw %cx, (%eax) + jne 2f + movl $PCIR_SIGNATURE, %ecx /* PCIR signature */ + addr32 es movzwl 0x18(%eax), %edx + addr32 es cmpl %ecx, (%eax,%edx) + jne 2f + addr32 es cmpl $_build_id, build_id(%eax) /* iPXE build ID */ + je 3f + movl $0x80, %ecx /* Last image */ + addr32 es testb %cl, 0x15(%eax,%edx) + jnz 2f + addr32 es movzwl 0x10(%eax,%edx), %ecx /* PCIR image length */ + shll $9, %ecx + addl %ecx, %eax + jmp 1b +2: /* Failure */ + stc + movl %eax, %esi /* Report failure address */ + jmp 99f +3: + + /* Copy payload to buffer, or set buffer address to BAR address */ + testl %esi, %esi + jz 1f + /* We have a buffer; copy payload to it. Since .mrom is + * designed specifically for real hardware, we assume that + * flat real mode is working properly. (In the unlikely event + * that this code is run inside a hypervisor that doesn't + * properly support flat real mode, it will die horribly.) + */ + pushl %esi + movl %esi, %edi + movl %eax, %esi + addr32 es movzbl 2(%esi), %ecx + shll $7, %ecx + addr32 es movzwl mpciheader_image_length(%esi,%ecx,4), %edx + shll $7, %edx + addl %edx, %ecx + addr32 es rep movsl + popl %esi + jmp 2f +1: /* We have no buffer; set %esi to the BAR address */ + movl %eax, %esi +2: + + /* Locate first payload block (after the dummy ROM header) */ + addr32 es movzbl 2(%esi), %ecx + shll $9, %ecx + addl $_pprefix_skip, %ecx + + clc + /* Restore registers and return */ +99: popw %ds + popw %es + popw %bp + popl %edi + popl %edx + popw %bx + popl %eax + lret + .size open_payload, . - open_payload + + .section ".text16.early.data", "aw", @progbits +payload_pci_busdevfn: + .word 0 + .size payload_pci_busdevfn, . - payload_pci_busdevfn + + .section ".text16.early.data", "aw", @progbits +rom_bar_orig_value: + .long 0 + .size rom_bar_orig_value, . - rom_bar_orig_value + + .section ".text16.early.data", "aw", @progbits +rom_bar_size: + .long 0 + .size rom_bar_size, . - rom_bar_size + + .section ".text16.early.data", "aw", @progbits +stolen_bar_register: + .word 0 + .size stolen_bar_register, . - stolen_bar_register + + .section ".text16.early.data", "aw", @progbits +stolen_bar_orig_value: + .long 0 + .size stolen_bar_orig_value, . - stolen_bar_orig_value + +/* Restore original BAR values + * + * Parameters: + * none + * Returns: + * none + */ + .section ".text16.early", "awx", @progbits + .globl close_payload +close_payload: + /* Preserve registers */ + pushw %bx + pushw %di + pushl %ecx + pushw %ds + + /* Set up %ds for access to .text16.early */ + pushw %cs + popw %ds + + /* Retrieve stored bus:dev.fn */ + movw payload_pci_busdevfn, %bx + + /* Restore expansion ROM BAR original value */ + movw $PCI_BAR_EXPROM, %di + movl rom_bar_orig_value, %ecx + call pci_write_config_dword + + /* Restore stolen BAR original value */ + movw stolen_bar_register, %di + movl stolen_bar_orig_value, %ecx + call pci_write_config_dword + + /* Restore registers and return */ + popw %ds + popl %ecx + popw %di + popw %bx + lret + .size close_payload, . - close_payload + +/* Get PCI BAR value + * + * Parameters: + * %bx : PCI bus:dev.fn + * %di : PCI BAR register number + * Returns: + * %edx:%eax : PCI BAR value + */ + .section ".text16.early", "awx", @progbits +pci_read_bar: + /* Preserve registers */ + pushl %ecx + pushw %di + + /* Read low dword value */ + call pci_read_config_dword + movl %ecx, %eax + + /* Read high dword value, if applicable */ + xorl %edx, %edx + andb $0x07, %cl + cmpb $0x04, %cl + jne 1f + addw $4, %di + call pci_read_config_dword + movl %ecx, %edx +1: + /* Restore registers and return */ + popw %di + popl %ecx + ret + .size pci_read_bar, . - pci_read_bar + +/* Get low dword of PCI memory BAR size + * + * Parameters: + * %bx : PCI bus:dev.fn + * %di : PCI BAR register number + * %eax : Low dword of current PCI BAR value + * Returns: + * %ecx : PCI BAR size + */ + .section ".text16.early", "awx", @progbits +pci_size_mem_bar_low: + /* Preserve registers */ + pushw %dx + + /* Disable memory accesses */ + xorw %dx, %dx + call pci_set_mem_access + + /* Write all ones to BAR */ + xorl %ecx, %ecx + decl %ecx + call pci_write_config_dword + + /* Read back BAR */ + call pci_read_config_dword + + /* Calculate size */ + notl %ecx + orb $0x0f, %cl + incl %ecx + + /* Restore original value */ + pushl %ecx + movl %eax, %ecx + call pci_write_config_dword + popl %ecx + + /* Enable memory accesses */ + movw $PCI_COMMAND_MEM, %dx + call pci_set_mem_access + + /* Restore registers and return */ + popw %dx + ret + .size pci_size_mem_bar_low, . - pci_size_mem_bar_low + +/* Read PCI config dword + * + * Parameters: + * %bx : PCI bus:dev.fn + * %di : PCI register number + * Returns: + * %ecx : Dword value + */ + .section ".text16.early", "awx", @progbits +pci_read_config_dword: + /* Preserve registers */ + pushl %eax + pushl %ebx + pushl %edx + + /* Issue INT 0x1a,b10a */ + movw $PCIBIOS_READ_CONFIG_DWORD, %ax + int $0x1a + + /* Restore registers and return */ + popl %edx + popl %ebx + popl %eax + ret + .size pci_read_config_dword, . - pci_read_config_dword + +/* Write PCI config dword + * + * Parameters: + * %bx : PCI bus:dev.fn + * %di : PCI register number + * %ecx : PCI BAR value + * Returns: + * none + */ + .section ".text16.early", "awx", @progbits +pci_write_config_dword: + /* Preserve registers */ + pushal + + /* Issue INT 0x1a,b10d */ + movw $PCIBIOS_WRITE_CONFIG_DWORD, %ax + int $0x1a + + /* Restore registers and return */ + popal + ret + .size pci_write_config_dword, . - pci_write_config_dword + +/* Enable/disable memory access response in PCI command word + * + * Parameters: + * %bx : PCI bus:dev.fn + * %dx : PCI_COMMAND_MEM, or zero + * Returns: + * none + */ + .section ".text16.early", "awx", @progbits +pci_set_mem_access: + /* Preserve registers */ + pushal + + /* Read current value of command register */ + pushw %bx + pushw %dx + movw $PCI_COMMAND, %di + movw $PCIBIOS_READ_CONFIG_WORD, %ax + int $0x1a + popw %dx + popw %bx + + /* Set memory access enable as appropriate */ + andw $~PCI_COMMAND_MEM, %cx + orw %dx, %cx + + /* Write new value of command register */ + movw $PCIBIOS_WRITE_CONFIG_WORD, %ax + int $0x1a + + /* Restore registers and return */ + popal + ret + .size pci_set_mem_access, . - pci_set_mem_access + +/* Payload prefix + * + * We include a dummy ROM header to cover the "hidden" portion of the + * overall ROM image. + */ + .globl _payload_align + .equ _payload_align, 512 + .section ".pprefix", "ax", @progbits + .org 0x00 +mromheader: + .word 0xaa55 /* BIOS extension signature */ + .byte 0x01 /* Dummy size (BIOS bug workaround) */ + .org 0x18 + .word mpciheader + .org 0x1a + .word 0 + .size mromheader, . - mromheader + +mpciheader: + .ascii "PCIR" /* Signature */ + .word pci_vendor_id /* Vendor identification */ + .word pci_device_id /* Device identification */ + .word 0x0000 /* Device list pointer */ + .word mpciheader_len /* PCI data structure length */ + .byte 0x03 /* PCI data structure revision */ + .byte 0x02, 0x00, 0x00 /* Class code */ +mpciheader_image_length: + .word 0 /* Image length */ + .word 0x0001 /* Revision level */ + .byte 0xff /* Code type */ + .byte 0x80 /* Last image indicator */ +mpciheader_runtime_length: + .word 0 /* Maximum run-time image length */ + .word 0x0000 /* Configuration utility code header */ + .word 0x0000 /* DMTF CLP entry point */ + .equ mpciheader_len, . - mpciheader + .size mpciheader, . - mpciheader + + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "APPW" + .long mpciheader_image_length + .long 512 + .long 0 + .ascii "APPW" + .long mpciheader_runtime_length + .long 512 + .long 0 + .previous + +/* Fix up additional image source size + * + */ + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADPW" + .long extra_size + .long 512 + .long 0 + .previous diff --git a/src/arch/x86/prefix/nbiprefix.S b/src/arch/x86/prefix/nbiprefix.S new file mode 100644 index 000000000..16c79566c --- /dev/null +++ b/src/arch/x86/prefix/nbiprefix.S @@ -0,0 +1,84 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .arch i386 + .code16 + .section ".prefix", "ax", @progbits + .org 0 + +nbi_header: + +/***************************************************************************** + * NBI file header + ***************************************************************************** + */ +file_header: + .long 0x1b031336 /* Signature */ + .byte 0x04 /* 16 bytes header, no vendor info */ + .byte 0 + .byte 0 + .byte 0 /* No flags */ + .word 0x0000, 0x07c0 /* Load header to 0x07c0:0x0000 */ + .word _nbi_start, 0x07c0 /* Start execution at 0x07c0:entry */ + .size file_header, . - file_header + +/***************************************************************************** + * NBI segment header + ***************************************************************************** + */ +segment_header: + .byte 0x04 /* 16 bytes header, no vendor info */ + .byte 0 + .byte 0 + .byte 0x04 /* Last segment */ + .long 0x00007e00 +imglen: .long -512 +memlen: .long -512 + .size segment_header, . - segment_header + + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADDL" + .long imglen + .long 1 + .long 0 + .ascii "ADDL" + .long memlen + .long 1 + .long 0 + .previous + +/***************************************************************************** + * NBI entry point + ***************************************************************************** + */ + .globl _nbi_start +_nbi_start: + /* Install iPXE */ + call install + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16", "awx", @progbits +1: + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall iPXE */ + call uninstall + + /* Reboot system */ + int $0x19 + + .previous + .size _nbi_start, . - _nbi_start + +nbi_header_end: + .org 512 diff --git a/src/arch/x86/prefix/nullprefix.S b/src/arch/x86/prefix/nullprefix.S new file mode 100644 index 000000000..bd0ff339e --- /dev/null +++ b/src/arch/x86/prefix/nullprefix.S @@ -0,0 +1,15 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .org 0 + .text + .arch i386 + + .section ".prefix", "ax", @progbits + .code16 +_prefix: + + .section ".text16", "ax", @progbits +prefix_exit: + +prefix_exit_end: + .previous diff --git a/src/arch/x86/prefix/pciromprefix.S b/src/arch/x86/prefix/pciromprefix.S new file mode 100644 index 000000000..5a5a49647 --- /dev/null +++ b/src/arch/x86/prefix/pciromprefix.S @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +#define BUSTYPE "PCIR" +#define _rom_start _pcirom_start +#include "romprefix.S" diff --git a/src/arch/x86/prefix/pxeprefix.S b/src/arch/x86/prefix/pxeprefix.S new file mode 100644 index 000000000..465ce4345 --- /dev/null +++ b/src/arch/x86/prefix/pxeprefix.S @@ -0,0 +1,862 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +#define PXENV_UNDI_SHUTDOWN 0x0005 +#define PXENV_UNDI_GET_NIC_TYPE 0x0012 +#define PXENV_UNDI_GET_IFACE_INFO 0x0013 +#define PXENV_STOP_UNDI 0x0015 +#define PXENV_UNLOAD_STACK 0x0070 +#define PXENV_GET_CACHED_INFO 0x0071 +#define PXENV_PACKET_TYPE_DHCP_ACK 0x0002 +#define PXENV_FILE_CMDLINE 0x00e8 + +#define PXE_HACK_EB54 0x0001 + + .text + .arch i386 + .org 0 + .code16 + +#include <undi.h> + +#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) +#define EB_MAGIC_1 ( 'E' + ( 't' << 8 ) + ( 'h' << 16 ) + ( 'e' << 24 ) ) +#define EB_MAGIC_2 ( 'r' + ( 'b' << 8 ) + ( 'o' << 16 ) + ( 'o' << 24 ) ) + +/* Prefix memory layout: + * + * iPXE binary image + * Temporary stack + * Temporary copy of DHCPACK packet + * Temporary copy of command line + */ +#define PREFIX_STACK_SIZE 2048 +#define PREFIX_TEMP_DHCPACK PREFIX_STACK_SIZE +#define PREFIX_TEMP_DHCPACK_SIZE ( 1260 /* sizeof ( BOOTPLAYER_t ) */ ) +#define PREFIX_TEMP_CMDLINE ( PREFIX_TEMP_DHCPACK + PREFIX_TEMP_DHCPACK_SIZE ) +#define PREFIX_TEMP_CMDLINE_SIZE 4096 + +/***************************************************************************** + * Entry point: set operating context, print welcome message + ***************************************************************************** + */ + .section ".prefix", "ax", @progbits + .globl _pxe_start +_pxe_start: + jmp $0x7c0, $1f +1: + /* Preserve registers for possible return to PXE */ + pushfl + pushal + pushw %gs + pushw %fs + pushw %es + pushw %ds + + /* Store magic word on PXE stack and remember PXE %ss:esp */ + pushl $STACK_MAGIC + movw %ss, %cs:pxe_ss + movl %esp, %cs:pxe_esp + + /* Set up segments */ + movw %cs, %ax + movw %ax, %ds + movw $0x40, %ax /* BIOS data segment access */ + movw %ax, %fs + /* Set up temporary stack immediately after the iPXE image */ + movw %cs, %ax + addw image_size_pgh, %ax + movw %ax, %ss + movl $PREFIX_STACK_SIZE, %esp + /* Clear direction flag, for the sake of sanity */ + cld + /* Print welcome message */ + movw $10f, %si + xorw %di, %di + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz "PXE->EB:" + .previous + + /* Image size (for stack placement calculation) */ + .section ".prefix.data", "aw", @progbits +image_size_pgh: + .word 0 + .previous + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADDW" + .long image_size_pgh + .long 16 + .long 0 + .previous + +/***************************************************************************** + * Find us a usable !PXE or PXENV+ entry point + ***************************************************************************** + */ +detect_pxe: + /* Plan A: !PXE pointer from the stack */ + lgsl pxe_esp, %ebp /* %gs:%bp -> original stack */ + lesw %gs:52(%bp), %bx + call is_valid_ppxe + je have_ppxe + + /* Plan B: PXENV+ pointer from initial ES:BX */ + movw %gs:32(%bp),%bx + movw %gs:8(%bp),%es + call is_valid_pxenv + je have_pxenv + + /* Plan C: PXENV+ structure via INT 1Ah */ + movw $0x5650, %ax + int $0x1a + jc 1f + cmpw $0x564e, %ax + jne 1f + call is_valid_pxenv + je have_pxenv +1: + /* Plan D: scan base memory for !PXE */ + call memory_scan_ppxe + je have_ppxe + + /* Plan E: scan base memory for PXENV+ */ + call memory_scan_pxenv + jne stack_not_found + +have_pxenv: + movw %bx, pxenv_offset + movw %es, pxenv_segment + + cmpw $0x201, %es:6(%bx) /* API version >= 2.01 */ + jb 1f + cmpb $0x2c, %es:8(%bx) /* ... and structure long enough */ + jb 2f + + lesw %es:0x28(%bx), %bx /* Find !PXE from PXENV+ */ + call is_valid_ppxe + je have_ppxe +2: + call memory_scan_ppxe /* We are *supposed* to have !PXE... */ + je have_ppxe +1: + lesw pxenv_segoff, %bx /* Nope, we're stuck with PXENV+ */ + + /* Record entry point and UNDI segments */ + pushl %es:0x0a(%bx) /* Entry point */ + pushw %es:0x24(%bx) /* UNDI code segment */ + pushw %es:0x26(%bx) /* UNDI code size */ + pushw %es:0x20(%bx) /* UNDI data segment */ + pushw %es:0x22(%bx) /* UNDI data size */ + + /* Print "PXENV+ at <address>" */ + movw $10f, %si + jmp check_have_stack + .section ".prefix.data", "aw", @progbits +10: .asciz " PXENV+ at " + .previous + +have_ppxe: + movw %bx, ppxe_offset + movw %es, ppxe_segment + + pushl %es:0x10(%bx) /* Entry point */ + pushw %es:0x30(%bx) /* UNDI code segment */ + pushw %es:0x36(%bx) /* UNDI code size */ + pushw %es:0x28(%bx) /* UNDI data segment */ + pushw %es:0x2e(%bx) /* UNDI data size */ + + /* Print "!PXE at <address>" */ + movw $10f, %si + jmp check_have_stack + .section ".prefix.data", "aw", @progbits +10: .asciz " !PXE at " + .previous + +is_valid_ppxe: + cmpl $0x45585021, %es:(%bx) + jne 1f + movzbw %es:4(%bx), %cx + cmpw $0x58, %cx + jae is_valid_checksum +1: + ret + +is_valid_pxenv: + cmpl $0x4e455850, %es:(%bx) + jne 1b + cmpw $0x2b56, %es:4(%bx) + jne 1b + movzbw %es:8(%bx), %cx + cmpw $0x28, %cx + jb 1b + +is_valid_checksum: + pushw %ax + movw %bx, %si + xorw %ax, %ax +2: + es lodsb + addb %al, %ah + loopw 2b + popw %ax + ret + +memory_scan_ppxe: + movw $is_valid_ppxe, %dx + jmp memory_scan_common + +memory_scan_pxenv: + movw $is_valid_pxenv, %dx + +memory_scan_common: + movw %fs:(0x13), %ax + shlw $6, %ax + decw %ax +1: incw %ax + cmpw $( 0xa000 - 1 ), %ax + ja 2f + movw %ax, %es + xorw %bx, %bx + call *%dx + jne 1b +2: ret + +/***************************************************************************** + * Sanity check: we must have an entry point + ***************************************************************************** + */ +check_have_stack: + /* Save common values pushed onto the stack */ + popl undi_data_segoff + popl undi_code_segoff + popl entry_segoff + + /* Print have !PXE/PXENV+ message; structure pointer in %es:%bx */ + call print_message + call print_segoff + movb $( ',' ), %al + call print_character + + /* Check for entry point */ + movl entry_segoff, %eax + testl %eax, %eax + jnz 99f + /* No entry point: print message and skip everything else */ +stack_not_found: + movw $10f, %si + call print_message + jmp finished + .section ".prefix.data", "aw", @progbits +10: .asciz " No PXE stack found!\n" + .previous +99: + +/***************************************************************************** + * Calculate base memory usage by UNDI + ***************************************************************************** + */ +find_undi_basemem_usage: + movw undi_code_segment, %ax + movw undi_code_size, %bx + movw undi_data_segment, %cx + movw undi_data_size, %dx + cmpw %ax, %cx + ja 1f + xchgw %ax, %cx + xchgw %bx, %dx +1: /* %ax:%bx now describes the lower region, %cx:%dx the higher */ + shrw $6, %ax /* Round down to nearest kB */ + movw %ax, undi_fbms_start + addw $0x0f, %dx /* Round up to next segment */ + shrw $4, %dx + addw %dx, %cx + addw $((1024 / 16) - 1), %cx /* Round up to next kB */ + shrw $6, %cx + movw %cx, undi_fbms_end + +/***************************************************************************** + * Print information about detected PXE stack + ***************************************************************************** + */ +print_structure_information: + /* Print entry point */ + movw $10f, %si + call print_message + les entry_segoff, %bx + call print_segoff + .section ".prefix.data", "aw", @progbits +10: .asciz " entry point at " + .previous + /* Print UNDI code segment */ + movw $10f, %si + call print_message + les undi_code_segoff, %bx + call print_segoff + .section ".prefix.data", "aw", @progbits +10: .asciz "\n UNDI code segment " + .previous + /* Print UNDI data segment */ + movw $10f, %si + call print_message + les undi_data_segoff, %bx + call print_segoff + .section ".prefix.data", "aw", @progbits +10: .asciz ", data segment " + .previous + /* Print UNDI memory usage */ + movw $10f, %si + call print_message + movw undi_fbms_start, %ax + call print_word + movb $( '-' ), %al + call print_character + movw undi_fbms_end, %ax + call print_word + movw $20f, %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz " (" +20: .asciz "kB)\n" + .previous + +/***************************************************************************** + * Determine physical device + ***************************************************************************** + */ +get_physical_device: + /* Issue PXENV_UNDI_GET_NIC_TYPE */ + movw $PXENV_UNDI_GET_NIC_TYPE, %bx + call pxe_call + jnc 1f + call print_pxe_error + jmp no_physical_device +1: /* Determine physical device type */ + movb ( pxe_parameter_structure + 0x02 ), %al + cmpb $2, %al + je pci_physical_device + jmp no_physical_device + +pci_physical_device: + /* Record PCI bus:dev.fn and vendor/device IDs */ + movl ( pxe_parameter_structure + 0x03 ), %eax + movl %eax, pci_vendor + movw ( pxe_parameter_structure + 0x0b ), %ax + movw %ax, pci_busdevfn + movw $10f, %si + call print_message + call print_pci_busdevfn + jmp 99f + .section ".prefix.data", "aw", @progbits +10: .asciz " UNDI device is PCI " + .previous + +no_physical_device: + /* No device found, or device type not understood */ + movw $10f, %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz " Unable to determine UNDI physical device" + .previous + +99: + +/***************************************************************************** + * Determine interface type + ***************************************************************************** + */ +get_iface_type: + /* Issue PXENV_UNDI_GET_IFACE_INFO */ + movw $PXENV_UNDI_GET_IFACE_INFO, %bx + call pxe_call + jnc 1f + call print_pxe_error + jmp 99f +1: /* Print interface type */ + movw $10f, %si + call print_message + leaw ( pxe_parameter_structure + 0x02 ), %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz ", type " + .previous + /* Check for "Etherboot" interface type */ + cmpl $EB_MAGIC_1, ( pxe_parameter_structure + 0x02 ) + jne 99f + cmpl $EB_MAGIC_2, ( pxe_parameter_structure + 0x06 ) + jne 99f + movw $10f, %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz " (workaround enabled)" + .previous + /* Flag Etherboot workarounds as required */ + orw $PXE_HACK_EB54, pxe_hacks + +99: movb $0x0a, %al + call print_character + +/***************************************************************************** + * Get cached DHCP_ACK packet + ***************************************************************************** + */ +get_dhcpack: + /* Issue PXENV_GET_CACHED_INFO */ + xorl %esi, %esi + movw %ss, %si + movw %si, ( pxe_parameter_structure + 0x08 ) + movw $PREFIX_TEMP_DHCPACK, ( pxe_parameter_structure + 0x06 ) + movw $PREFIX_TEMP_DHCPACK_SIZE, ( pxe_parameter_structure +0x04 ) + movw $PXENV_PACKET_TYPE_DHCP_ACK, ( pxe_parameter_structure + 0x02 ) + movw $PXENV_GET_CACHED_INFO, %bx + call pxe_call + jnc 1f + call print_pxe_error + jmp 99f +1: /* Store physical address of packet */ + shll $4, %esi + addl $PREFIX_TEMP_DHCPACK, %esi + movl %esi, pxe_cached_dhcpack +99: + .section ".prefix.data", "aw", @progbits +pxe_cached_dhcpack: + .long 0 + .previous + +/***************************************************************************** + * Check for a command line + ***************************************************************************** + */ +get_cmdline: + /* Issue PXENV_FILE_CMDLINE */ + xorl %esi, %esi + movw %ss, %si + movw %si, ( pxe_parameter_structure + 0x06 ) + movw $PREFIX_TEMP_CMDLINE, ( pxe_parameter_structure + 0x04 ) + movw $PREFIX_TEMP_CMDLINE_SIZE, ( pxe_parameter_structure + 0x02 ) + movw $PXENV_FILE_CMDLINE, %bx + call pxe_call + jc 99f /* Suppress errors; this is an iPXE extension API call */ + /* Check for non-NULL command line */ + movw ( pxe_parameter_structure + 0x02 ), %ax + testw %ax, %ax + jz 99f + /* Record command line */ + shll $4, %esi + addl $PREFIX_TEMP_CMDLINE, %esi + movl %esi, pxe_cmdline +99: + .section ".prefix.data", "aw", @progbits +pxe_cmdline: + .long 0 + .previous + +/***************************************************************************** + * Leave NIC in a safe state + ***************************************************************************** + */ +#ifndef PXELOADER_KEEP_PXE +shutdown_nic: + /* Issue PXENV_UNDI_SHUTDOWN */ + movw $PXENV_UNDI_SHUTDOWN, %bx + call pxe_call + jnc 1f + call print_pxe_error +1: +unload_base_code: + /* Etherboot treats PXENV_UNLOAD_STACK as PXENV_STOP_UNDI, so + * we must not issue this call if the underlying stack is + * Etherboot and we were not intending to issue a PXENV_STOP_UNDI. + */ +#ifdef PXELOADER_KEEP_UNDI + testw $PXE_HACK_EB54, pxe_hacks + jnz 99f +#endif /* PXELOADER_KEEP_UNDI */ + /* Issue PXENV_UNLOAD_STACK */ + movw $PXENV_UNLOAD_STACK, %bx + call pxe_call + jnc 1f + call print_pxe_error + jmp 99f +1: /* Free base memory used by PXE base code */ + movw undi_fbms_start, %ax + movw %fs:(0x13), %bx + call free_basemem +99: + andw $~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags +#endif /* PXELOADER_KEEP_PXE */ + +/***************************************************************************** + * Unload UNDI driver + ***************************************************************************** + */ +#ifndef PXELOADER_KEEP_UNDI +unload_undi: + /* Issue PXENV_STOP_UNDI */ + movw $PXENV_STOP_UNDI, %bx + call pxe_call + jnc 1f + call print_pxe_error + jmp 99f +1: /* Free base memory used by UNDI */ + movw undi_fbms_end, %ax + movw undi_fbms_start, %bx + call free_basemem + /* Clear UNDI_FL_STARTED */ + andw $~UNDI_FL_STARTED, flags +99: +#endif /* PXELOADER_KEEP_UNDI */ + +/***************************************************************************** + * Print remaining free base memory + ***************************************************************************** + */ +print_free_basemem: + movw $10f, %si + call print_message + movw %fs:(0x13), %ax + call print_word + movw $20f, %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz " " +20: .asciz "kB free base memory after PXE unload\n" + .previous + +/***************************************************************************** + * Exit point + ***************************************************************************** + */ +finished: + jmp run_ipxe + +/***************************************************************************** + * Subroutine: print segment:offset address + * + * Parameters: + * %es:%bx : segment:offset address to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ +print_segoff: + /* Preserve registers */ + pushw %ax + /* Print "<segment>:offset" */ + movw %es, %ax + call print_hex_word + movb $( ':' ), %al + call print_character + movw %bx, %ax + call print_hex_word + /* Restore registers and return */ + popw %ax + ret + +/***************************************************************************** + * Subroutine: print decimal word + * + * Parameters: + * %ax : word to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ +print_word: + /* Preserve registers */ + pushw %ax + pushw %bx + pushw %cx + pushw %dx + /* Build up digit sequence on stack */ + movw $10, %bx + xorw %cx, %cx +1: xorw %dx, %dx + divw %bx, %ax + pushw %dx + incw %cx + testw %ax, %ax + jnz 1b + /* Print digit sequence */ +1: popw %ax + call print_hex_nibble + loop 1b + /* Restore registers and return */ + popw %dx + popw %cx + popw %bx + popw %ax + ret + +/***************************************************************************** + * Subroutine: zero 1kB block of base memory + * + * Parameters: + * %bx : block to zero (in kB) + * Returns: + * Nothing + ***************************************************************************** + */ +zero_kb: + /* Preserve registers */ + pushw %ax + pushw %cx + pushw %di + pushw %es + /* Zero block */ + movw %bx, %ax + shlw $6, %ax + movw %ax, %es + movw $0x400, %cx + xorw %di, %di + xorw %ax, %ax + rep stosb + /* Restore registers and return */ + popw %es + popw %di + popw %cx + popw %ax + ret + +/***************************************************************************** + * Subroutine: free and zero base memory + * + * Parameters: + * %ax : Desired new free base memory counter (in kB) + * %bx : Expected current free base memory counter (in kB) + * %fs : BIOS data segment (0x40) + * Returns: + * None + * + * The base memory from %bx kB to %ax kB is unconditionally zeroed. + * It will be freed if and only if the expected current free base + * memory counter (%bx) matches the actual current free base memory + * counter in 0x40:0x13; if this does not match then the memory will + * be leaked. + ***************************************************************************** + */ +free_basemem: + /* Zero base memory */ + pushw %bx +1: cmpw %bx, %ax + je 2f + call zero_kb + incw %bx + jmp 1b +2: popw %bx + /* Free base memory */ + cmpw %fs:(0x13), %bx /* Update FBMS only if "old" value */ + jne 1f /* is correct */ +1: movw %ax, %fs:(0x13) + ret + +/***************************************************************************** + * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API. + * + * Parameters: + * %bx : PXE API call number + * %ds:pxe_parameter_structure : Parameters for PXE API call + * Returns: + * %ax : PXE status code (not exit code) + * CF set if %ax is non-zero + ***************************************************************************** + */ +pxe_call: + /* Preserve registers */ + pushw %di + pushw %es + /* Set up registers for PXENV+ API. %bx already set up */ + pushw %ds + popw %es + movw $pxe_parameter_structure, %di + /* Set up stack for !PXE API */ + pushw %es + pushw %di + pushw %bx + /* Make the API call */ + lcall *entry_segoff + /* Reset the stack */ + addw $6, %sp + movw pxe_parameter_structure, %ax + clc + testw %ax, %ax + jz 1f + stc +1: /* Clear direction flag, for the sake of sanity */ + cld + /* Restore registers and return */ + popw %es + popw %di + ret + +/***************************************************************************** + * Subroutine: print PXE API call error message + * + * Parameters: + * %ax : PXE status code + * %bx : PXE API call number + * Returns: + * Nothing + ***************************************************************************** + */ +print_pxe_error: + pushw %si + movw $10f, %si + call print_message + xchgw %ax, %bx + call print_hex_word + movw $20f, %si + call print_message + xchgw %ax, %bx + call print_hex_word + movw $30f, %si + call print_message + popw %si + ret + .section ".prefix.data", "aw", @progbits +10: .asciz " UNDI API call " +20: .asciz " failed: status code " +30: .asciz "\n" + .previous + +/***************************************************************************** + * PXE data structures + ***************************************************************************** + */ + .section ".prefix.data" + +pxe_esp: .long 0 +pxe_ss: .word 0 + +pxe_parameter_structure: .fill 64 + +undi_code_segoff: +undi_code_size: .word 0 +undi_code_segment: .word 0 + +undi_data_segoff: +undi_data_size: .word 0 +undi_data_segment: .word 0 + +pxe_hacks: .word 0 + +/* The following fields are part of a struct undi_device */ + +undi_device: + +pxenv_segoff: +pxenv_offset: .word 0 +pxenv_segment: .word 0 + +ppxe_segoff: +ppxe_offset: .word 0 +ppxe_segment: .word 0 + +entry_segoff: +entry_offset: .word 0 +entry_segment: .word 0 + +undi_fbms_start: .word 0 +undi_fbms_end: .word 0 + +pci_busdevfn: .word UNDI_NO_PCI_BUSDEVFN +isapnp_csn: .word UNDI_NO_ISAPNP_CSN +isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT + +pci_vendor: .word 0 +pci_device: .word 0 +flags: + .word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL ) + + .equ undi_device_size, ( . - undi_device ) + +/***************************************************************************** + * Run iPXE main code + ***************************************************************************** + */ + .section ".prefix" +run_ipxe: + /* Install iPXE */ + call install + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + +#ifdef PXELOADER_KEEP_UNDI + /* Copy our undi_device structure to the preloaded_undi variable */ + movw %bx, %es + movw $preloaded_undi, %di + movw $undi_device, %si + movw $undi_device_size, %cx + rep movsb +#endif + + /* Retrieve PXE %ss:esp */ + movw pxe_ss, %di + movl pxe_esp, %ebp + + /* Retrieve PXE command line, if any */ + movl pxe_cmdline, %esi + + /* Retrieve cached DHCPACK, if any */ + movl pxe_cached_dhcpack, %ecx + + /* Jump to .text16 segment with %ds pointing to .data16 */ + movw %bx, %ds + pushw %ax + pushw $1f + lret + .section ".text16", "ax", @progbits +1: + /* Update the exit hook */ + movw %cs, ( pxe_exit_hook + 2 ) + + /* Store command-line pointer */ + movl %esi, cmdline_phys + + /* Store cached DHCPACK pointer */ + movl %ecx, cached_dhcpack_phys + + /* Run main program */ + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall iPXE */ + call uninstall + + /* Restore PXE stack */ + movw %di, %ss + movl %ebp, %esp + + /* Jump to hook if applicable */ + ljmpw *pxe_exit_hook + + .section ".data16", "aw", @progbits + .globl pxe_exit_hook +pxe_exit_hook: + .word exit_ipxe, 0 + .previous + +exit_ipxe: + /* Check PXE stack magic */ + popl %eax + cmpl $STACK_MAGIC, %eax + jne 1f + + /* PXE stack OK: return to caller */ + popw %ds + popw %es + popw %fs + popw %gs + popal + popfl + xorw %ax, %ax /* Return success */ + lret + +1: /* PXE stack corrupt or removed: use INT 18 */ + int $0x18 + .previous diff --git a/src/arch/x86/prefix/romprefix.S b/src/arch/x86/prefix/romprefix.S new file mode 100644 index 000000000..8974c5398 --- /dev/null +++ b/src/arch/x86/prefix/romprefix.S @@ -0,0 +1,907 @@ +/* At entry, the processor is in 16 bit real mode and the code is being + * executed from an address it was not linked to. Code must be pic and + * 32 bit sensitive until things are fixed up. + * + * Also be very careful as the stack is at the rear end of the interrupt + * table so using a noticeable amount of stack space is a no-no. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +#include <config/general.h> +#include <config/branding.h> + +#define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) ) +#define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) ) +#define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) ) +#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) +#define PMM_ALLOCATE 0x0000 +#define PMM_FIND 0x0001 +#define PMM_HANDLE_BASE ( ( ( 'F' - 'A' + 1 ) << 26 ) + \ + ( ( 'E' - 'A' + 1 ) << 21 ) + \ + ( ( 'N' - 'A' + 1 ) << 16 ) ) +#define PMM_HANDLE_BASE_IMAGE_SOURCE \ + ( PMM_HANDLE_BASE | 0x00001000 ) +#define PMM_HANDLE_BASE_DECOMPRESS_TO \ + ( PMM_HANDLE_BASE | 0x00002000 ) +#define PCI_FUNC_MASK 0x07 + +/* ROM banner timeout, converted to a number of (18Hz) timer ticks. */ +#define ROM_BANNER_TIMEOUT_TICKS ( ( 18 * ROM_BANNER_TIMEOUT ) / 10 ) + +/* Allow payload to be excluded from ROM size + */ +#if ROMPREFIX_EXCLUDE_PAYLOAD +#define ZINFO_TYPE_ADxB "ADHB" +#define ZINFO_TYPE_ADxW "ADHW" +#else +#define ZINFO_TYPE_ADxB "ADDB" +#define ZINFO_TYPE_ADxW "ADDW" +#endif + +/* Allow ROM to be marked as containing multiple images + */ +#if ROMPREFIX_MORE_IMAGES +#define INDICATOR 0x00 +#else +#define INDICATOR 0x80 +#endif + +/* Default to building a PCI ROM if no bus type is specified + */ +#ifndef BUSTYPE +#define BUSTYPE "PCIR" +#endif + + .text + .code16 + .arch i386 + .section ".prefix", "ax", @progbits + .globl _rom_start +_rom_start: + + .org 0x00 +romheader: + .word 0xAA55 /* BIOS extension signature */ +romheader_size: .byte 0 /* Size in 512-byte blocks */ + jmp init /* Initialisation vector */ +checksum: + .byte 0 + .org 0x10 + .word ipxeheader + .org 0x16 + .word undiheader +.ifeqs BUSTYPE, "PCIR" + .org 0x18 + .word pciheader +.endif + .org 0x1a + .word pnpheader + .size romheader, . - romheader + + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii ZINFO_TYPE_ADxB + .long romheader_size + .long 512 + .long 0 + .previous + +.ifeqs BUSTYPE, "PCIR" +pciheader: + .ascii "PCIR" /* Signature */ + .word pci_vendor_id /* Vendor identification */ + .word pci_device_id /* Device identification */ + .word ( pci_devlist - pciheader ) /* Device list pointer */ + .word pciheader_len /* PCI data structure length */ + .byte 0x03 /* PCI data structure revision */ + .byte 0x02, 0x00, 0x00 /* Class code */ +pciheader_image_length: + .word 0 /* Image length */ + .word 0x0001 /* Revision level */ + .byte 0x00 /* Code type */ + .byte INDICATOR /* Last image indicator */ +pciheader_runtime_length: + .word 0 /* Maximum run-time image length */ + .word 0x0000 /* Configuration utility code header */ + .word 0x0000 /* DMTF CLP entry point */ + .equ pciheader_len, . - pciheader + .size pciheader, . - pciheader + + /* PCI additional device list (filled in by linker) */ + .section ".pci_devlist.00000000", "a", @progbits +pci_devlist: + .previous + .section ".pci_devlist.ffffffff", "a", @progbits +pci_devlist_end: + .short 0x0000 /* List terminator */ + .previous + /* Ensure that terminator is always present */ + .reloc pciheader, RELOC_TYPE_NONE, pci_devlist_end + + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii ZINFO_TYPE_ADxW + .long pciheader_image_length + .long 512 + .long 0 + .ascii "ADHW" + .long pciheader_runtime_length + .long 512 + .long 0 + .previous +.endif /* PCIR */ + + /* PnP doesn't require any particular alignment, but IBM + * BIOSes will scan on 16-byte boundaries rather than using + * the offset stored at 0x1a + */ + .align 16 +pnpheader: + .ascii "$PnP" /* Signature */ + .byte 0x01 /* Structure revision */ + .byte ( pnpheader_len / 16 ) /* Length (in 16 byte increments) */ + .word 0x0000 /* Offset of next header */ + .byte 0x00 /* Reserved */ + .byte 0x00 /* Checksum */ + .long 0x00000000 /* Device identifier */ + .word mfgstr /* Manufacturer string */ + .word prodstr /* Product name */ + .byte 0x02 /* Device base type code */ + .byte 0x00 /* Device sub-type code */ + .byte 0x00 /* Device interface type code */ + .byte 0xf4 /* Device indicator */ + .word 0x0000 /* Boot connection vector */ + .word 0x0000 /* Disconnect vector */ + .word bev_entry /* Boot execution vector */ + .word 0x0000 /* Reserved */ + .word 0x0000 /* Static resource information vector*/ + .equ pnpheader_len, . - pnpheader + .size pnpheader, . - pnpheader + +/* Manufacturer string */ +mfgstr: + .asciz "http://ipxe.org" + .size mfgstr, . - mfgstr + +/* Product string + * + * Defaults to PRODUCT_SHORT_NAME. If the ROM image is writable at + * initialisation time, it will be filled in to include the PCI + * bus:dev.fn number of the card as well. + */ +prodstr: + .ascii PRODUCT_SHORT_NAME +.ifeqs BUSTYPE, "PCIR" +prodstr_separator: + .byte 0 + .ascii "(PCI " +prodstr_pci_id: + .ascii "xx:xx.x)" /* Filled in by init code */ +.endif /* PCIR */ + .byte 0 + .size prodstr, . - prodstr + + .globl undiheader + .weak undiloader +undiheader: + .ascii "UNDI" /* Signature */ + .byte undiheader_len /* Length of structure */ + .byte 0 /* Checksum */ + .byte 0 /* Structure revision */ + .byte 0,1,2 /* PXE version: 2.1.0 */ + .word undiloader /* Offset to loader routine */ + .word _data16_memsz /* Stack segment size */ + .word _data16_memsz /* Data segment size */ + .word _text16_memsz /* Code segment size */ + .ascii BUSTYPE /* Bus type */ + .equ undiheader_len, . - undiheader + .size undiheader, . - undiheader + +ipxeheader: + .ascii "iPXE" /* Signature */ + .byte ipxeheader_len /* Length of structure */ + .byte 0 /* Checksum */ +shrunk_rom_size: + .byte 0 /* Shrunk size (in 512-byte blocks) */ + .byte 0 /* Reserved */ +build_id: + .long _build_id /* Randomly-generated build ID */ + .equ ipxeheader_len, . - ipxeheader + .size ipxeheader, . - ipxeheader + + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADHB" + .long shrunk_rom_size + .long 512 + .long 0 + .previous + +/* Initialisation (called once during POST) + * + * Determine whether or not this is a PnP system via a signature + * check. If it is PnP, return to the PnP BIOS indicating that we are + * a boot-capable device; the BIOS will call our boot execution vector + * if it wants to boot us. If it is not PnP, hook INT 19. + */ +init: + /* Preserve registers, clear direction flag, set %ds=%cs */ + pushaw + pushw %ds + pushw %es + pushw %fs + pushw %gs + cld + pushw %cs + popw %ds + + /* Print message as early as possible */ + movw $init_message, %si + xorw %di, %di + call print_message + + /* Store PCI 3.0 runtime segment address for later use, if + * applicable. + */ +.ifeqs BUSTYPE, "PCIR" + movw %bx, %gs +.endif + + /* Store PCI bus:dev.fn address, print PCI bus:dev.fn, and add + * PCI bus:dev.fn to product name string, if applicable. + */ +.ifeqs BUSTYPE, "PCIR" + xorw %di, %di + call print_space + movw %ax, init_pci_busdevfn + call print_pci_busdevfn + movw $prodstr_pci_id, %di + call print_pci_busdevfn + movb $( ' ' ), prodstr_separator +.endif + + /* Print segment address */ + xorw %di, %di + call print_space + movw %cs, %ax + call print_hex_word + + /* Check for PCI BIOS version, if applicable */ +.ifeqs BUSTYPE, "PCIR" + pushl %ebx + pushl %edx + pushl %edi + stc + movw $0xb101, %ax + int $0x1a + jc no_pci3 + cmpl $PCI_SIGNATURE, %edx + jne no_pci3 + testb %ah, %ah + jnz no_pci3 + movw $init_message_pci, %si + xorw %di, %di + call print_message + movb %bh, %al + call print_hex_nibble + movb $( '.' ), %al + call print_character + movb %bl, %al + call print_hex_byte + cmpb $3, %bh + jb no_pci3 + /* PCI >=3.0: leave %gs as-is if sane */ + movw %gs, %ax + cmpw $0xa000, %ax /* Insane if %gs < 0xa000 */ + jb pci3_insane + movw %cs, %bx /* Sane if %cs == %gs */ + cmpw %bx, %ax + je 1f + movzbw romheader_size, %cx /* Sane if %cs+len <= %gs */ + shlw $5, %cx + addw %cx, %bx + cmpw %bx, %ax + jae 1f + movw %cs, %bx /* Sane if %gs+len <= %cs */ + addw %cx, %ax + cmpw %bx, %ax + jbe 1f +pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */ + movb $( '!' ), %al + call print_character + movw %gs, %ax + call print_hex_word +no_pci3: + /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */ + pushw %cs + popw %gs +1: popl %edi + popl %edx + popl %ebx +.endif /* PCIR */ + + /* Check for PnP BIOS. Although %es:di should point to the + * PnP BIOS signature on entry, some BIOSes fail to do this. + */ + movw $( 0xf000 - 1 ), %bx +pnp_scan: + incw %bx + jz no_pnp + movw %bx, %es + cmpl $PNP_SIGNATURE, %es:0 + jne pnp_scan + xorw %dx, %dx + xorw %si, %si + movzbw %es:5, %cx +1: es lodsb + addb %al, %dl + loop 1b + jnz pnp_scan + /* Is PnP: print PnP message */ + movw $init_message_pnp, %si + xorw %di, %di + call print_message + jmp pnp_done +no_pnp: /* Not PnP-compliant - hook INT 19 */ +#ifdef NONPNP_HOOK_INT19 + movw $init_message_int19, %si + xorw %di, %di + call print_message + xorw %ax, %ax + movw %ax, %es + pushl %es:( 0x19 * 4 ) + popl orig_int19 + pushw %gs /* %gs contains runtime %cs */ + pushw $int19_entry + popl %es:( 0x19 * 4 ) +#endif /* NONPNP_HOOK_INT19 */ +pnp_done: + + /* Check for PMM */ + movw $( 0xe000 - 1 ), %bx +pmm_scan: + incw %bx + jz no_pmm + movw %bx, %es + cmpl $PMM_SIGNATURE, %es:0 + jne pmm_scan + xorw %dx, %dx + xorw %si, %si + movzbw %es:5, %cx +1: es lodsb + addb %al, %dl + loop 1b + jnz pmm_scan + /* PMM found: print PMM message */ + movw $init_message_pmm, %si + xorw %di, %di + call print_message + /* We have PMM and so a 1kB stack: preserve whole registers */ + pushal + /* Allocate image source PMM block. Round up the size to the + * nearest 4kB (8 512-byte sectors) to work around AMI BIOS bugs. + */ + movzbl romheader_size, %ecx + addw extra_size, %cx + addw $0x0007, %cx /* Round up to multiple of 8 512-byte sectors */ + andw $0xfff8, %cx + shll $5, %ecx + movl $PMM_HANDLE_BASE_IMAGE_SOURCE, %ebx + movw $get_pmm_image_source, %bp + call get_pmm + movl %esi, image_source + jz 1f + /* Copy ROM to image source PMM block */ + pushw %es + xorw %ax, %ax + movw %ax, %es + movl %esi, %edi + xorl %esi, %esi + movzbl romheader_size, %ecx + shll $7, %ecx + addr32 rep movsl /* PMM presence implies flat real mode */ + popw %es + /* Shrink ROM */ + movb shrunk_rom_size, %al + movb %al, romheader_size +1: /* Allocate decompression PMM block. Round up the size to the + * nearest 128kB and use the size within the PMM handle; this + * allows the same decompression area to be shared between + * multiple iPXE ROMs even with differing build IDs + */ + movl $_textdata_memsz_pgh, %ecx + addl $0x00001fff, %ecx + andl $0xffffe000, %ecx + movl %ecx, %ebx + shrw $12, %bx + orl $PMM_HANDLE_BASE_DECOMPRESS_TO, %ebx + movw $get_pmm_decompress_to, %bp + call get_pmm + movl %esi, decompress_to + /* Restore registers */ + popal +no_pmm: + + /* Update checksum */ + xorw %bx, %bx + xorw %si, %si + movzbw romheader_size, %cx + shlw $9, %cx +1: lodsb + addb %al, %bl + loop 1b + subb %bl, checksum + + /* Copy self to option ROM space, if applicable. Required for + * PCI3.0, which loads us to a temporary location in low + * memory. Will be a no-op for lower PCI versions. + */ +.ifeqs BUSTYPE, "PCIR" + /* Get runtime segment address and length */ + movw %gs, %ax + movw %ax, %es + movzbw romheader_size, %cx + /* Print runtime segment address */ + xorw %di, %di + call print_space + call print_hex_word + /* Fail if we have insufficient space in final location */ + movw %cs, %si + cmpw %si, %ax + je 1f + cmpw pciheader_runtime_length, %cx + jbe 1f + movb $( '!' ), %al + call print_character + xorw %cx, %cx +1: /* Copy to final location */ + shlw $9, %cx + xorw %si, %si + xorw %di, %di + cs rep movsb +.endif + + /* Skip prompt if this is not the first PCI function, if applicable */ +.ifeqs BUSTYPE, "PCIR" + testb $PCI_FUNC_MASK, init_pci_busdevfn + jnz no_shell +.endif + /* Prompt for POST-time shell */ + movw $init_message_prompt, %si + xorw %di, %di + call print_message + movw $prodstr, %si + call print_message + movw $init_message_dots, %si + call print_message + /* Wait for Ctrl-B */ + movw $0xff02, %bx + call wait_for_key + /* Clear prompt */ + pushf + xorw %di, %di + call print_kill_line + movw $init_message_done, %si + call print_message + popf + jnz no_shell + /* Ctrl-B was pressed: invoke iPXE. The keypress will be + * picked up by the initial shell prompt, and we will drop + * into a shell. + */ + xorl %ebp, %ebp /* Inhibit use of INT 15,e820 and INT 15,e801 */ + pushw %cs + call exec +no_shell: + movb $( '\n' ), %al + xorw %di, %di + call print_character + + /* Restore registers */ + popw %gs + popw %fs + popw %es + popw %ds + popaw + + /* Indicate boot capability to PnP BIOS, if present */ + movw $0x20, %ax + lret + .size init, . - init + +/* Attempt to find or allocate PMM block + * + * Parameters: + * %ecx : size of block to allocate, in paragraphs + * %ebx : PMM handle base + * %bp : routine to check acceptability of found blocks + * %es:0000 : PMM structure + * Returns: + * %ebx : PMM handle + * %esi : allocated block address, or zero (with ZF set) if allocation failed + */ +get_pmm: + /* Preserve registers */ + pushl %eax + pushw %di + movw $( ' ' ), %di +get_pmm_find: + /* Try to find existing block */ + pushl %ebx /* PMM handle */ + pushw $PMM_FIND + lcall *%es:7 + addw $6, %sp + pushw %dx + pushw %ax + popl %esi + /* Treat 0xffffffff (not supported) as 0x00000000 (not found) */ + incl %esi + jz get_pmm_allocate + decl %esi + jz get_pmm_allocate + /* Block found - check acceptability */ + call *%bp + jnc get_pmm_done + /* Block not acceptable - increment handle and retry */ + incl %ebx + jmp get_pmm_find +get_pmm_allocate: + /* Block not found - try to allocate new block */ + pushw $0x0002 /* Extended memory */ + pushl %ebx /* PMM handle */ + pushl %ecx /* Length */ + pushw $PMM_ALLOCATE + lcall *%es:7 + addw $12, %sp + pushw %dx + pushw %ax + popl %esi + movw $( '+' ), %di /* Indicate allocation attempt */ +get_pmm_done: + /* Print block address */ + movw %di, %ax + xorw %di, %di + call print_character + movl %esi, %eax + call print_hex_dword + /* Treat 0xffffffff (not supported) as 0x00000000 (allocation + * failed), and set ZF to indicate a zero result. + */ + incl %esi + jz 1f + decl %esi +1: /* Restore registers and return */ + popw %di + popl %eax + ret + .size get_pmm, . - get_pmm + + /* Check acceptability of image source block */ +get_pmm_image_source: + pushw %es + xorw %ax, %ax + movw %ax, %es + movl build_id, %eax + addr32 cmpl %es:build_id(%esi), %eax + je 1f + stc +1: popw %es + ret + .size get_pmm_image_source, . - get_pmm_image_source + + /* Check acceptability of decompression block */ +get_pmm_decompress_to: + clc + ret + .size get_pmm_decompress_to, . - get_pmm_decompress_to + +/* + * Note to hardware vendors: + * + * If you wish to brand this boot ROM, please do so by defining the + * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/branding.h. + * + * While nothing in the GPL prevents you from removing all references + * to iPXE or http://ipxe.org, we prefer you not to do so. + * + * If you have an OEM-mandated branding requirement that cannot be + * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME, + * please contact us. + * + * [ Including an ASCII NUL in PRODUCT_NAME is considered to be + * bypassing the spirit of this request! ] + */ +init_message: + .ascii "\n" + .ascii PRODUCT_NAME + .ascii "\n" + .ascii PRODUCT_SHORT_NAME + .ascii " (" + .ascii PRODUCT_URI + .asciz ")" + .size init_message, . - init_message +.ifeqs BUSTYPE, "PCIR" +init_message_pci: + .asciz " PCI" + .size init_message_pci, . - init_message_pci +.endif /* PCIR */ +init_message_pnp: + .asciz " PnP" + .size init_message_pnp, . - init_message_pnp +init_message_pmm: + .asciz " PMM" + .size init_message_pmm, . - init_message_pmm +init_message_int19: + .asciz " INT19" + .size init_message_int19, . - init_message_int19 +init_message_prompt: + .asciz "\nPress Ctrl-B to configure " + .size init_message_prompt, . - init_message_prompt +init_message_dots: + .asciz "..." + .size init_message_dots, . - init_message_dots +init_message_done: + .asciz "\n\n" + .size init_message_done, . - init_message_done + +/* PCI bus:dev.fn + * + */ +.ifeqs BUSTYPE, "PCIR" +init_pci_busdevfn: + .word 0 + .size init_pci_busdevfn, . - init_pci_busdevfn +.endif /* PCIR */ + +/* Image source area + * + * May be either zero (indicating to use option ROM space as source), + * or within a PMM-allocated block. + */ + .globl image_source +image_source: + .long 0 + .size image_source, . - image_source + +/* Additional image source size (in 512-byte sectors) + * + */ +extra_size: + .word 0 + .size extra_size, . - extra_size + +/* Temporary decompression area + * + * May be either zero (indicating to use default decompression area in + * high memory), or within a PMM-allocated block. + */ + .globl decompress_to +decompress_to: + .long 0 + .size decompress_to, . - decompress_to + +/* Boot Execution Vector entry point + * + * Called by the PnP BIOS when it wants to boot us. + */ +bev_entry: + orl $0xffffffff, %ebp /* Allow arbitrary relocation */ + pushw %cs + call exec + lret + .size bev_entry, . - bev_entry + +/* INT19 entry point + * + * Called via the hooked INT 19 if we detected a non-PnP BIOS. We + * attempt to return via the original INT 19 vector (if we were able + * to store it). + */ +int19_entry: + pushw %cs + popw %ds + /* Prompt user to press B to boot */ + movw $int19_message_prompt, %si + xorw %di, %di + call print_message + movw $prodstr, %si + call print_message + movw $int19_message_dots, %si + call print_message + movw $0xdf4e, %bx + call wait_for_key + pushf + xorw %di, %di + call print_kill_line + movw $int19_message_done, %si + call print_message + popf + jz 1f + /* Leave keypress in buffer and start iPXE. The keypress will + * cause the usual initial Ctrl-B prompt to be skipped. + */ + orl $0xffffffff, %ebp /* Allow arbitrary relocation */ + pushw %cs + call exec +1: /* Try to call original INT 19 vector */ + movl %cs:orig_int19, %eax + testl %eax, %eax + je 2f + ljmp *%cs:orig_int19 +2: /* No chained vector: issue INT 18 as a last resort */ + int $0x18 + .size int19_entry, . - int19_entry +orig_int19: + .long 0 + .size orig_int19, . - orig_int19 + +int19_message_prompt: + .asciz "Press N to skip booting from " + .size int19_message_prompt, . - int19_message_prompt +int19_message_dots: + .asciz "..." + .size int19_message_dots, . - int19_message_dots +int19_message_done: + .asciz "\n\n" + .size int19_message_done, . - int19_message_done + +/* Execute as a boot device + * + */ +exec: /* Set %ds = %cs */ + pushw %cs + popw %ds + + /* Print message as soon as possible */ + movw $prodstr, %si + xorw %di, %di + call print_message + movw $exec_message_pre_install, %si + call print_message + + /* Store magic word on BIOS stack and remember BIOS %ss:sp */ + pushl $STACK_MAGIC + movw %ss, %cx + movw %sp, %dx + + /* Obtain a reasonably-sized temporary stack */ + xorw %bx, %bx + movw %bx, %ss + movw $0x7c00, %sp + + /* Install iPXE */ + call alloc_basemem + movl image_source, %esi + movl decompress_to, %edi + call install_prealloc + + /* Print message indicating successful installation */ + movw $exec_message_post_install, %si + xorw %di, %di + call print_message + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16", "awx", @progbits +1: + /* Retrieve PCI bus:dev.fn, if applicable */ +.ifeqs BUSTYPE, "PCIR" + movw init_pci_busdevfn, %ax +.endif + + /* Set up %ds for access to .data16 */ + movw %bx, %ds + + /* Store PCI bus:dev.fn, if applicable */ +.ifeqs BUSTYPE, "PCIR" +#ifdef AUTOBOOT_ROM_FILTER + movw %ax, autoboot_busdevfn +#endif /* AUTOBOOT_ROM_FILTER */ +.endif + + /* Call main() */ + pushl $main + pushw %cs + call prot_call + popl %eax /* discard */ + + /* Set up flat real mode for return to BIOS */ + call flatten_real_mode + + /* Uninstall iPXE */ + call uninstall + + /* Restore BIOS stack */ + movw %cx, %ss + movw %dx, %sp + + /* Check magic word on BIOS stack */ + popl %eax + cmpl $STACK_MAGIC, %eax + jne 1f + /* BIOS stack OK: return to caller */ + lret +1: /* BIOS stack corrupt: use INT 18 */ + int $0x18 + .previous + +exec_message_pre_install: + .asciz " starting execution..." + .size exec_message_pre_install, . - exec_message_pre_install +exec_message_post_install: + .asciz "ok\n" + .size exec_message_post_install, . - exec_message_post_install + +/* Wait for key press specified by %bl (masked by %bh) + * + * Used by init and INT19 code when prompting user. If the specified + * key is pressed, it is left in the keyboard buffer. + * + * Returns with ZF set iff specified key is pressed. + */ +wait_for_key: + /* Preserve registers */ + pushw %cx + pushw %ax +1: /* Empty the keyboard buffer before waiting for input */ + movb $0x01, %ah + int $0x16 + jz 2f + xorw %ax, %ax + int $0x16 + jmp 1b +2: /* Wait for a key press */ + movw $ROM_BANNER_TIMEOUT_TICKS, %cx +3: decw %cx + js 99f /* Exit with ZF clear */ + /* Wait for timer tick to be updated */ + call wait_for_tick + /* Check to see if a key was pressed */ + movb $0x01, %ah + int $0x16 + jz 3b + /* Check to see if key was the specified key */ + andb %bh, %al + cmpb %al, %bl + je 99f /* Exit with ZF set */ + /* Not the specified key: remove from buffer and stop waiting */ + pushfw + xorw %ax, %ax + int $0x16 + popfw /* Exit with ZF clear */ +99: /* Restore registers and return */ + popw %ax + popw %cx + ret + .size wait_for_key, . - wait_for_key + +/* Wait for timer tick + * + * Used by wait_for_key + */ +wait_for_tick: + pushl %eax + pushw %fs + movw $0x40, %ax + movw %ax, %fs + movl %fs:(0x6c), %eax +1: pushf + sti + hlt + popf + cmpl %fs:(0x6c), %eax + je 1b + popw %fs + popl %eax + ret + .size wait_for_tick, . - wait_for_tick + +/* Drag in objects via _rom_start */ +REQUIRING_SYMBOL ( _rom_start ) + +/* Drag in ROM configuration */ +REQUIRE_OBJECT ( config_romprefix ) diff --git a/src/arch/x86/prefix/undiloader.S b/src/arch/x86/prefix/undiloader.S new file mode 100644 index 000000000..5cace44b7 --- /dev/null +++ b/src/arch/x86/prefix/undiloader.S @@ -0,0 +1,54 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .code16 + .arch i386 + .section ".prefix", "ax", @progbits + +/* UNDI loader + * + * Called by an external program to load our PXE stack. + */ + .globl undiloader +undiloader: + /* Save registers */ + pushl %esi + pushl %edi + pushl %ebp + pushw %ds + pushw %es + pushw %bx + /* ROM segment address to %ds */ + pushw %cs + popw %ds + /* UNDI loader parameter structure address into %es:%di */ + movw %sp, %bx + movw %ss:22(%bx), %di + movw %ss:24(%bx), %es + /* Install to specified real-mode addresses */ + pushw %di + movw %es:12(%di), %bx + movw %es:14(%di), %ax + movl image_source, %esi + movl decompress_to, %edi + orl $0xffffffff, %ebp /* Allow arbitrary relocation */ + call install_prealloc + popw %di + /* Call UNDI loader C code */ + pushl $pxe_loader_call + pushw %cs + pushw $1f + pushw %ax + pushw $prot_call + lret +1: popw %bx /* discard */ + popw %bx /* discard */ + /* Restore registers and return */ + popw %bx + popw %es + popw %ds + popl %ebp + popl %edi + popl %esi + lret + .size undiloader, . - undiloader diff --git a/src/arch/x86/prefix/unlzma.S b/src/arch/x86/prefix/unlzma.S new file mode 100644 index 000000000..8d4b3c1a8 --- /dev/null +++ b/src/arch/x86/prefix/unlzma.S @@ -0,0 +1,942 @@ +/* + * Copyright (C) 2015 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 (at your option) 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/**************************************************************************** + * + * This file provides the decompress() and decompress16() functions + * which can be called in order to decompress an LZMA-compressed + * image. The code is modelled on the public-domain "XZ Embedded" + * implementation as used by the Linux kernel. Symbol names are + * chosen to match the XZ Embedded implementation where possible, for + * ease of reference. + * + * This code is optimised for size rather than speed, since the amount + * of data to be decompressed is trivially small by modern standards. + * + * The same basic assembly code is used to compile both decompress() + * and decompress16(). + * + * Note that these functions require large amounts of stack space. + * + **************************************************************************** + */ + + .text + .arch i586 + .section ".prefix.lib", "ax", @progbits + +#ifdef CODE16 +#define ADDR16 +#define ADDR32 addr32 +#define decompress decompress16 + .code16 +#else /* CODE16 */ +#define ADDR16 addr16 +#define ADDR32 + .code32 +#endif /* CODE16 */ + +/**************************************************************************** + * Debugging + **************************************************************************** + * + * This code will usually run in 16-bit protected mode, in which case + * only the 0xe9 debug port (present on some virtual machines) can be + * used. + * + * To debug on real hardware, build with DEBUG=libprefix. This will + * cause this code to be called in flat real mode, and so DEBUG_INT10 + * may be used. + */ + +/* Enable debugging via 0xe9 debug port */ +#define DEBUG_E9 0 + +/* Enable debugging via BIOS INT 10 (works only when in flat real mode) */ +#define DEBUG_INT10 0 + +#if ( DEBUG_E9 || DEBUG_INT10 ) + .macro print_character, reg + pushfl + pushw %ax + pushw %bx + pushw %bp + movb \reg, %al + movw $0x0007, %bx + movb $0x0e, %ah +#if DEBUG_E9 + outb %al, $0xe9 +#endif +#if DEBUG_INT10 + cmpb $('\n'), %al + jne L\@ + int $0x10 + movb $('\r'), %al +L\@: int $0x10 +#endif + popw %bp + popw %bx + popw %ax + popfl + .endm + + .macro print_hex_nibble + pushfl + pushw %ax + cmpb $10, %al + sbb $0x69, %al + das + print_character %al + popw %ax + popfl + .endm + + .macro print_hex_byte, reg + pushfl + pushw %ax + movb \reg, %al + pushw %ax + shrb $4, %al + print_hex_nibble + popw %ax + andb $0x0f, %al + print_hex_nibble + popw %ax + popfl + .endm + + .macro print_hex_word, reg + pushw %ax + movw \reg, %ax + print_hex_byte %ah + print_hex_byte %al + popw %ax + .endm + + .macro print_hex_dword, reg + pushl %eax + movl \reg, %eax + rorl $16, %eax + print_hex_word %ax + rorl $16, %eax + print_hex_word %ax + popl %eax + .endm +#else + .macro print_character, char + .endm + .macro print_hex_byte, reg + .endm + .macro print_hex_word, reg + .endm + .macro print_hex_dword, reg + .endm +#endif + +/**************************************************************************** + * LZMA parameters and data structures + **************************************************************************** + */ + +/* LZMA decompressor states (as used in XZ Embedded) */ +#define STATE_LIT_LIT 0x00 +#define STATE_MATCH_LIT_LIT 0x01 +#define STATE_REP_LIT_LIT 0x02 +#define STATE_SHORTREP_LIT_LIT 0x03 +#define STATE_MATCH_LIT 0x04 +#define STATE_REP_LIT 0x05 +#define STATE_SHORTREP_LIT 0x06 +#define STATE_LIT_MATCH 0x07 +#define STATE_LIT_LONGREP 0x08 +#define STATE_LIT_SHORTREP 0x09 +#define STATE_NONLIT_MATCH 0x0a +#define STATE_NONLIT_REP 0x0b + +/* LZMA maximum decompressor state in which most recent symbol was a literal */ +#define STATE_LIT_MAX 0x06 + +/* LZMA number of literal context bits ("lc=" parameter) */ +#define LZMA_LC 2 + + .struct 0 +lzma_len_dec: +choice: .word 0 +choice2: .word 0 +low: .rept ( 1 << 3 ) + .word 0 + .endr +mid: .rept ( 1 << 3 ) + .word 0 + .endr +high: .rept ( 1 << 8 ) + .word 0 + .endr + .equ sizeof__lzma_len_dec, . - lzma_len_dec + .previous + + .struct 0 +lzma_dec: +out_start: .long 0 +rc_code: .long 0 +rc_range: .long 0 +len: .word 0 +reps: +rep0: .long 0 +rep1: .long 0 +rep2: .long 0 +rep3: .long 0 +probs: +is_match: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep0: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep1: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep2: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep0_long: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +dist_slot: .rept ( 4 * ( 1 << 6 ) ) + .word 0 + .endr +dist_special: .rept ( ( 1 << ( 14 / 2 ) ) - 14 ) + .word 0 + .endr +dist_align: .rept ( 1 << 4 ) + .word 0 + .endr +match_len_dec: .space sizeof__lzma_len_dec +rep_len_dec: .space sizeof__lzma_len_dec +literal: .rept ( ( 1 << LZMA_LC ) * 0x300 ) + .word 0 + .endr + .align 4 + .equ sizeof__lzma_dec, . - lzma_dec + .previous + + /* Some binutils versions seem not to handle .struct/.previous */ + .section ".prefix.lib", "ax", @progbits + +/***************************************************************************** + * Normalise range encoder + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : current range + ***************************************************************************** + */ +rc_normalise: + /* Check if rc_range is less than 1<<24 */ + testb $0xff, (rc_range+3)(%ebp) + jnz 1f + /* If it is, shift in a new byte from the compressed input data */ + shll $8, rc_range(%ebp) + shll $8, rc_code(%ebp) + ADDR32 lodsb + movb %al, (rc_code+0)(%ebp) +1: /* Return current range */ + movl rc_range(%ebp), %eax + ret + .size rc_normalise, . - rc_normalise + +/***************************************************************************** + * Decode single range-encoded bit using a probability estimate + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : probability estimate pointer (offset from %ebp) + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * CF : decoded bit + * ZF : inverse of decoded bit + * Corrupts: + * none + ***************************************************************************** + */ +rc_bit: + /* Preserve registers */ + pushl %eax + pushl %edx + /* Perform normalisation */ + call rc_normalise + /* Calculate bound in %eax and probability estimate in %dx */ + shrl $11, %eax + movzwl (%ebp,%ebx), %edx + mul %edx /* will zero %edx */ + movw (%ebp,%ebx), %dx + /* Compare code against bound */ + cmpl %eax, rc_code(%ebp) + jae 2f +1: /* Code is less than bound */ + movl %eax, rc_range(%ebp) + negw %dx + addw $(1<<11), %dx + shrw $5, %dx + addw %dx, (%ebp,%ebx) + xorw %ax, %ax /* Clear CF, set ZF */ + jmp 99f +2: /* Code is greater than or equal to bound */ + subl %eax, rc_range(%ebp) + subl %eax, rc_code(%ebp) + shrw $5, %dx + subw %dx, (%ebp,%ebx) + incw %dx /* Clear ZF (%dx is 11-bit; can never wrap) */ + stc /* Set CF */ +99: /* Restore registers and return */ + popl %edx + popl %eax + ret + .size rc_bit, . - rc_bit + +/***************************************************************************** + * Decode MSB-first bittree + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : probability estimate set pointer (offset from %ebp) + * %cx : number of bits to decode + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : decoded bittree + * Corrupts: + * none + ***************************************************************************** + */ +rc_bittree: + /* Preserve registers */ + pushl %edi + pushw %cx + movl %ebx, %edi + /* Initialise registers */ + movl $1, %eax +1: /* Decode bit */ + leaw (%edi,%eax,2), %bx /* high word always zero anyway */ + call rc_bit + rclw %ax + ADDR16 loop 1b + /* Restore registers, clear unwanted high bit of result, and return */ + movl %edi, %ebx + popw %cx + popl %edi + btrw %cx, %ax + ret + .size rc_bittree, . - rc_bittree + +/***************************************************************************** + * Decode LSB-first bittree + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : probability estimate set pointer (offset from %ebp) + * %cx : number of bits to decode + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : decoded bittree + * Corrupts: + * none + ***************************************************************************** + */ +rc_bittree_reverse: + /* Preserve registers */ + pushw %cx + /* Decode bittree */ + call rc_bittree +1: /* Reverse result */ + rcrb %al + rclb %ah + ADDR16 loop 1b + shrw $8, %ax + /* Restore registers and return */ + popw %cx + ret + .size rc_bittree_reverse, . - rc_bittree_reverse + +/***************************************************************************** + * Decode MSB-first bittree with optional match byte + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : probability estimate set pointer (offset from %ebp) + * %cl : match byte + * %ch : 1 to use match byte, 0 to ignore match byte + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : decoded bittree + * Corrupts: + * none + ***************************************************************************** + */ +rc_bittree_match: + /* Preserve registers */ + pushl %edi + pushw %cx + pushw %dx + movl %ebx, %edi + /* Initialise registers */ + movl $1, %eax +1: /* Decode bit */ + rolb $1, %cl + movw %cx, %dx + andb %dh, %dl /* match_bit in %dl */ + movw %dx, %bx + addb %bl, %bh + xorb %bl, %bl + addw %ax, %bx /* offset + match_bit + symbol */ + leaw (%edi,%ebx,2), %bx /* high word always zero anyway */ + call rc_bit + rclw %ax + movb %al, %dh + notb %dh + xorb %dh, %dl + andb %dl, %ch /* offset &= ( match_bit ^ bit ) */ + testb %ah, %ah + jz 1b + /* Restore registers, clear unwanted high bit of result, and return */ + movl %edi, %ebx + popw %dx + popw %cx + popl %edi + xorb %ah, %ah + ret + .size rc_bittree_match, . - rc_bittree_match + +/***************************************************************************** + * Decode direct bits (no probability estimates) + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %cx : number of bits to decode + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : decoded bits + * Corrupts: + * none + ***************************************************************************** + */ +rc_direct: + /* Preserve registers */ + pushl %ebx + pushw %cx + pushl %edx + /* Initialise registers */ + xorl %edx, %edx +1: /* Perform normalisation */ + call rc_normalise + /* Decode bit */ + shrl $1, %eax + movl %eax, rc_range(%ebp) + movl rc_code(%ebp), %ebx + subl %eax, %ebx + js 2f + movl %ebx, rc_code(%ebp) +2: rcll %ebx + rcll %edx + xorb $1, %dl + ADDR16 loop 1b + /* Restore registers and return */ + movl %edx, %eax + popl %edx + popw %cx + popl %ebx + ret + .size rc_direct, . - rc_direct + +/***************************************************************************** + * Decode an LZMA literal + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer (updated) + * %edx : LZMA state + * CF : end of payload marker found (always zero) + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + * + * Literals are coded as an eight-bit tree, using a match byte if the + * previous symbol was not a literal. + * + */ +lzma_literal: + /* Get most recent output byte, if available */ + xorl %ebx, %ebx + cmpl %edi, out_start(%ebp) + je 1f + movb %es:-1(%edi), %bh +1: /* Locate probability estimate set */ + shrb $( 8 - LZMA_LC ), %bh + shlb $1, %bh + leaw literal(%ebx,%ebx,2), %bx + /* Get match byte, if applicable */ + xorw %cx, %cx + cmpb $STATE_LIT_MAX, %dl + jbe 1f + movl rep0(%ebp), %eax + notl %eax + movb %es:(%edi,%eax), %cl + movb $1, %ch +1: /* Decode bittree */ + call rc_bittree_match + /* Store output byte */ + ADDR32 stosb + print_hex_byte %al + print_character $(' ') + /* Update LZMA state */ + subb $3, %dl + jns 1f + xorb %dl, %dl +1: cmpb $7, %dl + jb 1f + subb $3, %dl +1: /* Clear CF and return */ + clc + ret + .size lzma_literal, . - lzma_literal + +/***************************************************************************** + * Decode an LZMA length + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : length parameter pointer (offset from %ebp) + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * Corrupts: + * %ebx + ***************************************************************************** + * + * Lengths are encoded as: + * + * "0" + 3 bits : lengths 2-9 ("low") + * "10" + 3 bits : lengths 10-17 ("mid") + * "11" + 8 bits : lengths 18-273 ("high") + */ +lzma_len: + /* Preserve registers */ + pushl %eax + pushl %ecx + pushl %edi + movl %ebx, %edi + /* Start by assuming three bits and a base length of 2 */ + movw $3, %cx + movw $2, len(%ebp) + /* Check low-length choice bit */ + leal choice(%edi), %ebx + call rc_bit + leal low(%edi), %ebx + jz 1f + /* Check high-length choice bit */ + leal choice2(%edi), %ebx + call rc_bit + leal mid(%edi), %ebx + movb $10, len(%ebp) + jz 1f + leal high(%edi), %ebx + movb $8, %cl + movb $18, len(%ebp) +1: /* Get encoded length */ + call rc_bittree + addw %ax, len(%ebp) + /* Restore registers and return */ + movl %edi, %ebx + popl %edi + popl %ecx + popl %eax + ret + .size lzma_len, . - lzma_len + +/***************************************************************************** + * Copy (possibly repeated) matched data + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %cl : repeated match distance index (for repeated matches) + * %eax : match distance (for non-repeated matches) + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer + * CF : match distance is out of range + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + */ +match: /* Update repeated match list */ + print_character $('[') + movl $3, %ecx + jmp 1f +match_rep: + print_character $('[') + print_character $('R') + print_hex_byte %cl + print_character $('=') + movzbl %cl, %ecx + movl reps(%ebp,%ecx,4), %eax + jcxz 2f +1: movl (reps-4)(%ebp,%ecx,4), %ebx + movl %ebx, reps(%ebp,%ecx,4) + loop 1b + movl %eax, rep0(%ebp) +2: /* Preserve registers */ + pushl %esi + /* Get stored match length */ + movzwl len(%ebp), %ecx + print_hex_dword %eax + print_character $('+') + print_hex_word %cx + print_character $(']') + print_character $(' ') + /* Abort with CF set if match distance is out of range */ + movl out_start(%ebp), %esi + negl %esi + leal -1(%edi,%esi), %esi + cmpl %eax, %esi + jc 99f + /* Perform copy */ + notl %eax + leal (%edi,%eax), %esi + ADDR32 es rep movsb +99: /* Restore registers and return */ + popl %esi + ret + .size match, . - match + +/***************************************************************************** + * Decode an LZMA match + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * CF : end of payload marker found + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + * + * Matches are encoded as an LZMA length followed by a 6-bit "distance + * slot" code, 0-26 fixed-probability bits, and 0-5 context encoded + * bits. + */ +lzma_match: + /* Preserve registers */ + pushl %edi + /* Update LZMA state */ + cmpb $STATE_LIT_MAX, %dl + movb $STATE_LIT_MATCH, %dl + jbe 1f + movb $STATE_NONLIT_MATCH, %dl +1: /* Decode length */ + movl $match_len_dec, %ebx + call lzma_len + /* Decode distance slot */ + movw len(%ebp), %bx + subw $2, %bx + cmpw $4, %bx + jb 1f + movw $3, %bx +1: shlw $7, %bx + addw $dist_slot, %bx + movw $6, %cx + call rc_bittree + /* Distance slots 0-3 are literal distances */ + cmpb $4, %al + jb 99f + /* Determine initial bits: 10/11 for even/odd distance codes */ + movl %eax, %edi + andw $1, %di + orw $2, %di + /* Determine number of context-encoded bits */ + movw %ax, %cx + shrb $1, %cl + decb %cl + /* Select context to be used in absence of fixed-probability bits */ + movl %edi, %ebx + shlw %cl, %bx + subw %ax, %bx + leaw (dist_special-2)(%ebx,%ebx), %bx + /* Decode fixed-probability bits, if any */ + cmpb $6, %cl + jb 1f + subb $4, %cl + shll %cl, %edi + call rc_direct + orl %eax, %edi + /* Select context to be used in presence of fixed-probability bits */ + movb $4, %cl + movl $dist_align, %ebx +1: /* Decode context-encoded bits */ + shll %cl, %edi + call rc_bittree_reverse + orl %edi, %eax +99: /* Restore registers and tail-call */ + popl %edi + jmp match + .size lzma_match, . - lzma_match + +/***************************************************************************** + * Decode an LZMA repeated match + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * CF : end of payload marker found + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + * + * Repeated matches are encoded as: + * + * "00" : shortrep0 (implicit length 1) + * "01" + len : longrep0 + * "10" + len : longrep1 + * "110" + len : longrep2 + * "111" + len : longrep3 + */ +lzma_rep_match: + /* Initially assume longrep0 */ + movw $(STATE_LIT_LONGREP << 8), %cx + /* Get is_rep0 bit */ + leal is_rep0(,%edx,2), %ebx + call rc_bit + jnz 1f + /* Get is_rep0_long bit */ + leal is_rep0_long(,%edx,2), %ebx + call rc_bit + jnz 98f + movw $1, len(%ebp) + movb $STATE_LIT_SHORTREP, %ch + jmp 99f +1: /* Get is_rep1 bit */ + incb %cl + leal is_rep1(,%edx,2), %ebx + call rc_bit + jz 98f + /* Get is_rep2 bit */ + incb %cl + leal is_rep2(,%edx,2), %ebx + call rc_bit + adcb $0, %cl +98: /* Decode length */ + movl $rep_len_dec, %ebx + call lzma_len +99: /* Update LZMA state */ + cmpb $STATE_LIT_MAX, %dl + movb %ch, %dl + jbe 1f + movb $STATE_NONLIT_REP, %dl +1: /* Tail call */ + jmp match_rep + .size lzma_match, . - lzma_match + +/***************************************************************************** + * Decode one LZMA symbol + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer (updated) + * %edx : LZMA state + * CF : end of payload marker found + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + */ +lzma_decode: + /* Get is_match bit */ + leal is_match(,%edx,2), %ebx + call rc_bit + jz lzma_literal + /* Get is_rep bit */ + leal is_rep(,%edx,2), %ebx + call rc_bit + jz lzma_match + jmp lzma_rep_match + .size lzma_decode, . - lzma_decode + +/**************************************************************************** + * Undo effect of branch-call-jump (BCJ) filter + * + * Parameters: + * %es:%esi : start of uncompressed output data (note %es) + * %es:%edi : end of uncompressed output data + * Returns: + * Corrupts: + * %eax + * %ebx + * %ecx + * %edx + * %esi + ***************************************************************************** + */ +bcj_filter: + /* Store (negative) start of data in %edx */ + movl %esi, %edx + negl %edx + /* Calculate limit in %ecx */ + leal -5(%edi,%edx), %ecx +1: /* Calculate offset in %ebx */ + leal (%esi,%edx), %ebx + /* Check for end of data */ + cmpl %ecx, %ebx + ja 99f + /* Check for an opcode which would be followed by a rel32 address */ + ADDR32 es lodsb + andb $0xfe, %al + cmpb $0xe8, %al + jne 1b + /* Get current jump target value in %eax */ + ADDR32 es lodsl + /* Convert absolute addresses in the range [0,limit) back to + * relative addresses in the range [-offset,limit-offset). + */ + cmpl %ecx, %eax + jae 2f + subl %ebx,%es:-4(%esi) +2: /* Convert negative numbers in the range [-offset,0) back to + * positive numbers in the range [limit-offset,limit). + */ + notl %eax /* Range is now [0,offset) */ + cmpl %ebx, %eax + jae 1b + addl %ecx,%es:-4(%esi) + jmp 1b +99: /* Return */ + ret + .size bcj_filter, . - bcj_filter + +/**************************************************************************** + * decompress (real-mode or 16/32-bit protected-mode near call) + * + * Decompress data + * + * Parameters (passed via registers): + * %ds:%esi : Start of compressed input data + * %es:%edi : Start of output buffer + * Returns: + * %ds:%esi - End of compressed input data + * %es:%edi - End of decompressed output data + * All other registers are preserved + * + * NOTE: It would be possible to build a smaller version of the + * decompression code for -DKEEP_IT_REAL by using 16-bit registers + * where possible. + **************************************************************************** + */ + .globl decompress +decompress: + /* Preserve registers */ + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + pushl %ebp + /* Allocate parameter block */ + subl $sizeof__lzma_dec, %esp + movl %esp, %ebp + /* Zero parameter block and set all probabilities to 0.5 */ + pushl %edi + pushw %es + pushw %ss + popw %es + movl %ebp, %edi + xorl %eax, %eax + movl $( sizeof__lzma_dec / 4 ), %ecx + ADDR32 rep stosl + leal probs(%ebp), %edi + movw $( ( 1 << 11 ) / 2 ), %ax + movl $( ( sizeof__lzma_dec - probs ) / 2 ), %ecx + ADDR32 rep stosw + popw %es + popl %edi + /* Initialise remaining parameters */ + movl %edi, out_start(%ebp) + print_character $('\n') + ADDR32 lodsb /* discard initial byte */ + print_hex_byte %al + ADDR32 lodsl + bswapl %eax + print_hex_dword %eax + print_character $('\n') + movl %eax, rc_code(%ebp) + decl rc_range(%ebp) + movl $STATE_LIT_LIT, %edx +1: /* Decompress until we reach end of buffer */ + call lzma_decode + jnc 1b + call rc_normalise + print_character $('\n') + /* Undo BCJ filter */ + pushl %esi + movl out_start(%ebp), %esi + call bcj_filter + popl %esi + /* Restore registers and return */ + addl $sizeof__lzma_dec, %esp + popl %ebp + popl %edx + popl %ecx + popl %ebx + popl %eax + ret + + /* Specify minimum amount of stack space required */ + .globl _min_decompress_stack + .equ _min_decompress_stack, ( sizeof__lzma_dec + 512 /* margin */ ) diff --git a/src/arch/x86/prefix/unlzma16.S b/src/arch/x86/prefix/unlzma16.S new file mode 100644 index 000000000..32b43f0dc --- /dev/null +++ b/src/arch/x86/prefix/unlzma16.S @@ -0,0 +1,9 @@ +/* + * 16-bit version of the decompressor + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +#define CODE16 +#include "unlzma.S" diff --git a/src/arch/x86/prefix/usbdisk.S b/src/arch/x86/prefix/usbdisk.S new file mode 100644 index 000000000..9676406e2 --- /dev/null +++ b/src/arch/x86/prefix/usbdisk.S @@ -0,0 +1,34 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .arch i386 + .section ".prefix", "awx", @progbits + .code16 + .org 0 + +#include "mbr.S" + +/* Partition table: 64 heads, 32 sectors/track (ZIP-drive compatible) */ + .org 446 + .space 16 + .space 16 + /* Partition 3: log partition (for CONSOLE_INT13) */ + .byte 0x00, 0x01, 0x01, 0x00 + .byte 0xe0, 0x3f, 0x20, 0x00 + .long 0x00000020 + .long 0x000007e0 + /* Partition 4: boot partition */ + .byte 0x80, 0x00, 0x01, 0x01 + .byte 0xeb, 0x3f, 0x20, 0x02 + .long 0x00000800 + .long 0x00001000 + + .org 510 + .byte 0x55, 0xaa + +/* Skip to start of log partition */ + .org 32 * 512 + .ascii "iPXE LOG\n\n" + +/* Skip to start of boot partition */ + .org 2048 * 512 |
