diff options
author | Pierre-Alexandre Meyer <pierre@ning.com> | 2009-10-27 13:04:31 -0700 |
---|---|---|
committer | Pierre-Alexandre Meyer <pierre@ning.com> | 2009-10-27 13:04:31 -0700 |
commit | 57c8555003f53800da1bb93a765d80678ae230ae (patch) | |
tree | ab40516182979cff6e3a7d2c5846bc6065aca4b1 | |
parent | 47ac912cd5d8df0a96b8f5b98d7476f7cfb1cb19 (diff) | |
parent | 3fc3126d0c7df5701680e043c34f200166f24564 (diff) | |
download | syslinux-57c8555003f53800da1bb93a765d80678ae230ae.tar.gz |
Merge commit 'erwan/master' into hdt-0.3.5
-rw-r--r-- | MCONFIG | 3 | ||||
-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | com32/gplinclude/cpuid.h | 2 | ||||
-rw-r--r-- | com32/gpllib/cpuid.c | 2 | ||||
-rw-r--r-- | com32/hdt/Makefile | 39 | ||||
-rw-r--r-- | com32/hdt/README | 19 | ||||
-rw-r--r-- | com32/hdt/floppy/hdt.cfg | 6 | ||||
-rw-r--r-- | com32/hdt/floppy/mtools.conf | 2 | ||||
-rw-r--r-- | com32/hdt/hdt-cli-cpu.c | 4 | ||||
-rw-r--r-- | com32/hdt/hdt-cli-hdt.c | 4 | ||||
-rw-r--r-- | com32/hdt/hdt-menu-processor.c | 4 | ||||
-rw-r--r-- | com32/hdt/hdt.c | 4 | ||||
-rw-r--r-- | com32/include/cpufeature.h | 2 | ||||
-rw-r--r-- | com32/mboot/map.c | 2 | ||||
-rw-r--r-- | com32/modules/Makefile | 8 | ||||
-rw-r--r-- | com32/modules/chain.c | 21 | ||||
-rw-r--r-- | com32/modules/cpuidtest.c | 4 | ||||
-rw-r--r-- | com32/modules/gpxecmd.c | 137 | ||||
-rw-r--r-- | core/localboot.inc | 1 | ||||
-rw-r--r-- | core/pxelinux.asm | 184 | ||||
-rw-r--r-- | core/serirq.inc | 28 | ||||
-rw-r--r-- | modules/Makefile | 2 | ||||
-rw-r--r-- | modules/int18.asm | 16 | ||||
-rw-r--r-- | utils/Makefile | 3 | ||||
-rwxr-xr-x | utils/pxelinux-options | 499 |
25 files changed, 974 insertions, 29 deletions
@@ -52,6 +52,9 @@ NM = nm RANLIB = ranlib GZIPPROG = gzip PNGTOPNM = pngtopnm +MCOPY = mcopy +MFORMAT = mformat +MKISOFS = mkisofs com32 = $(topdir)/com32 @@ -18,6 +18,13 @@ Changes in 3.83: * Simple menu: fix Ctrl-W (word erase) in command-line edit. * Simple menu: fix crash on some platforms. * Gfxboot: fixes to the configuration file parsing. + * PXELINUX: add a tool to override specific DHCP options via + values hardcoded in the pxelinux.0 file. These hardcoded + values can be either "before DHCP" (defaults if DHCP do not + provide values), or "after DHCP" (overrides DHCP). The tool + pxelinux-options can be used to set these options. This + feature does not apply to gpxelinux.0; when used with gPXE + this is better handled by modifying the embedded script. Changes in 3.82: * isohybrid: fix the -partok logic for loading from a partition. diff --git a/com32/gplinclude/cpuid.h b/com32/gplinclude/cpuid.h index f984751f..f85e6ab3 100644 --- a/com32/gplinclude/cpuid.h +++ b/com32/gplinclude/cpuid.h @@ -65,6 +65,8 @@ typedef struct { bool nowext; /* AMD 3DNow! extensions */ bool now; /* 3DNow! */ bool smp; /* A smp configuration has been found */ + bool vmx; /* Hardware virtualization */ + bool svm; /* Secure virtual machine */ } s_cpu_flags; typedef struct { diff --git a/com32/gpllib/cpuid.c b/com32/gpllib/cpuid.c index 6d464c7e..fb69cef6 100644 --- a/com32/gpllib/cpuid.c +++ b/com32/gpllib/cpuid.c @@ -295,6 +295,8 @@ void set_cpu_flags(struct cpuinfo_x86 *c, s_cpu * cpu) cpu->flags.nowext = cpu_has(c, X86_FEATURE_3DNOWEXT); cpu->flags.now = cpu_has(c, X86_FEATURE_3DNOW); cpu->flags.smp = find_smp_config(); + cpu->flags.vmx = cpu_has(c, X86_FEATURE_VMX); + cpu->flags.svm = cpu_has(c, X86_FEATURE_SVM); } void set_generic_info(struct cpuinfo_x86 *c, s_cpu * cpu) diff --git a/com32/hdt/Makefile b/com32/hdt/Makefile index 8f8cae21..a939d7b2 100644 --- a/com32/hdt/Makefile +++ b/com32/hdt/Makefile @@ -27,11 +27,47 @@ TESTFILES = OBJS = $(patsubst %.c,%.o,$(wildcard *.c)) +KERNEL_VERSION ?= $(shell uname -r) +MODULES_ALIAS_FILE ?= /lib/modules/$(KERNEL_VERSION)/modules.alias +MODULES_PCIMAP_FILE ?= /lib/modules/$(KERNEL_VERSION)/modules.pcimap +ISO_DIR ?= iso +ISOLINUX_DIR ?= isolinux +FLOPPY_DIR ?= floppy +PCI_IDS_FILE ?= $(PWD)/$(FLOPPY_DIR)/pci.ids + all: $(MODULES) $(TESTFILES) hdt.elf : $(OBJS) $(LIBS) $(C_LIBS) $(LD) $(LDFLAGS) -o $@ $^ +hdt.img: hdt.c32 $(FLOPPY_DIR)/hdt.cfg $(FLOPPY_DIR)/mtools.conf $(topdir)/mtools/syslinux + rm -f hdt.img + MTOOLSRC=$(PWD)/$(FLOPPY_DIR)/mtools.conf $(MFORMAT) -v HDT -f 2880 -C a: + $(topdir)/mtools/syslinux hdt.img + -[ ! -f $(PCI_IDS_FILE) ] && cp /usr/share/hwdata/pci.ids $(PCI_IDS_FILE) + -[ ! -f $(PCI_IDS_FILE) ] && cp /usr/share/pci.ids $(PCI_IDS_FILE) + -[ -f $(MODULES_ALIAS_FILE) ] && MTOOLSRC=$(PWD)/$(FLOPPY_DIR)/mtools.conf $(MCOPY) $(MODULES_ALIAS_FILE) a: + -[ -f $(MODULES_PCIMAP_FILE) ] && MTOOLSRC=$(PWD)/$(FLOPPY_DIR)/mtools.conf $(MCOPY) $(MODULES_PCIMAP_FILE) a: + MTOOLSRC=$(PWD)/$(FLOPPY_DIR)/mtools.conf $(MCOPY) hdt.c32 a: + @ [ -f $(PCI_IDS_FILE) ] && MTOOLSRC=$(PWD)/$(FLOPPY_DIR)/mtools.conf $(MCOPY) $(PCI_IDS_FILE) a: || printf "\nThe $(FLOPPY_DIR)/pci.ids file is missing and can be downloaded from http://pciids.sourceforge.net and put in\nthe ./com32/hdt/$(FLOPPY_DIR) directory of the extracted Syslinux source.\n\n" + MTOOLSRC=$(PWD)/$(FLOPPY_DIR)/mtools.conf $(MCOPY) $(FLOPPY_DIR)/hdt.cfg a:syslinux.cfg + +hdt.iso: hdt.c32 $(topdir)/core/isolinux.bin $(FLOPPY_DIR)/hdt.cfg + rm -rf $(ISO_DIR) + rm -f hdt.iso + mkdir -p $(ISO_DIR)/$(ISOLINUX_DIR) + cp $(topdir)/core/isolinux.bin $(ISO_DIR)/$(ISOLINUX_DIR) + cp $(FLOPPY_DIR)/hdt.cfg $(ISO_DIR)/$(ISOLINUX_DIR)/isolinux.cfg + cp hdt.c32 $(ISO_DIR)/$(ISOLINUX_DIR) + -[ ! -f $(PCI_IDS_FILE) ] && cp /usr/share/hwdata/pci.ids $(ISO_DIR)/$(ISOLINUX_DIR) + -[ ! -f $(PCI_IDS_FILE) ] && cp /usr/share/pci.ids $(ISO_DIR)/$(ISOLINUX_DIR) + -[ -f $(MODULES_ALIAS_FILE) ] && cp $(MODULES_ALIAS_FILE) $(ISO_DIR)/$(ISOLINUX_DIR) + -[ -f $(MODULES_PCIMAP_FILE) ] && cp $(MODULES_PCIMAP_FILE) $(ISO_DIR)/$(ISOLINUX_DIR) + -[ ! -f $(ISO_DIR)/$(ISOLINUX_DIR)/pci.ids ] && printf "\nThe $(FLOPPY_DIR)/pci.ids file is missing and can be downloaded from http://pciids.sourceforge.net and put in\nthe ./com32/hdt/$(FLOPPY_DIR) directory of the extracted Syslinux source.\n\n" + $(MKISOFS) -o hdt.iso -b $(ISOLINUX_DIR)/isolinux.bin -c $(ISOLINUX_DIR)/boot.cat \ + -no-emul-boot -boot-load-size 4 -boot-info-table \ + $(ISO_DIR) + tidy dist: rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp @@ -39,7 +75,8 @@ clean: tidy rm -f *.lnx spotless: clean - rm -f *.lss *.c32 *.com + rm -f *.lss *.c32 *.com hdt.img hdt.iso + rm -rf $(ISO_DIR) rm -f *~ \#* install: diff --git a/com32/hdt/README b/com32/hdt/README new file mode 100644 index 00000000..8e171616 --- /dev/null +++ b/com32/hdt/README @@ -0,0 +1,19 @@ +-------------- +Compiling HDT +-------------- +To build HDT, you just have to do a "make" call in this directory. + +--------------------------- +Creating a bootable floppy +-------------------------- +To build a bootable HDT floppy image, you can do a "make hdt.img" call. +This will requires the mtools (http://mtools.linux.lu) to be installed. +The script will try to pick several files from your system : +- /lib/modules/`uname -r`/modules.alias +- /lib/modules/`uname -r`/modules.pcimap +- /usr/share/pci.ids or /usr/share/hwdata/pci.ids + +This paths can be overrided with the following command line: +make MODULES_ALIAS_FILE=$(PWD)/floppy/modules.alias MODULES_PCIMAP_FILE=$(PWD)/floppy/modules.pcimap PCI_IDS_FILE=$(PWD)/floppy/pci.ids hdt.img + +If your system doesn't have pci.ids, please download it from http://pciids.sourceforge.net/ and put it into the floppy/ directory. diff --git a/com32/hdt/floppy/hdt.cfg b/com32/hdt/floppy/hdt.cfg new file mode 100644 index 00000000..04e30476 --- /dev/null +++ b/com32/hdt/floppy/hdt.cfg @@ -0,0 +1,6 @@ +DEFAULT hdt +PROMPT 0 + +LABEL hdt +COM32 hdt.c32 +APPEND modules_pcimap=modules.pcimap modules_alias=modules.alias pciids=pci.ids diff --git a/com32/hdt/floppy/mtools.conf b/com32/hdt/floppy/mtools.conf new file mode 100644 index 00000000..adbe2c83 --- /dev/null +++ b/com32/hdt/floppy/mtools.conf @@ -0,0 +1,2 @@ +# Floppy image for HDT +drive a: file="hdt.img" diff --git a/com32/hdt/hdt-cli-cpu.c b/com32/hdt/hdt-cli-cpu.c index f0f0a1bb..d2b5979f 100644 --- a/com32/hdt/hdt-cli-cpu.c +++ b/com32/hdt/hdt-cli-cpu.c @@ -174,6 +174,10 @@ static void show_cpu(int argc __unused, char **argv __unused, strcat(buffer1, "3dnowext "); if (hardware->cpu.flags.now) strcat(buffer1, "3dnow! "); + if (hardware->cpu.flags.svm) + strcat(buffer1, "svm "); + if (hardware->cpu.flags.vmx) + strcat(buffer1, "vmx "); if (buffer1[0]) { snprintf(buffer, sizeof buffer, "Flags : %s\n", buffer1); more_printf(buffer); diff --git a/com32/hdt/hdt-cli-hdt.c b/com32/hdt/hdt-cli-hdt.c index 5bebf584..b5cd5359 100644 --- a/com32/hdt/hdt-cli-hdt.c +++ b/com32/hdt/hdt-cli-hdt.c @@ -191,8 +191,8 @@ static void goto_menu(int argc __unused, char** argv __unused, struct s_hardware *hardware) { char version_string[256]; - snprintf(version_string, sizeof version_string, "%s %s by %s", - PRODUCT_NAME, VERSION, AUTHOR); + snprintf(version_string, sizeof version_string, "%s %s", + PRODUCT_NAME, VERSION); start_menu_mode(hardware, version_string); return; } diff --git a/com32/hdt/hdt-menu-processor.c b/com32/hdt/hdt-menu-processor.c index a30cf0f6..4e102cb5 100644 --- a/com32/hdt/hdt-menu-processor.c +++ b/com32/hdt/hdt-menu-processor.c @@ -231,6 +231,10 @@ void compute_processor(struct s_my_menu *menu, struct s_hardware *hardware) strcat(buffer1, "3dnowext "); if (hardware->cpu.flags.now) strcat(buffer1, "3dnow! "); + if (hardware->cpu.flags.vmx) + strcat(buffer1, "vmx "); + if (hardware->cpu.flags.svm) + strcat(buffer1, "svm "); snprintf(buffer, sizeof buffer, "Flags : %s", buffer1); snprintf(statbuffer, sizeof statbuffer, "Flags: %s", buffer1); add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); diff --git a/com32/hdt/hdt.c b/com32/hdt/hdt.c index 93c4aae2..e10e56f5 100644 --- a/com32/hdt/hdt.c +++ b/com32/hdt/hdt.c @@ -48,8 +48,8 @@ int main(const int argc, const char *argv[]) const char *arg; struct s_hardware hardware; - snprintf(version_string, sizeof version_string, "%s %s by %s", - PRODUCT_NAME,VERSION,AUTHOR); + snprintf(version_string, sizeof version_string, "%s %s", + PRODUCT_NAME,VERSION); /* Opening the Syslinux console */ console_ansi_raw(); diff --git a/com32/include/cpufeature.h b/com32/include/cpufeature.h index 2fd47579..036631a7 100644 --- a/com32/include/cpufeature.h +++ b/com32/include/cpufeature.h @@ -72,6 +72,7 @@ #define X86_FEATURE_XMM3 (4*32+ 0) /* Streaming SIMD Extensions-3 */ #define X86_FEATURE_MWAIT (4*32+ 3) /* Monitor/Mwait support */ #define X86_FEATURE_DSCPL (4*32+ 4) /* CPL Qualified Debug Store */ +#define X86_FEATURE_VMX (4*32+ 5) /* Hardware virtualization */ #define X86_FEATURE_EST (4*32+ 7) /* Enhanced SpeedStep */ #define X86_FEATURE_TM2 (4*32+ 8) /* Thermal Monitor 2 */ #define X86_FEATURE_CID (4*32+10) /* Context ID */ @@ -87,6 +88,7 @@ /* More extended AMD flags: CPUID level 0x80000001, ecx, word 6 */ #define X86_FEATURE_LAHF_LM (6*32+ 0) /* LAHF/SAHF in long mode */ #define X86_FEATURE_CMP_LEGACY (6*32+ 1) /* If yes HyperThreading not valid */ +#define X86_FEATURE_SVM (6*32+ 2) /* Secure virtual machine */ #endif /* __ASM_I386_CPUFEATURE_H */ diff --git a/com32/mboot/map.c b/com32/mboot/map.c index 1a788ef7..887776fe 100644 --- a/com32/mboot/map.c +++ b/com32/mboot/map.c @@ -127,7 +127,7 @@ int map_image(void *ptr, size_t len) else mbh_len = 12; - if (i + mbh_len < len) + if (i + mbh_len > len) mbh_len = 0; /* Invalid... */ else break; /* Found something... */ diff --git a/com32/modules/Makefile b/com32/modules/Makefile index c93d3afc..52327b61 100644 --- a/com32/modules/Makefile +++ b/com32/modules/Makefile @@ -18,10 +18,10 @@ topdir = ../.. include ../MCONFIG -MODULES = chain.c32 config.c32 ethersel.c32 dmitest.c32 cpuidtest.c32 disk.c32 \ - pcitest.c32 elf.c32 linux.c32 reboot.c32 pmload.c32 meminfo.c32 \ - sdi.c32 sanboot.c32 ifcpu64.c32 vesainfo.c32 kbdmap.c32 cmd.c32 \ - vpdtest.c32 +MODULES = chain.c32 config.c32 ethersel.c32 dmitest.c32 cpuidtest.c32 \ + disk.c32 pcitest.c32 elf.c32 linux.c32 reboot.c32 pmload.c32 \ + meminfo.c32 sdi.c32 sanboot.c32 ifcpu64.c32 vesainfo.c32 \ + kbdmap.c32 cmd.c32 vpdtest.c32 gpxecmd.c32 TESTFILES = diff --git a/com32/modules/chain.c b/com32/modules/chain.c index bdeb82d5..53746c85 100644 --- a/com32/modules/chain.c +++ b/com32/modules/chain.c @@ -446,7 +446,7 @@ static void do_boot(void *boot_sector, size_t boot_size, mmap = syslinux_memory_map(); if (!mmap) { - error("Cannot read system memory map"); + error("Cannot read system memory map\n"); return; } @@ -547,11 +547,11 @@ static void do_boot(void *boot_sector, size_t boot_size, return; too_big: - error("Loader file too large"); + error("Loader file too large\n"); return; enomem: - error("Out of memory"); + error("Out of memory\n"); return; } @@ -614,7 +614,7 @@ int main(int argc, char *argv[]) } else if (!strncmp(argv[i], "seg=", 4)) { uint32_t segval = strtoul(argv[i] + 4, NULL, 0); if (segval < 0x50 || segval > 0x9f000) { - error("Invalid segment"); + error("Invalid segment\n"); goto bail; } opt.seg = segval; @@ -650,7 +650,18 @@ int main(int argc, char *argv[]) } } else { error - ("Usage: chain.c32 (hd#|fd#|mbr:#|boot)[,partition] [options]\n"); + ("Usage: chain.c32 hd<disk#> [<partition>] [options]\n" + " chain.c32 fd<disk#> [options]\n" + " chain.c32 mbr:<id> [<partition>] [options]\n" + " chain.c32 boot [<partition>] [options]\n" + "Options: file=<loader> load file, instead of boot sector\n" + " ntldr=<loader> load Windows bootloaders: NTLDR, SETUPLDR, BOOTMGR\n" + " freedos=<loader> load FreeDOS kernel.sys\n" + " msdos=<loader> load MS-DOS io.sys\n" + " pcdos=<loader> load PC-DOS ibmbio.com\n" + " seg=<segment> jump to <seg>:0000 instead of 0000:7C00\n" + " swap swap drive numbers, if bootdisk is not fd0/hd0\n" + " hide hide primary partitions, except selected partition\n"); goto bail; } } diff --git a/com32/modules/cpuidtest.c b/com32/modules/cpuidtest.c index bfc1c190..b7688852 100644 --- a/com32/modules/cpuidtest.c +++ b/com32/modules/cpuidtest.c @@ -121,6 +121,10 @@ int main(void) printf("3dnowext "); if (cpu.flags.now) printf("3dnow! "); + if (cpu.flags.vmx) + printf("vmx "); + if (cpu.flags.svm) + printf("svm "); printf("\n"); printf("SMP = "); if (cpu.flags.smp) diff --git a/com32/modules/gpxecmd.c b/com32/modules/gpxecmd.c new file mode 100644 index 00000000..de6ffb21 --- /dev/null +++ b/com32/modules/gpxecmd.c @@ -0,0 +1,137 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * + * 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * gpxecmd.c + * + * Invoke an arbitrary gPXE command, if available. + */ + +#include <alloca.h> +#include <inttypes.h> +#include <stdio.h> +#include <console.h> +#include <com32.h> +#include <stdbool.h> +#include <string.h> +#include <syslinux/config.h> + +struct segoff16 { + uint16_t offs, seg; +}; + +struct s_PXENV_FILE_CHECK_API { + uint16_t Status; + uint16_t Size; + uint32_t Magic; + uint32_t Provider; + uint32_t APIMask; + uint32_t Flags; +}; + +static bool is_gpxe(void) +{ + const struct syslinux_version *sv; + com32sys_t reg; + struct s_PXENV_FILE_CHECK_API *fca; + + sv = syslinux_version(); + if (sv->filesystem != SYSLINUX_FS_PXELINUX) + return false; /* Not PXELINUX */ + + fca = __com32.cs_bounce; + memset(fca, 0, sizeof *fca); + fca->Size = sizeof *fca; + fca->Magic = 0x91d447b2; + + memset(®, 0, sizeof reg); + reg.eax.w[0] = 0x0009; + reg.ebx.w[0] = 0x00e6; /* PXENV_FILE_API_CHECK */ + reg.edi.w[0] = OFFS(fca); + reg.es = SEG(fca); + + __intcall(0x22, ®, ®); + + if (reg.eflags.l & EFLAGS_CF) + return false; /* Cannot invoke PXE stack */ + + if (reg.eax.w[0] || fca->Status) + return false; /* PXE failure */ + + if (fca->Magic != 0xe9c17b20) + return false; /* Incorrect magic */ + + if (fca->Size < sizeof *fca) + return false; /* Short return */ + + if (!(fca->APIMask & (1 << 5))) + return false; /* No FILE EXEC */ + + return true; +} + +struct s_PXENV_FILE_EXEC { + uint16_t Status; + struct segoff16 Command; +}; + +static void gpxecmd(const char **args) +{ + char *q; + struct s_PXENV_FILE_EXEC *fx; + com32sys_t reg; + + memset(®, 0, sizeof reg); + + fx = __com32.cs_bounce; + q = (char *)(fx + 1); + + fx->Status = 1; + fx->Command.offs = OFFS(q); + fx->Command.seg = SEG(q); + + while (*args) { + q = stpcpy(q, *args); + *q++ = ' '; + args++; + } + *--q = '\0'; + + memset(®, 0, sizeof reg); + reg.eax.w[0] = 0x0009; + reg.ebx.w[0] = 0x00e5; /* PXENV_FILE_EXEC */ + reg.edi.w[0] = OFFS(fx); + reg.es = SEG(fx); + + __intcall(0x22, ®, ®); + + /* This should not return... */ +} + +int main(int argc, const char *argv[]) +{ + openconsole(&dev_null_r, &dev_stdcon_w); + + if (argc < 2) { + printf("Usage: gpxecmd command...\n"); + return 1; + } + + if (!is_gpxe()) { + printf("gpxecmd: gPXE API not detected\n"); + return 1; + } + + gpxecmd(argv + 1); + + return 0; +} diff --git a/core/localboot.inc b/core/localboot.inc index ae54737a..03d5cfd9 100644 --- a/core/localboot.inc +++ b/core/localboot.inc @@ -31,6 +31,7 @@ local_boot: mov gs,dx mov si,localboot_msg call writestr + call cleanup_hardware cmp ax,-1 je .int18 diff --git a/core/pxelinux.asm b/core/pxelinux.asm index 190f4c66..651dd4c3 100644 --- a/core/pxelinux.asm +++ b/core/pxelinux.asm @@ -224,6 +224,25 @@ StackBuf equ $ ; Base of stack if we use our own ; bootsec equ $ _start: + jmp 0:_start1 ; Canonicalize the address and skip + ; the patch header + +; +; Patch area for adding hardwired DHCP options +; + align 4 + +hcdhcp_magic dd 0x2983c8ac ; Magic number +hcdhcp_len dd 7*4 ; Size of this structure +hcdhcp_flags dd 0 ; Reserved for the future + ; Parameters to be parsed before the ones from PXE +bdhcp_offset dd 0 ; Offset (entered by patcher) +bdhcp_len dd 0 ; Length (entered by patcher) + ; Parameters to be parsed *after* the ones from PXE +adhcp_offset dd 0 ; Offset (entered by patcher) +adhcp_len dd 0 ; Length (entered by patcher) + +_start1: pushfd ; Paranoia... in case of return to PXE pushad ; ... save as much state as possible push ds @@ -236,8 +255,6 @@ _start: mov ds,ax mov es,ax - jmp 0:_start1 ; Canonicalize address -_start1: ; That is all pushed onto the PXE stack. Save the pointer ; to it and switch to an internal stack. mov [InitStack],sp @@ -252,6 +269,54 @@ _start1: lss esp,[BaseStack] sti ; Stack set up and ready +; +; Move the hardwired DHCP options (if present) to a safe place... +; +bdhcp_copy: + mov cx,[bdhcp_len] + mov ax,trackbufsize/2 + jcxz .none + cmp cx,ax + jbe .oksize + mov cx,ax + mov [bdhcp_len],ax +.oksize: + mov eax,[bdhcp_offset] + add eax,_start + mov si,ax + and si,000Fh + shr eax,4 + push ds + mov ds,ax + mov di,trackbuf + add cx,3 + shr cx,2 + rep movsd + pop ds +.none: + +adhcp_copy: + mov cx,[adhcp_len] + mov ax,trackbufsize/2 + jcxz .none + cmp cx,ax + jbe .oksize + mov cx,ax + mov [adhcp_len],ax +.oksize: + mov eax,[adhcp_offset] + add eax,_start + mov si,ax + and si,000Fh + shr eax,4 + push ds + mov ds,ax + mov di,trackbuf+trackbufsize/2 + add cx,3 + shr cx,2 + rep movsd + pop ds +.none: ; ; Initialize screen (if we're using one) @@ -268,6 +333,81 @@ _start1: call writestr_early ; +; Look to see if we are on an EFI CSM system. Some EFI +; CSM systems put the BEV stack in low memory, which means +; a return to the PXE stack will crash the system. However, +; INT 18h works reliably, so in that case hack the stack and +; point the "return address" to an INT 18h instruction. +; +; Hack the stack instead of the much simpler "just invoke INT 18h +; if we want to reset", so that chainloading other NBPs will work. +; +efi_csm_workaround: + les bp,[InitStack] ; GS:SP -> original stack + les bx,[es:bp+44] ; Return address + cmp word [es:bx],18CDh ; Already pointing to INT 18h? + je .skip + + ; Search memory from E0000 to FFFFF for a $EFI structure + mov bx,0E000h +.scan_mem: + mov es,bx + cmp dword [es:0],'IFE$' ; $EFI is byte-reversed... + jne .not_here + ; + ; Verify the table. We don't check the checksum because + ; it seems some CSMs leave it at zero. + ; + movzx cx,byte [es:5] ; Table length + cmp cx,83 ; 83 bytes is the current length... + jae .found_it + +.not_here: + inc bx + jnz .scan_mem + jmp .skip ; No $EFI structure found + + ; + ; Found a $EFI structure. Move down the original stack + ; and put an INT 18h instruction there instead. + ; +.found_it: +%if USE_PXE_PROVIDED_STACK + mov cx,efi_csm_hack_size + mov si,sp + sub sp,cx + mov di,sp + mov ax,ss + mov es,ax + sub [InitStack],cx + sub [BaseStack],cx +%else + les si,[InitStack] + lea di,[si-efi_csm_hack_size] + mov [InitStack],di +%endif + lea cx,[bp+52] ; End of the stack we care about + sub cx,si + es rep movsb + mov [es:di-8],di ; Clobber the return address + mov [es:di-6],es + mov si,efi_csm_hack + mov cx,efi_csm_hack_size + rep movsb + +.skip: + + section .data + alignz 4 +efi_csm_hack: + int 18h + jmp 0F000h:0FFF0h + hlt +efi_csm_hack_size equ $-efi_csm_hack + + section .text + +; ; Assume API version 2.1, in case we find the !PXE structure without ; finding the PXENV+ structure. This should really look at the Base ; Code ROM ID structure in have_pxe, but this is adequate for now -- @@ -450,6 +590,24 @@ have_entrypoint: xor ax,ax mov [LocalDomain],al ; No LocalDomain received + +; This is a good time to initialize DHCPMagic... +; Initialize it to 1 meaning we will accept options found; +; in earlier versions of PXELINUX bit 0 was used to indicate +; we have found option 208 with the appropriate magic number; +; we no longer require that, but MAY want to re-introduce +; it in the future for vendor encapsulated options. + mov byte [DHCPMagic],1 + +; +; Process any hardwired options the user may have specified. This is +; different than the actual packets in that there is no header, just +; an option field. +; + mov cx,[bdhcp_len] + mov si,trackbuf + call parse_dhcp_options + ; ; The DHCP client identifiers are best gotten from the DHCPREQUEST ; packet (query info 1). @@ -462,15 +620,6 @@ query_bootp_1: call pxe_get_cached_info call parse_dhcp - ; We don't use flags from the request packet, so - ; this is a good time to initialize DHCPMagic... - ; Initialize it to 1 meaning we will accept options found; - ; in earlier versions of PXELINUX bit 0 was used to indicate - ; we have found option 208 with the appropriate magic number; - ; we no longer require that, but MAY want to re-introduce - ; it in the future for vendor encapsulated options. - mov byte [DHCPMagic],1 - ; ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP ; address). This lives in the DHCPACK packet (query info 2). @@ -514,6 +663,15 @@ query_bootp_3: call crlf ; +; Process any hardwired options the user may have specified. This is +; different than the actual packets in that there is no header, just +; an option field. This handles the "after" options +; + mov cx,[adhcp_len] + mov si,trackbuf+trackbufsize/2 + call parse_dhcp_options + +; ; Generate the bootif string, and the hardware-based config string. ; make_bootif_string: @@ -789,6 +947,7 @@ local_boot: mov si,localboot_msg call writestr_early ; Restore the environment we were called with + call cleanup_hardware lss sp,[InitStack] pop gs pop fs @@ -2176,6 +2335,7 @@ xchexbytes: ; pxe_get_cached_info ; ; Get a DHCP packet from the PXE stack into the trackbuf. +; Leaves the upper half of the trackbuf untouched. ; ; Input: ; DL = packet type @@ -2196,7 +2356,7 @@ pxe_get_cached_info: stosw ; Status movzx ax,dl stosw ; Packet type - mov ax,trackbufsize + mov ax,trackbufsize/2 stosw ; Buffer size mov ax,trackbuf stosw ; Buffer offset diff --git a/core/serirq.inc b/core/serirq.inc index 579c42b4..b7d79e0f 100644 --- a/core/serirq.inc +++ b/core/serirq.inc @@ -91,6 +91,9 @@ SerialIRQPort dw 0 ; Serial port w IRQ service SerialHead dw 0 ; Head of serial port rx buffer SerialTail dw 0 ; Tail of serial port rx buffer + section .bss +IRQMask resw 1 ; PIC IRQ mask status + section .text sirq_install: @@ -133,6 +136,22 @@ sirq_install: mov al,1 ; Enable receive interrupt slow_out dx,al + ; + ; Enable all ther interupt lines at the PIC. Some BIOSes + ; only enable the timer interrupts and other interrupts + ; actively in use by the BIOS. + ; + in al,0xA1 ; Secondary PIC mask register + mov ah,al + in al,0x21 ; Primary PIC mask register + mov [IRQMask],ax + + io_delay + + xor ax,ax ; Remove all interrupt masks + out 0x21,al + out 0xA1,al + popad ret @@ -156,6 +175,12 @@ sirq_cleanup_nowipe: xor ax,ax slow_out dx,al ; Clear IER + ; Restore PIC masks + mov ax,[IRQMask] + out 0x21,al + mov al,ah + out 0xA1,al + ; Restore the original interrupt vectors mov si,oldirq0 mov di,4*08h @@ -165,6 +190,9 @@ sirq_cleanup_nowipe: mov cx,8 rep movsd + xor ax,ax + mov [SerialIRQPort],ax ; No active interrupt system + .done: pop es pop ds diff --git a/modules/Makefile b/modules/Makefile index 80eb995d..77020ea0 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -19,7 +19,7 @@ include $(topdir)/MCONFIG.embedded INCLUDES = -I$(com32)/include -BINS = pxechain.com gfxboot.com poweroff.com +BINS = pxechain.com gfxboot.com poweroff.com int18.com all: $(BINS) diff --git a/modules/int18.asm b/modules/int18.asm new file mode 100644 index 00000000..a13ada75 --- /dev/null +++ b/modules/int18.asm @@ -0,0 +1,16 @@ + bits 16 + org 100h +_start: + mov ax,5 + int 22h + mov ah,09h + mov dx,msg + int 21h + mov ax,000Ch + xor dx,dx + int 22h + int 18h + jmp 0F000h:0FFF0h ; INT 18h should not return... + + section .data +msg: db 'Local boot via INT 18...', 13, 10, '$' diff --git a/utils/Makefile b/utils/Makefile index 9df9595a..39adf7d3 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -21,7 +21,8 @@ CFLAGS = -W -Wall -Os -fomit-frame-pointer -D_FILE_OFFSET_BITS=64 LDFLAGS = -O2 -s TARGETS = mkdiskimage isohybrid gethostip -ASIS = keytab-lilo lss16toppm md5pass ppmtolss16 sha1pass syslinux2ansi +ASIS = keytab-lilo lss16toppm md5pass ppmtolss16 sha1pass syslinux2ansi \ + pxelinux-options ISOHDPFX = ../mbr/isohdpfx.bin ../mbr/isohdpfx_f.bin ../mbr/isohdpfx_c.bin \ ../mbr/isohdppx.bin ../mbr/isohdppx_f.bin ../mbr/isohdppx_c.bin diff --git a/utils/pxelinux-options b/utils/pxelinux-options new file mode 100755 index 00000000..ab7075b5 --- /dev/null +++ b/utils/pxelinux-options @@ -0,0 +1,499 @@ +#!/usr/bin/perl +# +# Set PXELINUX hard-coded options +# + +use Socket; # For gethostbyname +use Fcntl; +use bytes; + +%option_names = ( + 6 => 'domain-name-servers', + 15 => 'domain-name', + 54 => 'next-server', + 209 => 'config-file', + 210 => 'path-prefix', + 211 => 'reboottime' + ); + +@fmt_oneip = ("ip-address", \&parse_oneip, \&show_ip); +@fmt_multiip = ("ip-address-list", \&parse_multiip, \&show_ip); +@fmt_string = ("string", \&parse_string, \&show_string); +@fmt_uint32 = ("uint32", \&parse_uint32, \&show_uint32); + +%option_format = ( + 6 => \@fmt_multiip, + 15 => \@fmt_string, + 54 => \@fmt_oneip, + 67 => \@fmt_string, + 209 => \@fmt_string, + 210 => \@fmt_string, + 211 => \@fmt_uint32 + ); + +sub parse_oneip($) +{ + my($s) = @_; + my($name,$aliases,$addrtype,$length,@addrs) = gethostbyname($s); + + return ($addrtype == AF_INET) ? $addrs[0] : undef; +} + +sub parse_multiip($) +{ + my($l) = @_; + my $s; + my @a = (); + my $addr; + my $d = ''; + + foreach $s (split(/,/, $l)) { + my($name,$aliases,$addrtype,$length,@addrs) + = gethostbyname($s); + if ($addrtype == AF_INET) { + foreach $addr (@addrs) { + $d .= $addr; + } + } + } + + return $d ne '' ? $d : undef; +} + +sub show_ip($) +{ + my($l) = @_; + + if (length($l) & 3) { + return undef; + } else { + my @h = (); + my $i; + + for ($i = 0; $i < length($l); $i += 4) { + push(@h, inet_ntoa(substr($l, $i, 4))); + } + + return join(',', @h); + } +} + +sub parse_string($) +{ + return $_[0]; +} + +sub show_string($) +{ + my($s) = @_; + my $o, $i, $c; + + $o = "\'"; + for ($i = 0; $i < length($s); $i++) { + $c = substr($s, $i, 1); + if ($c eq "\'" || $c eq '!') { + $o .= "\'\\$c\'"; + } else { + $o .= $c; + } + } + $o .= "\'"; + + return $o; +} + +sub parse_uint32($) +{ + my($s) = @_; + + if ($s =~ /^[0-9]+$/) { + return pack("N", $s); + } else { + return undef; + } +} + +sub show_uint32($) +{ + my($l) = @_; + + if (length($l) == 4) { + return unpack("N", $l); + } else { + return undef; + } +} + +sub parse_generic($) +{ + my($s) = @_; + + if ($s =~ /^[0-9a-f]{1,2}(:[0-9a-f]{1,2})*$/) { + my $h; + my @b = (); + + foreach $h (split(/\:/, $s)) { + push(@b, hex $h); + } + + return pack("C", @b); + } else { + return undef; + } +} + +sub show_generic($) +{ + my($l) = @_; + my $i; + my @h; + + for ($i = 0; $i < length($l); $i++) { + push(@h, sprintf("%02x", unpack("C", substr($l, $i, $1)))); + } + + return join(':', @h); +} + +sub parse_option($$) +{ + my($opt, $arg) = @_; + my $v; + + if (defined($option_format{$opt})) { + $v = $option_format{$opt}[1]($arg); + return $v if (defined($v)); + } + + return parse_generic($arg); +} + +sub show_option($$) +{ + my($opt, $arg) = @_; + my $v; + + if (defined($option_format{$opt})) { + $v = $option_format{$opt}[2]($arg); + return $v if (defined($v)); + } + + return show_generic($arg); +} + +sub option_number($) +{ + my($n) = @_; + + if (defined($option_rnames{$n})) { + return $option_rnames{$n}; + } elsif ($n =~ /^[0-9]+$/ && $n >= 1 && $n <= 254) { + return $n+0; + } else { + return undef; + } +} + +sub read_optsets($) +{ + my($file) = @_; + my $data, $bdata, $adata; + my $patch_start = (stat($file))[7]; + + return undef unless (seek($file, 8, SEEK_SET)); + return undef unless (read($file, $data, 7*4) == 7*4); + + my($magic, $len, $flags, $boff, $blen, $aoff, $alen) + = unpack("VVVVVVV", $data); + return undef if ($magic != 0x2983c8ac); + return undef if ($len < 7*4); + + if ($blen == 0) { + $bdata = ''; + } else { + return undef unless (seek($file, $boff, SEEK_SET)); + return undef unless (read($file, $bdata, $blen) == $blen); + $patch_start = $boff if ($boff < $patch_start); + } + + if ($alen == 0) { + $adata = ''; + } else { + return undef unless (seek($file, $aoff, SEEK_SET)); + return undef unless (read($file, $adata, $alen) == $alen); + $patch_start = $aoff if ($aoff < $patch_start); + } + + return ($patch_start, $bdata, $adata); +} + +sub write_optsets($$@) +{ + my($file, $patch_start, $bdata, $adata) = @_; + my $boff = 0; + my $aoff = 0; + + if (length($bdata) > 0) { + $bdata .= "\xff"; + $boff = $patch_start; + return undef unless (seek($file, $boff, SEEK_SET)); + return undef unless (print $file $bdata); + $patch_start += length($bdata); + } + + if (length($adata) > 0) { + $adata .= "\xff"; + $aoff = $patch_start; + return undef unless (seek($file, $aoff, SEEK_SET)); + return undef unless (print $file $adata); + $patch_start += length($adata); + } + + my $hdr = pack("VVVV", $boff, length($bdata), $aoff, length($adata)); + + return undef unless (seek($file, 8+3*4, SEEK_SET)); + return undef unless (print $file $hdr); + + truncate($file, $patch_start); + return 1; +} + +sub delete_option($$) +{ + my ($num, $block) = @_; + my $o, $l, $c, $x; + + $x = 0; + while ($x < length($block)) { + ($o, $l) = unpack("CC", substr($block, $x, 2)); + if ($o == $num) { + # Delete this option + substr($block, $x, $l+2) = ''; + } elsif ($o == 0) { + # Delete a null option + substr($block, $x, 1) = ''; + } elsif ($o == 255) { + # End marker - truncate block + $block = substr($block, 0, $x); + last; + } else { + # Skip to the next option + $x += $l+2; + } + } + + return $block; +} + +sub add_option($$$) +{ + my ($num, $data, $block) = @_; + + $block = delete_option($num, $block); + + if (length($data) == 0) { + return $block; + } elsif (length($data) > 255) { + die "$0: option $num has too much data (max 255 bytes)\n"; + } else { + return $block . pack("CC", $num, length($data)) . $data; + } +} + +sub list_options($$) +{ + my($pfx, $data) = @_; + my $x, $o, $l; + + while ($x < length($data)) { + ($o, $l) = unpack("CC", substr($data, $x, 2)); + + if ($o == 0) { + $x++; + } elsif ($o == 255) { + last; + } else { + my $odata = substr($data, $x+2, $l); + last if (length($odata) != $l); # Incomplete option + + printf "%s%-20s %s\n", $pfx, + $option_names{$o} || sprintf("%d", $o), + show_option($o, $odata); + + $x += $l+2; + } + } +} + +sub usage() +{ + my $i; + + print STDERR "Usage: $0 options pxelinux.0\n"; + print STDERR "Options:\n"; + print STDERR "--before option value -b Add an option before DHCP data\n"; + print STDERR "--after option value -a Add an option after DHCP data\n"; + print STDERR "--delete option -d Delete an option\n"; + print STDERR "--list -l List set options\n"; + print STDERR "--dry-run -n Don't modify the target file\n"; + print STDERR "--help -h Display this help text\n"; + print STDERR "\n"; + print STDERR "The following DHCP options are currently recognized:\n"; + printf STDERR "%-23s %-3s %s\n", 'Name', 'Num', 'Value Format'; + + foreach $i (sort { $a <=> $b } keys(%option_names)) { + printf STDERR "%-23s %3d %s\n", + $option_names{$i}, $i, $option_format{$i}[0]; + } +} + +%option_rnames = (); +foreach $opt (keys(%option_names)) { + $option_rnames{$option_names{$opt}} = $opt; +} + +%before = (); +%after = (); +@clear = (); +$usage = 0; +$err = 0; +$list = 0; +$no_write = 0; +undef $file; + +while (defined($opt = shift(@ARGV))) { + if ($opt !~ /^-/) { + if (defined($file)) { + $err = $usage = 1; + last; + } + $file = $opt; + } elsif ($opt eq '-b' || $opt eq '--before') { + $oname = shift(@ARGV); + $odata = shift(@ARGV); + + if (!defined($odata)) { + $err = $usage = 1; + last; + } + + $onum = option_number($oname); + if (!defined($onum)) { + print STDERR "$0: unknown option name: $oname\n"; + $err = 1; + next; + } + + $odata = parse_option($onum, $odata); + if (!defined($odata)) { + print STDERR "$0: unable to parse data for option $oname\n"; + $err = 1; + next; + } + + delete $after{$onum}; + $before{$onum} = $odata; + push(@clear, $onum); + } elsif ($opt eq '-a' || $opt eq '--after') { + $oname = shift(@ARGV); + $odata = shift(@ARGV); + + if (!defined($odata)) { + $err = $usage = 1; + last; + } + + $onum = option_number($oname); + if (!defined($onum)) { + print STDERR "$0: unknown option name: $oname\n"; + $err = 1; + next; + } + + $odata = parse_option($onum, $odata); + if (!defined($odata)) { + print STDERR "$0: unable to parse data for option $oname\n"; + $err = 1; + next; + } + + delete $before{$onum}; + $after{$onum} = $odata; + push(@clear, $onum); + } elsif ($opt eq '-d' || $opt eq '--delete') { + $oname = shift(@ARGV); + + if (!defined($oname)) { + $err = $usage = 1; + last; + } + + $onum = option_number($oname); + if (!defined($onum)) { + print STDERR "$0: unknown option name: $oname\n"; + $err = 1; + next; + } + + push(@clear, $onum); + delete $before{$onum}; + delete $after{$onum}; + } elsif ($opt eq '-n' || $opt eq '--no-write' || $opt eq '--dry-run') { + $no_write = 1; + } elsif ($opt eq '-l' || $opt eq '--list') { + $list = 1; + } elsif ($opt eq '-h' || $opt eq '--help') { + $usage = 1; + } else { + print STDERR "Invalid option: $opt\n"; + $err = $usage = 1; + } +} + +if (!defined($file) && !$usage) { + $err = $usage = 1; +} +if ($usage) { + usage(); +} +if ($err || $usage) { + exit($err); +} + +if (!scalar(@clear)) { + $no_write = 1; # No modifications requested +} + +$mode = $no_write ? '<' : '+<'; + +open(FILE, $mode, $file) + or die "$0: cannot open: $file: $!\n"; +($patch_start, @data) = read_optsets(\*FILE); +if (!defined($patch_start)) { + die "$0: $file: patch block not found or file corrupt\n"; +} + +foreach $o (@clear) { + $data[0] = delete_option($o, $data[0]); + $data[1] = delete_option($o, $data[1]); +} +foreach $o (keys(%before)) { + $data[0] = add_option($o, $before{$o}, $data[0]); +} +foreach $o (keys(%after)) { + $data[1] = add_option($o, $after{$o}, $data[1]); +} + +if ($list) { + list_options('-b ', $data[0]); + list_options('-a ', $data[1]); +} + +if (!$no_write) { + if (!write_optsets(\*FILE, $patch_start, @data)) { + die "$0: $file: failed to write options: $!\n"; + } +} + +close(FILE); +exit 0; |