diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c index 4d567bfe..0d296993 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/i386/interface/pxe/pxe_call.c @@ -18,6 +18,7 @@ #include #include +#include #include /** @file @@ -25,6 +26,21 @@ * PXE API entry point */ +/** Vector for chaining INT 1A */ +extern struct segoff __text16 ( pxe_int_1a_vector ); +#define pxe_int_1a_vector __use_text16 ( pxe_int_1a_vector ) + +/** INT 1A handler */ +extern void pxe_int_1a ( void ); + +/** !PXE structure */ +extern struct s_PXE __text16 ( pxe ); +#define pxe __use_text16 ( pxe ) + +/** PXENV+ structure */ +extern struct s_PXENV __text16 ( pxenv ); +#define pxenv __use_text16 ( pxenv ) + /** A function pointer to hold any PXE API call * * Used by pxe_api_call() to avoid large swathes of duplicated code. @@ -288,3 +304,95 @@ void pxe_api_call ( struct i386_all_regs *ix86 ) { copy_to_user ( parameters, 0, &pxenv_any, param_len ); ix86->regs.ax = ret; } + +/** + * Hook INT 1A for PXE + * + */ +void pxe_hook_int ( void ) { + hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a, + &pxe_int_1a_vector ); +} + +/** + * Unhook INT 1A for PXE + * + * @ret rc Return status code + */ +int pxe_unhook_int ( void ) { + return unhook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a, + &pxe_int_1a_vector ); +} + +/** + * Calculate byte checksum as used by PXE + * + * @v data Data + * @v size Length of data + * @ret sum Checksum + */ +static uint8_t pxe_checksum ( void *data, size_t size ) { + uint8_t *bytes = data; + uint8_t sum = 0; + + while ( size-- ) { + sum += *bytes++; + } + return sum; +} + +/** + * Initialise PXE stack + * + */ +void init_pxe ( void ) { + uint32_t rm_cs_phys = ( rm_cs << 4 ); + uint32_t rm_ds_phys = ( rm_ds << 4 ); + + /* Fill in missing segment fields */ + pxe.EntryPointSP.segment = rm_cs; + pxe.EntryPointESP.segment = rm_cs; + pxe.Stack.segment_address = rm_ds; + pxe.Stack.Physical_address = rm_ds_phys; + pxe.UNDIData.segment_address = rm_ds; + pxe.UNDIData.Physical_address = rm_ds_phys; + pxe.UNDICode.segment_address = rm_cs; + pxe.UNDICode.Physical_address = rm_cs_phys; + pxe.UNDICodeWrite.segment_address = rm_cs; + pxe.UNDICodeWrite.Physical_address = rm_cs_phys; + pxenv.RMEntry.segment = rm_cs; + pxenv.StackSeg = rm_ds; + pxenv.UNDIDataSeg = rm_ds; + pxenv.UNDICodeSeg = rm_cs; + pxenv.PXEPtr.segment = rm_cs; + + /* Update checksums */ + pxe.StructCksum -= pxe_checksum ( &pxe, sizeof ( pxe ) ); + pxenv.Checksum -= pxe_checksum ( &pxenv, sizeof ( pxenv ) ); +} + +/** + * Boot via PXE NBP + * + * @ret rc Return status code + */ +int pxe_boot ( void ) { + int discard_b, discard_c; + uint16_t rc; + + /* Ensure that PXE stack is ready to use */ + init_pxe(); + pxe_hook_int(); + + /* Far call to PXE NBP */ + __asm__ __volatile__ ( REAL_CODE ( "pushw %%cx\n\t" + "pushw %%ax\n\t" + "movw %%cx, %%es\n\t" + "lcall $0, $0x7c00\n\t" ) + : "=a" ( rc ), "=b" ( discard_b ), + "=c" ( discard_c ) + : "a" ( &pxe ), "b" ( &pxenv ), "c" ( rm_cs ) + : "edx", "esi", "edi", "ebp", "memory" ); + + return rc; +} diff --git a/src/arch/i386/interface/pxe/pxe_entry.S b/src/arch/i386/interface/pxe/pxe_entry.S new file mode 100644 index 00000000..20ca4237 --- /dev/null +++ b/src/arch/i386/interface/pxe/pxe_entry.S @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2006 Michael Brown . + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + .arch i386 + .section ".text16", "awx", @progbits + .section ".text16.data", "aw", @progbits + .section ".data16", "aw", @progbits + +/**************************************************************************** + * !PXE structure + **************************************************************************** + */ + .section ".text16.data" + .globl pxe +pxe: + .ascii "!PXE" /* Signature */ + .byte pxe_length /* StructLength */ + .byte 0 /* StructCksum */ + .byte 0 /* StructRev */ + .byte 0 /* reserved_1 */ + .word 0, 0 /* UNDIROMID */ + .word 0, 0 /* BaseROMID */ + .word pxe_entry_sp, 0 /* EntryPointSP */ + .word pxe_entry_esp, 0 /* EntryPointESP */ + .word -1, -1 /* StatusCallout */ + .byte 0 /* reserved_2 */ + .byte SegDescCnt /* SegDescCnt */ + .word 0 /* FirstSelector */ +pxe_segments: + .word 0, 0, 0, _data16_size /* Stack */ + .word 0, 0, 0, _data16_size /* UNDIData */ + .word 0, 0, 0, _text16_size /* UNDICode */ + .word 0, 0, 0, _text16_size /* UNDICodeWrite */ + .word 0, 0, 0, 0 /* BC_Data */ + .word 0, 0, 0, 0 /* BC_Code */ + .word 0, 0, 0, 0 /* BC_CodeWrite */ + .equ SegDescCnt, ( ( . - pxe_segments ) / 8 ) + .equ pxe_length, . - pxe + .size pxe, . - pxe + +/**************************************************************************** + * PXENV+ structure + **************************************************************************** + */ + .section ".text16.data" + .globl pxenv +pxenv: + .ascii "PXENV+" /* Signature */ + .word 0x0201 /* Version */ + .byte pxenv_length /* Length */ + .byte 0 /* Checksum */ + .word pxenv_entry, 0 /* RMEntry */ + .long 0 /* PMEntry */ + .word 0 /* PMSelector */ + .word 0 /* StackSeg */ + .word _data16_size /* StackSize */ + .word 0 /* BC_CodeSeg */ + .word 0 /* BC_CodeSize */ + .word 0 /* BC_DataSeg */ + .word 0 /* BC_DataSize */ + .word 0 /* UNDIDataSeg */ + .word _data16_size /* UNDIDataSize */ + .word 0 /* UNDICodeSeg */ + .word _text16_size /* UNDICodeSize */ + .word pxe, 0 /* PXEPtr */ + .equ pxenv_length, . - pxenv + .size pxenv, . - pxenv + +/**************************************************************************** + * pxenv_entry (16-bit far call) + * + * PXE API call PXENV+ entry point + * + * Parameters: + * %es:di : Far pointer to PXE parameter structure + * %bx : PXE API call + * Returns: + * %ax : PXE exit status + * Corrupts: + * none + **************************************************************************** + */ + .section ".text16" + .code16 +pxenv_entry: + pushl $pxe_api_call + pushw %cs + call prot_call + addl $4, %esp + lret + .size pxenv_entry, . - pxenv_entry + +/**************************************************************************** + * pxe_entry + * + * PXE API call !PXE entry point + * + * Parameters: + * stack : Far pointer to PXE parameter structure + * stack : PXE API call + * Returns: + * %ax : PXE exit status + * Corrupts: + * none + **************************************************************************** + */ + .section ".text16" + .code16 +pxe_entry: +pxe_entry_sp: + /* Preserve original %esp */ + pushl %esp + /* Zero high word of %esp to allow use of common code */ + movzwl %sp, %esp + jmp pxe_entry_common +pxe_entry_esp: + /* Preserve %esp to match behaviour of pxe_entry_sp */ + pushl %esp +pxe_entry_common: + /* Save PXENV+ API call registers */ + pushw %es + pushw %di + pushw %bx + /* Load !PXE parameters from stack into PXENV+ registers */ + movw 16(%esp), %bx + movw %bx, %es + movw 14(%esp), %di + movw 12(%esp), %bx + /* Make call as for PXENV+ */ + pushw %cs + call pxenv_entry + /* Restore PXENV+ registers */ + popw %bx + popw %di + popw %es + /* Restore original %esp and return */ + popl %esp + lret + .size pxe_entry, . - pxe_entry + +/**************************************************************************** + * pxe_int_1a + * + * PXE INT 1A handler + * + * Parameters: + * %ax : 0x5650 + * Returns: + * %ax : 0x564e + * %es:bx : Far pointer to the PXENV+ structure + * CF cleared + * Corrupts: + * none + **************************************************************************** + */ + .section ".text16" + .code16 +pxe_int_1a: + pushfw + cmpw $0x5650, %ax + jne 1f + /* INT 1A,5650 - PXE installation check */ + pushw %cs + popw %es + movw $pxenv, %bx + movw $0x564e, %ax + popfw + clc + lret $2 +1: /* INT 1A,other - pass through */ + popfw + ljmp *%cs:pxe_int_1a_vector + + .section ".text16.data" + .globl pxe_int_1a_vector +pxe_int_1a_vector: .long 0