From e8b22f203ffe4ae11b654f758270790a2e5e11f1 Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Thu, 28 Aug 2008 23:52:19 +0100 Subject: [comboot] Add COMBOOT and COM32 support --- src/arch/i386/Makefile | 1 + src/arch/i386/image/com32.c | 275 ++++++++++ src/arch/i386/image/comboot.c | 312 +++++++++++ src/arch/i386/include/bits/errfile.h | 3 + src/arch/i386/include/comboot.h | 102 ++++ src/arch/i386/include/pxe_call.h | 1 + src/arch/i386/interface/syslinux/com32_call.c | 188 +++++++ src/arch/i386/interface/syslinux/com32_wrapper.S | 92 ++++ src/arch/i386/interface/syslinux/comboot_call.c | 598 ++++++++++++++++++++++ src/arch/i386/interface/syslinux/comboot_resolv.c | 58 +++ src/config.h | 1 + src/core/config.c | 8 + src/include/gpxe/features.h | 1 + src/tests/comboot/shuffle-simple.asm | 40 ++ src/tests/comboot/version.asm | 136 +++++ 15 files changed, 1816 insertions(+) create mode 100644 src/arch/i386/image/com32.c create mode 100644 src/arch/i386/image/comboot.c create mode 100644 src/arch/i386/include/comboot.h create mode 100644 src/arch/i386/interface/syslinux/com32_call.c create mode 100644 src/arch/i386/interface/syslinux/com32_wrapper.S create mode 100644 src/arch/i386/interface/syslinux/comboot_call.c create mode 100644 src/arch/i386/interface/syslinux/comboot_resolv.c create mode 100644 src/tests/comboot/shuffle-simple.asm create mode 100644 src/tests/comboot/version.asm diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile index 926daa1a..97ca0774 100644 --- a/src/arch/i386/Makefile +++ b/src/arch/i386/Makefile @@ -11,6 +11,7 @@ SRCDIRS += arch/i386/drivers SRCDIRS += arch/i386/drivers/net SRCDIRS += arch/i386/interface/pcbios SRCDIRS += arch/i386/interface/pxe +SRCDIRS += arch/i386/interface/syslinux # The various xxx_loader.c files are #included into core/loader.c and # should not be compiled directly. diff --git a/src/arch/i386/image/com32.c b/src/arch/i386/image/com32.c new file mode 100644 index 00000000..da604625 --- /dev/null +++ b/src/arch/i386/image/com32.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2008 Daniel Verkamp . + * + * 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. + */ + +/** + * @file + * + * SYSLINUX COM32 image format + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct image_type com32_image_type __image_type ( PROBE_NORMAL ); + +/** + * Execute COMBOOT image + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_exec ( struct image *image ) { + struct memory_map memmap; + unsigned int i; + int state; + uint32_t avail_mem_top; + + state = setjmp ( comboot_return ); + + switch ( state ) { + case 0: /* First time through; invoke COM32 program */ + + /* Get memory map */ + get_memmap ( &memmap ); + + /* Find end of block covering COM32 image loading area */ + for ( i = 0, avail_mem_top = 0 ; i < memmap.count ; i++ ) { + if ( (memmap.regions[i].start <= COM32_START_PHYS) && + (memmap.regions[i].end > COM32_START_PHYS + image->len) ) { + avail_mem_top = memmap.regions[i].end; + break; + } + } + + DBGC ( image, "COM32 %p: available memory top = 0x%x\n", + image, (int)avail_mem_top ); + + assert ( avail_mem_top != 0 ); + + com32_external_esp = phys_to_virt ( avail_mem_top ); + + /* Hook COMBOOT API interrupts */ + hook_comboot_interrupts( ); + + /* Temporarily de-register image, so that a "boot" command + * doesn't throw us into an execution loop. Hold a reference + * to avoid the image's being freed. + */ + image_get ( image ); + unregister_image ( image ); + + __asm__ __volatile__ ( + "movl %%esp, (com32_internal_esp)\n\t" /* Save internal virtual address space ESP */ + "movl (com32_external_esp), %%esp\n\t" /* Switch to COM32 ESP (top of available memory) */ + "call _virt_to_phys\n\t" /* Switch to flat physical address space */ + "pushl %0\n\t" /* Pointer to CDECL helper function */ + "pushl %1\n\t" /* Pointer to FAR call helper function */ + "pushl %2\n\t" /* Size of low memory bounce buffer */ + "pushl %3\n\t" /* Pointer to low memory bounce buffer */ + "pushl %4\n\t" /* Pointer to INT call helper function */ + "pushl %5\n\t" /* Pointer to the command line arguments */ + "pushl $6\n\t" /* Number of additional arguments */ + "call *%6\n\t" /* Execute image */ + "call _phys_to_virt\n\t" /* Switch back to internal virtual address space */ + "movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */ + : + : + /* %0 */ "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ), + /* %1 */ "r" ( virt_to_phys ( com32_farcall_wrapper ) ), + /* %2 */ "r" ( get_fbms() * 1024 - (COM32_BOUNCE_SEG << 4) ), + /* %3 */ "i" ( COM32_BOUNCE_SEG << 4 ), + /* %4 */ "r" ( virt_to_phys ( com32_intcall_wrapper ) ), + /* %5 */ "r" ( virt_to_phys ( image->cmdline ) ), + /* %6 */ "r" ( COM32_START_PHYS ) + : + "memory" ); + break; + + case COMBOOT_RETURN_RUN_KERNEL: + DBGC ( image, "COM32 %p: returned to run kernel...\n", image ); + comboot_run_kernel ( ); + break; + + case COMBOOT_RETURN_EXIT: + break; + + } + + comboot_force_text_mode ( ); + + DBGC ( image, "COM32 %p returned\n", image ); + + /* Re-register image and return */ + register_image ( image ); + image_put ( image ); + + return 0; +} + +/** + * Check image name extension + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_identify ( struct image *image ) { + const char *ext; + static const uint8_t magic[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 }; + uint8_t buf[5]; + + if ( image->len >= 5 ) { + /* Check for magic number + * mov eax,21cd4cffh + * B8 FF 4C CD 21 + */ + copy_from_user ( buf, image->data, 0, sizeof(buf) ); + if ( ! memcmp ( buf, magic, sizeof(buf) ) ) { + DBGC ( image, "COM32 %p: found magic number\n", + image ); + return 0; + } + } + + /* Magic number not found; check filename extension */ + + ext = strrchr( image->name, '.' ); + + if ( ! ext ) { + DBGC ( image, "COM32 %p: no extension\n", + image ); + return -ENOEXEC; + } + + ++ext; + + if ( strcasecmp( ext, "c32" ) ) { + DBGC ( image, "COM32 %p: unrecognized extension %s\n", + image, ext ); + return -ENOEXEC; + } + + return 0; +} + + +/** + * Load COM32 image into memory + * @v image COM32 image + * @ret rc Return status code + */ +static int comboot_load_image ( struct image *image ) { + size_t filesz, memsz; + userptr_t buffer; + int rc; + + filesz = image->len; + memsz = filesz; + buffer = phys_to_user ( COM32_START_PHYS ); + if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) { + DBGC ( image, "COM32 %p: could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Copy image to segment */ + memcpy_user ( buffer, 0, image->data, 0, filesz ); + + return 0; +} + +/** + * Prepare COM32 low memory bounce buffer + * @v image COM32 image + * @ret rc Return status code + */ +static int comboot_prepare_bounce_buffer ( struct image * image ) { + unsigned int seg; + userptr_t seg_userptr; + size_t filesz, memsz; + int rc; + + seg = COM32_BOUNCE_SEG; + seg_userptr = real_to_user ( seg, 0 ); + + /* Ensure the entire 64k segment is free */ + memsz = 0xFFFF; + filesz = 0; + + /* Prepare, verify, and load the real-mode segment */ + if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) { + DBGC ( image, "COM32 %p: could not prepare bounce buffer segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Load COM32 image into memory + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_load ( struct image *image ) { + int rc; + + DBGC ( image, "COM32 %p: name '%s', cmdline '%s'\n", + image, image->name, image->cmdline ); + + /* Check if this is a COMBOOT image */ + if ( ( rc = com32_identify ( image ) ) != 0 ) { + return rc; + } + + /* This is a COM32 image, valid or otherwise */ + if ( ! image->type ) + image->type = &com32_image_type; + + /* Load image */ + if ( ( rc = comboot_load_image ( image ) ) != 0 ) { + return rc; + } + + /* Prepare bounce buffer segment */ + if ( ( rc = comboot_prepare_bounce_buffer ( image ) ) != 0 ) { + return rc; + } + + return 0; +} + +/** SYSLINUX COM32 image type */ +struct image_type com32_image_type __image_type ( PROBE_NORMAL ) = { + .name = "COM32", + .load = com32_load, + .exec = com32_exec, +}; diff --git a/src/arch/i386/image/comboot.c b/src/arch/i386/image/comboot.c new file mode 100644 index 00000000..63d02c0f --- /dev/null +++ b/src/arch/i386/image/comboot.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2008 Daniel Verkamp . + * + * 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. + */ + +/** + * @file + * + * SYSLINUX COMBOOT (16-bit) image format + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FEATURE ( FEATURE_IMAGE, "COMBOOT", DHCP_EB_FEATURE_COMBOOT, 1 ); + +struct image_type comboot_image_type __image_type ( PROBE_NORMAL ); + +/** + * COMBOOT PSP, copied to offset 0 of code segment + */ +struct comboot_psp { + /** INT 20 instruction, executed if COMBOOT image returns with RET */ + uint16_t int20; + /** Segment of first non-free paragraph of memory */ + uint16_t first_non_free_para; +}; + +/** Offset in PSP of command line */ +#define COMBOOT_PSP_CMDLINE_OFFSET 0x81 + +/** Maximum length of command line in PSP + * (127 bytes minus space and CR) */ +#define COMBOOT_MAX_CMDLINE_LEN 125 + + +/** + * Copy command line to PSP + * + * @v image COMBOOT image + */ +static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) { + const char *cmdline = ( image->cmdline ? image->cmdline : "" ); + int cmdline_len = strlen ( cmdline ); + if( cmdline_len > COMBOOT_MAX_CMDLINE_LEN ) + cmdline_len = COMBOOT_MAX_CMDLINE_LEN; + uint8_t len_byte = cmdline_len; + char spc = ' ', cr = '\r'; + + /* Copy length to byte before command line */ + copy_to_user ( seg_userptr, COMBOOT_PSP_CMDLINE_OFFSET - 1, + &len_byte, 1 ); + + /* Command line starts with space */ + copy_to_user ( seg_userptr, + COMBOOT_PSP_CMDLINE_OFFSET, + &spc, 1 ); + + /* Copy command line */ + copy_to_user ( seg_userptr, + COMBOOT_PSP_CMDLINE_OFFSET + 1, + cmdline, cmdline_len ); + + /* Command line ends with CR */ + copy_to_user ( seg_userptr, + COMBOOT_PSP_CMDLINE_OFFSET + cmdline_len + 1, + &cr, 1 ); +} + +/** + * Initialize PSP + * + * @v image COMBOOT image + * @v seg_userptr segment to initialize + */ +static void comboot_init_psp ( struct image * image, userptr_t seg_userptr ) { + struct comboot_psp psp; + + /* Fill PSP */ + + /* INT 20h instruction, byte order reversed */ + psp.int20 = 0x20CD; + + /* get_fbms() returns BIOS free base memory counter, which is in + * kilobytes; x * 1024 / 16 == x * 64 == x << 6 */ + psp.first_non_free_para = get_fbms() << 6; + + DBGC ( image, "COMBOOT %p: first non-free paragraph = 0x%x\n", + image, psp.first_non_free_para ); + + /* Copy the PSP to offset 0 of segment. + * The rest of the PSP was already zeroed by + * comboot_prepare_segment. */ + copy_to_user ( seg_userptr, 0, &psp, sizeof( psp ) ); + + /* Copy the command line to the PSP */ + comboot_copy_cmdline ( image, seg_userptr ); +} + +/** + * Execute COMBOOT image + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_exec ( struct image *image ) { + userptr_t seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 ); + int state; + + state = setjmp ( comboot_return ); + + switch ( state ) { + case 0: /* First time through; invoke COMBOOT program */ + + /* Initialize PSP */ + comboot_init_psp ( image, seg_userptr ); + + /* Hook COMBOOT API interrupts */ + hook_comboot_interrupts ( ); + + DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n", + COMBOOT_PSP_SEG ); + + /* Temporarily de-register image, so that a "boot" command + * doesn't throw us into an execution loop. Hold a reference + * to avoid the image's being freed. + */ + image_get ( image ); + unregister_image ( image ); + + /* Store stack segment at 0x38 and stack pointer at 0x3A + * in the PSP and jump to the image */ + __asm__ __volatile__ ( + REAL_CODE ( /* Save return address with segment on old stack */ + "popw %%ax\n\t" + "pushw %%cs\n\t" + "pushw %%ax\n\t" + /* Set DS=ES=segment with image */ + "movw %w0, %%ds\n\t" + "movw %w0, %%es\n\t" + /* Set SS:SP to new stack (end of image segment) */ + "movw %w0, %%ss\n\t" + "xor %%sp, %%sp\n\t" + "pushw $0\n\t" + "pushw %w0\n\t" + "pushw $0x100\n\t" + /* Zero registers (some COM files assume GP regs are 0) */ + "xorw %%ax, %%ax\n\t" + "xorw %%bx, %%bx\n\t" + "xorw %%cx, %%cx\n\t" + "xorw %%dx, %%dx\n\t" + "xorw %%si, %%si\n\t" + "xorw %%di, %%di\n\t" + "xorw %%bp, %%bp\n\t" + "lret\n\t" ) + : : "r" ( COMBOOT_PSP_SEG ) : "eax" ); + break; + + case COMBOOT_RETURN_RUN_KERNEL: + DBGC ( image, "COMBOOT %p: returned to run kernel...\n", image ); + comboot_run_kernel ( ); + break; + + case COMBOOT_RETURN_EXIT: + break; + + } + + comboot_force_text_mode ( ); + + DBGC ( image, "COMBOOT %p returned\n", image ); + + /* Re-register image and return */ + register_image ( image ); + image_put ( image ); + + return 0; +} + +/** + * Check image name extension + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_identify ( struct image *image ) { + const char *ext; + + ext = strrchr( image->name, '.' ); + + if ( ! ext ) { + DBGC ( image, "COMBOOT %p: no extension\n", + image ); + return -ENOEXEC; + } + + ++ext; + + if ( strcasecmp( ext, "com" ) && strcasecmp( ext, "cbt" ) ) { + DBGC ( image, "COMBOOT %p: unrecognized extension %s\n", + image, ext ); + return -ENOEXEC; + } + + return 0; +} + +/** + * Load COMBOOT image into memory, preparing a segment and returning it + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_prepare_segment ( struct image *image ) +{ + userptr_t seg_userptr; + size_t filesz, memsz; + int rc; + + /* Load image in segment */ + seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 ); + + /* Allow etra 0x100 bytes before image for PSP */ + filesz = image->len + 0x100; + + /* Ensure the entire 64k segment is free */ + memsz = 0xFFFF; + + /* Prepare, verify, and load the real-mode segment */ + if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) { + DBGC ( image, "COMBOOT %p: could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Zero PSP */ + memset_user ( seg_userptr, 0, 0, 0x100 ); + + /* Copy image to segment:0100 */ + memcpy_user ( seg_userptr, 0x100, image->data, 0, image->len ); + + return 0; +} + +/** + * Load COMBOOT image into memory + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_load ( struct image *image ) { + int rc; + + DBGC ( image, "COMBOOT %p: name '%s'\n", + image, image->name ); + + /* Check if this is a COMBOOT image */ + if ( ( rc = comboot_identify ( image ) ) != 0 ) { + + return rc; + } + + /* This is a 16-bit COMBOOT image, valid or otherwise */ + if ( ! image->type ) + image->type = &comboot_image_type; + + /* Sanity check for filesize */ + if( image->len >= 0xFF00 ) { + DBGC( image, "COMBOOT %p: image too large\n", + image ); + return -ENOEXEC; + } + + /* Prepare segment and load image */ + if ( ( rc = comboot_prepare_segment ( image ) ) != 0 ) { + return rc; + } + + return 0; +} + +/** SYSLINUX COMBOOT (16-bit) image type */ +struct image_type comboot_image_type __image_type ( PROBE_NORMAL ) = { + .name = "COMBOOT", + .load = comboot_load, + .exec = comboot_exec, +}; diff --git a/src/arch/i386/include/bits/errfile.h b/src/arch/i386/include/bits/errfile.h index c5b04a26..99927c28 100644 --- a/src/arch/i386/include/bits/errfile.h +++ b/src/arch/i386/include/bits/errfile.h @@ -21,6 +21,9 @@ #define ERRFILE_nbi ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00040000 ) #define ERRFILE_pxe_image ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00050000 ) #define ERRFILE_elfboot ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00060000 ) +#define ERRFILE_comboot ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00070000 ) +#define ERRFILE_com32 ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00080000 ) +#define ERRFILE_comboot_resolv ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00090000 ) #define ERRFILE_undi ( ERRFILE_ARCH | ERRFILE_NET | 0x00000000 ) #define ERRFILE_undiload ( ERRFILE_ARCH | ERRFILE_NET | 0x00010000 ) diff --git a/src/arch/i386/include/comboot.h b/src/arch/i386/include/comboot.h new file mode 100644 index 00000000..1fc3b718 --- /dev/null +++ b/src/arch/i386/include/comboot.h @@ -0,0 +1,102 @@ +#ifndef COMBOOT_H +#define COMBOOT_H + +/** + * @file + * + * SYSLINUX COMBOOT + */ + +#include +#include +#include + +/** Segment used for COMBOOT PSP and image */ +#define COMBOOT_PSP_SEG 0x07C0 + +/** Entry point address of COM32 images */ +#define COM32_START_PHYS 0x101000 + +/** COM32 bounce buffer segment */ +#define COM32_BOUNCE_SEG 0x07C0 + +/** Size of SYSLINUX file block in bytes */ +#define COMBOOT_FILE_BLOCKSZ 512 + +/** COMBOOT feature flags (INT 22h AX=15h) */ +#define COMBOOT_FEATURE_LOCAL_BOOT (1 << 0) +#define COMBOOT_FEATURE_IDLE_LOOP (1 << 1) + +/** Maximum number of shuffle descriptors for + * shuffle and boot functions + * (INT 22h AX=0012h, 001Ah, 001Bh) + */ +#define COMBOOT_MAX_SHUFFLE_DESCRIPTORS 682 + +typedef union { + uint32_t l; + uint16_t w[2]; + uint8_t b[4]; +} com32_reg32_t; + +typedef struct { + uint16_t gs; /* Offset 0 */ + uint16_t fs; /* Offset 2 */ + uint16_t es; /* Offset 4 */ + uint16_t ds; /* Offset 6 */ + + com32_reg32_t edi; /* Offset 8 */ + com32_reg32_t esi; /* Offset 12 */ + com32_reg32_t ebp; /* Offset 16 */ + com32_reg32_t _unused_esp; /* Offset 20 */ + com32_reg32_t ebx; /* Offset 24 */ + com32_reg32_t edx; /* Offset 28 */ + com32_reg32_t ecx; /* Offset 32 */ + com32_reg32_t eax; /* Offset 36 */ + + com32_reg32_t eflags; /* Offset 40 */ +} com32sys_t; + +typedef struct { + uint32_t dest; + uint32_t src; + uint32_t len; +} comboot_shuffle_descriptor; + +extern void hook_comboot_interrupts ( ); + +/* These are not the correct prototypes, but it doens't matter, + * as we only ever get the address of these functions; + * they are only called from COM32 code running in PHYS_CODE + */ +extern void com32_intcall_wrapper ( ); +extern void com32_farcall_wrapper ( ); +extern void com32_cfarcall_wrapper ( ); + +/* Resolve a hostname to an (IPv4) address */ +extern int comboot_resolv ( const char *name, struct in_addr *address ); + +/* setjmp/longjmp context buffer used to return after loading an image */ +extern jmp_buf comboot_return; + +/* Command line to execute when returning via comboot_return + * with COMBOOT_RETURN_RUN_KERNEL + */ +extern char *comboot_kernel_cmdline; + +/* Execute comboot_image_cmdline */ +extern void comboot_run_kernel ( ); + +extern void *com32_external_esp; + +#define COMBOOT_RETURN_EXIT 1 +#define COMBOOT_RETURN_RUN_KERNEL 2 + +extern void comboot_force_text_mode ( void ); + +#define COMBOOT_VIDEO_GRAPHICS 0x01 +#define COMBOOT_VIDEO_NONSTANDARD 0x02 +#define COMBOOT_VIDEO_VESA 0x04 +#define COMBOOT_VIDEO_NOTEXT 0x08 + +#endif diff --git a/src/arch/i386/include/pxe_call.h b/src/arch/i386/include/pxe_call.h index dc585310..7a38d314 100644 --- a/src/arch/i386/include/pxe_call.h +++ b/src/arch/i386/include/pxe_call.h @@ -30,5 +30,6 @@ extern void pxe_hook_int1a ( void ); extern int pxe_unhook_int1a ( void ); extern void pxe_init_structures ( void ); extern int pxe_start_nbp ( void ); +extern __cdecl void pxe_api_call ( struct i386_all_regs *ix86 ); #endif /* _PXE_CALL_H */ diff --git a/src/arch/i386/interface/syslinux/com32_call.c b/src/arch/i386/interface/syslinux/com32_call.c new file mode 100644 index 00000000..586730cf --- /dev/null +++ b/src/arch/i386/interface/syslinux/com32_call.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2008 Daniel Verkamp . + * + * 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. + */ + +/** + * @file SYSLINUX COM32 helpers + * + */ + +#include +#include +#include +#include +#include + +static com32sys_t __bss16 ( com32_regs ); +#define com32_regs __use_data16 ( com32_regs ) + +static uint8_t __bss16 ( com32_int_vector ); +#define com32_int_vector __use_data16 ( com32_int_vector ) + +static uint32_t __bss16 ( com32_farcall_proc ); +#define com32_farcall_proc __use_data16 ( com32_farcall_proc ) + +uint16_t __bss16 ( com32_saved_sp ); + +/** + * Interrupt call helper + */ +void __cdecl com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physaddr_t outregs_phys ) { + + memcpy_user ( virt_to_user( &com32_regs ), 0, + phys_to_user ( inregs_phys ), 0, + sizeof(com32sys_t) ); + + com32_int_vector = interrupt; + + __asm__ __volatile__ ( + REAL_CODE ( /* Save all registers */ + "pushal\n\t" + "pushw %%ds\n\t" + "pushw %%es\n\t" + "pushw %%fs\n\t" + "pushw %%gs\n\t" + /* Mask off unsafe flags */ + "movl (com32_regs + 40), %%eax\n\t" + "andl $0x200cd7, %%eax\n\t" + "movl %%eax, (com32_regs + 40)\n\t" + /* Load com32_regs into the actual registers */ + "movw %%sp, %%ss:(com32_saved_sp)\n\t" + "movw $com32_regs, %%sp\n\t" + "popw %%gs\n\t" + "popw %%fs\n\t" + "popw %%es\n\t" + "popw %%ds\n\t" + "popal\n\t" + "popfl\n\t" + "movw %%ss:(com32_saved_sp), %%sp\n\t" + /* patch INT instruction */ + "pushw %%ax\n\t" + "movb %%ss:(com32_int_vector), %%al\n\t" + "movb %%al, %%cs:(com32_intcall_instr + 1)\n\t" + /* perform a jump to avoid problems with cache + * consistency in self-modifying code on some CPUs (486) + */ + "jmp 1f\n" + "1:\n\t" + "popw %%ax\n\t" + "com32_intcall_instr:\n\t" + /* INT instruction to be patched */ + "int $0xFF\n\t" + /* Copy regs back to com32_regs */ + "movw %%sp, %%ss:(com32_saved_sp)\n\t" + "movw $(com32_regs + 44), %%sp\n\t" + "pushfl\n\t" + "pushal\n\t" + "pushw %%ds\n\t" + "pushw %%es\n\t" + "pushw %%fs\n\t" + "pushw %%gs\n\t" + "movw %%ss:(com32_saved_sp), %%sp\n\t" + /* Restore registers */ + "popw %%gs\n\t" + "popw %%fs\n\t" + "popw %%es\n\t" + "popw %%ds\n\t" + "popal\n\t") + : : ); + + if ( outregs_phys ) { + memcpy_user ( phys_to_user ( outregs_phys ), 0, + virt_to_user( &com32_regs ), 0, + sizeof(com32sys_t) ); + } +} + +/** + * Farcall helper + */ +void __cdecl com32_farcall ( uint32_t proc, physaddr_t inregs_phys, physaddr_t outregs_phys ) { + + memcpy_user ( virt_to_user( &com32_regs ), 0, + phys_to_user ( inregs_phys ), 0, + sizeof(com32sys_t) ); + + com32_farcall_proc = proc; + + __asm__ __volatile__ ( + REAL_CODE ( /* Save all registers */ + "pushal\n\t" + "pushw %%ds\n\t" + "pushw %%es\n\t" + "pushw %%fs\n\t" + "pushw %%gs\n\t" + /* Mask off unsafe flags */ + "movl (com32_regs + 40), %%eax\n\t" + "andl $0x200cd7, %%eax\n\t" + "movl %%eax, (com32_regs + 40)\n\t" + /* Load com32_regs into the actual registers */ + "movw %%sp, %%ss:(com32_saved_sp)\n\t" + "movw $com32_regs, %%sp\n\t" + "popw %%gs\n\t" + "popw %%fs\n\t" + "popw %%es\n\t" + "popw %%ds\n\t" + "popal\n\t" + "popfl\n\t" + "movw %%ss:(com32_saved_sp), %%sp\n\t" + /* Call procedure */ + "lcall *%%ss:(com32_farcall_proc)\n\t" + /* Copy regs back to com32_regs */ + "movw %%sp, %%ss:(com32_saved_sp)\n\t" + "movw $(com32_regs + 44), %%sp\n\t" + "pushfl\n\t" + "pushal\n\t" + "pushw %%ds\n\t" + "pushw %%es\n\t" + "pushw %%fs\n\t" + "pushw %%gs\n\t" + "movw %%ss:(com32_saved_sp), %%sp\n\t" + /* Restore registers */ + "popw %%gs\n\t" + "popw %%fs\n\t" + "popw %%es\n\t" + "popw %%ds\n\t" + "popal\n\t") + : : ); + + if ( outregs_phys ) { + memcpy_user ( phys_to_user ( outregs_phys ), 0, + virt_to_user( &com32_regs ), 0, + sizeof(com32sys_t) ); + } +} + +/** + * CDECL farcall helper + */ +int __cdecl com32_cfarcall ( uint32_t proc, physaddr_t stack, size_t stacksz ) { + int32_t eax; + + copy_user_to_rm_stack ( phys_to_user ( stack ), stacksz ); + com32_farcall_proc = proc; + + __asm__ __volatile__ ( + REAL_CODE ( "lcall *%%ss:(com32_farcall_proc)\n\t" ) + : "=a" (eax) + : + : "ecx", "edx" ); + + remove_user_from_rm_stack ( 0, stacksz ); + + return eax; +} diff --git a/src/arch/i386/interface/syslinux/com32_wrapper.S b/src/arch/i386/interface/syslinux/com32_wrapper.S new file mode 100644 index 00000000..08d7398a --- /dev/null +++ b/src/arch/i386/interface/syslinux/com32_wrapper.S @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2008 Daniel Verkamp . + * + * 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. + */ + + .text + .arch i386 + .code32 + + .globl com32_farcall_wrapper +com32_farcall_wrapper: + + movl $com32_farcall, %eax + jmp com32_wrapper + + + .globl com32_cfarcall_wrapper +com32_cfarcall_wrapper: + + movl $com32_cfarcall, %eax + jmp com32_wrapper + + + .globl com32_intcall_wrapper +com32_intcall_wrapper: + + movl $com32_intcall, %eax + /*jmp com32_wrapper*/ /* fall through */ + +com32_wrapper: + + /* Switch to internal virtual address space */ + call _phys_to_virt + + mov %eax, (com32_helper_function) + + /* Save external COM32 stack pointer */ + movl %esp, (com32_external_esp) + + /* Copy arguments to caller-save registers */ + movl 12(%esp), %eax + movl 8(%esp), %ecx + movl 4(%esp), %edx + + /* Switch to internal stack */ + movl (com32_internal_esp), %esp + + /* Copy arguments to internal stack */ + pushl %eax + pushl %ecx + pushl %edx + + call *(com32_helper_function) + + /* Clean up stack */ + addl $12, %esp + + /* Save internal stack pointer and restore external stack pointer */ + movl %esp, (com32_internal_esp) + movl (com32_external_esp), %esp + + /* Switch to external flat physical address space */ + call _virt_to_phys + + ret + + + .data + +/* Internal gPXE virtual address space %esp */ +.globl com32_internal_esp +.lcomm com32_internal_esp, 4 + +/* External flat physical address space %esp */ +.globl com32_external_esp +.lcomm com32_external_esp, 4 + +/* Function pointer of helper to call */ +.lcomm com32_helper_function, 4 diff --git a/src/arch/i386/interface/syslinux/comboot_call.c b/src/arch/i386/interface/syslinux/comboot_call.c new file mode 100644 index 00000000..5a400ede --- /dev/null +++ b/src/arch/i386/interface/syslinux/comboot_call.c @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2008 Daniel Verkamp . + * + * 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. + */ + +/** + * @file SYSLINUX COMBOOT API + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** The "SYSLINUX" version string */ +static char __data16_array ( syslinux_version, [] ) = "gPXE " VERSION; +#define syslinux_version __use_data16 ( syslinux_version ) + +/** The "SYSLINUX" copyright string */ +static char __data16_array ( syslinux_copyright, [] ) = "http://etherboot.org"; +#define syslinux_copyright __use_data16 ( syslinux_copyright ) + +static char __data16_array ( syslinux_configuration_file, [] ) = ""; +#define syslinux_configuration_file __use_data16 ( syslinux_configuration_file ) + +/** Feature flags */ +static uint8_t __data16 ( comboot_feature_flags ) = COMBOOT_FEATURE_IDLE_LOOP; +#define comboot_feature_flags __use_data16 ( comboot_feature_flags ) + +static struct segoff __text16 ( int20_vector ); +#define int20_vector __use_text16 ( int20_vector ) + +static struct segoff __text16 ( int21_vector ); +#define int21_vector __use_text16 ( int21_vector ) + +static struct segoff __text16 ( int22_vector ); +#define int22_vector __use_text16 ( int22_vector ) + +extern void int20_wrapper ( void ); +extern void int21_wrapper ( void ); +extern void int22_wrapper ( void ); + +/* setjmp/longjmp context buffer used to return after loading an image */ +jmp_buf comboot_return; + +/* Command line to execute when returning via comboot_return + * with COMBOOT_RETURN_RUN_KERNEL + */ +char *comboot_kernel_cmdline; + +/* Mode flags set by INT 22h AX=0017h */ +static uint16_t comboot_graphics_mode = 0; + + +/** + * Print a string with a particular terminator + */ +static void print_user_string ( unsigned int segment, unsigned int offset, char terminator ) { + int i = 0; + char c; + userptr_t str = real_to_user ( segment, offset ); + for ( ; ; ) { + copy_from_user ( &c, str, i, 1 ); + if ( c == terminator ) break; + putchar ( c ); + i++; + } +} + + +/** + * Perform a series of memory copies from a list in low memory + */ +static void shuffle ( unsigned int list_segment, unsigned int list_offset, unsigned int count ) +{ + comboot_shuffle_descriptor shuf[COMBOOT_MAX_SHUFFLE_DESCRIPTORS]; + unsigned int i; + + /* Copy shuffle descriptor list so it doesn't get overwritten */ + copy_from_user ( shuf, real_to_user ( list_segment, list_offset ), 0, + count * sizeof( comboot_shuffle_descriptor ) ); + + /* Do the copies */ + for ( i = 0; i < count; i++ ) { + userptr_t src_u = phys_to_user ( shuf[ i ].src ); + userptr_t dest_u = phys_to_user ( shuf[ i ].dest ); + + if ( shuf[ i ].src == 0xFFFFFFFF ) { + /* Fill with 0 instead of copying */ + memset_user ( dest_u, 0, 0, shuf[ i ].len ); + } else if ( shuf[ i ].dest == 0xFFFFFFFF ) { + /* Copy new list of descriptors */ + count = shuf[ i ].len / sizeof( comboot_shuffle_descriptor ); + assert ( count <= COMBOOT_MAX_SHUFFLE_DESCRIPTORS ); + copy_from_user ( shuf, src_u, 0, shuf[ i ].len ); + i = -1; + } else { + /* Regular copy */ + memmove_user ( dest_u, 0, src_u, 0, shuf[ i ].len ); + } + } +} + + +/** + * Set default text mode + */ +void comboot_force_text_mode ( void ) { + if ( comboot_graphics_mode & COMBOOT_VIDEO_VESA ) { + /* Set VGA mode 3 via VESA VBE mode set */ + __asm__ __volatile__ ( + REAL_CODE ( + "mov $0x4F02, %%ax\n\t" + "mov $0x03, %%bx\n\t" + "int $0x10\n\t" + ) + : : ); + } else if ( comboot_graphics_mode & COMBOOT_VIDEO_GRAPHICS ) { + /* Set VGA mode 3 via standard VGA mode set */ + __asm__ __volatile__ ( + REAL_CODE ( + "mov $0x03, %%ax\n\t" + "int $0x10\n\t" + ) + : : ); + } + + comboot_graphics_mode = 0; +} + + +/** + * Run the kernel specified in comboot_kernel_cmdline + */ +void comboot_run_kernel ( ) +{ + char *initrd; + + comboot_force_text_mode ( ); + + DBG ( "COMBOOT: executing image '%s'\n", comboot_kernel_cmdline ); + + /* Find initrd= parameter, if any */ + if ( ( initrd = strstr ( comboot_kernel_cmdline, "initrd=" ) ) ) { + char old_char = '\0'; + char *initrd_end = strchr( initrd, ' ' ); + + /* Replace space after end of parameter + * with a nul terminator if this is not + * the last parameter + */ + if ( initrd_end ) { + old_char = *initrd_end; + *initrd_end = '\0'; + } + + /* Replace = with space to get 'initrd filename' + * command suitable for system() + */ + initrd[6] = ' '; + + DBG( "COMBOOT: loading initrd '%s'\n", initrd ); + + system ( initrd ); + + /* Restore space after parameter */ + if ( initrd_end ) { + *initrd_end = old_char; + } + + /* Restore = */ + initrd[6] = '='; + } + + /* Load kernel */ + DBG ( "COMBOOT: loading kernel '%s'\n", comboot_kernel_cmdline ); + system ( comboot_kernel_cmdline ); + + free ( comboot_kernel_cmdline ); + + /* Boot */ + system ( "boot" ); + + DBG ( "COMBOOT: back from executing command\n" ); +} + + +/** + * Terminate program interrupt handler + */ +static __cdecl void int20 ( struct i386_all_regs *ix86 __unused ) { + longjmp ( comboot_return, COMBOOT_RETURN_EXIT ); +} + + +/** + * DOS-compatible API + */ +static __cdecl void int21 ( struct i386_all_regs *ix86 ) { + ix86->flags |= CF; + + switch ( ix86->regs.ah ) { + case 0x00: + case 0x4C: /* Terminate program */ + longjmp ( comboot_return, COMBOOT_RETURN_EXIT ); + break; + + case 0x01: /* Get Key with Echo */ + case 0x08: /* Get Key without Echo */ + /* TODO: handle extended characters? */ + ix86->regs.al = getchar( ); + + /* Enter */ + if ( ix86->regs.al == 0x0A ) + ix86->regs.al = 0x0D; + + if ( ix86->regs.ah == 0x01 ) + putchar ( ix86->regs.al ); + + ix86->flags &= ~CF; + break; + + case 0x02: /* Write Character */ + putchar ( ix86->regs.dl ); + ix86->flags &= ~CF; + break; + + case 0x04: /* Write Character to Serial Port */ + serial_putc ( ix86->regs.dl ); + ix86->flags &= ~CF; + break; + + case 0x09: /* Write DOS String to Console */ + print_user_string ( ix86->segs.ds, ix86->regs.dx, '$' ); + ix86->flags &= ~CF; + break; + + case 0x0B: /* Check Keyboard */ + if ( iskey() ) + ix86->regs.al = 0xFF; + else + ix86->regs.al = 0x00; + + ix86->flags &= ~CF; + break; + + case 0x30: /* Check DOS Version */ + /* Bottom halves all 0; top halves spell "SYSLINUX" */ + ix86->regs.eax = 0x59530000; + ix86->regs.ebx = 0x4C530000; + ix86->regs.ecx = 0x4E490000; + ix86->regs.edx = 0x58550000; + ix86->flags &= ~CF; + break; + + default: + DBG ( "COMBOOT unknown int21 function %02x\n", ix86->regs.ah ); + break; + } +} + + +/** + * SYSLINUX API + */ +static __cdecl void int22 ( struct i386_all_regs *ix86 ) { + ix86->flags |= CF; + + switch ( ix86->regs.ax ) { + case 0x0001: /* Get Version */ + + /* Number of INT 22h API functions available */ + ix86->regs.ax = 0x0018; + + /* SYSLINUX version number */ + ix86->regs.ch = 0; /* major */ + ix86->regs.cl = 0; /* minor */ + + /* SYSLINUX derivative ID */ + ix86->regs.dl = BZI_LOADER_TYPE_GPXE; + + /* SYSLINUX version and copyright strings */ + ix86->segs.es = rm_ds; + ix86->regs.si = ( ( unsigned ) __from_data16 ( syslinux_version ) ); + ix86->regs.di = ( ( unsigned ) __from_data16 ( syslinux_copyright ) ); + + ix86->flags &= ~CF; + break; + + case 0x0002: /* Write String */ + print_user_string ( ix86->segs.es, ix86->regs.bx, '\0' ); + ix86->flags &= ~CF; + break; + + case 0x0003: /* Run command */ + { + userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx ); + int len = strlen_user ( cmd_u, 0 ); + char cmd[len + 1]; + copy_from_user ( cmd, cmd_u, 0, len + 1 ); + DBG ( "COMBOOT: executing command '%s'\n", cmd ); + + comboot_kernel_cmdline = strdup ( cmd ); + + DBG ( "COMBOOT: returning to run image...\n" ); + longjmp ( comboot_return, COMBOOT_RETURN_RUN_KERNEL ); + } + break; + + case 0x0004: /* Run default command */ + /* FIXME: just exit for now */ + longjmp ( comboot_return, COMBOOT_RETURN_EXIT ); + break; + + case 0x0005: /* Force text mode */ + comboot_force_text_mode ( ); + ix86->flags &= ~CF; + break; + + case 0x0006: /* Open file */ + { + int fd; + userptr_t file_u = real_to_user ( ix86->segs.es, ix86->regs.si ); + int len = strlen_user ( file_u, 0 ); + char file[len + 1]; + + copy_from_user ( file, file_u, 0, len + 1 ); + + if ( file[0] == '\0' ) { + DBG ( "COMBOOT: attempted open with empty file name\n" ); + break; + } + + DBG ( "COMBOOT: opening file '%s'\n", file ); + + fd = open ( file ); + + if ( fd < 0 ) { + DBG ( "COMBOOT: error opening file %s\n", file ); + break; + } + + /* This relies on the fact that a gPXE POSIX fd will + * always fit in 16 bits. + */ +#if (POSIX_FD_MAX > 65535) +#error POSIX_FD_MAX too large +#endif + ix86->regs.si = (uint16_t) fd; + + ix86->regs.cx = COMBOOT_FILE_BLOCKSZ; + ix86->regs.eax = fsize ( fd ); + ix86->flags &= ~CF; + } + break; + + case 0x0007: /* Read file */ + { + int fd = ix86->regs.si; + int len = ix86->regs.cx * COMBOOT_FILE_BLOCKSZ; + int rc; + fd_set fds; + userptr_t buf = real_to_user ( ix86->segs.es, ix86->regs.bx ); + + /* Wait for data ready to read */ + FD_ZERO ( &fds ); + FD_SET ( fd, &fds ); + + select ( &fds, 1 ); + + rc = read_user ( fd, buf, 0, len ); + if ( rc < 0 ) { + DBG ( "COMBOOT: read failed\n" ); + ix86->regs.si = 0; + break; + } + + ix86->regs.ecx = rc; + ix86->flags &= ~CF; + } + break; + + case 0x0008: /* Close file */ + { + int fd = ix86->regs.si; + close ( fd ); + ix86->flags &= ~CF; + } + break; + + case 0x0009: /* Call PXE Stack */ + pxe_api_call ( ix86 ); + ix86->flags &= ~CF; + break; + + case 0x000A: /* Get Derivative-Specific Information */ + + /* gPXE has its own derivative ID, so there is no defined + * output here; just return AL for now */ + ix86->regs.al = BZI_LOADER_TYPE_GPXE; + ix86->flags &= ~CF; + break; + + case 0x000B: /* Get Serial Console Configuration */ + /* FIXME: stub */ + ix86->regs.dx = 0; + ix86->flags &= ~CF; + break; + + case 0x000E: /* Get configuration file name */ + /* FIXME: stub */ + ix86->segs.es = rm_ds; + ix86->regs.bx = ( ( unsigned ) __from_data16 ( syslinux_configuration_file ) ); + ix86->flags &= ~CF; + break; + + case 0x000F: /* Get IPAPPEND strings */ + /* FIXME: stub */ + ix86->regs.cx = 0; + ix86->segs.es = 0; + ix86->regs.bx = 0; + ix86->flags &= ~CF; + break; + + case 0x0010: /* Resolve hostname */ + { + userptr_t hostname_u = real_to_user ( ix86->segs.es, ix86->regs.bx ); + int len = strlen_user ( hostname_u, 0 ); + char hostname[len]; + struct in_addr addr; + + copy_from_user ( hostname, hostname_u, 0, len + 1 ); + + /* TODO: + * "If the hostname does not contain a dot (.), the + * local domain name is automatically appended." + */ + + comboot_resolv ( hostname, &addr ); + + ix86->regs.eax = addr.s_addr; + ix86->flags &= ~CF; + } + break; + + case 0x0011: /* Maximum number of shuffle descriptors */ + ix86->regs.cx = COMBOOT_MAX_SHUFFLE_DESCRIPTORS; + ix86->flags &= ~CF; + break; + + case 0x0012: /* Cleanup, shuffle and boot */ + if ( ix86->regs.cx > COMBOOT_MAX_SHUFFLE_DESCRIPTORS ) + break; + + /* Perform final cleanup */ + shutdown ( SHUTDOWN_BOOT ); + + /* Perform sequence of copies */ + shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx ); + + /* Jump to real-mode entry point */ + __asm__ __volatile__ ( + REAL_CODE ( + "pushw %0\n\t" + "popw %%ds\n\t" + "pushl %1\n\t" + "lret\n\t" + ) + : + : "r" ( ix86->segs.ds ), + "r" ( ix86->regs.ebp ), + "d" ( ix86->regs.ebx ), + "S" ( ix86->regs.esi ) ); + + assert ( 0 ); /* Execution should never reach this point */ + + break; + + case 0x0013: /* Idle loop call */ + step ( ); + ix86->flags &= ~CF; + break; + + case 0x0015: /* Get feature flags */ + ix86->segs.es = rm_ds; + ix86->regs.bx = ( ( unsigned ) __from_data16 ( &comboot_feature_flags ) ); + ix86->regs.cx = 1; /* Number of feature flag bytes */ + ix86->flags &= ~CF; + break; + + case 0x0016: /* Run kernel image */ + { + userptr_t file_u = real_to_user ( ix86->segs.ds, ix86->regs.si ); + userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx ); + int file_len = strlen_user ( file_u, 0 ); + int cmd_len = strlen_user ( cmd_u, 0 ); + char file[file_len + 1 + cmd_len + 7 + 1]; + char cmd[cmd_len + 1]; + + memcpy( file, "kernel ", 7 ); + copy_from_user ( file + 7, file_u, 0, file_len + 1 ); + copy_from_user ( cmd, cmd_u, 0, cmd_len + 1 ); + strcat ( file, " " ); + strcat ( file, cmd ); + + DBG ( "COMBOOT: run kernel image '%s'\n", file ); + + comboot_kernel_cmdline = strdup ( file ); + + DBG ( "COMBOOT: returning to run image...\n" ); + longjmp ( comboot_return, COMBOOT_RETURN_RUN_KERNEL ); + } + break; + + case 0x0017: /* Report video mode change */ + comboot_graphics_mode = ix86->regs.bx; + ix86->flags &= ~CF; + break; + + case 0x0018: /* Query custom font */ + /* FIXME: stub */ + ix86->regs.al = 0; + ix86->segs.es = 0; + ix86->regs.bx = 0; + ix86->flags &= ~CF; + break; + + default: + DBG ( "COMBOOT unknown int22 function %04x\n", ix86->regs.ax ); + break; + } +} + +/** + * Hook BIOS interrupts related to COMBOOT API (INT 20h, 21h, 22h) + */ +void hook_comboot_interrupts ( ) { + + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint20_wrapper:\n\t" + "pushl %0\n\t" + "pushw %%cs\n\t" + "call prot_call\n\t" + "addw $4, %%sp\n\t" + "iret\n\t" ) + : : "i" ( int20 ) ); + + hook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper, + &int20_vector ); + + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint21_wrapper:\n\t" + "pushl %0\n\t" + "pushw %%cs\n\t" + "call prot_call\n\t" + "addw $4, %%sp\n\t" + "iret\n\t" ) + : : "i" ( int21 ) ); + + hook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper, + &int21_vector ); + + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint22_wrapper:\n\t" + "pushl %0\n\t" + "pushw %%cs\n\t" + "call prot_call\n\t" + "addw $4, %%sp\n\t" + "iret\n\t" ) + : : "i" ( int22) ); + + hook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper, + &int22_vector ); +} diff --git a/src/arch/i386/interface/syslinux/comboot_resolv.c b/src/arch/i386/interface/syslinux/comboot_resolv.c new file mode 100644 index 00000000..41c3af7a --- /dev/null +++ b/src/arch/i386/interface/syslinux/comboot_resolv.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include + +static int comboot_resolv_rc; +static struct in_addr comboot_resolv_addr; + +static void comboot_resolv_done ( struct resolv_interface *resolv, + struct sockaddr *sa, int rc ) { + struct sockaddr_in *sin; + + resolv_unplug ( resolv ); + + if ( rc != 0 ) { + comboot_resolv_rc = rc; + return; + } + + if ( sa->sa_family != AF_INET ) { + comboot_resolv_rc = -EAFNOSUPPORT; + return; + } + + sin = ( ( struct sockaddr_in * ) sa ); + comboot_resolv_addr = sin->sin_addr; + + comboot_resolv_rc = 0; +} + +static struct resolv_interface_operations comboot_resolv_ops = { + .done = comboot_resolv_done, +}; + +static struct resolv_interface comboot_resolver = { + .intf = { + .dest = &null_resolv.intf, + .refcnt = NULL, + }, + .op = &comboot_resolv_ops, +}; + +int comboot_resolv ( const char *name, struct in_addr *address ) { + int rc; + + comboot_resolv_rc = -EINPROGRESS; + + if ( ( rc = resolv ( &comboot_resolver, name, NULL ) ) != 0 ) + return rc; + + while ( comboot_resolv_rc == -EINPROGRESS ) + step(); + + *address = comboot_resolv_addr; + return comboot_resolv_rc; +} diff --git a/src/config.h b/src/config.h index 13d9f7e0..ae39fb55 100644 --- a/src/config.h +++ b/src/config.h @@ -131,6 +131,7 @@ #define IMAGE_PXE /* PXE image support */ #define IMAGE_SCRIPT /* gPXE script image support */ #define IMAGE_BZIMAGE /* Linux bzImage image support */ +#define IMAGE_COMBOOT /* SYSLINUX COMBOOT image support */ /* @END general.h */ diff --git a/src/core/config.c b/src/core/config.c index 220b7cdb..b5624fae 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -158,6 +158,14 @@ REQUIRE_OBJECT ( bzimage ); #ifdef IMAGE_ELTORITO REQUIRE_OBJECT ( eltorito ); #endif +#ifdef IMAGE_COMBOOT +REQUIRE_OBJECT ( comboot ); +REQUIRE_OBJECT ( com32 ); +REQUIRE_OBJECT ( comboot_call ); +REQUIRE_OBJECT ( com32_call ); +REQUIRE_OBJECT ( com32_wrapper ); +REQUIRE_OBJECT ( comboot_resolv ); +#endif /* * Drag in all requested commands diff --git a/src/include/gpxe/features.h b/src/include/gpxe/features.h index 70daac37..107ff085 100644 --- a/src/include/gpxe/features.h +++ b/src/include/gpxe/features.h @@ -45,6 +45,7 @@ #define DHCP_EB_FEATURE_NBI 0x20 /**< NBI format */ #define DHCP_EB_FEATURE_PXE 0x21 /**< PXE format */ #define DHCP_EB_FEATURE_ELF 0x22 /**< ELF format */ +#define DHCP_EB_FEATURE_COMBOOT 0x23 /**< COMBOOT format */ /** @} */ diff --git a/src/tests/comboot/shuffle-simple.asm b/src/tests/comboot/shuffle-simple.asm new file mode 100644 index 00000000..efc7d9b4 --- /dev/null +++ b/src/tests/comboot/shuffle-simple.asm @@ -0,0 +1,40 @@ + bits 16 + org 100h + + jmp start + +shuffle_start: + push 0xB800 + pop es + mov cx, 80*24*2 + mov ax, 'AA' + xor di, di + rep stosw +.lbl: jmp .lbl +shuffle_end: + nop +shuffle_len equ (shuffle_end - shuffle_start + 1) + +start: + ; calculate physical address of shuffled part + xor eax, eax + push ds + pop ax + shl eax, 4 + add ax, shuffle_start + mov dword [source], eax + + mov ax, 0012h + mov di, shuffle_descriptors + mov cx, num_shuffle_descriptors + mov ebp, 0x7c00 + int 22h + int3 + +shuffle_descriptors: + dd 0x7C00 +source: dd 0 + dd shuffle_len + +num_shuffle_descriptors equ 1 + diff --git a/src/tests/comboot/version.asm b/src/tests/comboot/version.asm new file mode 100644 index 00000000..01140423 --- /dev/null +++ b/src/tests/comboot/version.asm @@ -0,0 +1,136 @@ + bits 16 + org 100h + +_start: + ; first check for SYSLINUX + mov ah, 30h + int 21h + + cmp eax, 59530000h + jne .not_syslinux + cmp ebx, 4c530000h + jne .not_syslinux + cmp ecx, 4e490000h + jne .not_syslinux + cmp edx, 58550000h + jne .not_syslinux + + ; now get syslinux version + mov ax, 0001h + int 22h + + push cx + push dx + push di + push si + push es + + ; print version string + mov dx, str_version + mov ah, 09h + int 21h + + pop es + pop bx + push es + mov ax, 0002h + int 22h + + ; print copyright string + mov dx, str_copyright + mov ah, 09h + int 21h + + pop es + pop bx + mov ax, 0002h + int 22h + + ; print syslinux derivative id + mov dx, str_derivative + mov ah, 09h + int 21h + + pop ax + call print_hex_byte + + ; print version number + mov dx, str_version_num + mov ah, 09h + int 21h + + pop cx + push cx + mov ax, cx + and ax, 0FFh + call print_dec_word + + mov dl, '.' + mov ah, 02h + int 21h + + pop cx + mov ax, cx + shr ax, 8 + call print_dec_word + + ret + + +.not_syslinux: + mov dx, str_not_syslinux + mov ah, 09h + int 21h + ret + +; input: al = byte to print in hex +print_hex_byte: + push ax + shr al, 4 + call print_hex_nybble + pop ax + call print_hex_nybble + ret + +; input: bottom half of al = nybble to print in hex +print_hex_nybble: + push ax + mov bl, al + and bx, 1111b + mov dl, [str_hex + bx] + mov ah, 02h + int 21h + pop ax + ret + +str_hex: db "01234567890abcdef" + +; input: ax = word to print +print_dec_word: + mov cx, 10 + mov word [.count], 0 +.loop: + xor dx, dx + div cx + inc word [.count] + push dx + test ax, ax + jnz .loop + +.print: + pop dx + add dx, '0' + mov ah, 02h + int 21h + dec word [.count] + jnz .print + + ret + +.count: dw 0 + +str_not_syslinux: db "Not SYSLINUX or derivative (running on DOS?)$" +str_version: db "Version: $" +str_copyright: db 10, "Copyright: $" +str_derivative: db 10, "Derivative ID: 0x$" +str_version_num: db 10, "Version number: $" -- cgit v1.2.3-55-g7522