diff options
author | Jonathan Maw <jonathan.maw@codethink.co.uk> | 2012-11-29 11:58:47 +0000 |
---|---|---|
committer | Jonathan Maw <jonathan.maw@codethink.co.uk> | 2012-11-29 11:58:47 +0000 |
commit | 0846de0e1795a30512f5575388d163ea548125de (patch) | |
tree | e1bc03fa83c61165f48ffe3f528ac5496332872a | |
parent | ede8500d7c710e4ddf650f1a1ef3ccf2a89e5313 (diff) | |
parent | 7307d60063ee4303da4de45f9d984fdc8df92146 (diff) | |
download | syslinux-0846de0e1795a30512f5575388d163ea548125de.tar.gz |
Merge the latest changes from upstreambaserock/genivi/morph
145 files changed, 13366 insertions, 2876 deletions
@@ -34,7 +34,7 @@ include $(MAKEDIR)/syslinux.mk MODULES = memdisk/memdisk memdump/memdump.com modules/*.com \ com32/menu/*.c32 com32/modules/*.c32 com32/mboot/*.c32 \ com32/hdt/*.c32 com32/rosh/*.c32 com32/gfxboot/*.c32 \ - com32/sysdump/*.c32 com32/lua/src/*.c32 + com32/sysdump/*.c32 com32/lua/src/*.c32 com32/chain/*.c32 # syslinux.exe is BTARGET so as to not require everyone to have the # mingw suite installed @@ -2,6 +2,28 @@ Starting with 1.47, changes marked with SYSLINUX, PXELINUX, ISOLINUX or EXTLINUX apply to that specific program only; other changes apply to all derivatives. +Changes in 4.06: + * Support for NTFS, by Paulo Alcantara. + * EXTLINUX: more robust device detection, allow user to override. + * kontron_wdt.c32: Add a new module to enable the hardware + watchdog of some Kontron boards. It allows enabling the watchdog + and then booting a given image. + * HDT updated, and now can display images regarding some detection + steps. Add postexec command to run a particular entry after + HDT's execution, add silent option and various fixes. + * ifcpu.c32: Detect hypervisor presence. + * lua.c32: Add dhcp support and support for native Syslinux + functions syslinux_config(), syslinux_ipappend_strings() and + syslinux_reboot(). + * isohybrid: Workaround for various EFI systems. + * pxechn.c32, a PXE NBP chainloader. More versatile alternative + to pxechain.com and resolves the PXELINUX -> WDS issue with + Microsoft Windows Server 2008R2 (Gene Cumm). + * btrfs: Fix booting off of a subvolume. + * com32: Add device tree support. + * SYSLINUX: Fix relative paths for VFAT. The CONFIG and APPEND + directives now support entirely relative paths. + Changes in 4.05: * HDT updated, and now supports uploading data to a TFTP server. @@ -20,6 +20,13 @@ SYSLINUX now builds in a Linux environment, using nasm. You need nasm version 2.03 or later (2.07 or later recommended) to build SYSLINUX from source. See http://www.nasm.us/ for information about nasm. +"utils/isohybrid" needs the UUID library and following header file, + + /usr/include/uuid/uuid.h + +You can get them from the "uuid-dev" package on Debian based systems +or from the "libuuid-devel" package on RPM based distributions. + There is now a mailing list for SYSLINUX. See the end of syslinux.txt for details. diff --git a/com32/Makefile b/com32/Makefile index da632a17..b59fd3f9 100644 --- a/com32/Makefile +++ b/com32/Makefile @@ -1,5 +1,5 @@ SUBDIRS = libupload tools lib gpllib libutil modules mboot menu samples rosh cmenu \ - hdt gfxboot sysdump lua/src + hdt gfxboot sysdump lua/src chain all tidy dist clean spotless install: set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done diff --git a/com32/chain/Makefile b/com32/chain/Makefile new file mode 100644 index 00000000..9d398a85 --- /dev/null +++ b/com32/chain/Makefile @@ -0,0 +1,42 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2010 H. Peter Anvin - All Rights Reserved +## Copyright 2010 Michal Soltys +## +## 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., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + + +topdir = ../.. +MAKEDIR = $(topdir)/mk +include $(MAKEDIR)/com32.mk + +OBJS = chain.o partiter.o utility.o options.o mangle.o + +all: chain.c32 + +chain.elf: $(OBJS) $(LIBS) $(C_LIBS) + $(LD) $(LDFLAGS) -o $@ $^ + +%.o: %.c + $(CC) $(MAKEDEPS) $(CFLAGS) $(CHAINEXTOPT) -c -o $@ $< + +tidy dist: + rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp + +clean: tidy + rm -f *.lnx + +spotless: clean + rm -f *.lss *.c32 *.com + rm -f *~ \#* + +install: + + +-include .*.d diff --git a/com32/chain/chain.c b/com32/chain/chain.c new file mode 100644 index 00000000..30153c4d --- /dev/null +++ b/com32/chain/chain.c @@ -0,0 +1,658 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin + * Copyright 2010 Shao Miller + * Copyright 2010 Michal Soltys + * + * 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., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * Please see doc/chain.txt for the detailed documentation. + */ + +#include <com32.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <console.h> +#include <consoles.h> +#include <minmax.h> +#include <stdbool.h> +#include <dprintf.h> +#include <errno.h> +#include <unistd.h> +#include <syslinux/loadfile.h> +#include <syslinux/bootrm.h> +#include <syslinux/config.h> +#include <syslinux/disk.h> +#include <syslinux/video.h> +#include "common.h" +#include "chain.h" +#include "utility.h" +#include "options.h" +#include "partiter.h" +#include "mangle.h" + +static int fixed_cnt = 128; /* see comments in main() */ + +static int overlap(const struct data_area *a, const struct data_area *b) +{ + return + a->base + a->size > b->base && + b->base + b->size > a->base; +} + +static int is_phys(uint8_t sdifs) +{ + return + sdifs == SYSLINUX_FS_SYSLINUX || + sdifs == SYSLINUX_FS_EXTLINUX || + sdifs == SYSLINUX_FS_ISOLINUX; +} + +/* + * Search for a specific drive, based on the MBR signature. + * Return drive and iterator at 0th position. + */ +static int find_by_sig(uint32_t mbr_sig, + struct part_iter **_boot_part) +{ + struct part_iter *boot_part = NULL; + struct disk_info diskinfo; + int drive; + + for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) { + if (disk_get_params(drive, &diskinfo)) + continue; /* Drive doesn't exist */ + if (!(boot_part = pi_begin(&diskinfo, 0))) + continue; + /* Check for a MBR disk */ + if (boot_part->type != typedos) { + pi_del(&boot_part); + continue; + } + if (boot_part->sub.dos.disk_sig == mbr_sig) { + goto ok; + } + } + drive = -1; +ok: + *_boot_part = boot_part; + return drive; +} + +/* + * Search for a specific drive/partition, based on the GPT GUID. + * Return drive and iterator at proper position. + */ +static int find_by_guid(const struct guid *gpt_guid, + struct part_iter **_boot_part) +{ + struct part_iter *boot_part = NULL; + struct disk_info diskinfo; + int drive; + + for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) { + if (disk_get_params(drive, &diskinfo)) + continue; /* Drive doesn't exist */ + if (!(boot_part = pi_begin(&diskinfo, 0))) + continue; + /* Check for a GPT disk */ + if (boot_part->type != typegpt) { + pi_del(&boot_part); + continue; + } + /* Check for a matching GPT disk guid */ + if (!memcmp(&boot_part->sub.gpt.disk_guid, gpt_guid, sizeof(*gpt_guid))) { + goto ok; + } + /* disk guid doesn't match, maybe partition guid will */ + while (!pi_next(&boot_part)) { + if (!memcmp(&boot_part->sub.gpt.part_guid, gpt_guid, sizeof(*gpt_guid))) + goto ok; + } + } + drive = -1; +ok: + *_boot_part = boot_part; + return drive; +} + +/* + * Search for a specific drive/partition, based on the GPT label. + * Return drive and iterator at proper position. + */ +static int find_by_label(const char *label, struct part_iter **_boot_part) +{ + struct part_iter *boot_part = NULL; + struct disk_info diskinfo; + int drive; + + for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) { + if (disk_get_params(drive, &diskinfo)) + continue; /* Drive doesn't exist */ + if (!(boot_part = pi_begin(&diskinfo, 0))) + continue; + /* Check for a GPT disk */ + if (!(boot_part->type == typegpt)) { + pi_del(&boot_part); + continue; + } + /* Check for a matching partition */ + while (!pi_next(&boot_part)) { + if (!strcmp(label, boot_part->sub.gpt.part_label)) + goto ok; + } + } + drive = -1; +ok: + *_boot_part = boot_part; + return drive; +} + +static void do_boot(struct data_area *data, int ndata) +{ + uint16_t *const bios_fbm = (uint16_t *) 0x413; + addr_t dosmem = (addr_t)(*bios_fbm << 10); /* Technically a low bound */ + struct syslinux_memmap *mmap; + struct syslinux_movelist *mlist = NULL; + addr_t endimage; + uint8_t driveno = opt.regs.edx.b[0]; + uint8_t swapdrive = driveno & 0x80; + int i; + + mmap = syslinux_memory_map(); + + if (!mmap) { + error("Cannot read system memory map\n"); + return; + } + + endimage = 0; + for (i = 0; i < ndata; i++) { + if (data[i].base + data[i].size > endimage) + endimage = data[i].base + data[i].size; + } + if (endimage > dosmem) + goto too_big; + + for (i = 0; i < ndata; i++) { + if (syslinux_add_movelist(&mlist, data[i].base, + (addr_t) data[i].data, data[i].size)) + goto enomem; + } + + if (opt.swap && driveno != swapdrive) { + static const uint8_t swapstub_master[] = { + /* The actual swap code */ + 0x53, /* 00: push bx */ + 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */ + 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */ + 0x5b, /* 08: pop bx */ + 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */ + 0x90, 0x90, /* 0E: nop; nop */ + /* Code to install this in the right location */ + /* Entry with DS = CS; ES = SI = 0; CX = 256 */ + 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */ + 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */ + 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */ + 0x4f, /* 1F: dec di */ + 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */ + 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */ + 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */ + 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */ + 0x8e, 0xc7, /* 32: mov es,di */ + 0x31, 0xff, /* 34: xor di,di */ + 0xf3, 0x66, 0xa5, /* 36: rep movsd */ + 0xbe, 0, 0, /* 39: mov si,0 */ + 0xbf, 0, 0, /* 3C: mov di,0 */ + 0x8e, 0xde, /* 3F: mov ds,si */ + 0x8e, 0xc7, /* 41: mov es,di */ + 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */ + 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */ + 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */ + 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */ + /* pad out to segment boundary */ + 0x90, 0x90, /* 5A: ... */ + 0x90, 0x90, 0x90, 0x90, /* 5C: ... */ + }; + static uint8_t swapstub[1024]; + uint8_t *p; + + /* Note: we can't rely on either INT 13h nor the dosmem + vector to be correct at this stage, so we have to use an + installer stub to put things in the right place. + Round the installer location to a 1K boundary so the only + possible overlap is the identity mapping. */ + endimage = (endimage + 1023u) & ~1023u; + + /* Create swap stub */ + memcpy(swapstub, swapstub_master, sizeof swapstub_master); + *(uint16_t *) & swapstub[0x3a] = opt.regs.ds; + *(uint16_t *) & swapstub[0x3d] = opt.regs.es; + *(uint32_t *) & swapstub[0x45] = opt.regs.ecx.l; + *(uint32_t *) & swapstub[0x4b] = opt.regs.esi.l; + *(uint32_t *) & swapstub[0x51] = opt.regs.edi.l; + *(uint16_t *) & swapstub[0x56] = opt.regs.ip; + *(uint16_t *) & swapstub[0x58] = opt.regs.cs; + p = &swapstub[sizeof swapstub_master]; + + /* Mapping table; start out with identity mapping everything */ + for (i = 0; i < 256; i++) + p[i] = (uint8_t)i; + + /* And the actual swap */ + p[driveno] = swapdrive; + p[swapdrive] = driveno; + + /* Adjust registers */ + opt.regs.ds = opt.regs.cs = (uint16_t)(endimage >> 4); + opt.regs.esi.l = opt.regs.es = 0; + opt.regs.ecx.l = sizeof swapstub >> 2; + opt.regs.ip = 0x10; /* Installer offset */ + opt.regs.ebx.b[0] = opt.regs.edx.b[0] = swapdrive; + + if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub, + sizeof swapstub)) + goto enomem; + + endimage += sizeof swapstub; + } + + /* Tell the shuffler not to muck with this area... */ + syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED); + + /* Force text mode */ + syslinux_force_text_mode(); + + fputs("Booting...\n", stdout); + syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, &opt.regs); + error("Chainboot failed!\n"); + return; + +too_big: + error("Loader file too large\n"); + return; + +enomem: + error("Out of memory\n"); + return; +} + +int find_dp(struct part_iter **_iter) +{ + struct part_iter *iter = NULL; + struct disk_info diskinfo; + struct guid gpt_guid; + uint64_t fs_lba; + int drive, hd, partition; + const union syslinux_derivative_info *sdi; + + sdi = syslinux_derivative_info(); + + if (!strncmp(opt.drivename, "mbr", 3)) { + if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) { + error("Unable to find requested MBR signature.\n"); + goto bail; + } + } else if (!strncmp(opt.drivename, "guid", 4)) { + if (str_to_guid(opt.drivename + 5, &gpt_guid)) + goto bail; + if (find_by_guid(&gpt_guid, &iter) < 0) { + error("Unable to find requested GPT disk or partition by guid.\n"); + goto bail; + } + } else if (!strncmp(opt.drivename, "label", 5)) { + if (!opt.drivename[6]) { + error("No label specified.\n"); + goto bail; + } + if (find_by_label(opt.drivename + 6, &iter) < 0) { + error("Unable to find requested GPT partition by label.\n"); + goto bail; + } + } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') && + opt.drivename[1] == 'd') { + hd = opt.drivename[0] == 'h' ? 0x80 : 0; + opt.drivename += 2; + drive = hd | strtol(opt.drivename, NULL, 0); + + if (disk_get_params(drive, &diskinfo)) + goto bail; + /* this will start iteration over FDD, possibly raw */ + if (!(iter = pi_begin(&diskinfo, 0))) + goto bail; + + } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) { + if (!is_phys(sdi->c.filesystem)) { + error("When syslinux is not booted from physical disk (or its emulation),\n" + "'boot' and 'fs' are meaningless.\n"); + goto bail; + } + /* offsets match, but in case it changes in the future */ + if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) { + drive = sdi->iso.drive_number; + fs_lba = *sdi->iso.partoffset; + } else { + drive = sdi->disk.drive_number; + fs_lba = *sdi->disk.partoffset; + } + if (disk_get_params(drive, &diskinfo)) + goto bail; + /* this will start iteration over disk emulation, possibly raw */ + if (!(iter = pi_begin(&diskinfo, 0))) + goto bail; + + /* 'fs' => we should lookup the syslinux partition number and use it */ + if (!strcmp(opt.drivename, "fs")) { + while (!pi_next(&iter)) { + if (iter->start_lba == fs_lba) + break; + } + /* broken part structure or other problems */ + if (iter->status) { + error("Can't find myself on the drive I booted from.\n"); + goto bail; + } + } + } else { + error("Unparsable drive specification.\n"); + goto bail; + } + /* main options done - only thing left is explicit partition specification, + * if we're still at the disk stage with the iterator AND user supplied + * partition number (including disk pseudo-partition). + */ + if (!iter->index && opt.partition) { + partition = strtol(opt.partition, NULL, 0); + /* search for matching part#, including disk */ + do { + if (iter->index == partition) + break; + } while (!pi_next(&iter)); + if (iter->status) { + error("Requested disk / partition combination not found.\n"); + goto bail; + } + } + + if (!(iter->di.disk & 0x80) && iter->index) { + error("WARNING: Partitions on floppy devices may not work.\n"); + } + + *_iter = iter; + + return 0; + +bail: + pi_del(&iter); + return -1; +} + +static int setup_handover(const struct part_iter *iter, + struct data_area *data) +{ + struct disk_dos_part_entry *ha; + uint32_t synth_size; + uint32_t *plen; + + if (!iter->index) { /* implies typeraw or non-iterated */ + uint32_t len; + /* RAW handover protocol */ + synth_size = sizeof(struct disk_dos_part_entry); + ha = malloc(synth_size); + if (!ha) { + error("Could not build RAW hand-over record!\n"); + goto bail; + } + len = ~0u; + if (iter->length < len) + len = (uint32_t)iter->length; + lba2chs(&ha->start, &iter->di, 0, l2c_cadd); + lba2chs(&ha->end, &iter->di, len - 1, l2c_cadd); + ha->active_flag = 0x80; + ha->ostype = 0xDA; /* "Non-FS Data", anything is good here though ... */ + ha->start_lba = 0; + ha->length = len; + } else if (iter->type == typegpt) { + /* GPT handover protocol */ + synth_size = sizeof(struct disk_dos_part_entry) + + sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size; + ha = malloc(synth_size); + if (!ha) { + error("Could not build GPT hand-over record!\n"); + goto bail; + } + lba2chs(&ha->start, &iter->di, iter->start_lba, l2c_cadd); + lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, l2c_cadd); + ha->active_flag = 0x80; + ha->ostype = 0xED; + /* All bits set by default */ + ha->start_lba = ~0u; + ha->length = ~0u; + /* If these fit the precision, pass them on */ + if (iter->start_lba < ha->start_lba) + ha->start_lba = (uint32_t)iter->start_lba; + if (iter->length < ha->length) + ha->length = (uint32_t)iter->length; + /* Next comes the GPT partition record length */ + plen = (uint32_t *) (ha + 1); + plen[0] = (uint32_t)iter->sub.gpt.pe_size; + /* Next comes the GPT partition record copy */ + memcpy(plen + 1, iter->record, plen[0]); +#ifdef DEBUG + dprintf("GPT handover:\n"); + disk_dos_part_dump(ha); + disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1)); +#endif + } else if (iter->type == typedos) { + /* MBR handover protocol */ + synth_size = sizeof(struct disk_dos_part_entry); + ha = malloc(synth_size); + if (!ha) { + error("Could not build MBR hand-over record!\n"); + goto bail; + } + memcpy(ha, iter->record, synth_size); + /* make sure these match bios imaginations and are ebr agnostic */ + lba2chs(&ha->start, &iter->di, iter->start_lba, l2c_cadd); + lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, l2c_cadd); + ha->start_lba = (uint32_t)iter->start_lba; + ha->length = (uint32_t)iter->length; + +#ifdef DEBUG + dprintf("MBR handover:\n"); + disk_dos_part_dump(ha); +#endif + } else { + /* shouldn't ever happen */ + goto bail; + } + + data->base = 0x7be; + data->size = synth_size; + data->data = (void *)ha; + + return 0; +bail: + return -1; +} + +int main(int argc, char *argv[]) +{ + struct part_iter *iter = NULL; + void *sbck = NULL; + struct data_area fdat, hdat, sdat, data[3]; + int ndata = 0; + + console_ansi_raw(); + + memset(&fdat, 0, sizeof(fdat)); + memset(&hdat, 0, sizeof(hdat)); + memset(&sdat, 0, sizeof(sdat)); + + opt_set_defs(); + if (opt_parse_args(argc, argv)) + goto bail; + +#if 0 + /* Get max fixed disk number */ + fixed_cnt = *(uint8_t *)(0x475); + + /* + * hmm, looks like we can't do that - + * some bioses/vms just set it to 1 + * and go on living happily + * any better options than hardcoded 0x80 - 0xFF ? + */ +#endif + + /* Get disk/part iterator matching user supplied options */ + if (find_dp(&iter)) + goto bail; + + /* Perform initial partition entry mangling */ + if (manglepe_fixchs(iter)) + goto bail; + if (manglepe_hide(iter)) + goto bail; + + /* Load the boot file */ + if (opt.file) { + fdat.base = (opt.fseg << 4) + opt.foff; + + if (loadfile(opt.file, &fdat.data, &fdat.size)) { + error("Couldn't read the boot file.\n"); + goto bail; + } + if (fdat.base + fdat.size - 1 > ADDRMAX) { + error("The boot file is too big to load at this address.\n"); + goto bail; + } + } + + /* Load the sector */ + if (opt.sect) { + sdat.base = (opt.sseg << 4) + opt.soff; + sdat.size = iter->di.bps; + + if (sdat.base + sdat.size - 1 > ADDRMAX) { + error("The sector cannot be loaded at such high address.\n"); + goto bail; + } + if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) { + error("Couldn't read the sector.\n"); + goto bail; + } + if (opt.save) { + if (!(sbck = malloc(sdat.size))) { + error("Couldn't allocate cmp-buf for option 'save'.\n"); + goto bail; + } + memcpy(sbck, sdat.data, sdat.size); + } + if (opt.file && opt.maps && overlap(&fdat, &sdat)) { + error("WARNING: The sector won't be mmapped, as it would conflict with the boot file.\n"); + opt.maps = false; + } + } + + /* Prep the handover */ + if (opt.hand) { + if (setup_handover(iter, &hdat)) + goto bail; + /* Verify possible conflicts */ + if ( ( opt.file && overlap(&fdat, &hdat)) || + ( opt.maps && overlap(&sdat, &hdat)) ) { + error("WARNING: Handover area won't be prepared,\n" + "as it would conflict with the boot file and/or the sector.\n"); + opt.hand = false; + } + } + + /* Adjust registers */ + + mangler_init(iter); + mangler_handover(iter, &hdat); + mangler_grldr(iter); + + /* Patching functions */ + + if (manglef_isolinux(&fdat)) + goto bail; + + if (manglef_grub(iter, &fdat)) + goto bail; +#if 0 + if (manglef_drmk(&fdat)) + goto bail; +#endif + if (manglef_bpb(iter, &fdat)) + goto bail; + + if (mangles_bpb(iter, &sdat)) + goto bail; + + if (mangles_save(iter, &sdat, sbck)) + goto bail; + + if (manglesf_bss(&sdat, &fdat)) + goto bail; + + /* This *must* be after BPB saving or copying */ + if (mangles_cmldr(&sdat)) + goto bail; + + /* + * Prepare boot-time mmap data. We should to it here, as manglers could + * potentially alter some of the data. + */ + + if (opt.file) + memcpy(data + ndata++, &fdat, sizeof(fdat)); + if (opt.maps) + memcpy(data + ndata++, &sdat, sizeof(sdat)); + if (opt.hand) + memcpy(data + ndata++, &hdat, sizeof(hdat)); + +#ifdef DEBUG + printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %"PRIu64", %u\n" + "iter->di C, H, S: %u, %u, %u\n", + iter->di.disk, iter->di.bps, + iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt, + iter->di.cyl, iter->di.head, iter->di.spt); + printf("iter idx: %d\n", iter->index); + printf("iter lba: %"PRIu64"\n", iter->start_lba); + if (opt.hand) + printf("hand lba: %u\n", + ((struct disk_dos_part_entry *)hdat.data)->start_lba); +#endif + + if (opt.warn) { + puts("Press any key to continue booting..."); + wait_key(); + } + + if (ndata && !opt.brkchain) /* boot only if we actually chainload */ + do_boot(data, ndata); + else + error("Service-only run completed, exiting.\n"); +bail: + pi_del(&iter); + /* Free allocated areas */ + free(fdat.data); + free(sdat.data); + free(hdat.data); + free(sbck); + return 255; +} + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/chain.h b/com32/chain/chain.h new file mode 100644 index 00000000..fc481bc6 --- /dev/null +++ b/com32/chain/chain.h @@ -0,0 +1,14 @@ +#ifndef _COM32_CHAIN_CHAIN_H +#define _COM32_CHAIN_CHAIN_H + +#include <syslinux/movebits.h> + +struct data_area { + void *data; + addr_t base; + addr_t size; +}; + +#endif + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/common.h b/com32/chain/common.h new file mode 100644 index 00000000..b170a732 --- /dev/null +++ b/com32/chain/common.h @@ -0,0 +1,9 @@ +#ifndef _COM32_CHAIN_COMMON_H +#define _COM32_CHAIN_COMMON_H + +#define ADDRMAX 0x9EFFFu +#define ADDRMIN 0x500u + +#endif + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/mangle.c b/com32/chain/mangle.c new file mode 100644 index 00000000..8358106e --- /dev/null +++ b/com32/chain/mangle.c @@ -0,0 +1,618 @@ +#include <com32.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <dprintf.h> +#include <syslinux/config.h> +#include "common.h" +#include "chain.h" +#include "options.h" +#include "utility.h" +#include "partiter.h" +#include "mangle.h" + +static const char cmldr_signature[8] = "cmdcons"; + +/* Create boot info table: needed when you want to chainload + * another version of ISOLINUX (or another bootlaoder that needs + * the -boot-info-table switch of mkisofs) + * (will only work when run from ISOLINUX) + */ +int manglef_isolinux(struct data_area *data) +{ + const union syslinux_derivative_info *sdi; + unsigned char *isolinux_bin; + uint32_t *checksum, *chkhead, *chktail; + uint32_t file_lba = 0; + + if (!(opt.file && opt.isolinux)) + return 0; + + sdi = syslinux_derivative_info(); + + if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) { + error ("The isolinux= option is only valid when run from ISOLINUX.\n"); + goto bail; + } + + /* Boot info table info (integers in little endian format) + + Offset Name Size Meaning + 8 bi_pvd 4 bytes LBA of primary volume descriptor + 12 bi_file 4 bytes LBA of boot file + 16 bi_length 4 bytes Boot file length in bytes + 20 bi_csum 4 bytes 32-bit checksum + 24 bi_reserved 40 bytes Reserved + + The 32-bit checksum is the sum of all the 32-bit words in the + boot file starting at byte offset 64. All linear block + addresses (LBAs) are given in CD sectors (normally 2048 bytes). + + LBA of primary volume descriptor should already be set to 16. + */ + + isolinux_bin = (unsigned char *)data->data; + + /* Get LBA address of bootfile */ + file_lba = get_file_lba(opt.file); + + if (file_lba == 0) { + error("Failed to find LBA offset of the boot file\n"); + goto bail; + } + /* Set it */ + *((uint32_t *) & isolinux_bin[12]) = file_lba; + + /* Set boot file length */ + *((uint32_t *) & isolinux_bin[16]) = data->size; + + /* Calculate checksum */ + checksum = (uint32_t *) & isolinux_bin[20]; + chkhead = (uint32_t *) & isolinux_bin[64]; + chktail = (uint32_t *) & isolinux_bin[data->size & ~3u]; + *checksum = 0; + while (chkhead < chktail) + *checksum += *chkhead++; + + /* + * Deal with possible fractional dword at the end; + * this *should* never happen... + */ + if (data->size & 3) { + uint32_t xword = 0; + memcpy(&xword, chkhead, data->size & 3); + *checksum += xword; + } + return 0; +bail: + return -1; +} + +/* + * Legacy grub's stage2 chainloading + */ +int manglef_grub(const struct part_iter *iter, struct data_area *data) +{ + /* Layout of stage2 file (from byte 0x0 to 0x270) */ + struct grub_stage2_patch_area { + /* 0x0 to 0x205 */ + char unknown[0x206]; + /* 0x206: compatibility version number major */ + uint8_t compat_version_major; + /* 0x207: compatibility version number minor */ + uint8_t compat_version_minor; + + /* 0x208: install_partition variable */ + struct { + /* 0x208: sub-partition in sub-partition part2 */ + uint8_t part3; + /* 0x209: sub-partition in top-level partition */ + uint8_t part2; + /* 0x20a: top-level partiton number */ + uint8_t part1; + /* 0x20b: BIOS drive number (must be 0) */ + uint8_t drive; + } __attribute__ ((packed)) install_partition; + + /* 0x20c: deprecated (historical reason only) */ + uint32_t saved_entryno; + /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */ + uint8_t stage2_id; + /* 0x211: force LBA */ + uint8_t force_lba; + /* 0x212: version string (will probably be 0.97) */ + char version_string[5]; + /* 0x217: config filename */ + char config_file[89]; + /* 0x270: start of code (after jump from 0x200) */ + char codestart[1]; + } __attribute__ ((packed)) *stage2; + + if (!(opt.file && opt.grub)) + return 0; + + if (data->size < sizeof(struct grub_stage2_patch_area)) { + error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.\n"); + goto bail; + } + stage2 = data->data; + + /* + * Check the compatibility version number to see if we loaded a real + * stage2 file or a stage2 file that we support. + */ + if (stage2->compat_version_major != 3 + || stage2->compat_version_minor != 2) { + error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.\n"); + goto bail; + } + + /* + * GRUB Legacy wants the partition number in the install_partition + * variable, located at offset 0x208 of stage2. + * When GRUB Legacy is loaded, it is located at memory address 0x8208. + * + * It looks very similar to the "boot information format" of the + * Multiboot specification: + * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format + * + * 0x208 = part3: sub-partition in sub-partition part2 + * 0x209 = part2: sub-partition in top-level partition + * 0x20a = part1: top-level partition number + * 0x20b = drive: BIOS drive number (must be 0) + * + * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at + * another location. + * + * Partition numbers always start from zero. + * Unused partition bytes must be set to 0xFF. + * + * We only care about top-level partition, so we only need to change + * "part1" to the appropriate value: + * -1: whole drive (default) (-1 = 0xFF) + * 0-3: primary partitions + * 4-*: logical partitions + */ + stage2->install_partition.part1 = (uint8_t)(iter->index - 1); + + /* + * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the + * config filename. The filename passed via grubcfg= will overwrite + * the default config filename "/boot/grub/menu.lst". + */ + if (opt.grubcfg) { + if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) { + error ("The config filename length can't exceed 88 characters.\n"); + goto bail; + } + + strcpy((char *)stage2->config_file, opt.grubcfg); + } + + return 0; +bail: + return -1; +} +#if 0 +/* + * Dell's DRMK chainloading. + */ +int manglef_drmk(struct data_area *data) +{ + /* + * DRMK entry is different than MS-DOS/PC-DOS + * A new size, aligned to 16 bytes to ease use of ds:[bp+28]. + * We only really need 4 new, usable bytes at the end. + */ + + if (!(opt.file && opt.drmk)) + return 0; + + uint32_t tsize = (data->size + 19) & 0xfffffff0; + const union syslinux_derivative_info *sdi; + uint64_t fs_lba; + + sdi = syslinux_derivative_info(); + /* We should lookup the Syslinux partition offset and use it */ + fs_lba = *sdi->disk.partoffset; + + /* + * fs_lba should be verified against the disk as some DRMK + * variants will check and fail if it does not match + */ + dprintf(" fs_lba offset is %d\n", fs_lba); + /* DRMK only uses a DWORD */ + if (fs_lba > 0xffffffff) { + error("LBA very large; Only using lower 32 bits; DRMK will probably fail\n"); + } + opt.regs.ss = opt.regs.fs = opt.regs.gs = 0; /* Used before initialized */ + if (!realloc(data->data, tsize)) { + error("Failed to realloc for DRMK.\n"); + goto bail; + } + data->size = tsize; + /* ds:bp is assumed by DRMK to be the boot sector */ + /* offset 28 is the FAT HiddenSectors value */ + opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2)); + /* "Patch" into tail of the new space */ + *(uint32_t *)((char*)data->data + tsize - 4) = (uint32_t)fs_lba; + + return 0; +bail: + return -1; +} +#endif +/* Adjust BPB common function */ +static int mangle_bpb(const struct part_iter *iter, struct data_area *data, const char *tag) +{ + unsigned int off; + int type = bpb_detect(data->data, tag); + + /* BPB: hidden sectors 32bit*/ + if (type >= bpbV34) { + if (iter->start_lba < ~0u) + *(uint32_t *) ((char *)data->data + 0x1c) = (uint32_t)iter->start_lba; + else + /* won't really help much, but ... */ + *(uint32_t *) ((char *)data->data + 0x1c) = ~0u; + } + /* BPB: hidden sectors 16bit*/ + if (bpbV30 <= type && type <= bpbV32) { + if (iter->start_lba < 0xFFFF) + *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)iter->start_lba; + else + /* won't really help much, but ... */ + *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u; + } + /* BPB: legacy geometry */ + if (type >= bpbV30) { + if (iter->di.cbios) + *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.spt); + else { + if (iter->di.disk & 0x80) + *(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F; + else + *(uint32_t *)((char *)data->data + 0x18) = 0x00020012; + } + } + /* BPB: drive */ + if (drvoff_detect(type, &off)) { + *(uint8_t *)((char *)data->data + off) = (uint8_t) + (opt.swap ? iter->di.disk & 0x80 : iter->di.disk); + } + + return 0; +} + +/* + * Adjust BPB of a BPB-compatible file + */ +int manglef_bpb(const struct part_iter *iter, struct data_area *data) +{ + if (!(opt.file && opt.filebpb)) + return 0; + + return mangle_bpb(iter, data, "file"); +} + +/* + * Adjust BPB of a sector + */ +int mangles_bpb(const struct part_iter *iter, struct data_area *data) +{ + if (!(opt.sect && opt.setbpb)) + return 0; + + return mangle_bpb(iter, data, "sect"); +} + +/* + * This function performs full BPB patching, analogously to syslinux's + * native BSS. + */ +int manglesf_bss(struct data_area *sec, struct data_area *fil) +{ + int type1, type2; + unsigned int cnt = 0; + + if (!(opt.sect && opt.file && opt.bss)) + return 0; + + type1 = bpb_detect(fil->data, "bss/file"); + type2 = bpb_detect(sec->data, "bss/sect"); + + if (!type1 || !type2) { + error("Couldn't determine the BPB type for option 'bss'.\n"); + goto bail; + } + if (type1 != type2) { + error("Option 'bss' can't be used,\n" + "when a sector and a file have incompatible BPBs.\n"); + goto bail; + } + + /* Copy common 2.0 data */ + memcpy((char *)fil->data + 0x0B, (char *)sec->data + 0x0B, 0x0D); + + /* Copy 3.0+ data */ + if (type1 <= bpbV30) { + cnt = 0x06; + } else if (type1 <= bpbV32) { + cnt = 0x08; + } else if (type1 <= bpbV34) { + cnt = 0x0C; + } else if (type1 <= bpbV40) { + cnt = 0x2E; + } else if (type1 <= bpbVNT) { + cnt = 0x3C; + } else if (type1 <= bpbV70) { + cnt = 0x42; + } + memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt); + + return 0; +bail: + return -1; +} + +/* + * Save sector. + */ +int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org) +{ + if (!(opt.sect && opt.save)) + return 0; + + if (memcmp(org, data->data, data->size)) { + if (disk_write_sectors(&iter->di, iter->start_lba, data->data, 1)) { + error("Cannot write the updated sector.\n"); + goto bail; + } + /* function can be called again */ + memcpy(org, data->data, data->size); + } + + return 0; +bail: + return -1; +} + +/* + * To boot the Recovery Console of Windows NT/2K/XP we need to write + * the string "cmdcons\0" to memory location 0000:7C03. + * Memory location 0000:7C00 contains the bootsector of the partition. + */ +int mangles_cmldr(struct data_area *data) +{ + if (!(opt.sect && opt.cmldr)) + return 0; + + memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature)); + return 0; +} + +/* Set common registers */ +int mangler_init(const struct part_iter *iter) +{ + /* Set initial registry values */ + if (opt.file) { + opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.fseg; + opt.regs.ip = (uint16_t)opt.fip; + } else { + opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg; + opt.regs.ip = (uint16_t)opt.sip; + } + + if (opt.regs.ip == 0x7C00 && !opt.regs.cs) + opt.regs.esp.l = 0x7C00; + + /* DOS kernels want the drive number in BL instead of DL. Indulge them. */ + opt.regs.ebx.b[0] = opt.regs.edx.b[0] = (uint8_t)iter->di.disk; + + return 0; +} + +/* ds:si & ds:bp */ +int mangler_handover(const struct part_iter *iter, const struct data_area *data) +{ + if (opt.file && opt.maps && !opt.hptr) { + opt.regs.esi.l = opt.regs.ebp.l = opt.soff; + opt.regs.ds = (uint16_t)opt.sseg; + opt.regs.eax.l = 0; + } else if (opt.hand) { + /* base is really 0x7be */ + opt.regs.esi.l = opt.regs.ebp.l = data->base; + opt.regs.ds = 0; + if (iter->index && iter->type == typegpt) /* must be iterated and GPT */ + opt.regs.eax.l = 0x54504721; /* '!GPT' */ + else + opt.regs.eax.l = 0; + } + + return 0; +} + +/* + * GRLDR of GRUB4DOS wants the partition number in DH: + * -1: whole drive (default) + * 0-3: primary partitions + * 4-*: logical partitions + */ +int mangler_grldr(const struct part_iter *iter) +{ + if (opt.grldr) + opt.regs.edx.b[1] = (uint8_t)(iter->index - 1); + + return 0; +} + +/* + * try to copy values from temporary iterator, if positions match + */ +static void push_embr(struct part_iter *diter, struct part_iter *siter) +{ + if (diter->sub.dos.cebr_lba == siter->sub.dos.cebr_lba && + diter->di.disk == siter->di.disk) { + memcpy(diter->data, siter->data, sizeof(struct disk_dos_mbr)); + } +} + +static int mpe_sethide(struct part_iter *iter, struct part_iter *miter) +{ + struct disk_dos_part_entry *dp; + static const uint16_t mask = + (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | + (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e); + uint8_t t; + + dp = (struct disk_dos_part_entry *)iter->record; + t = dp->ostype; + + if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) { + /* It's a hideable partition type */ + if (miter->index == iter->index || opt.hide & 4) + t &= (uint8_t)(~0x10u); /* unhide */ + else + t |= 0x10u; /* hide */ + } + if (dp->ostype != t) { + dp->ostype = t; + return -1; + } + return 0; +} + +/* + * miter - iterator we match against + * hide bits meaning: + * ..| - enable (1) / disable (0) + * .|. - all (1) / pri (0) + * |.. - unhide (1) / hide (0) + */ +int manglepe_hide(struct part_iter *miter) +{ + int wb = 0, werr = 0; + struct part_iter *iter = NULL; + struct disk_dos_part_entry *dp; + int ridx; + + if (!opt.hide) + return 0; + + if (miter->type != typedos) { + error("Options '*hide*' is meaningful only for legacy partition scheme.\n"); + return -1; + } + + if (miter->index < 1) + error("WARNING: It's impossible to unhide a disk.\n"); + + if (miter->index > 4 && !(opt.hide & 2)) + error("WARNING: your partition is beyond mbr, so it can't be unhidden without '*hideall'.\n"); + + if (!(iter = pi_begin(&miter->di, 1))) /* turn stepall on */ + return -1; + + while (!pi_next(&iter) && !werr) { + ridx = iter->rawindex; + if (!(opt.hide & 2) && ridx > 4) + break; /* skip when we're constrained to pri only */ + + dp = (struct disk_dos_part_entry *)iter->record; + if (dp->ostype) + wb |= mpe_sethide(iter, miter); + + if (ridx >= 4 && wb && !werr) { + push_embr(miter, iter); + werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1); + wb = 0; + } + } + + if (iter->status > PI_DONE) + goto bail; + + /* last write */ + if (wb && !werr) { + push_embr(miter, iter); + werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1); + } + if (werr) + error("WARNING: failed to write E/MBR during '*hide*'\n"); + +bail: + pi_del(&iter); + return 0; +} + +static int mpe_setchs(const struct disk_info *di, + struct disk_dos_part_entry *dp, + uint32_t lba1) +{ + uint32_t ochs1, ochs2; + + ochs1 = *(uint32_t *)dp->start; + ochs2 = *(uint32_t *)dp->end; + + lba2chs(&dp->start, di, lba1, l2c_cadd); + lba2chs(&dp->end, di, lba1 + dp->length - 1, l2c_cadd); + + return + *(uint32_t *)dp->start != ochs1 || + *(uint32_t *)dp->end != ochs2; +} + +/* + * miter - iterator we match against + */ +int manglepe_fixchs(struct part_iter *miter) +{ + int wb = 0, werr = 0; + struct part_iter *iter = NULL; + struct disk_dos_part_entry *dp; + int ridx; + + if (!opt.fixchs) + return 0; + + if (miter->type != typedos) { + error("Options 'fixchs' is meaningful only for legacy partition scheme.\n"); + return -1; + } + + if (!(iter = pi_begin(&miter->di, 1))) /* turn stepall on */ + return -1; + + while (!pi_next(&iter) && !werr) { + ridx = iter->rawindex; + dp = (struct disk_dos_part_entry *)iter->record; + + wb |= mpe_setchs(&iter->di, dp, (uint32_t)iter->start_lba); + if (ridx > 4) + wb |= mpe_setchs(&iter->di, dp + 1, iter->sub.dos.nebr_lba); + + if (ridx >= 4 && wb && !werr) { + push_embr(miter, iter); + werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1); + wb = 0; + } + } + + if (iter->status > PI_DONE) + goto bail; + + /* last write */ + if (wb && !werr) { + push_embr(miter, iter); + werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1); + } + if (werr) + error("WARNING: failed to write E/MBR during 'fixchs'\n"); + +bail: + pi_del(&iter); + return 0; +} + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/mangle.h b/com32/chain/mangle.h new file mode 100644 index 00000000..bcefea3b --- /dev/null +++ b/com32/chain/mangle.h @@ -0,0 +1,32 @@ +#ifndef _COM32_CHAIN_MANGLE_H +#define _COM32_CHAIN_MANGLE_H + +#include "chain.h" +#include "partiter.h" + +/* file's manglers */ +int manglef_isolinux(struct data_area *data); +int manglef_grub(const struct part_iter *iter, struct data_area *data); +int manglef_bpb(const struct part_iter *iter, struct data_area *data); +/* int manglef_drmk(struct data_area *data);*/ + +/* sector's manglers */ +int mangles_bpb(const struct part_iter *iter, struct data_area *data); +int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org); +int mangles_cmldr(struct data_area *data); + +/* sector + file's manglers */ +int manglesf_bss(struct data_area *sec, struct data_area *fil); + +/* registers' manglers */ +int mangler_init(const struct part_iter *iter); +int mangler_handover(const struct part_iter *iter, const struct data_area *data); +int mangler_grldr(const struct part_iter *iter); + +/* partition layout's manglers */ +int manglepe_fixchs(struct part_iter *miter); +int manglepe_hide(struct part_iter *miter); + +#endif + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/options.c b/com32/chain/options.c new file mode 100644 index 00000000..658a45ca --- /dev/null +++ b/com32/chain/options.c @@ -0,0 +1,376 @@ +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include "common.h" +#include "chain.h" +#include "utility.h" +#include "options.h" + +struct options opt; + +static int soi_s2n(char *ptr, unsigned int *seg, + unsigned int *off, + unsigned int *ip, + unsigned int def) +{ + unsigned int segval = 0, offval, ipval, val; + char *p; + + offval = def; + ipval = def; + + segval = strtoul(ptr, &p, 0); + if (p[0] == ':' && p[1] && p[1] != ':') + offval = strtoul(p+1, &p, 0); + if (p[0] == ':' && p[1] && p[1] != ':') + ipval = strtoul(p+1, NULL, 0); + + val = (segval << 4) + offval; + + if (val < ADDRMIN || val > ADDRMAX) { + error("Invalid seg:off:* address specified..\n"); + goto bail; + } + + val = (segval << 4) + ipval; + + if (ipval > 0xFFFE || val < ADDRMIN || val > ADDRMAX) { + error("Invalid seg:*:ip address specified.\n"); + goto bail; + } + + if (seg) + *seg = segval; + if (off) + *off = offval; + if (ip) + *ip = ipval; + + return 0; +bail: + return -1; +} + +static void usage(void) +{ + unsigned int i; + static const char key[] = "Press any key...\n"; + static const char *const usage[] = { +"\ +Usage:\n\ + chain.c32 [options]\n\ + chain.c32 {fd|hd}<disk#>{,| }[<part#>] [options]\n\ + chain.c32 mbr{:|=}<id>{,| }[<part#>] [options]\n\ + chain.c32 guid{:|=}<guid>{,| }[<part#>] [options]\n\ + chain.c32 label{:|=}<label> [<part#>] [options]\n\ + chain.c32 boot{,| }[<part#>] [options]\n\ + chain.c32 fs [options]\n\ +", "\ +\nOptions ('no' prefix specifies default value):\n\ + sect[=<s[:o[:i]]>] Load sector at <s:o>, jump to <s:i>\n\ + - defaults to 0:0x7C00:0x7C00\n\ + - ommited o/i values default to 0\n\ + maps Map loaded sector into real memory\n\ + nosetbpb Fix BPB fields in loaded sector\n\ + nofilebpb Apply 'setbpb' to loaded file\n\ + nosave Write adjusted sector back to disk\n\ + hand Prepare handover area\n\ + nohptr Force ds:si and ds:bp to point to handover area\n\ + noswap Swap drive numbers, if bootdisk is not fd0/hd0\n\ + nohide Disable all hide variations (also the default)\n\ + hide Hide primary partitions, unhide selected partition\n\ + hideall Hide *all* partitions, unhide selected partition\n\ + unhide Unhide primary partitions\n\ + unhideall Unhide *all* partitions\n\ + nofixchs Walk *all* partitions and fix E/MBRs' chs values\n\ + nokeeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\ + nowarn Wait for a keypress to continue chainloading\n\ + - useful to see emited warnings\n\ + nobreak Actually perform the chainloading\n\ +", "\ +\nOptions continued ...\n\ + file=<file> Load and execute <file>\n\ + seg=<s[:o[:i]]> Load file at <s:o>, jump to <s:i>\n\ + - defaults to 0:0x7C00:0x7C00\n\ + - ommited o/i values default to 0\n\ + isolinux=<loader> Load another version of ISOLINUX\n\ + ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\ + reactos=<loader> Load ReactOS's loader\n\ + cmldr=<loader> Load Recovery Console of Windows NT/2K/XP/2003\n\ + freedos=<loader> Load FreeDOS KERNEL.SYS\n\ + msdos=<loader> Load MS-DOS 2.xx - 6.xx IO.SYS\n\ + msdos7=<loader> Load MS-DOS 7+ IO.SYS\n\ + pcdos=<loader> Load PC-DOS IBMBIO.COM\n\ + drmk=<loader> Load DRMK DELLBIO.BIN\n\ + grub=<loader> Load GRUB Legacy stage2\n\ + grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\ + grldr=<loader> Load GRUB4DOS grldr\n\ + bss=<filename> Emulate syslinux's BSS\n\ + bs=<filename> Emulate syslinux's BS\n\ +\nPlease see doc/chain.txt for the detailed documentation.\n\ +" + }; + for (i = 0; i < sizeof(usage)/sizeof(usage[0]); i++) { + if (i) { + error(key); + wait_key(); + } + error(usage[i]); + } +} + +void opt_set_defs(void) +{ + memset(&opt, 0, sizeof(opt)); + opt.sect = true; /* by def. load sector */ + opt.maps = true; /* by def. map sector */ + opt.hand = true; /* by def. prepare handover */ + opt.brkchain = false; /* by def. do chainload */ + opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00; + opt.drivename = "boot"; +#ifdef DEBUG + opt.warn = true; +#endif +} + +int opt_parse_args(int argc, char *argv[]) +{ + int i; + unsigned int v; + char *p; + + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "file=", 5)) { + opt.file = argv[i] + 5; + } else if (!strcmp(argv[i], "nofile")) { + opt.file = NULL; + } else if (!strncmp(argv[i], "seg=", 4)) { + if (soi_s2n(argv[i] + 4, &opt.fseg, &opt.foff, &opt.fip, 0)) + goto bail; + } else if (!strncmp(argv[i], "bss=", 4)) { + opt.file = argv[i] + 4; + opt.bss = true; + opt.maps = false; + opt.setbpb = true; + /* opt.save = true; */ + } else if (!strncmp(argv[i], "bs=", 3)) { + opt.file = argv[i] + 3; + opt.sect = false; + opt.filebpb = true; + } else if (!strncmp(argv[i], "isolinux=", 9)) { + opt.file = argv[i] + 9; + opt.isolinux = true; + opt.hand = false; + opt.sect = false; + } else if (!strncmp(argv[i], "ntldr=", 6)) { + opt.fseg = 0x2000; /* NTLDR wants this address */ + opt.foff = 0; + opt.fip = 0; + opt.file = argv[i] + 6; + opt.setbpb = true; + /* opt.save = true; */ + opt.hand = false; + } else if (!strncmp(argv[i], "reactos=", 8)) { + /* + * settings based on commit + * ad4cf1470977f648ee1dd45e97939589ccb0393c + * note, conflicts with: + * http://reactos.freedoors.org/Reactos%200.3.13/ReactOS-0.3.13-REL-src/boot/freeldr/notes.txt + */ + opt.fseg = 0; + opt.foff = 0x8000; + opt.fip = 0x8100; + opt.file = argv[i] + 8; + opt.setbpb = true; + /* opt.save = true; */ + opt.hand = false; + } else if (!strncmp(argv[i], "cmldr=", 6)) { + opt.fseg = 0x2000; /* CMLDR wants this address */ + opt.foff = 0; + opt.fip = 0; + opt.file = argv[i] + 6; + opt.cmldr = true; + opt.setbpb = true; + /* opt.save = true; */ + opt.hand = false; + } else if (!strncmp(argv[i], "freedos=", 8)) { + opt.fseg = 0x60; /* FREEDOS wants this address */ + opt.foff = 0; + opt.fip = 0; + opt.sseg = 0x1FE0; + opt.file = argv[i] + 8; + opt.setbpb = true; + /* opt.save = true; */ + opt.hand = false; + } else if ( (v = 6, !strncmp(argv[i], "msdos=", v) || + !strncmp(argv[i], "pcdos=", v)) || + (v = 7, !strncmp(argv[i], "msdos7=", v)) ) { + opt.fseg = 0x70; /* MS-DOS 2.00 .. 6.xx wants this address */ + opt.foff = 0; + opt.fip = v == 7 ? 0x200 : 0; /* MS-DOS 7.0+ wants this ip */ + opt.sseg = 0x8000; + opt.file = argv[i] + v; + opt.setbpb = true; + /* opt.save = true; */ + opt.hand = false; + } else if (!strncmp(argv[i], "drmk=", 5)) { + opt.fseg = 0x70; /* DRMK wants this address */ + opt.foff = 0; + opt.fip = 0; + opt.sseg = 0x2000; + opt.soff = 0; + opt.sip = 0; + opt.file = argv[i] + 5; + /* opt.drmk = true; */ + opt.setbpb = true; + /* opt.save = true; */ + opt.hand = false; + } else if (!strncmp(argv[i], "grub=", 5)) { + opt.fseg = 0x800; /* stage2 wants this address */ + opt.foff = 0; + opt.fip = 0x200; + opt.file = argv[i] + 5; + opt.grub = true; + opt.hand = false; + opt.sect = false; + } else if (!strncmp(argv[i], "grubcfg=", 8)) { + opt.grubcfg = argv[i] + 8; + } else if (!strncmp(argv[i], "grldr=", 6)) { + opt.file = argv[i] + 6; + opt.grldr = true; + opt.hand = false; + opt.sect = false; + } else if (!strcmp(argv[i], "keeppxe")) { + opt.keeppxe = 3; + } else if (!strcmp(argv[i], "nokeeppxe")) { + opt.keeppxe = 0; + } else if (!strcmp(argv[i], "maps")) { + opt.maps = true; + } else if (!strcmp(argv[i], "nomaps")) { + opt.maps = false; + } else if (!strcmp(argv[i], "hand")) { + opt.hand = true; + } else if (!strcmp(argv[i], "nohand")) { + opt.hand = false; + } else if (!strcmp(argv[i], "hptr")) { + opt.hptr = true; + } else if (!strcmp(argv[i], "nohptr")) { + opt.hptr = false; + } else if (!strcmp(argv[i], "swap")) { + opt.swap = true; + } else if (!strcmp(argv[i], "noswap")) { + opt.swap = false; + } else if (!strcmp(argv[i], "nohide")) { + opt.hide = 0; + } else if (!strcmp(argv[i], "hide")) { + opt.hide = 1; /* 001b */ + } else if (!strcmp(argv[i], "hideall")) { + opt.hide = 2; /* 010b */ + } else if (!strcmp(argv[i], "unhide")) { + opt.hide = 5; /* 101b */ + } else if (!strcmp(argv[i], "unhideall")) { + opt.hide = 6; /* 110b */ + } else if (!strcmp(argv[i], "setbpb")) { + opt.setbpb = true; + } else if (!strcmp(argv[i], "nosetbpb")) { + opt.setbpb = false; + } else if (!strcmp(argv[i], "filebpb")) { + opt.filebpb = true; + } else if (!strcmp(argv[i], "nofilebpb")) { + opt.filebpb = false; + } else if (!strncmp(argv[i], "sect=", 5) || + !strcmp(argv[i], "sect")) { + if (argv[i][4]) { + if (soi_s2n(argv[i] + 5, &opt.sseg, &opt.soff, &opt.sip, 0)) + goto bail; + } + opt.sect = true; + } else if (!strcmp(argv[i], "nosect")) { + opt.sect = false; + opt.maps = false; + } else if (!strcmp(argv[i], "save")) { + opt.save = true; + } else if (!strcmp(argv[i], "nosave")) { + opt.save = false; + } else if (!strcmp(argv[i], "fixchs")) { + opt.fixchs = true; + } else if (!strcmp(argv[i], "nofixchs")) { + opt.fixchs = false; + } else if (!strcmp(argv[i], "warn")) { + opt.warn = true; + } else if (!strcmp(argv[i], "nowarn")) { + opt.warn = false; + } else if (!strcmp(argv[i], "nobreak")) { + opt.brkchain = false; + } else if (!strcmp(argv[i], "break")) { + opt.brkchain = true; + opt.file = NULL; + opt.maps = false; + opt.hand = false; + } else if (((argv[i][0] == 'h' || argv[i][0] == 'f') + && argv[i][1] == 'd') + || !strncmp(argv[i], "mbr:", 4) + || !strncmp(argv[i], "mbr=", 4) + || !strncmp(argv[i], "guid:", 5) + || !strncmp(argv[i], "guid=", 5) + || !strncmp(argv[i], "label:", 6) + || !strncmp(argv[i], "label=", 6) + || !strcmp(argv[i], "boot") + || !strncmp(argv[i], "boot,", 5) + || !strcmp(argv[i], "fs")) { + opt.drivename = argv[i]; + if (strncmp(argv[i], "label", 5)) + p = strchr(opt.drivename, ','); + else + p = NULL; + if (p) { + *p = '\0'; + opt.partition = p + 1; + } else if (argv[i + 1] && argv[i + 1][0] >= '0' + && argv[i + 1][0] <= '9') { + opt.partition = argv[++i]; + } + } else { + usage(); + goto bail; + } + } + + if (opt.grubcfg && !opt.grub) { + error("grubcfg=<filename> must be used together with grub=<loader>.\n"); + goto bail; + } + +#if 0 + if ((!opt.maps || !opt.sect) && !opt.file) { + error("You have to load something.\n"); + goto bail; + } +#endif + + if (opt.filebpb && !opt.file) { + error("Option 'filebpb' requires a file.\n"); + goto bail; + } + + if (opt.save && !opt.sect) { + error("Option 'save' requires a sector.\n"); + goto bail; + } + + if (opt.setbpb && !opt.sect) { + error("Option 'setbpb' requires a sector.\n"); + goto bail; + } + + if (opt.maps && !opt.sect) { + error("Option 'maps' requires a sector.\n"); + goto bail; + } + + return 0; +bail: + return -1; +} + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/options.h b/com32/chain/options.h new file mode 100644 index 00000000..4493ef1f --- /dev/null +++ b/com32/chain/options.h @@ -0,0 +1,47 @@ +#ifndef _COM32_CHAIN_OPTIONS_H +#define _COM32_CHAIN_OPTIONS_H + +#include <stdint.h> +#include <syslinux/bootrm.h> + +struct options { + unsigned int fseg; + unsigned int foff; + unsigned int fip; + unsigned int sseg; + unsigned int soff; + unsigned int sip; + const char *drivename; + const char *partition; + const char *file; + const char *grubcfg; + bool isolinux; + bool cmldr; + bool drmk; + bool grub; + bool grldr; + bool maps; + bool hand; + bool hptr; + bool swap; + int hide; + bool sect; + bool save; + bool bss; + bool setbpb; + bool filebpb; + bool fixchs; + bool warn; + bool brkchain; + uint16_t keeppxe; + struct syslinux_rm_regs regs; +}; + +extern struct options opt; + +void opt_set_defs(void); +int opt_parse_args(int argc, char *argv[]); + +#endif + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/partiter.c b/com32/chain/partiter.c new file mode 100644 index 00000000..1acd1958 --- /dev/null +++ b/com32/chain/partiter.c @@ -0,0 +1,805 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2010 H. Peter Anvin - All Rights Reserved + * Copyright 2010 Shao Miller + * Copyright 2010 Michal Soltys + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * partiter.c + * + * Provides disk / partition iteration. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <zlib.h> +#include <syslinux/disk.h> +#include "common.h" +#include "partiter.h" +#include "utility.h" + +#define ost_is_ext(type) ((type) == 0x05 || (type) == 0x0F || (type) == 0x85) +#define ost_is_nondata(type) (ost_is_ext(type) || (type) == 0x00) +#define sane(s,l) ((s)+(l) > (s)) + +/* forwards */ + +static int iter_ctor(struct part_iter *, va_list *); +static int iter_dos_ctor(struct part_iter *, va_list *); +static int iter_gpt_ctor(struct part_iter *, va_list *); +static void iter_dtor(struct part_iter *); +static struct part_iter *pi_dos_next(struct part_iter *); +static struct part_iter *pi_gpt_next(struct part_iter *); +static struct part_iter *pi_raw_next(struct part_iter *); + +static struct itertype types[] = { + [0] = { + .ctor = &iter_dos_ctor, + .dtor = &iter_dtor, + .next = &pi_dos_next, +}, [1] = { + .ctor = &iter_gpt_ctor, + .dtor = &iter_dtor, + .next = &pi_gpt_next, +}, [2] = { + .ctor = &iter_ctor, + .dtor = &iter_dtor, + .next = &pi_raw_next, +}}; + +const struct itertype * const typedos = types; +const struct itertype * const typegpt = types+1; +const struct itertype * const typeraw = types+2; + +#ifdef DEBUG +static int inv_type(const void *type) +{ + int i, cnt = sizeof(types)/sizeof(types[0]); + for (i = 0; i < cnt; i++) { + if (type == types + i) + return 0; + } + return -1; +} +#endif + +/** + * iter_ctor() - common iterator initialization + * @iter: iterator pointer + * @args(0): disk_info structure used for disk functions + * @args(1): stepall modifier + * + * Second and further arguments are passed as a pointer to va_list + **/ +static int iter_ctor(struct part_iter *iter, va_list *args) +{ + const struct disk_info *di = va_arg(*args, const struct disk_info *); + int stepall = va_arg(*args, int); + +#ifdef DEBUG + if (!di) + return -1; +#endif + + memcpy(&iter->di, di, sizeof(struct disk_info)); + iter->stepall = stepall; + iter->index0 = -1; + iter->length = di->lbacnt; + + return 0; +} + +/** + * iter_dtor() - common iterator cleanup + * @iter: iterator pointer + * + **/ +static void iter_dtor(struct part_iter *iter) +{ + free(iter->data); +} + +/** + * iter_dos_ctor() - MBR/EBR iterator specific initialization + * @iter: iterator pointer + * @args(0): disk_info structure used for disk functions + * @args(1): pointer to buffer with loaded valid MBR + * + * Second and further arguments are passed as a pointer to va_list. + * This function only makes rudimentary checks. If user uses + * pi_new(), he/she is responsible for doing proper sanity checks. + **/ +static int iter_dos_ctor(struct part_iter *iter, va_list *args) +{ + const struct disk_dos_mbr *mbr; + + /* uses args(0) */ + if (iter_ctor(iter, args)) + return -1; + + mbr = va_arg(*args, const struct disk_dos_mbr *); + +#ifdef DEBUG + if (!mbr) + goto bail; +#endif + + if (!(iter->data = malloc(sizeof(struct disk_dos_mbr)))) + goto bail; + + memcpy(iter->data, mbr, sizeof(struct disk_dos_mbr)); + + iter->sub.dos.bebr_index0 = -1; + iter->sub.dos.disk_sig = mbr->disk_sig; + + return 0; +bail: + iter->type->dtor(iter); + return -1; +} + +/** + * iter_gpt_ctor() - GPT iterator specific initialization + * @iter: iterator pointer + * @args(0): ptr to disk_info structure + * @args(1): ptr to buffer with GPT header + * @args(2): ptr to buffer with GPT partition list + * + * Second and further arguments are passed as a pointer to va_list. + * This function only makes rudimentary checks. If user uses + * pi_new(), he/she is responsible for doing proper sanity checks. + **/ +static int iter_gpt_ctor(struct part_iter *iter, va_list *args) +{ + uint64_t siz; + const struct disk_gpt_header *gpth; + const struct disk_gpt_part_entry *gptl; + + /* uses args(0) */ + if (iter_ctor(iter, args)) + return -1; + + gpth = va_arg(*args, const struct disk_gpt_header *); + gptl = va_arg(*args, const struct disk_gpt_part_entry *); + +#ifdef DEBUG + if (!gpth || !gptl) + goto bail; +#endif + + siz = (uint64_t)gpth->part_count * gpth->part_size; + +#ifdef DEBUG + if (!siz || (siz + iter->di.bps - 1) / iter->di.bps > 255u || + gpth->part_size < sizeof(struct disk_gpt_part_entry)) { + goto bail; + } +#endif + + if (!(iter->data = malloc((size_t)siz))) + goto bail; + + memcpy(iter->data, gptl, (size_t)siz); + + iter->sub.gpt.pe_count = (int)gpth->part_count; + iter->sub.gpt.pe_size = (int)gpth->part_size; + iter->sub.gpt.ufirst = gpth->lba_first_usable; + iter->sub.gpt.ulast = gpth->lba_last_usable; + + memcpy(&iter->sub.gpt.disk_guid, &gpth->disk_guid, sizeof(struct guid)); + + return 0; +bail: + iter->type->dtor(iter); + return -1; +} + +/* Logical partition must be sane, meaning: + * - must be data or empty + * - must have non-0 start and length + * - values must not wrap around 32bit + * - must be inside current EBR frame + */ + +static int notsane_logical(const struct part_iter *iter) +{ + const struct disk_dos_part_entry *dp; + uint32_t end_log; + + dp = ((struct disk_dos_mbr *)iter->data)->table; + + if (!dp[0].ostype) + return 0; + + if (ost_is_ext(dp[0].ostype)) { + error("1st EBR entry must be data or empty.\n"); + return -1; + } + + end_log = dp[0].start_lba + dp[0].length; + + if (!dp[0].start_lba || + !dp[0].length || + !sane(dp[0].start_lba, dp[0].length) || + end_log > iter->sub.dos.ebr_size) { + + error("Insane logical partition.\n"); + return -1; + } + + return 0; +} + +/* Extended partition must be sane, meaning: + * - must be extended or empty + * - must have non-0 start and length + * - values must not wrap around 32bit + * - must be inside base EBR frame + */ + +static int notsane_extended(const struct part_iter *iter) +{ + const struct disk_dos_part_entry *dp; + uint32_t end_ebr; + + dp = ((struct disk_dos_mbr *)iter->data)->table; + + if (!dp[1].ostype) + return 0; + + if (!ost_is_nondata(dp[1].ostype)) { + error("2nd EBR entry must be extended or empty.\n"); + return -1; + } + + end_ebr = dp[1].start_lba + dp[1].length; + + if (!dp[1].start_lba || + !dp[1].length || + !sane(dp[1].start_lba, dp[1].length) || + end_ebr > iter->sub.dos.bebr_size) { + + error("Insane extended partition.\n"); + return -1; + } + + return 0; +} + +/* Primary partition must be sane, meaning: + * - must have non-0 start and length + * - values must not wrap around 32bit + */ + +static int notsane_primary(const struct part_iter *iter) +{ + const struct disk_dos_part_entry *dp; + dp = ((struct disk_dos_mbr *)iter->data)->table + iter->index0; + + if (!dp->ostype) + return 0; + + if (!dp->start_lba || + !dp->length || + !sane(dp->start_lba, dp->length) || + dp->start_lba + dp->length > iter->di.lbacnt) { + error("Insane primary (MBR) partition.\n"); + return -1; + } + + return 0; +} + +static int notsane_gpt(const struct part_iter *iter) +{ + const struct disk_gpt_part_entry *gp; + gp = (const struct disk_gpt_part_entry *) + (iter->data + iter->index0 * iter->sub.gpt.pe_size); + + if (guid_is0(&gp->type)) + return 0; + + if (gp->lba_first < iter->sub.gpt.ufirst || + gp->lba_last > iter->sub.gpt.ulast) { + error("Insane GPT partition.\n"); + return -1; + } + + return 0; +} + +static int pi_dos_next_mbr(struct part_iter *iter, uint32_t *lba, + struct disk_dos_part_entry **_dp) +{ + struct disk_dos_part_entry *dp; + + while (++iter->index0 < 4) { + dp = ((struct disk_dos_mbr *)iter->data)->table + iter->index0; + + if (notsane_primary(iter)) { + iter->status = PI_INSANE; + goto bail; + } + + if (ost_is_ext(dp->ostype)) { + if (iter->sub.dos.bebr_index0 >= 0) { + error("You have more than 1 extended partition.\n"); + iter->status = PI_INSANE; + goto bail; + } + /* record base EBR index */ + iter->sub.dos.bebr_index0 = iter->index0; + } + if (!ost_is_nondata(dp->ostype) || iter->stepall) { + *lba = dp->start_lba; + *_dp = dp; + break; + } + } + + return 0; +bail: + return -1; +} + +static int prep_base_ebr(struct part_iter *iter) +{ + struct disk_dos_part_entry *dp; + + if (iter->sub.dos.bebr_index0 < 0) /* if we don't have base extended partition at all */ + return -1; + else if (!iter->sub.dos.bebr_start) { /* if not initialized yet */ + dp = ((struct disk_dos_mbr *)iter->data)->table + iter->sub.dos.bebr_index0; + + iter->sub.dos.bebr_start = dp->start_lba; + iter->sub.dos.bebr_size = dp->length; + + iter->sub.dos.ebr_start = 0; + iter->sub.dos.ebr_size = iter->sub.dos.bebr_size; + + iter->sub.dos.cebr_lba = 0; + iter->sub.dos.nebr_lba = iter->sub.dos.bebr_start; + + iter->index0--; + } + return 0; +} + +static int pi_dos_next_ebr(struct part_iter *iter, uint32_t *lba, + struct disk_dos_part_entry **_dp) +{ + struct disk_dos_part_entry *dp; + + if (prep_base_ebr(iter)) { + iter->status = PI_DONE; + return -1; + } + + while (++iter->index0 < 1024 && iter->sub.dos.nebr_lba) { + free(iter->data); + if (!(iter->data = + disk_read_sectors(&iter->di, iter->sub.dos.nebr_lba, 1))) { + error("Couldn't load EBR.\n"); + iter->status = PI_ERRLOAD; + return -1; + } + + if (notsane_logical(iter) || notsane_extended(iter)) { + iter->status = PI_INSANE; + return -1; + } + + dp = ((struct disk_dos_mbr *)iter->data)->table; + + iter->sub.dos.cebr_lba = iter->sub.dos.nebr_lba; + + /* setup next frame values */ + if (dp[1].ostype) { + iter->sub.dos.ebr_start = dp[1].start_lba; + iter->sub.dos.ebr_size = dp[1].length; + iter->sub.dos.nebr_lba = iter->sub.dos.bebr_start + dp[1].start_lba; + } else { + iter->sub.dos.ebr_start = 0; + iter->sub.dos.ebr_size = 0; + iter->sub.dos.nebr_lba = 0; + } + + if (!dp[0].ostype) + iter->sub.dos.skipcnt++; + + if (dp[0].ostype || iter->stepall) { + *lba = iter->sub.dos.cebr_lba + dp[0].start_lba; + *_dp = dp; + return 0; + } + /* + * This way it's possible to continue, if some crazy soft left a "hole" + * - EBR with a valid extended partition without a logical one. In + * such case, linux will not reserve a number for such hole - so we + * don't increase index0. If stepall flag is set, we will never reach + * this place. + */ + } + iter->status = PI_DONE; + return -1; +} + +static struct part_iter *pi_dos_next(struct part_iter *iter) +{ + uint32_t start_lba = 0; + struct disk_dos_part_entry *dos_part = NULL; + + if (iter->status) + goto bail; + + /* look for primary partitions */ + if (iter->index0 < 4 && + pi_dos_next_mbr(iter, &start_lba, &dos_part)) + goto bail; + + /* look for logical partitions */ + if (iter->index0 >= 4 && + pi_dos_next_ebr(iter, &start_lba, &dos_part)) + goto bail; + + /* + * note special index handling, if we have stepall set - + * this is made to keep index consistent with non-stepall + * iterators + */ + + if (iter->index0 >= 4 && !dos_part->ostype) + iter->index = -1; + else + iter->index = iter->index0 - iter->sub.dos.skipcnt + 1; + iter->rawindex = iter->index0 + 1; + iter->start_lba = start_lba; + iter->length = dos_part->length; + iter->record = (char *)dos_part; + +#ifdef DEBUG + disk_dos_part_dump(dos_part); +#endif + + return iter; +bail: + return NULL; +} + +static void gpt_conv_label(struct part_iter *iter) +{ + const struct disk_gpt_part_entry *gp; + const int16_t *orig_lab; + + gp = (const struct disk_gpt_part_entry *) + (iter->data + iter->index0 * iter->sub.gpt.pe_size); + orig_lab = (const int16_t *)gp->name; + + /* caveat: this is very crude conversion */ + for (int i = 0; i < PI_GPTLABSIZE/2; i++) { + iter->sub.gpt.part_label[i] = (char)orig_lab[i]; + } + iter->sub.gpt.part_label[PI_GPTLABSIZE/2] = 0; +} + +static struct part_iter *pi_gpt_next(struct part_iter *iter) +{ + const struct disk_gpt_part_entry *gpt_part = NULL; + + if (iter->status) + goto bail; + + while (++iter->index0 < iter->sub.gpt.pe_count) { + gpt_part = (const struct disk_gpt_part_entry *) + (iter->data + iter->index0 * iter->sub.gpt.pe_size); + + if (notsane_gpt(iter)) { + iter->status = PI_INSANE; + goto bail; + } + + if (!guid_is0(&gpt_part->type) || iter->stepall) + break; + } + /* no more partitions ? */ + if (iter->index0 == iter->sub.gpt.pe_count) { + iter->status = PI_DONE; + goto bail; + } + /* gpt_part is guaranteed to be valid here */ + iter->index = iter->index0 + 1; + iter->rawindex = iter->index0 + 1; + iter->start_lba = gpt_part->lba_first; + iter->length = gpt_part->lba_last - gpt_part->lba_first + 1; + iter->record = (char *)gpt_part; + memcpy(&iter->sub.gpt.part_guid, &gpt_part->uid, sizeof(struct guid)); + gpt_conv_label(iter); + +#ifdef DEBUG + disk_gpt_part_dump(gpt_part); +#endif + + return iter; +bail: + return NULL; +} + +static struct part_iter *pi_raw_next(struct part_iter *iter) +{ + iter->status = PI_DONE; + return NULL; +} + +static int check_crc(uint32_t crc_match, const uint8_t *buf, unsigned int siz) +{ + uint32_t crc; + + crc = crc32(0, NULL, 0); + crc = crc32(crc, buf, siz); + + return crc_match != crc; +} + +static int gpt_check_hdr_crc(const struct disk_info * const diskinfo, struct disk_gpt_header **_gh) +{ + struct disk_gpt_header *gh = *_gh; + uint64_t lba_alt; + uint32_t hold_crc32; + + hold_crc32 = gh->chksum; + gh->chksum = 0; + if (check_crc(hold_crc32, (const uint8_t *)gh, gh->hdr_size)) { + error("WARNING: Primary GPT header checksum invalid.\n"); + /* retry with backup */ + lba_alt = gh->lba_alt; + free(gh); + if (!(gh = *_gh = disk_read_sectors(diskinfo, lba_alt, 1))) { + error("Couldn't read backup GPT header.\n"); + return -1; + } + hold_crc32 = gh->chksum; + gh->chksum = 0; + if (check_crc(hold_crc32, (const uint8_t *)gh, gh->hdr_size)) { + error("Secondary GPT header checksum invalid.\n"); + return -1; + } + } + /* restore old checksum */ + gh->chksum = hold_crc32; + + return 0; +} + +/* + * ---------------------------------------------------------------------------- + * Following functions are for users to call. + * ---------------------------------------------------------------------------- + */ + + +int pi_next(struct part_iter **_iter) +{ + struct part_iter *iter; + + if(!_iter || !*_iter) + return 0; + iter = *_iter; +#ifdef DEBUG + if (inv_type(iter->type)) { + error("This is not a valid iterator.\n"); + return 0; + } +#endif + if ((iter = iter->type->next(iter))) { + *_iter = iter; + } + return (*_iter)->status; +} + +/** + * pi_new() - get new iterator + * @itertype: iterator type + * @...: variable arguments passed to ctors + * + * Variable arguments depend on the type. Please see functions: + * iter_gpt_ctor() and iter_dos_ctor() for details. + **/ +struct part_iter *pi_new(const struct itertype *type, ...) +{ + int badctor = 0; + struct part_iter *iter = NULL; + va_list ap; + + va_start(ap, type); + +#ifdef DEBUG + if (inv_type(type)) { + error("Unknown iterator requested.\n"); + goto bail; + } +#endif + + if (!(iter = malloc(sizeof(struct part_iter)))) { + error("Couldn't allocate memory for the iterator.\n"); + goto bail; + } + + memset(iter, 0, sizeof(struct part_iter)); + iter->type = type; + + if (type->ctor(iter, &ap)) { + badctor = -1; + error("Cannot initialize the iterator.\n"); + goto bail; + } + +bail: + va_end(ap); + if (badctor) { + free(iter); + iter = NULL; + } + return iter; +} + +/** + * pi_del() - delete iterator + * @iter: iterator double pointer + * + **/ + +void pi_del(struct part_iter **_iter) +{ + struct part_iter *iter; + + if(!_iter || !*_iter) + return; + iter = *_iter; + +#ifdef DEBUG + if (inv_type(iter->type)) { + error("This is not a valid iterator.\n"); + return; + } +#endif + + iter->type->dtor(iter); + free(iter); + *_iter = NULL; +} + +/** + * pi_begin() - check disk, validate, and get proper iterator + * @di: diskinfo struct pointer + * + * This function checks the disk for GPT or legacy partition table and allocates + * an appropriate iterator. + **/ +struct part_iter *pi_begin(const struct disk_info *di, int stepall) +{ + int setraw = 0; + struct part_iter *iter = NULL; + struct disk_dos_mbr *mbr = NULL; + struct disk_gpt_header *gpth = NULL; + struct disk_gpt_part_entry *gptl = NULL; + + /* Read MBR */ + if (!(mbr = disk_read_sectors(di, 0, 1))) { + error("Couldn't read first disk sector.\n"); + goto bail; + } + + setraw = -1; + + /* Check for MBR magic*/ + if (mbr->sig != disk_mbr_sig_magic) { + error("No MBR magic.\n"); + goto bail; + } + + /* Check for GPT protective MBR */ + if (mbr->table[0].ostype == 0xEE) { + if (!(gpth = disk_read_sectors(di, 1, 1))) { + error("Couldn't read potential GPT header.\n"); + goto bail; + } + } + + if (gpth && gpth->rev.uint32 == 0x00010000 && + !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof(disk_gpt_sig_magic))) { + /* looks like GPT v1.0 */ + uint64_t gpt_loff; /* offset to GPT partition list in sectors */ + uint64_t gpt_lsiz; /* size of GPT partition list in bytes */ + uint64_t gpt_lcnt; /* size of GPT partition in sectors */ +#ifdef DEBUG + puts("Looks like a GPT v1.0 disk."); + disk_gpt_header_dump(gpth); +#endif + /* Verify checksum, fallback to backup, then bail if invalid */ + if (gpt_check_hdr_crc(di, &gpth)) + goto bail; + + gpt_loff = gpth->lba_table; + gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count; + gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps; + + /* + * disk_read_sectors allows reading of max 255 sectors, so we use + * it as a sanity check base. EFI doesn't specify max (AFAIK). + * Apart from that, some extensive sanity checks. + */ + if (!gpt_loff || !gpt_lsiz || gpt_lcnt > 255u || + gpth->lba_first_usable > gpth->lba_last_usable || + !sane(gpt_loff, gpt_lcnt) || + gpt_loff + gpt_lcnt > gpth->lba_first_usable || + !sane(gpth->lba_last_usable, gpt_lcnt) || + gpth->lba_last_usable + gpt_lcnt >= gpth->lba_alt || + gpth->lba_alt >= di->lbacnt || + gpth->part_size < sizeof(struct disk_gpt_part_entry)) { + error("Invalid GPT header's values.\n"); + goto bail; + } + if (!(gptl = disk_read_sectors(di, gpt_loff, (uint8_t)gpt_lcnt))) { + error("Couldn't read GPT partition list.\n"); + goto bail; + } + /* Check array checksum(s). */ + if (check_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) { + error("WARNING: GPT partition list checksum invalid, trying backup.\n"); + free(gptl); + /* secondary array directly precedes secondary header */ + if (!(gptl = disk_read_sectors(di, gpth->lba_alt - gpt_lcnt, (uint8_t)gpt_lcnt))) { + error("Couldn't read backup GPT partition list.\n"); + goto bail; + } + if (check_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) { + error("Backup GPT partition list checksum invalid.\n"); + goto bail; + } + } + /* allocate iterator and exit */ + iter = pi_new(typegpt, di, stepall, gpth, gptl); + } else { + /* looks like MBR */ + iter = pi_new(typedos, di, stepall, mbr); + } + + setraw = 0; +bail: + if (setraw) { + error("WARNING: treating disk as raw.\n"); + iter = pi_new(typeraw, di, stepall); + } + free(mbr); + free(gpth); + free(gptl); + + return iter; +} + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/partiter.h b/com32/chain/partiter.h new file mode 100644 index 00000000..7deeb534 --- /dev/null +++ b/com32/chain/partiter.h @@ -0,0 +1,107 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2010 H. Peter Anvin - All Rights Reserved + * Copyright 2010 Michal Soltys + * Copyright 2010 Shao Miller + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * partiter.h + * + * Provides disk / partition iteration. + */ + +#ifndef _COM32_CHAIN_PARTITER_H +#define _COM32_CHAIN_PARTITER_H + +#include <stdint.h> +#include <syslinux/disk.h> + +#define PI_ERRLOAD 3 +#define PI_INSANE 2 +#define PI_DONE 1 +#define PI_OK 0 + +struct itertype; +struct part_iter; + +struct itertype { + int (*ctor)(struct part_iter *, va_list *); + void (*dtor)(struct part_iter *); + struct part_iter *(*next) (struct part_iter *); +}; + +#define PI_GPTLABSIZE ((int)sizeof(((struct disk_gpt_part_entry *)0)->name)) + +struct part_iter { + const struct itertype *type; + char *data; + char *record; + uint64_t start_lba; + uint64_t length; + int index; + int rawindex; + struct disk_info di; + int stepall; + int status; + /* internal */ + int index0; + union _sub { + struct _dos { + uint32_t disk_sig; + uint32_t nebr_lba; + uint32_t cebr_lba; + /* internal */ + uint32_t ebr_start; + uint32_t ebr_size; + uint32_t bebr_start; + uint32_t bebr_size; + int bebr_index0; + int skipcnt; + } dos; + struct _gpt { + struct guid disk_guid; + struct guid part_guid; + char part_label[PI_GPTLABSIZE/2+1]; + int pe_count; + int pe_size; + uint64_t ufirst; + uint64_t ulast; + } gpt; + } sub; +}; + +extern const struct itertype * const typedos; +extern const struct itertype * const typegpt; +extern const struct itertype * const typeraw; + +struct part_iter *pi_begin(const struct disk_info *, int stepall); +struct part_iter *pi_new(const struct itertype *, ...); +void pi_del(struct part_iter **); +int pi_next(struct part_iter **); + +#endif + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/utility.c b/com32/chain/utility.c new file mode 100644 index 00000000..fb59551b --- /dev/null +++ b/com32/chain/utility.c @@ -0,0 +1,214 @@ +#include <com32.h> +#include <stdint.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <syslinux/disk.h> +#include "utility.h" + +static const char *bpbtypes[] = { + [0] = "unknown", + [1] = "2.0", + [2] = "3.0", + [3] = "3.2", + [4] = "3.4", + [5] = "4.0", + [6] = "8.0 (NT+)", + [7] = "7.0", +}; + +void error(const char *msg) +{ + fputs(msg, stderr); +} + +int guid_is0(const struct guid *guid) +{ + return !*(const uint64_t *)guid && !*((const uint64_t *)guid + 1); +} + +void wait_key(void) +{ + int cnt; + char junk; + + /* drain */ + do { + errno = 0; + cnt = read(0, &junk, 1); + } while (cnt > 0 || (cnt < 0 && errno == EAGAIN)); + + /* wait */ + do { + errno = 0; + cnt = read(0, &junk, 1); + } while (!cnt || (cnt < 0 && errno == EAGAIN)); +} + +void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t mode) +{ + uint32_t c, h, s, t; + uint32_t cs, hs, ss; + + /* + * Not much reason here, but if we have no valid CHS geometry, we assume + * "typical" ones to have something to return. + */ + if (di->cbios) { + cs = di->cyl; + hs = di->head; + ss = di->spt; + if (mode == l2c_cadd && cs < 1024 && di->lbacnt > cs*hs*ss) + cs++; + else if (mode == l2c_cmax) + cs = 1024; + } else { + if (di->disk & 0x80) { + cs = 1024; + hs = 255; + ss = 63; + } else { + cs = 80; + hs = 2; + ss = 18; + } + } + + if (lba >= cs*hs*ss) { + s = ss; + h = hs - 1; + c = cs - 1; + } else { + s = ((uint32_t)lba % ss) + 1; + t = (uint32_t)lba / ss; + h = t % hs; + c = t / hs; + } + + (*dst)[0] = h; + (*dst)[1] = s | ((c & 0x300) >> 2); + (*dst)[2] = c; +} + +uint32_t get_file_lba(const char *filename) +{ + com32sys_t inregs; + uint32_t lba; + + /* Start with clean registers */ + memset(&inregs, 0, sizeof(com32sys_t)); + + /* Put the filename in the bounce buffer */ + strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size); + + /* Call comapi_open() which returns a structure pointer in SI + * to a structure whose first member happens to be the LBA. + */ + inregs.eax.w[0] = 0x0006; + inregs.esi.w[0] = OFFS(__com32.cs_bounce); + inregs.es = SEG(__com32.cs_bounce); + __com32.cs_intcall(0x22, &inregs, &inregs); + + if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) { + return 0; /* Filename not found */ + } + + /* Since the first member is the LBA, we simply cast */ + lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0])); + + /* Clean the registers for the next call */ + memset(&inregs, 0, sizeof(com32sys_t)); + + /* Put the filename in the bounce buffer */ + strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size); + + /* Call comapi_close() to free the structure */ + inregs.eax.w[0] = 0x0008; + inregs.esi.w[0] = OFFS(__com32.cs_bounce); + inregs.es = SEG(__com32.cs_bounce); + __com32.cs_intcall(0x22, &inregs, &inregs); + + return lba; +} + +/* drive offset detection */ +int drvoff_detect(int type, unsigned int *off) +{ + if (bpbV40 <= type && type <= bpbVNT) { + *off = 0x24; + } else if (type == bpbV70) { + *off = 0x40; + } else + return 0; + + return -1; +} + +/* + * heuristics could certainly be improved + */ +int bpb_detect(const uint8_t *sec, const char *tag) +{ + int a, b, c, jmp = -1, rev = 0; + + /* media descriptor check */ + if ((sec[0x15] & 0xF0) != 0xF0) + goto out; + + if (sec[0] == 0xEB) /* jump short */ + jmp = 2 + *(int8_t *)(sec + 1); + else if (sec[0] == 0xE9) /* jump near */ + jmp = 3 + *(int16_t *)(sec + 1); + + if (jmp < 0) /* no boot code at all ? */ + goto nocode; + + /* sanity */ + if (jmp < 0x18 || jmp > 0x1F0) + goto out; + + /* detect by jump */ + if (jmp >= 0x18 && jmp < 0x1E) + rev = bpbV20; + else if (jmp >= 0x1E && jmp < 0x20) + rev = bpbV30; + else if (jmp >= 0x20 && jmp < 0x24) + rev = bpbV32; + else if (jmp >= 0x24 && jmp < 0x46) + rev = bpbV34; + + /* TODO: some better V2 - V3.4 checks ? */ + + if (rev) + goto out; + /* + * BPB info: + * 2.0 == 0x0B - 0x17 + * 3.0 == 2.0 + 0x18 - 0x1D + * 3.2 == 3.0 + 0x1E - 0x1F + * 3.4 ==!2.0 + 0x18 - 0x23 + * 4.0 == 3.4 + 0x24 - 0x45 + * NT ==~3.4 + 0x24 - 0x53 + * 7.0 == 3.4 + 0x24 - 0x59 + */ + +nocode: + a = memcmp(sec + 0x03, "NTFS", 4); + b = memcmp(sec + 0x36, "FAT", 3); + c = memcmp(sec + 0x52, "FAT", 3); /* ext. DOS 7+ bs */ + + if ((sec[0x26] & 0xFE) == 0x28 && !b) { + rev = bpbV40; + } else if (sec[0x26] == 0x80 && !a) { + rev = bpbVNT; + } else if ((sec[0x42] & 0xFE) == 0x28 && !c) { + rev = bpbV70; + } + +out: + printf("BPB detection (%s): %s\n", tag, bpbtypes[rev]); + return rev; +} + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/utility.h b/com32/chain/utility.h new file mode 100644 index 00000000..8a08be71 --- /dev/null +++ b/com32/chain/utility.h @@ -0,0 +1,30 @@ +#ifndef _COM32_CHAIN_UTILITY_H +#define _COM32_CHAIN_UTILITY_H + +#include <stdint.h> +#include <syslinux/disk.h> + +#define bpbUNK 0 +#define bpbV20 1 +#define bpbV30 2 +#define bpbV32 3 +#define bpbV34 4 +#define bpbV40 5 +#define bpbVNT 6 +#define bpbV70 7 + +#define l2c_cnul 0 +#define l2c_cadd 1 +#define l2c_cmax 2 + +void error(const char *msg); +int guid_is0(const struct guid *guid); +void wait_key(void); +void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t mode); +uint32_t get_file_lba(const char *filename); +int drvoff_detect(int type, unsigned int *off); +int bpb_detect(const uint8_t *bpb, const char *tag); + +#endif + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/gfxboot/gfxboot.c b/com32/gfxboot/gfxboot.c index 35d180a6..aa05caf8 100644 --- a/com32/gfxboot/gfxboot.c +++ b/com32/gfxboot/gfxboot.c @@ -962,7 +962,7 @@ void boot_entry(menu_t *menu_ptr, char *arg) gfx_done(); - syslinux_boot_linux(kernel, kernel_size, initrd, arg); + syslinux_boot_linux(kernel, kernel_size, initrd, NULL, arg); } diff --git a/com32/gplinclude/dmi/dmi_bios.h b/com32/gplinclude/dmi/dmi_bios.h index 5d47e899..4af7e0bd 100644 --- a/com32/gplinclude/dmi/dmi_bios.h +++ b/com32/gplinclude/dmi/dmi_bios.h @@ -22,7 +22,7 @@ #define BIOS_BIOS_REVISION_SIZE 16 #define BIOS_FIRMWARE_REVISION_SIZE 16 -#define BIOS_CHAR_NB_ELEMENTS 28 +#define BIOS_CHAR_NB_ELEMENTS 29 #define BIOS_CHAR_X1_NB_ELEMENTS 8 #define BIOS_CHAR_X2_NB_ELEMENTS 3 @@ -46,6 +46,7 @@ typedef struct { bool boot_from_cd; bool selectable_boot; bool bios_rom_socketed; + bool boot_from_pcmcia; bool edd; bool japanese_floppy_nec_9800_1_2MB; bool japanese_floppy_toshiba_1_2MB; diff --git a/com32/gpllib/disk/labels.c b/com32/gpllib/disk/labels.c index ad3d33b3..f27ff655 100644 --- a/com32/gpllib/disk/labels.c +++ b/com32/gpllib/disk/labels.c @@ -36,29 +36,29 @@ void get_label(int label, char **buffer_label) strlcpy(buffer, "DOS 3.31+ 16-bit FAT (over 32M)", buffer_size); break; case 0x07: - strlcpy(buffer, "OS/2 IFS (e.g., HPFS)", buffer_size); - break; + strlcpy(buffer, "NTFS/exFAT/HPFS", buffer_size); //case 0x07: strlcpy(buffer, "Advanced Unix", buffer_size); break; //case 0x07: strlcpy(buffer, "Windows NT NTFS", buffer_size); break; //case 0x07: strlcpy(buffer, "QNX2.x (pre-1988)", buffer_size); break; - case 0x08: - strlcpy(buffer, "OS/2 (v1.0-1.3 only)", buffer_size); break; + case 0x08: + strlcpy(buffer, "AIX", buffer_size); //case 0x08: strlcpy(buffer, "AIX boot partition", buffer_size); break; //case 0x08: strlcpy(buffer, "SplitDrive", buffer_size); break; //case 0x08: strlcpy(buffer, "DELL partition spanning multiple drives", buffer_size); break; //case 0x08: strlcpy(buffer, "Commodore DOS", buffer_size); break; //case 0x08: strlcpy(buffer, "QNX 1.x and 2.x ("qny")", buffer_size); break; - case 0x09: - strlcpy(buffer, "AIX data partition", buffer_size); break; + case 0x09: + strlcpy(buffer, "AIX bootable partition", buffer_size); //case 0x09: strlcpy(buffer, "Coherent filesystem", buffer_size); break; //case 0x09: strlcpy(buffer, "QNX 1.x and 2.x ("qnz")", buffer_size); break; + break; case 0x0a: strlcpy(buffer, "OS/2 Boot Manager", buffer_size); - break; //case 0x0a: strlcpy(buffer, "Coherent swap partition", buffer_size); break; //case 0x0a: strlcpy(buffer, "OPUS", buffer_size); break; + break; case 0x0b: strlcpy(buffer, "WIN95 OSR2 32-bit FAT", buffer_size); break; @@ -72,13 +72,13 @@ void get_label(int label, char **buffer_label) strlcpy(buffer, "WIN95: Extended partition, LBA-mapped", buffer_size); break; case 0x10: - strlcpy(buffer, "OPUS (?)", buffer_size); + strlcpy(buffer, "OPUS", buffer_size); break; case 0x11: strlcpy(buffer, "Hidden DOS 12-bit FAT", buffer_size); break; case 0x12: - strlcpy(buffer, "Compaq config partition", buffer_size); + strlcpy(buffer, "Compaq diagnostic partition", buffer_size); break; case 0x14: strlcpy(buffer, "Hidden DOS 16-bit FAT <32M", buffer_size); @@ -87,7 +87,7 @@ void get_label(int label, char **buffer_label) strlcpy(buffer, "Hidden DOS 16-bit FAT >=32M", buffer_size); break; case 0x17: - strlcpy(buffer, "Hidden IFS (e.g., HPFS)", buffer_size); + strlcpy(buffer, "Hidden HPFS/exFAT/NTFS", buffer_size); break; case 0x18: strlcpy(buffer, "AST SmartSleep Partition", buffer_size); @@ -111,8 +111,8 @@ void get_label(int label, char **buffer_label) break; case 0x21: strlcpy(buffer, "Reserved", buffer_size); - break; //case 0x21: strlcpy(buffer, "Unused", buffer_size); break; + break; case 0x22: strlcpy(buffer, "Unused", buffer_size); break; @@ -125,6 +125,18 @@ void get_label(int label, char **buffer_label) case 0x26: strlcpy(buffer, "Reserved", buffer_size); break; + case 0x27: + strlcpy(buffer, "PQService (Acer laptop hidden rescue partition)", buffer_size); + //Windows RE hidden partition + //MirOS BSD partition + //RouterBOOT kernel partition + break; + case 0x2a: + strlcpy(buffer, "AtheOS File System (AFS)", buffer_size); + break; + case 0x2b: + strlcpy(buffer, "SyllableSecure (SylStor)", buffer_size); + break; case 0x31: strlcpy(buffer, "Reserved", buffer_size); break; @@ -148,8 +160,8 @@ void get_label(int label, char **buffer_label) break; case 0x39: strlcpy(buffer, "Plan 9 partition", buffer_size); - break; //case 0x39: strlcpy(buffer, "THEOS ver 4 spanned partition", buffer_size); break; + break; case 0x3a: strlcpy(buffer, "THEOS ver 4 4gb partition", buffer_size); break; @@ -166,15 +178,15 @@ void get_label(int label, char **buffer_label) strlcpy(buffer, "Venix 80286", buffer_size); break; case 0x41: - strlcpy(buffer, "Linux/MINIX (sharing disk with DRDOS)", buffer_size); - break; + strlcpy(buffer, "PPC PReP Boot", buffer_size); break; //case 0x41: strlcpy(buffer, "Personal RISC Boot", buffer_size); break; - //case 0x41: strlcpy(buffer, "PPC PReP (Power PC Reference Platform) Boot", buffer_size); break; - case 0x42: - strlcpy(buffer, "Linux swap (sharing disk with DRDOS)", buffer_size); + // strlcpy(buffer, "Linux/MINIX (sharing disk with DRDOS)", buffer_size); break; - //case 0x42: strlcpy(buffer, "SFS (Secure Filesystem)", buffer_size); break; + case 0x42: + strlcpy(buffer, "SFS (Secure Filesystem)", buffer_size); break; + // strlcpy(buffer, "Linux swap (sharing disk with DRDOS)", buffer_size); //case 0x42: strlcpy(buffer, "Windows 2000 marker", buffer_size); break; + break; case 0x43: strlcpy(buffer, "Linux native (sharing disk with DRDOS)", buffer_size); break; @@ -183,9 +195,9 @@ void get_label(int label, char **buffer_label) break; case 0x45: strlcpy(buffer, "Boot-US boot manager", buffer_size); - break; //case 0x45: strlcpy(buffer, "Priam", buffer_size); break; //case 0x45: strlcpy(buffer, "EUMEL/Elan", buffer_size); break; + break; case 0x46: strlcpy(buffer, "EUMEL/Elan", buffer_size); break; @@ -197,8 +209,8 @@ void get_label(int label, char **buffer_label) break; case 0x4a: strlcpy(buffer, "AdaOS Aquila (Default)", buffer_size); - break; //case 0x4a: strlcpy(buffer, "ALFS/THIN lightweight filesystem for DOS", buffer_size); break; + break; case 0x4c: strlcpy(buffer, "Oberon partition", buffer_size); break; @@ -210,22 +222,22 @@ void get_label(int label, char **buffer_label) break; case 0x4f: strlcpy(buffer, "QNX4.x 3rd part", buffer_size); - break; //case 0x4f: strlcpy(buffer, "Oberon partition", buffer_size); break; + break; case 0x50: strlcpy(buffer, "OnTrack Disk Manager (older versions) RO", buffer_size); - break; //case 0x50: strlcpy(buffer, "Lynx RTOS", buffer_size); break; //case 0x50: strlcpy(buffer, "Native Oberon (alt)", buffer_size); break; + break; case 0x51: strlcpy(buffer, "OnTrack Disk Manager RW (DM6 Aux1)", buffer_size); - break; //case 0x51: strlcpy(buffer, "Novell", buffer_size); break; + break; case 0x52: strlcpy(buffer, "CP/M", buffer_size); - break; //case 0x52: strlcpy(buffer, "Microport SysV/AT", buffer_size); break; + break; case 0x53: strlcpy(buffer, "Disk Manager 6.0 Aux3", buffer_size); break; @@ -237,12 +249,12 @@ void get_label(int label, char **buffer_label) break; case 0x56: strlcpy(buffer, "Golden Bow VFeature Partitioned Volume.", buffer_size); - break; //case 0x56: strlcpy(buffer, "DM converted to EZ-BIOS", buffer_size); break; + break; case 0x57: strlcpy(buffer, "DrivePro", buffer_size); - break; //case 0x57: strlcpy(buffer, "VNDI Partition", buffer_size); break; + break; case 0x5c: strlcpy(buffer, "Priam EDisk", buffer_size); break; @@ -255,9 +267,9 @@ void get_label(int label, char **buffer_label) buffer_size); break; case 0x64: - strlcpy(buffer, "PC-ARMOUR protected partition", buffer_size); + strlcpy(buffer, "Novell Netware 286, 2.xx", buffer_size); break; + //strlcpy(buffer, "PC-ARMOUR protected partition", buffer_size); break; - //case 0x64: strlcpy(buffer, "Novell Netware 286, 2.xx", buffer_size); break; case 0x65: strlcpy(buffer, "Novell Netware 386, 3.xx or 4.xx", buffer_size); break; @@ -280,13 +292,15 @@ void get_label(int label, char **buffer_label) case 0x71: strlcpy(buffer, "Reserved", buffer_size); break; + case 0x72: + strlcpy(buffer, "V7/x86", buffer_size); + break; case 0x73: strlcpy(buffer, "Reserved", buffer_size); break; case 0x74: - strlcpy(buffer, "Reserved", buffer_size); - break; - //case 0x74: strlcpy(buffer, "Scramdisk partition", buffer_size); break; + //strlcpy(buffer, "Reserved", buffer_size); + strlcpy(buffer, "Scramdisk partition", buffer_size); break; case 0x75: strlcpy(buffer, "IBM PC/IX", buffer_size); break; @@ -301,37 +315,40 @@ void get_label(int label, char **buffer_label) strlcpy(buffer, "XOSL FS", buffer_size); break; case 0x7E: - strlcpy(buffer, " ", buffer_size); + strlcpy(buffer, "Unused", buffer_size); break; case 0x80: strlcpy(buffer, "MINIX until 1.4a", buffer_size); break; case 0x81: strlcpy(buffer, "MINIX since 1.4b, early Linux", buffer_size); - break; //case 0x81: strlcpy(buffer, "Mitac disk manager", buffer_size); break; - //case 0x82: strlcpy(buffer, "Prime", buffer_size); break; - //case 0x82: strlcpy(buffer, "Solaris x86", buffer_size); break; + break; case 0x82: strlcpy(buffer, "Linux swap", buffer_size); + //case 0x82: strlcpy(buffer, "Prime", buffer_size); break; + //case 0x82: strlcpy(buffer, "Solaris x86", buffer_size); break; break; case 0x83: strlcpy(buffer, "Linux native (usually ext2fs)", buffer_size); break; case 0x84: strlcpy(buffer, "OS/2 hidden C: drive", buffer_size); - break; //case 0x84: strlcpy(buffer, "Hibernation partition", buffer_size); break; + break; case 0x85: strlcpy(buffer, "Linux extended partition", buffer_size); break; - //case 0x86: strlcpy(buffer, "Old Linux RAID partition superblock", buffer_size); break; case 0x86: strlcpy(buffer, "NTFS volume set", buffer_size); + //case 0x86: strlcpy(buffer, "Old Linux RAID partition superblock", buffer_size); break; break; case 0x87: strlcpy(buffer, "NTFS volume set", buffer_size); break; + case 0x88: + strlcpy(buffer, "Linux Plaintext", buffer_size); + break; case 0x8a: strlcpy(buffer, "Linux Kernel Partition (used by AiR-BOOT)", buffer_size); @@ -349,7 +366,7 @@ void get_label(int label, char **buffer_label) buffer_size); break; case 0x8e: - strlcpy(buffer, "Linux Logical Volume Manager partition", buffer_size); + strlcpy(buffer, "Linux LVM partition", buffer_size); break; case 0x90: strlcpy(buffer, "Free FDISK hidden Primary DOS FAT16 partitition", @@ -365,14 +382,17 @@ void get_label(int label, char **buffer_label) break; case 0x93: strlcpy(buffer, "Hidden Linux native partition", buffer_size); - break; //case 0x93: strlcpy(buffer, "Amoeba", buffer_size); break; + break; case 0x94: strlcpy(buffer, "Amoeba bad block table", buffer_size); break; case 0x95: strlcpy(buffer, "MIT EXOPC native partitions", buffer_size); break; + case 0x96: + strlcpy(buffer, "CHRP ISO-9660 filesystem", buffer_size); + break; case 0x97: strlcpy(buffer, "Free FDISK hidden Primary DOS FAT32 partitition", buffer_size); @@ -392,6 +412,9 @@ void get_label(int label, char **buffer_label) strlcpy(buffer, "Free FDISK hidden DOS extended partitition (LBA)", buffer_size); break; + case 0x9e: + strlcpy(buffer, "ForthOS partition", buffer_size); + break; case 0x9f: strlcpy(buffer, "BSD/OS", buffer_size); break; @@ -400,13 +423,13 @@ void get_label(int label, char **buffer_label) break; case 0xa1: strlcpy(buffer, "Laptop hibernation partition", buffer_size); - break; //case 0xa1: strlcpy(buffer, "HP Volume Expansion (SpeedStor variant)", buffer_size); break; + break; case 0xa3: - strlcpy(buffer, "Reserved", buffer_size); + strlcpy(buffer, "HP Volume Expansion (SpeedStor variant)", buffer_size); break; case 0xa4: - strlcpy(buffer, "Reserved", buffer_size); + strlcpy(buffer, "HP Volume Expansion (SpeedStor variant)", buffer_size); break; case 0xa5: strlcpy(buffer, "BSD/386, 386BSD, NetBSD, FreeBSD", buffer_size); @@ -415,7 +438,7 @@ void get_label(int label, char **buffer_label) strlcpy(buffer, "OpenBSD", buffer_size); break; case 0xa7: - strlcpy(buffer, "NEXTSTEP", buffer_size); + strlcpy(buffer, "NeXTSTEP", buffer_size); break; case 0xa8: strlcpy(buffer, "Mac OS-X", buffer_size); @@ -429,8 +452,8 @@ void get_label(int label, char **buffer_label) break; case 0xab: strlcpy(buffer, "Mac OS-X Boot partition", buffer_size); - break; //case 0xab: strlcpy(buffer, "GO! partition", buffer_size); break; + break; case 0xae: strlcpy(buffer, "ShagOS filesystem", buffer_size); break; @@ -441,16 +464,19 @@ void get_label(int label, char **buffer_label) strlcpy(buffer, "BootStar Dummy", buffer_size); break; case 0xb1: - strlcpy(buffer, "Reserved", buffer_size); + strlcpy(buffer, "HP Volume Expansion (SpeedStor variant)", buffer_size); + break; + case 0xb2: + strlcpy(buffer, "QNX Neutrino Power-Safe filesystem", buffer_size); break; case 0xb3: - strlcpy(buffer, "Reserved", buffer_size); + strlcpy(buffer, "HP Volume Expansion (SpeedStor variant)", buffer_size); break; case 0xb4: - strlcpy(buffer, "Reserved", buffer_size); + strlcpy(buffer, "HP Volume Expansion (SpeedStor variant)", buffer_size); break; case 0xb6: - strlcpy(buffer, "Reserved", buffer_size); + strlcpy(buffer, "HP Volume Expansion (SpeedStor variant)", buffer_size); break; case 0xb7: strlcpy(buffer, "BSDI BSD/386 filesystem", buffer_size); @@ -461,21 +487,27 @@ void get_label(int label, char **buffer_label) case 0xbb: strlcpy(buffer, "Boot Wizard hidden", buffer_size); break; + case 0xbc: + strlcpy(buffer, "Acronis backup partition", buffer_size); + break; case 0xbe: strlcpy(buffer, "Solaris 8 boot partition", buffer_size); break; + case 0xbf: + strlcpy(buffer, "Solaris partition", buffer_size); + break; case 0xc0: strlcpy(buffer, "CTOS", buffer_size); - break; //case 0xc0: strlcpy(buffer, "REAL/32 secure small partition", buffer_size); break; //case 0xc0: strlcpy(buffer, "NTFT Partition", buffer_size); break; + break; case 0xc1: strlcpy(buffer, "DRDOS/secured (FAT-12)", buffer_size); break; case 0xc2: - strlcpy(buffer, "Reserved for DR-DOS 7+", buffer_size); + strlcpy(buffer, "Hidden Linux", buffer_size); break; + //strlcpy(buffer, "Reserved for DR-DOS 7+", buffer_size); break; - //case 0xc2: strlcpy(buffer, "Hidden Linux", buffer_size); break; case 0xc3: strlcpy(buffer, "Hidden Linux swap", buffer_size); break; @@ -487,21 +519,21 @@ void get_label(int label, char **buffer_label) break; case 0xc6: strlcpy(buffer, "DRDOS/secured (FAT-16, >= 32M)", buffer_size); - break; //case 0xc6: strlcpy(buffer, "Windows NT corrupted FAT16 volume/stripe set", buffer_size); break; + break; case 0xc7: strlcpy(buffer, "Windows NT corrupted NTFS volume/stripe set", buffer_size); - break; //case 0xc7: strlcpy(buffer, "Syrinx boot", buffer_size); break; + break; case 0xc8: - strlcpy(buffer, "(See also ID c2.)", buffer_size); + strlcpy(buffer, "Reserved for DR-DOS 8.0+", buffer_size); break; case 0xc9: - strlcpy(buffer, "(See also ID c2.)", buffer_size); + strlcpy(buffer, "Reserved for DR-DOS 8.0+", buffer_size); break; case 0xca: - strlcpy(buffer, "(See also ID c2.)", buffer_size); + strlcpy(buffer, "Reserved for DR-DOS 8.0+", buffer_size); break; case 0xcb: strlcpy(buffer, "reserved for DRDOS/secured (FAT32)", buffer_size); @@ -515,6 +547,9 @@ void get_label(int label, char **buffer_label) case 0xce: strlcpy(buffer, "reserved for DRDOS/secured (FAT16, LBA)", buffer_size); break; + case 0xcf: + strlcpy(buffer, "DR-DOS 7.04+ secured EXT DOS (LBA)", buffer_size); + break; case 0xd0: strlcpy(buffer, "REAL/32 secure big partition", buffer_size); break; @@ -541,9 +576,9 @@ void get_label(int label, char **buffer_label) strlcpy(buffer, "Digital Research CP/M, Concurrent CP/M, Concurrent DOS", buffer_size); - break; //case 0xdb: strlcpy(buffer, "CTOS (Convergent Technologies OS -Unisys)", buffer_size); break; //case 0xdb: strlcpy(buffer, "KDG Telemetry SCPU boot", buffer_size); break; + break; case 0xdd: strlcpy(buffer, "Hidden CTOS Memdump?", buffer_size); break; @@ -575,24 +610,30 @@ void get_label(int label, char **buffer_label) strlcpy(buffer, "Tandy DOS with logical sectored FAT (According to Powerquest.)", buffer_size); - break; //case 0xe5: strlcpy(buffer, "Reserved", buffer_size); break; + break; case 0xe6: - strlcpy(buffer, "Reserved", buffer_size); + strlcpy(buffer, "Storage Dimensions SpeedStor", buffer_size); + break; + case 0xe8: + strlcpy(buffer, "LUKS", buffer_size); break; case 0xeb: - strlcpy(buffer, "BFS (aka BeFS)", buffer_size); + strlcpy(buffer, "BeOS", buffer_size); + break; + case 0xec: + strlcpy(buffer, "SkyOS SkyFS", buffer_size); break; case 0xed: strlcpy(buffer, "Reserved for Matthias Paul's Sprytix", buffer_size); break; case 0xee: strlcpy(buffer, - "Indication that this legacy MBR is followed by an EFI header", + "GPT", buffer_size); break; case 0xef: - strlcpy(buffer, "Partition that contains an EFI file system", + strlcpy(buffer, "EFI file system", buffer_size); break; case 0xf0: @@ -613,8 +654,8 @@ void get_label(int label, char **buffer_label) break; case 0xf4: strlcpy(buffer, "SpeedStor large partition", buffer_size); - break; //case 0xf4: strlcpy(buffer, "Prologue single-volume partition", buffer_size); break; + break; case 0xf5: strlcpy(buffer, "Prologue multi-volume partition", buffer_size); break; @@ -623,6 +664,12 @@ void get_label(int label, char **buffer_label) "Reserved (Powerquest writes: Storage Dimensions SpeedStor. )", buffer_size); break; + case 0xf7: + strlcpy(buffer, "DDRdrive Solid State File System", buffer_size); + break; + case 0xf9: + strlcpy(buffer, "pCache", buffer_size); + break; case 0xfa: strlcpy(buffer, "Bochs", buffer_size); break; @@ -638,12 +685,12 @@ void get_label(int label, char **buffer_label) buffer_size); break; case 0xfe: - strlcpy(buffer, "SpeedStor > 1024 cyl.", buffer_size); - break; - //case 0xfe: strlcpy(buffer, "LANstep", buffer_size); break; + strlcpy(buffer, "LANstep", buffer_size); break; + //strlcpy(buffer, "SpeedStor > 1024 cyl.", buffer_size); //case 0xfe: strlcpy(buffer, "IBM PS/2 IML (Initial Microcode Load) partition, located at the end of the disk.", buffer_size); break; //case 0xfe: strlcpy(buffer, "Windows NT Disk Administrator hidden partition", buffer_size); break; //case 0xfe: strlcpy(buffer, "Linux Logical Volume Manager partition (old)", buffer_size); break; + break; case 0xff: strlcpy(buffer, "Xenix Bad Block Table ", buffer_size); break; diff --git a/com32/hdt/.gitignore b/com32/hdt/.gitignore index 98927943..82d5b472 100644 --- a/com32/hdt/.gitignore +++ b/com32/hdt/.gitignore @@ -5,3 +5,4 @@ floppy/syslinux.cfg *.iso iso/ *gz +hdt*checksums diff --git a/com32/hdt/Makefile b/com32/hdt/Makefile index f1873465..add640a7 100644 --- a/com32/hdt/Makefile +++ b/com32/hdt/Makefile @@ -45,7 +45,7 @@ FLOPPY_DIR ?= floppy PCI_IDS_FILE ?= $(PWD)/$(FLOPPY_DIR)/pci.ids GZ_PCI_IDS_FILE ?= $(PCI_IDS_FILE).gz MENU_COM32 ?= $(com32)/menu/menu.c32 -CHAIN_COM32 ?= $(com32)/modules/chain.c32 +CHAIN_COM32 ?= $(com32)/chain/chain.c32 ART_DIR ?= art/ QEMU ?= qemu-kvm @@ -74,6 +74,8 @@ hdt.img: hdt.c32 $(FLOPPY_DIR)/hdt.cfg $(FLOPPY_DIR)/mtools.conf $(topdir)/mtool MTOOLSRC=$(PWD)/$(FLOPPY_DIR)/mtools.conf $(MCOPY) $(FLOPPY_DIR)/syslinux.cfg a: MTOOLSRC=$(PWD)/$(FLOPPY_DIR)/mtools.conf $(MCOPY) $(FLOPPY_DIR)/$(MEMTEST) a: MTOOLSRC=$(PWD)/$(FLOPPY_DIR)/mtools.conf $(MCOPY) $(ART_DIR)/backgnd.png a: + MTOOLSRC=$(PWD)/$(FLOPPY_DIR)/mtools.conf $(MCOPY) $(ART_DIR)/display.png a: + MTOOLSRC=$(PWD)/$(FLOPPY_DIR)/mtools.conf $(MCOPY) $(ART_DIR)/red.png a: mv hdt.img hdt-$(VERSION).img ln -sf hdt-$(VERSION).img hdt.img @@ -94,6 +96,8 @@ hdt.iso: hdt.c32 $(topdir)/core/isolinux.bin $(FLOPPY_DIR)/hdt.cfg memtest cp $(MENU_COM32) $(ISO_DIR)/$(ISOLINUX_DIR) cp $(CHAIN_COM32) $(ISO_DIR)/$(ISOLINUX_DIR) cp -av $(ART_DIR)/backgnd.png $(ISO_DIR)/$(ISOLINUX_DIR) + cp -av $(ART_DIR)/display.png $(ISO_DIR)/$(ISOLINUX_DIR) + cp -av $(ART_DIR)/red.png $(ISO_DIR)/$(ISOLINUX_DIR) -[ ! -f $(GZ_PCI_IDS_FILE) ] && cp /usr/share/hwdata/pci.ids $(PCI_IDS_FILE) && $(GZIPPROG) $(PCI_IDS_FILE) -[ ! -f $(GZ_PCI_IDS_FILE) ] && cp /usr/share/pci.ids $(PCI_IDS_FILE) && $(GZIPPROG) $(PCI_IDS_FILE) -[ -f $(MODULES_ALIAS_FILE) ] && cp $(MODULES_ALIAS_FILE) $(ISO_DIR)/$(ISOLINUX_DIR)\ @@ -110,10 +114,16 @@ hdt.iso: hdt.c32 $(topdir)/core/isolinux.bin $(FLOPPY_DIR)/hdt.cfg memtest mv hdt.iso hdt-$(VERSION).iso ln -sf hdt-$(VERSION).iso hdt.iso -release: spotless hdt.c32 hdt.img hdt.img.gz hdt.iso +hdt-hybrid.iso: hdt.iso ../../utils/isohybrid + cp hdt-$(VERSION).iso hdt-hybrid-$(VERSION).iso + ../../utils/isohybrid --partok hdt-hybrid-$(VERSION).iso + ln -sf hdt-hybrid-$(VERSION).iso hdt-hybrid.iso + +release: spotless hdt.c32 hdt.img hdt.img.gz hdt.iso hdt-hybrid.iso mv hdt.c32 hdt_$(NODASH_VERSION).c32 md5sum hdt_$(NODASH_VERSION).c32 >$(SUM_FILE) md5sum hdt-$(VERSION).iso >>$(SUM_FILE) + md5sum hdt-hybrid-$(VERSION).iso >>$(SUM_FILE) md5sum hdt-$(VERSION).img >>$(SUM_FILE) md5sum hdt-$(VERSION).img.gz >>$(SUM_FILE) diff --git a/com32/hdt/art/display.png b/com32/hdt/art/display.png Binary files differnew file mode 100644 index 00000000..31fabef6 --- /dev/null +++ b/com32/hdt/art/display.png diff --git a/com32/hdt/art/red.png b/com32/hdt/art/red.png Binary files differnew file mode 100644 index 00000000..c5616ac2 --- /dev/null +++ b/com32/hdt/art/red.png diff --git a/com32/hdt/floppy/hdt.cfg b/com32/hdt/floppy/hdt.cfg index c876d239..524c4e06 100644 --- a/com32/hdt/floppy/hdt.cfg +++ b/com32/hdt/floppy/hdt.cfg @@ -86,6 +86,25 @@ ENDTEXT COM32 hdt.c32 APPEND modules_pcimap=modules.pcimap modules_alias=modules.alias pciids=pci.ids quiet vesa nomenu auto='dump' +LABEL postexec +MENU LABEL Dump memory info, sleep 5 and start memtest entry +TEXT HELP + Starts HDT using the Command Line Interface (CLI), show an item, say a message reboot and start memtest + VESA mode is enabled +ENDTEXT +COM32 hdt.c32 +APPEND modules_pcimap=modules.pcimap modules_alias=modules.alias pciids=pci.ids quiet vesa nomenu auto='show memory;say `########`;say `Starting memtest in 5 sec`;sleep 5;exit' postexec='memtest' + +LABEL display +MENU LABEL Display feature (VESA mode) +TEXT HELP + Starts HDT using the Command Line Interface (CLI) + VESA mode is enabled + A Picture is shown by using the display command +ENDTEXT +COM32 hdt.c32 +APPEND modules_pcimap=modules.pcimap modules_alias=modules.alias pciids=pci.ids silent nomenu vesa auto='display display.png; sleep 5000; display red.png' + MENU SEPARATOR LABEL memtest diff --git a/com32/hdt/hdt-cli-acpi.c b/com32/hdt/hdt-cli-acpi.c index a978bb36..55b0c3c7 100644 --- a/com32/hdt/hdt-cli-acpi.c +++ b/com32/hdt/hdt-cli-acpi.c @@ -267,10 +267,12 @@ struct cli_callback_descr list_acpi_show_modules[] = { { .name = "apic", .exec = show_acpi_apic, + .nomodule = false, }, { .name = NULL, .exec = NULL, + .nomodule = false, }, }; diff --git a/com32/hdt/hdt-cli-disk.c b/com32/hdt/hdt-cli-disk.c index 24fce676..10c95d7d 100644 --- a/com32/hdt/hdt-cli-disk.c +++ b/com32/hdt/hdt-cli-disk.c @@ -225,14 +225,17 @@ struct cli_callback_descr list_disk_show_modules[] = { { .name = "disks", .exec = main_show_disks, + .nomodule = false, }, { .name = "disk", .exec = main_show_disk, + .nomodule = false, }, { .name = NULL, .exec = NULL, + .nomodule = false, }, }; diff --git a/com32/hdt/hdt-cli-dmi.c b/com32/hdt/hdt-cli-dmi.c index 45cbb240..02bea0f7 100644 --- a/com32/hdt/hdt-cli-dmi.c +++ b/com32/hdt/hdt-cli-dmi.c @@ -617,62 +617,77 @@ struct cli_callback_descr list_dmi_show_modules[] = { { .name = CLI_DMI_BASE_BOARD, .exec = show_dmi_base_board, + .nomodule = false, }, { .name = CLI_DMI_BIOS, .exec = show_dmi_bios, + .nomodule = false, }, { .name = CLI_DMI_BATTERY, .exec = show_dmi_battery, + .nomodule = false, }, { .name = CLI_DMI_CHASSIS, .exec = show_dmi_chassis, + .nomodule = false, }, { .name = CLI_DMI_MEMORY, .exec = show_dmi_memory_modules, + .nomodule = false, }, { .name = CLI_DMI_MEMORY_BANK, .exec = show_dmi_memory_bank, + .nomodule = false, }, { .name = "module", .exec = show_dmi_memory_module, + .nomodule = false, }, { .name = CLI_DMI_PROCESSOR, .exec = show_dmi_cpu, + .nomodule = false, }, { .name = CLI_DMI_SYSTEM, .exec = show_dmi_system, + .nomodule = false, }, { .name = CLI_DMI_OEM, .exec = show_dmi_oem_strings, + .nomodule = false, }, { .name = CLI_DMI_SECURITY, .exec = show_dmi_hardware_security, + .nomodule = false, }, { .name = CLI_DMI_IPMI, .exec = show_dmi_ipmi, + .nomodule = false, }, { .name = CLI_DMI_CACHE, .exec = show_dmi_cache, + .nomodule = false, }, { .name = CLI_DMI_LIST, .exec = show_dmi_modules, + .nomodule = false, }, { .name = NULL, .exec = NULL, + .nomodule = false, }, }; diff --git a/com32/hdt/hdt-cli-hdt.c b/com32/hdt/hdt-cli-hdt.c index e9752612..3c571d60 100644 --- a/com32/hdt/hdt-cli-hdt.c +++ b/com32/hdt/hdt-cli-hdt.c @@ -54,12 +54,12 @@ static void main_show_modes(int argc __unused, char **argv __unused, int i = 0; reset_more_printf(); - printf("Available modes:\n"); + more_printf("Available modes:\n"); while (list_modes[i]) { printf("%s ", list_modes[i]->name); i++; } - printf("\n"); + more_printf("\n"); } /** @@ -119,7 +119,7 @@ static void show_cli_help(int argc __unused, char **argv __unused, find_cli_mode_descr(hdt_cli.mode, ¤t_mode); - printf("Available commands are:\n"); + more_printf("Available commands are:\n"); /* List first default modules of the mode */ if (current_mode->default_modules && current_mode->default_modules->modules) { @@ -154,7 +154,7 @@ static void show_cli_help(int argc __unused, char **argv __unused, /* List secondly the show modules of the mode */ if (current_mode->show_modules && current_mode->show_modules->modules) { - printf("\nshow commands:\n"); + more_printf("\nshow commands:\n"); j = 0; while (current_mode->show_modules->modules[j].name) { printf("%s ", current_mode->show_modules->modules[j].name); @@ -165,7 +165,7 @@ static void show_cli_help(int argc __unused, char **argv __unused, /* List thirdly the set modules of the mode */ if (current_mode->set_modules && current_mode->set_modules->modules) { - printf("\nset commands:\n"); + more_printf("\nset commands:\n"); j = 0; while (current_mode->set_modules->modules[j].name) { printf("%s ", current_mode->set_modules->modules[j].name); @@ -259,101 +259,200 @@ static void do_dump(int argc __unused, char **argv __unused, dump(hardware); } +/** + * do_sleep - sleep a number of milliseconds + **/ +static void do_sleep(int argc , char **argv , + struct s_hardware *hardware) +{ + (void) hardware; + if (argc != 1) return; + more_printf("Sleep %d milliseconds\n",atoi(argv[0])); + msleep(atoi(argv[0])); +} + +/** + * do_display - display an image to user + **/ +static void do_display(int argc , char **argv , + struct s_hardware *hardware) +{ + (void) hardware; + if ((argc != 1) || (vesamode == false)) return; + more_printf("Display %s file\n",argv[0]); + vesacon_load_background(argv[0]); +} + +/** + * do_say - say message to user + **/ +static void do_say(int argc , char **argv , + struct s_hardware *hardware) +{ + (void) hardware; + if (argc == 0) return; + + char text_to_say[255]={0}; + int arg=0; +#if DEBUG + for (int i=0; i<argc;i++) dprintf("SAY: arg[%d]={%s}\n",i,argv[i]); +#endif + char *argument = strchr(argv[arg],'`'); + if ( argument != NULL) { + argument++; + strcat(text_to_say, argument); + + while ((strchr(argument, '`') == NULL) && (arg+1<argc)) { + arg++; + argument = (char *)argv[arg]; + strcat(text_to_say, " "); + strcat(text_to_say, argument); + } + + /* Removing last ` if any */ + char *last_quote = strrchr(text_to_say,'`'); + if ( last_quote != NULL ) { + *last_quote='\0'; + dprintf("SAY CMD = [%s]\n",text_to_say); + } + + more_printf("%s\n",text_to_say); + } +} + /* Default hdt mode */ struct cli_callback_descr list_hdt_default_modules[] = { { .name = CLI_CLEAR, .exec = cli_clear_screen, + .nomodule = false, }, { .name = CLI_EXIT, .exec = do_exit, + .nomodule = false, }, { .name = CLI_HELP, .exec = show_cli_help, + .nomodule = false, }, { .name = CLI_MENU, .exec = goto_menu, + .nomodule = false, }, { .name = CLI_REBOOT, .exec = do_reboot, + .nomodule = false, }, { .name = CLI_HISTORY, .exec = print_history, + .nomodule = false, }, { .name = CLI_DUMP, .exec = do_dump, + .nomodule = false, + }, + { + .name = CLI_SAY, + .exec = do_say, + .nomodule = true, + }, + { + .name = CLI_DISPLAY, + .exec = do_display, + .nomodule = true, + }, + { + .name = CLI_SLEEP, + .exec = do_sleep, + .nomodule = true, }, { .name = NULL, - .exec = NULL}, + .exec = NULL, + .nomodule = false}, }; struct cli_callback_descr list_hdt_show_modules[] = { { .name = CLI_SUMMARY, .exec = main_show_summary, + .nomodule = false, }, { .name = CLI_PCI, .exec = main_show_pci, + .nomodule = false, }, { .name = CLI_DMI, .exec = main_show_dmi, + .nomodule = false, }, { .name = CLI_CPU, .exec = main_show_cpu, + .nomodule = false, }, { .name = CLI_DISK, .exec = disks_summary, + .nomodule = false, }, { .name = CLI_PXE, .exec = main_show_pxe, + .nomodule = false, }, { .name = CLI_SYSLINUX, .exec = main_show_syslinux, + .nomodule = false, }, { .name = CLI_KERNEL, .exec = main_show_kernel, + .nomodule = false, }, { .name = CLI_VESA, .exec = main_show_vesa, + .nomodule = false, }, { .name = CLI_HDT, .exec = main_show_hdt, + .nomodule = false, }, { .name = CLI_VPD, .exec = main_show_vpd, + .nomodule = false, }, { .name = CLI_MEMORY, .exec = show_dmi_memory_modules, + .nomodule = false, }, { .name = CLI_ACPI, .exec = main_show_acpi, + .nomodule = false, }, { .name = "modes", .exec = main_show_modes, + .nomodule = false, }, { .name = NULL, .exec = NULL, + .nomodule = false, }, }; @@ -361,10 +460,12 @@ struct cli_callback_descr list_hdt_set_modules[] = { { .name = CLI_MODE, .exec = cli_set_mode, + .nomodule = false, }, { .name = NULL, .exec = NULL, + .nomodule = false, }, }; diff --git a/com32/hdt/hdt-cli-memory.c b/com32/hdt/hdt-cli-memory.c index 51d087e8..c05b7cd6 100644 --- a/com32/hdt/hdt-cli-memory.c +++ b/com32/hdt/hdt-cli-memory.c @@ -101,22 +101,27 @@ struct cli_callback_descr list_memory_show_modules[] = { { .name = "e820", .exec = show_memory_e820, + .nomodule=false, }, { .name = "e801", .exec = show_memory_e801, + .nomodule=false, }, { .name = "88", .exec = show_memory_88, + .nomodule=false, }, { .name = CLI_DMI_MEMORY_BANK, .exec = show_dmi_memory_bank, + .nomodule=false, }, { .name = NULL, .exec = NULL, + .nomodule=false, }, }; diff --git a/com32/hdt/hdt-cli-pci.c b/com32/hdt/hdt-cli-pci.c index 07c079d5..75fc0011 100644 --- a/com32/hdt/hdt-cli-pci.c +++ b/com32/hdt/hdt-cli-pci.c @@ -266,14 +266,17 @@ struct cli_callback_descr list_pci_show_modules[] = { { .name = CLI_IRQ, .exec = show_pci_irq, + .nomodule=false, }, { .name = CLI_PCI_DEVICE, .exec = show_pci_device, + .nomodule=false, }, { .name = NULL, .exec = NULL, + .nomodule=false, }, }; diff --git a/com32/hdt/hdt-cli-vesa.c b/com32/hdt/hdt-cli-vesa.c index 702f8bd6..ca44987a 100644 --- a/com32/hdt/hdt-cli-vesa.c +++ b/com32/hdt/hdt-cli-vesa.c @@ -98,10 +98,12 @@ struct cli_callback_descr list_vesa_show_modules[] = { { .name = CLI_MODES, .exec = show_vesa_modes, + .nomodule=false, }, { .name = NULL, .exec = NULL, + .nomodule=false, }, }; @@ -109,15 +111,18 @@ struct cli_callback_descr list_vesa_commands[] = { { .name = CLI_ENABLE, .exec = enable_vesa, + .nomodule=false, }, { .name = CLI_DISABLE, .exec = disable_vesa, + .nomodule=false, }, { .name = NULL, .exec = NULL, + .nomodule=false, }, }; diff --git a/com32/hdt/hdt-cli.c b/com32/hdt/hdt-cli.c index 330f93c4..7542da83 100644 --- a/com32/hdt/hdt-cli.c +++ b/com32/hdt/hdt-cli.c @@ -132,7 +132,7 @@ void set_mode(cli_mode_t mode, struct s_hardware *hardware) break; case PXE_MODE: if (hardware->sv->filesystem != SYSLINUX_FS_PXELINUX) { - printf("You are not currently using PXELINUX\n"); + more_printf("You are not currently using PXELINUX\n"); break; } hdt_cli.mode = mode; @@ -160,7 +160,7 @@ void set_mode(cli_mode_t mode, struct s_hardware *hardware) break; case DMI_MODE: if (!hardware->is_dmi_valid) { - printf("No valid DMI table found, exiting.\n"); + more_printf("No valid DMI table found, exiting.\n"); break; } hdt_cli.mode = mode; @@ -172,7 +172,7 @@ void set_mode(cli_mode_t mode, struct s_hardware *hardware) break; case VPD_MODE: if (!hardware->is_vpd_valid) { - printf("No valid VPD table found, exiting.\n"); + more_printf("No valid VPD table found, exiting.\n"); break; } hdt_cli.mode = mode; @@ -188,9 +188,9 @@ void set_mode(cli_mode_t mode, struct s_hardware *hardware) break; default: /* Invalid mode */ - printf("Unknown mode, please choose among:\n"); + more_printf("Unknown mode, please choose among:\n"); while (list_modes[i]) { - printf("\t%s\n", list_modes[i]->name); + more_printf("\t%s\n", list_modes[i]->name); i++; } } @@ -199,7 +199,7 @@ void set_mode(cli_mode_t mode, struct s_hardware *hardware) /* There is not cli_mode_descr struct for the exit mode */ if (current_mode == NULL && hdt_cli.mode != EXIT_MODE) { /* Shouldn't get here... */ - printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode); + more_printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode); } } @@ -365,14 +365,14 @@ static void parse_command_line(char *line, char **command, char **module, *command = malloc((token_len + 1) * sizeof(char)); strlcpy(*command, pch, token_len); (*command)[token_len] = '\0'; - dprintf("CLI DEBUG: command = %s\n", *command); + dprintf("CLI DEBUG parse: command = %s\n", *command); args_pos += args_len; } else if (token_found == 1) { /* Module */ *module = malloc((token_len + 1) * sizeof(char)); strlcpy(*module, pch, token_len); (*module)[token_len] = '\0'; - dprintf("CLI DEBUG: module = %s\n", *module); + dprintf("CLI DEBUG parse: module = %s\n", *module); args_pos += args_len; } else (*argc)++; @@ -380,7 +380,7 @@ static void parse_command_line(char *line, char **command, char **module, token_found++; pch = pch_next; } - dprintf("CLI DEBUG: argc = %d\n", *argc); + dprintf("CLI DEBUG parse: argc = %d\n", *argc); /* Skip arguments handling if none is supplied */ if (!*argc) @@ -390,9 +390,9 @@ static void parse_command_line(char *line, char **command, char **module, *argv = malloc(*argc * sizeof(char *)); pch = strtok(line + args_pos, CLI_SPACE); while (pch != NULL) { - dprintf("CLI DEBUG: argv[%d] = %s\n", argc_iter, pch); - argv[argc_iter] = malloc(sizeof(pch) * sizeof(char)); - strlcpy(argv[argc_iter], pch, sizeof(pch)); + dprintf("CLI DEBUG parse: argv[%d] = %s\n", argc_iter, pch); + argv[argc_iter] = malloc(strlen(pch) * sizeof(char)); + strlcpy(argv[argc_iter], pch, strlen(pch)); argc_iter++; pch = strtok(NULL, CLI_SPACE); /* @@ -585,6 +585,7 @@ static void autocomplete(char *line) parse_command_line(line, &command, &module, &argc, argv); + dprintf("CLI DEBUG autocomplete: before checking args\n"); /* If the user specified arguments, there is nothing we can complete */ if (argc != 0) goto out; @@ -625,22 +626,43 @@ static void exec_command(char *line, struct s_hardware *hardware) /* This will allocate memory for command and module */ parse_command_line(line, &command, &module, &argc, argv); + dprintf("CLI DEBUG exec: Checking for aliases\n"); /* * Expand shortcuts, if needed * This will allocate memory for argc/argv */ expand_aliases(line, &command, &module, &argc, argv); + + find_cli_callback_descr(command, current_mode->default_modules, + ¤t_module); - if (module == NULL) { - dprintf("CLI DEBUG: single command detected\n"); + if ((module == NULL) || (current_module->nomodule == true)) { + dprintf("CLI DEBUG exec : single command detected\n"); /* * A single word was specified: look at the list of default * commands in the current mode to see if there is a match. * If not, it may be a generic function (exit, help, ...). These * are stored in the list of default commands of the hdt mode. */ - find_cli_callback_descr(command, current_mode->default_modules, - ¤t_module); + + /* First of all it the command doesn't need module, let's rework the arguments */ + if ((current_module->nomodule == true) && ( module != NULL)) { + dprintf("CLI_DEBUG exec: Reworking arguments with argc=%d\n",argc); + char **new_argv=NULL; + new_argv=malloc((argc + 2)*sizeof(char)); + for (int argc_iter=0; argc_iter<argc; argc_iter++) { + dprintf("CLI_DEBUG exec rework : copy %d to %d (%s)\n",argc_iter,argc_iter+1,argv[argc_iter]); + new_argv[argc_iter+1] = malloc(strlen(argv[argc_iter])); + strlcpy(new_argv[argc_iter+1], argv[argc_iter], strlen(argv[argc_iter])); + free(argv[argc_iter]); + } + new_argv[0] = malloc(strlen(module)*sizeof(char)); + strlcpy(new_argv[0], module, strlen(module)); + argc++; + free(argv); + argv=new_argv; + } + if (current_module != NULL) current_module->exec(argc, argv, hardware); else if (!strncmp(command, CLI_SHOW, sizeof(CLI_SHOW) - 1) && @@ -657,7 +679,7 @@ static void exec_command(char *line, struct s_hardware *hardware) if (current_module != NULL) current_module->exec(argc, argv, hardware); else - printf("unknown command: '%s'\n", command); + more_printf("unknown command: '%s'\n", command); } } else { /* @@ -673,7 +695,7 @@ static void exec_command(char *line, struct s_hardware *hardware) * hdt> set mode dmi */ if (!strncmp(command, CLI_SHOW, sizeof(CLI_SHOW) - 1)) { - dprintf("CLI DEBUG: %s command detected\n", CLI_SHOW); + dprintf("CLI DEBUG exec: %s command detected\n", CLI_SHOW); /* Look first for a 'show' callback in the current mode */ find_cli_callback_descr(module, current_mode->show_modules, ¤t_module); @@ -681,6 +703,7 @@ static void exec_command(char *line, struct s_hardware *hardware) if (current_module != NULL) current_module->exec(argc, argv, hardware); else { + dprintf("CLI DEBUG exec: Looking for callback\n"); /* Look now for a 'show' callback in the hdt mode */ find_cli_callback_descr(module, hdt_mode.show_modules, ¤t_module); @@ -691,7 +714,7 @@ static void exec_command(char *line, struct s_hardware *hardware) printf("unknown module: '%s'\n", module); } } else if (!strncmp(command, CLI_SET, sizeof(CLI_SET) - 1)) { - dprintf("CLI DEBUG: %s command detected\n", CLI_SET); + dprintf("CLI DEBUG exec : %s command detected\n", CLI_SET); /* Look now for a 'set' callback in the hdt mode */ find_cli_callback_descr(module, current_mode->set_modules, ¤t_module); @@ -810,7 +833,7 @@ void start_cli_mode(struct s_hardware *hardware) find_cli_mode_descr(hdt_cli.mode, ¤t_mode); if (current_mode == NULL) { /* Shouldn't get here... */ - printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode); + more_printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode); return; } @@ -819,7 +842,7 @@ void start_cli_mode(struct s_hardware *hardware) start_auto_mode(hardware); } - printf("Entering CLI mode\n"); + more_printf("Entering CLI mode\n"); reset_prompt(); diff --git a/com32/hdt/hdt-cli.h b/com32/hdt/hdt-cli.h index 68b33158..82a4fc99 100644 --- a/com32/hdt/hdt-cli.h +++ b/com32/hdt/hdt-cli.h @@ -66,6 +66,9 @@ #define CLI_ENABLE "enable" #define CLI_DISABLE "disable" #define CLI_DUMP "dump" +#define CLI_SAY "say" +#define CLI_DISPLAY "display" +#define CLI_SLEEP "sleep" typedef enum { INVALID_MODE, @@ -120,6 +123,7 @@ struct cli_module_descr { struct cli_callback_descr { const char *name; void (*exec) (int argc, char **argv, struct s_hardware * hardware); + bool nomodule; }; /* Manage aliases */ diff --git a/com32/hdt/hdt-common.c b/com32/hdt/hdt-common.c index aac50eb9..dcad28cd 100644 --- a/com32/hdt/hdt-common.c +++ b/com32/hdt/hdt-common.c @@ -63,6 +63,9 @@ void detect_parameters(const int argc, const char *argv[], /* Quiet mode - make the output more quiet */ quiet = true; + /* Silent mode - make not output at all */ + silent = false; + /* Vesa mode isn't set until we explictly call it */ vesamode = false; @@ -75,6 +78,8 @@ void detect_parameters(const int argc, const char *argv[], for (int i = 1; i < argc; i++) { if (!strncmp(argv[i], "quiet", 5)) { quiet = true; + } else if (!strncmp(argv[i], "silent", 6)) { + silent = true; } else if (!strncmp(argv[i], "verbose", 7)) { quiet = false; } else if (!strncmp(argv[i], "modules_pcimap=", 15)) { @@ -106,12 +111,36 @@ void detect_parameters(const int argc, const char *argv[], max_console_lines = MAX_CLI_LINES; } else if (!strncmp(argv[i], "nomenu", 6)) { menumode = false; + } else if (!strncmp(argv[i], "dump_filename=", 14)) { + strlcpy(hardware->dump_filename, argv[i] + 14, + sizeof(hardware->dump_filename)); } else if (!strncmp(argv[i], "dump_path=", 10)) { strlcpy(hardware->dump_path, argv[i] + 10, sizeof(hardware->dump_path)); } else if (!strncmp(argv[i], "tftp_ip=", 8)) { strlcpy(hardware->tftp_ip, argv[i] + 8, sizeof(hardware->tftp_ip)); + } else if (!strncmp(argv[i], "postexec=", 9)) { + /* The postexec= parameter is separated in several argv[] + * as it can contains spaces. + * We use the AUTO_DELIMITER char to define the limits + * of this parameter. + * i.e postexec='linux memtest.bin' + */ + + char *argument = (char*)argv[i]+10; + /* Extracting the first parameter */ + strcpy(hardware->postexec, argument); + + /* While we can't find the other AUTO_DELIMITER, let's process the argv[] */ + while ((strchr(argument, AUTO_DELIMITER) == NULL) && (i+1<argc)) { + i++; + argument = (char *)argv[i]; + strcat(hardware->postexec, " "); + strcat(hardware->postexec, argument); + } + + hardware->postexec[strlen(hardware->postexec) - 1] = 0; } else if (!strncmp(argv[i], "auto=", 5)) { /* The auto= parameter is separated in several argv[] * as it can contains spaces. @@ -204,9 +233,12 @@ void init_hardware(struct s_hardware *hardware) memset(hardware->memtest_label, 0, sizeof hardware->memtest_label); memset(hardware->auto_label, 0, sizeof hardware->auto_label); memset(hardware->dump_path, 0, sizeof hardware->dump_path); + memset(hardware->dump_filename, 0, sizeof hardware->dump_filename); memset(hardware->vesa_background, 0, sizeof hardware->vesa_background); memset(hardware->tftp_ip, 0, sizeof hardware->tftp_ip); + memset(hardware->postexec, 0, sizeof hardware->postexec); strcat(hardware->dump_path, "hdt"); + strcat(hardware->dump_filename, "%{m}+%{p}+%{v}"); strcat(hardware->pciids_path, "pci.ids"); strcat(hardware->modules_pcimap_path, "modules.pcimap"); strcat(hardware->modules_alias_path, "modules.alias"); @@ -712,8 +744,8 @@ void detect_hardware(struct s_hardware *hardware) if (!quiet) more_printf("DMI: Detecting Table\n"); if (detect_dmi(hardware) == -ENODMITABLE) { - printf("DMI: ERROR ! Table not found ! \n"); - printf("DMI: Many hardware components will not be detected ! \n"); + more_printf("DMI: ERROR ! Table not found ! \n"); + more_printf("DMI: Many hardware components will not be detected ! \n"); } else { if (!quiet) more_printf("DMI: Table found ! (version %u.%u)\n", diff --git a/com32/hdt/hdt-common.h b/com32/hdt/hdt-common.h index d37fcc8a..c2299b48 100644 --- a/com32/hdt/hdt-common.h +++ b/com32/hdt/hdt-common.h @@ -87,6 +87,9 @@ struct upload_backend *upload; /* Defines if the cli is quiet*/ bool quiet; +/* Defines if the cli is totally silent*/ +bool silent; + /* Defines if we must use the vesa mode */ bool vesamode; @@ -114,16 +117,18 @@ extern bool disable_more_printf; * one \n (and only one) */ #define more_printf(...) do {\ - if (__likely(!disable_more_printf)) {\ - if (display_line_nb == max_console_lines) {\ - display_line_nb=0;\ - printf("\n--More--");\ - get_key(stdin, 0);\ - printf("\033[2K\033[1G\033[1F");\ + if (__likely(!silent)) {\ + if (__likely(!disable_more_printf)) {\ + if (display_line_nb == max_console_lines) {\ + display_line_nb=0;\ + printf("\n--More--");\ + get_key(stdin, 0);\ + printf("\033[2K\033[1G\033[1F");\ + }\ + display_line_nb++;\ }\ - display_line_nb++;\ + printf(__VA_ARGS__);\ }\ - printf(__VA_ARGS__);\ } while (0); /* Display CPU registers for debugging purposes */ @@ -214,10 +219,12 @@ struct s_hardware { char modules_alias_path[255]; char pciids_path[255]; char dump_path[255]; /* Dump path on the tftp server */ + char dump_filename[255]; /* Dump filename on the tftp server */ char tftp_ip[255]; /* IP address of tftp server (dump mode) */ char memtest_label[255]; char auto_label[AUTO_COMMAND_SIZE]; char vesa_background[255]; + char postexec[255]; }; void reset_more_printf(void); diff --git a/com32/hdt/hdt-dump-disks.c b/com32/hdt/hdt-dump-disks.c index dcbcaa9e..ff744b30 100644 --- a/com32/hdt/hdt-dump-disks.c +++ b/com32/hdt/hdt-dump-disks.c @@ -42,6 +42,7 @@ static void show_partition_information(struct driveinfo *drive_info, char ostype[64]={0}; char *parttype; unsigned int start, end; + char bootable[6] = {0}; int i = nb_partitions_seen; start = partition_offset; @@ -52,6 +53,10 @@ static void show_partition_information(struct driveinfo *drive_info, get_label(ptab->ostype, &parttype); get_bootloader_string(drive_info, ptab, bootloader_name, 9); + if (ptab->active_flag == 0x80) + snprintf(bootable,sizeof(bootable),"%s","true"); + else + snprintf(bootable,sizeof(bootable),"%s","false"); snprintf(ostype,sizeof(ostype),"%02X",ptab->ostype); @@ -62,6 +67,7 @@ static void show_partition_information(struct driveinfo *drive_info, add_as("partition->size",size) add_as("partition->type",parttype) add_as("partition->os_type",ostype) + add_as("partition->boot_flag",bootable) END_OF_APPEND; free(parttype); } @@ -117,7 +123,9 @@ void show_disk(struct s_hardware *hardware, ZZJSON_CONFIG *conf, ZZJSON **it, in void dump_disks(struct s_hardware *hardware, ZZJSON_CONFIG *config, ZZJSON **item) { bool found=false; - for (int drive = 0x80; drive < 0xff; drive++) { + + if (hardware->disks_count > 0) + for (int drive = 0x80; drive < 0xff; drive++) { if (hardware->disk_info[drive - 0x80].cbios) { if (found==false) { CREATE_NEW_OBJECT; @@ -131,7 +139,7 @@ void dump_disks(struct s_hardware *hardware, ZZJSON_CONFIG *config, ZZJSON **ite if (found==false) { CREATE_NEW_OBJECT; add_b("disks->is_valid",false); - FLUSH_OBJECT; } + FLUSH_OBJECT; to_cpio("disks"); } diff --git a/com32/hdt/hdt-dump.c b/com32/hdt/hdt-dump.c index 8c221405..b1748c8e 100644 --- a/com32/hdt/hdt-dump.c +++ b/com32/hdt/hdt-dump.c @@ -37,56 +37,117 @@ struct print_buf p_buf; -void compute_filename(struct s_hardware *hardware, char *filename, int size) { - - snprintf(filename,size,"%s/",hardware->dump_path); - - if (hardware->is_pxe_valid) { - strncat(filename, hardware->pxe.mac_addr, sizeof(hardware->pxe.mac_addr)); - strncat(filename, "+", 1); - } - - if (hardware->is_dmi_valid) { - strncat(filename, remove_spaces(hardware->dmi.system.product_name), sizeof(hardware->dmi.system.manufacturer)); - strncat(filename, "+", 1); - strncat(filename, remove_spaces(hardware->dmi.system.manufacturer), sizeof(hardware->dmi.system.product_name)); +struct dump_option { + char *flag; + char *item; +}; + +char *get_value_from_option(struct s_hardware *hardware, char *option) +{ + struct dump_option dump_options[10]; + dump_options[0].flag = "%{m}"; + dump_options[0].item = hardware->pxe.mac_addr; + + dump_options[1].flag = "%{v}"; + dump_options[1].item = hardware->dmi.system.manufacturer; + + dump_options[2].flag = "%{p}"; + dump_options[2].item = hardware->dmi.system.product_name; + + dump_options[3].flag = "%{ba}"; + dump_options[3].item = hardware->dmi.base_board.asset_tag; + + dump_options[4].flag = "%{bs}"; + dump_options[4].item = hardware->dmi.base_board.serial; + + dump_options[5].flag = "%{ca}"; + dump_options[5].item = hardware->dmi.chassis.asset_tag; + + dump_options[6].flag = "%{cs}"; + dump_options[6].item = hardware->dmi.chassis.serial; + + dump_options[7].flag = "%{sk}"; + dump_options[7].item = hardware->dmi.system.sku_number; + + dump_options[8].flag = "%{ss}"; + dump_options[8].item = hardware->dmi.system.serial; + + dump_options[9].flag = NULL; + dump_options[9].item = NULL; + + for (int i = 0; i < 9; i++) { + if (strcmp(option, dump_options[i].flag) == 0) { + return remove_spaces(dump_options[i].item); + } + } + + return NULL; +} + +char *compute_filename(struct s_hardware *hardware) +{ + + char *filename = malloc(512); + snprintf(filename, 512, "%s/%s", hardware->dump_path, + hardware->dump_filename); + + /* Until we found some dump parameters */ + char *buffer; + while ((buffer = strstr(filename, "%{"))) { + // Find the end of the parameter + char *buffer_end = strstr(buffer, "}"); + + // Extracting the parameter between %{ and } + char option[8] = { 0 }; + strncpy(option, buffer, buffer_end - buffer + 1); + + /* Replace this option by its value in the filename + * Filename is longer than the previous filename we had + * so let's restart from the beginning */ + filename = + strreplace(filename, option, + get_value_from_option(hardware, option)); } /* We replace the ":" in the filename by some "-" * This will avoid Microsoft FS turning crazy */ - chrreplace(filename,':','-'); + chrreplace(filename, ':', '-'); /* Avoid space to make filename easier to manipulate */ - chrreplace(filename,' ','_'); + chrreplace(filename, ' ', '_'); + return filename; } -int dumpprintf(FILE *p, const char *format, ...) { - va_list ap; - int rv; - - (void) p; - va_start(ap, format); - rv = vbufprintf(&p_buf,format, ap); - va_end(ap); - return rv; +int dumpprintf(FILE * p, const char *format, ...) +{ + va_list ap; + int rv; + + (void)p; + va_start(ap, format); + rv = vbufprintf(&p_buf, format, ap); + va_end(ap); + return rv; } -void to_cpio(char *filename) { - cpio_writefile(upload,filename,p_buf.buf,p_buf.len); - if ((p_buf.buf) && (p_buf.len > 0)){ - memset(p_buf.buf,0,p_buf.len); - free(p_buf.buf); - p_buf.buf=NULL; - p_buf.size=0; - p_buf.len=0; - } +void to_cpio(char *filename) +{ + cpio_writefile(upload, filename, p_buf.buf, p_buf.len); + if ((p_buf.buf) && (p_buf.len > 0)) { + memset(p_buf.buf, 0, p_buf.len); + free(p_buf.buf); + p_buf.buf = NULL; + p_buf.size = 0; + p_buf.len = 0; + } } -void flush (ZZJSON_CONFIG *config, ZZJSON ** item) { - zzjson_print(config, *item); - zzjson_free(config, *item); - *item=NULL; +void flush(ZZJSON_CONFIG * config, ZZJSON ** item) +{ + zzjson_print(config, *item); + zzjson_free(config, *item); + *item = NULL; } /** @@ -94,53 +155,52 @@ void flush (ZZJSON_CONFIG *config, ZZJSON ** item) { **/ void dump(struct s_hardware *hardware) { - if (hardware->is_pxe_valid==false) { - printf("PXE stack was not detected, Dump feature is not available\n"); - return; + if (hardware->is_pxe_valid == false) { + more_printf("PXE stack was not detected, Dump feature is not available\n"); + return; } const union syslinux_derivative_info *sdi = syslinux_derivative_info(); - int err=0; + int err = 0; ZZJSON *json = NULL; ZZJSON_CONFIG config = { ZZJSON_VERY_STRICT, NULL, - (int(*)(void*)) fgetc, - NULL, - malloc, calloc, free, realloc, - stderr, NULL, stdout, - (int(*)(void *,const char*,...)) dumpprintf, - (int(*)(int,void*)) fputc + (int (*)(void *))fgetc, + NULL, + malloc, calloc, free, realloc, + stderr, NULL, stdout, + (int (*)(void *, const char *,...))dumpprintf, + (int (*)(int, void *))fputc }; - memset(&p_buf,0,sizeof(p_buf)); + memset(&p_buf, 0, sizeof(p_buf)); /* By now, we only support TFTP reporting */ - upload=&upload_tftp; - upload->name="tftp"; + upload = &upload_tftp; + upload->name = "tftp"; /* The following defines the behavior of the reporting */ char *arg[64]; - char filename[512]={0}; - compute_filename(hardware, filename, sizeof(filename)); + char *filename = compute_filename(hardware); /* The filename */ arg[0] = filename; /* The server to upload the file */ if (strlen(hardware->tftp_ip) != 0) { - arg[1] = hardware->tftp_ip; - arg[2] = NULL; + arg[1] = hardware->tftp_ip; + arg[2] = NULL; } else { - arg[1] = NULL; - snprintf(hardware->tftp_ip, sizeof(hardware->tftp_ip), - "%u.%u.%u.%u", - ((uint8_t *)&sdi->pxe.ipinfo->serverip)[0], - ((uint8_t *)&sdi->pxe.ipinfo->serverip)[1], - ((uint8_t *)&sdi->pxe.ipinfo->serverip)[2], - ((uint8_t *)&sdi->pxe.ipinfo->serverip)[3]); + arg[1] = NULL; + snprintf(hardware->tftp_ip, sizeof(hardware->tftp_ip), + "%u.%u.%u.%u", + ((uint8_t *) & sdi->pxe.ipinfo->serverip)[0], + ((uint8_t *) & sdi->pxe.ipinfo->serverip)[1], + ((uint8_t *) & sdi->pxe.ipinfo->serverip)[2], + ((uint8_t *) & sdi->pxe.ipinfo->serverip)[3]); } /* We initiate the cpio to send */ - cpio_init(upload,(const char **)arg); + cpio_init(upload, (const char **)arg); dump_cpu(hardware, &config, &json); dump_pxe(hardware, &config, &json); @@ -158,12 +218,12 @@ void dump(struct s_hardware *hardware) /* We close & flush the file to send */ cpio_close(upload); - if ((err=flush_data(upload)) != TFTP_OK) { + if ((err = flush_data(upload)) != TFTP_OK) { /* As we manage a tftp connection, let's display the associated error message */ more_printf("Dump failed !\n"); - more_printf("TFTP ERROR on : %s:/%s \n",hardware->tftp_ip, filename); - more_printf("TFTP ERROR msg : %s \n",tftp_string_error_message[-err]); + more_printf("TFTP ERROR on : %s:/%s \n", hardware->tftp_ip, filename); + more_printf("TFTP ERROR msg : %s \n", tftp_string_error_message[-err]); } else { - more_printf("Dump file sent at %s:/%s\n",hardware->tftp_ip, filename); + more_printf("Dump file sent at %s:/%s\n", hardware->tftp_ip, filename); } } diff --git a/com32/hdt/hdt-menu-summary.c b/com32/hdt/hdt-menu-summary.c index ad87c299..cef7e69e 100644 --- a/com32/hdt/hdt-menu-summary.c +++ b/com32/hdt/hdt-menu-summary.c @@ -52,8 +52,7 @@ void compute_summarymenu(struct s_my_menu *menu, struct s_hardware *hardware) add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); menu->items_count++; - char features[SUBMENULEN + 1]; - memset(features, 0, sizeof(features)); + char features[255]={0}; if (hardware->dmi.processor.thread_count != 0) sprintf(buffer, ", %d thread", hardware->dmi.processor.thread_count); else diff --git a/com32/hdt/hdt.c b/com32/hdt/hdt.c index a1e3923c..653995d0 100644 --- a/com32/hdt/hdt.c +++ b/com32/hdt/hdt.c @@ -72,16 +72,23 @@ int main(const int argc, const char *argv[]) clear_screen(); printf("\033[1;1H"); - printf("%s\n", version_string); + more_printf("%s\n", version_string); + + int return_code = 0; if (!menumode || automode) start_cli_mode(&hardware); else { - int return_code = start_menu_mode(&hardware, version_string); + return_code = start_menu_mode(&hardware, version_string); if (return_code == HDT_RETURN_TO_CLI) start_cli_mode(&hardware); - else - return return_code; } - return 0; + + /* Do we got request to do something at exit time ? */ + if (strlen(hardware.postexec)>0) { + more_printf("Executing postexec instructions : %s\n",hardware.postexec); + runsyslinuxcmd(hardware.postexec); + } + + return return_code; } diff --git a/com32/hdt/hdt.h b/com32/hdt/hdt.h index 7b35236e..e385417a 100644 --- a/com32/hdt/hdt.h +++ b/com32/hdt/hdt.h @@ -33,8 +33,8 @@ #define AUTHOR "Erwan Velu" #define CORE_DEVELOPER "Pierre-Alexandre Meyer" #define CONTACT "hdt@zytor.com" -#define VERSION "0.5.0" -#define CODENAME "Van De Keizer" +#define VERSION "0.5.2" +#define CODENAME "Manon" #define NB_CONTRIBUTORS 3 #define CONTRIBUTORS {"Sebastien Gonzalve (Patches)", "Gert Hulselmans (Tests)", "Alexander Andino (Design)"} #define WEBSITE_URL "http://hdt-project.org" diff --git a/com32/include/dhcp.h b/com32/include/dhcp.h new file mode 100644 index 00000000..afef9242 --- /dev/null +++ b/com32/include/dhcp.h @@ -0,0 +1,40 @@ +#ifndef DHCP_H +#define DHCP_H + +#include <inttypes.h> + +struct dhcp_option { + void *data; + int len; +}; + +struct dhcp_packet { + uint8_t op; /* 0 */ + uint8_t htype; /* 1 */ + uint8_t hlen; /* 2 */ + uint8_t hops; /* 3 */ + uint32_t xid; /* 4 */ + uint16_t secs; /* 8 */ + uint16_t flags; /* 10 */ + uint32_t ciaddr; /* 12 */ + uint32_t yiaddr; /* 16 */ + uint32_t siaddr; /* 20 */ + uint32_t giaddr; /* 24 */ + uint8_t chaddr[16]; /* 28 */ + uint8_t sname[64]; /* 44 */ + uint8_t file[128]; /* 108 */ + uint32_t magic; /* 236 */ + uint8_t options[4]; /* 240 */ +}; + +#define DHCP_VENDOR_MAGIC 0x63825363 + +int dhcp_pack_packet(void *packet, size_t *len, + const struct dhcp_option opt[256]); + +int dhcp_unpack_packet(const void *packet, size_t len, + struct dhcp_option opt[256]); + +#endif /* DHCP_H */ + + diff --git a/com32/include/string.h b/com32/include/string.h index af9792b6..d847440d 100644 --- a/com32/include/string.h +++ b/com32/include/string.h @@ -42,5 +42,6 @@ __extern char *strsep(char **, const char *); __extern size_t strspn(const char *, const char *); __extern char *strstr(const char *, const char *); __extern char *strtok(char *, const char *); +__extern char *strreplace(const char *, const char *, const char *); #endif /* _STRING_H */ diff --git a/com32/include/syslinux/disk.h b/com32/include/syslinux/disk.h new file mode 100644 index 00000000..f96ca686 --- /dev/null +++ b/com32/include/syslinux/disk.h @@ -0,0 +1,180 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin + * Copyright (C) 2010 Shao Miller + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/** + * @file syslinux/disk.h + * + * Deal with disks and partitions + */ + +#ifndef _SYSLINUX_DISK_H +#define _SYSLINUX_DISK_H + +#include <com32.h> +#include <stdint.h> + +#define SECTOR 512u /* bytes/sector */ + +struct disk_info { + int disk; + int ebios; /* EBIOS supported on this disk */ + int cbios; /* CHS geometry is valid */ + uint32_t bps; /* bytes per sector */ + uint64_t lbacnt; /* total amount of sectors */ + uint32_t cyl; + uint32_t head; + uint32_t spt; +}; + +struct disk_ebios_dapa { + uint16_t len; + uint16_t count; + uint16_t off; + uint16_t seg; + uint64_t lba; +} __attribute__ ((packed)); + +struct disk_ebios_eparam { + uint16_t len; + uint16_t info; + uint32_t cyl; + uint32_t head; + uint32_t spt; + uint64_t lbacnt; + uint16_t bps; /* bytes per sector */ + uint32_t edd; + uint16_t dpi_sig; + uint8_t dpi_len; + char reserved1[3]; + char hostbus[4]; + char if_type[8]; + char if_path[8]; + char dev_path[8]; + char reserved2; + uint8_t checksum; +} __attribute__ ((packed)); + +/** + * CHS (cylinder, head, sector) value extraction macros. + * Taken from WinVBlock. None expand to an lvalue. +*/ +#define chs_head(chs) chs[0] +#define chs_sector(chs) (chs[1] & 0x3F) +#define chs_cyl_high(chs) (((uint16_t)(chs[1] & 0xC0)) << 2) +#define chs_cyl_low(chs) ((uint16_t)chs[2]) +#define chs_cylinder(chs) (chs_cyl_high(chs) | chs_cyl_low(chs)) +typedef uint8_t disk_chs[3]; + +/* A DOS partition table entry */ +struct disk_dos_part_entry { + uint8_t active_flag; /* 0x80 if "active" */ + disk_chs start; + uint8_t ostype; + disk_chs end; + uint32_t start_lba; + uint32_t length; +} __attribute__ ((packed)); + +/* A DOS MBR */ +struct disk_dos_mbr { + char code[440]; + uint32_t disk_sig; + char pad[2]; + struct disk_dos_part_entry table[4]; + uint16_t sig; +} __attribute__ ((packed)); +#define disk_mbr_sig_magic 0xAA55 + +/** + * A GPT disk/partition GUID + * + * Be careful with endianness, you must adjust it yourself + * iff you are directly using the fourth data chunk. + * There might be a better header for this... + */ +struct guid { + uint32_t data1; + uint16_t data2; + uint16_t data3; + uint64_t data4; +} __attribute__ ((packed)); + +/* A GPT partition */ +struct disk_gpt_part_entry { + struct guid type; + struct guid uid; + uint64_t lba_first; + uint64_t lba_last; + uint64_t attribs; + char name[72]; +} __attribute__ ((packed)); + +/* A GPT header */ +struct disk_gpt_header { + char sig[8]; + union { + struct { + uint16_t minor; + uint16_t major; + } fields __attribute__ ((packed)); + uint32_t uint32; + char raw[4]; + } rev __attribute__ ((packed)); + uint32_t hdr_size; + uint32_t chksum; + char reserved1[4]; + uint64_t lba_cur; + uint64_t lba_alt; + uint64_t lba_first_usable; + uint64_t lba_last_usable; + struct guid disk_guid; + uint64_t lba_table; + uint32_t part_count; + uint32_t part_size; + uint32_t table_chksum; + char reserved2[1]; +} __attribute__ ((packed)); +static const char disk_gpt_sig_magic[] = "EFI PART"; + +extern int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg); +extern int disk_get_params(int disk, struct disk_info *const diskinfo); +extern void *disk_read_sectors(const struct disk_info *const diskinfo, + uint64_t lba, uint8_t count); +extern int disk_write_sectors(const struct disk_info *const diskinfo, + uint64_t lba, const void *data, uint8_t count); +extern int disk_write_verify_sectors(const struct disk_info *const diskinfo, + uint64_t lba, const void *buf, uint8_t count); +extern void disk_dos_part_dump(const struct disk_dos_part_entry *const part); +extern void guid_to_str(char *buf, const struct guid *const id); +extern int str_to_guid(const char *buf, struct guid *const id); +extern void disk_gpt_part_dump(const struct disk_gpt_part_entry *const + gpt_part); +extern void disk_gpt_header_dump(const struct disk_gpt_header *const gpt); + +#endif /* _SYSLINUX_DISK_H */ diff --git a/com32/include/syslinux/linux.h b/com32/include/syslinux/linux.h index 754d1b64..f5f95fb0 100644 --- a/com32/include/syslinux/linux.h +++ b/com32/include/syslinux/linux.h @@ -1,6 +1,7 @@ /* ----------------------------------------------------------------------- * * * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2012 Intel Corporation; author: H. Peter Anvin * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -51,8 +52,26 @@ struct initramfs { }; #define INITRAMFS_MAX_ALIGN 4096 +struct setup_data_header { + uint64_t next; + uint32_t type; + uint32_t len; +} __packed; + +struct setup_data { + struct setup_data *prev, *next; + const void *data; + struct setup_data_header hdr; +}; + +#define SETUP_NONE 0 +#define SETUP_E820_EXT 1 +#define SETUP_DTB 2 + int syslinux_boot_linux(void *kernel_buf, size_t kernel_size, - struct initramfs *initramfs, char *cmdline); + struct initramfs *initramfs, + struct setup_data *setup_data, + char *cmdline); /* Initramfs manipulation functions */ @@ -70,4 +89,12 @@ int initramfs_load_file(struct initramfs *ihead, const char *src_filename, int initramfs_add_trailer(struct initramfs *ihead); int initramfs_load_archive(struct initramfs *ihead, const char *filename); +/* Setup data manipulation functions */ + +struct setup_data *setup_data_init(void); +int setup_data_add(struct setup_data *head, uint32_t type, + const void *data, size_t data_len); +int setup_data_load(struct setup_data *head, uint32_t type, + const char *filename); + #endif /* _SYSLINUX_LINUX_H */ diff --git a/com32/lib/Makefile b/com32/lib/Makefile index 105c2bd6..5ab1fac4 100644 --- a/com32/lib/Makefile +++ b/com32/lib/Makefile @@ -31,7 +31,8 @@ LIBOBJS = \ skipspace.o \ chrreplace.o \ bufprintf.o \ - inet.o \ + inet.o dhcppack.o dhcpunpack.o \ + strreplace.o \ \ lmalloc.o lstrdup.o \ \ @@ -126,7 +127,11 @@ LIBOBJS = \ syslinux/setadv.o \ \ syslinux/video/fontquery.o syslinux/video/forcetext.o \ - syslinux/video/reportmode.o + syslinux/video/reportmode.o \ + \ + syslinux/disk.o \ + \ + syslinux/setup_data.o # These are the objects which are also imported into the core LIBCOREOBJS = \ diff --git a/com32/lib/com32.ld b/com32/lib/com32.ld index 37ee46cf..008e4ceb 100644 --- a/com32/lib/com32.ld +++ b/com32/lib/com32.ld @@ -36,36 +36,23 @@ SECTIONS .rodata1 : { *(.rodata1) } __rodata_end = .; - /* Ensure the __preinit_array_start label is properly aligned. We - could instead move the label definition inside the section, but - the linker would then create the section even if it turns out to - be empty, which isn't pretty. */ + /* + * The difference betwee .ctors/.dtors and .init_array/.fini_array + * is the ordering, but we don't use prioritization for libcom32, so + * just lump them all together and hope that's okay. + */ . = ALIGN(4); - .preinit_array : { - PROVIDE (__preinit_array_start = .); - *(.preinit_array) - PROVIDE (__preinit_array_end = .); - } - .init_array : { - PROVIDE (__init_array_start = .); - *(.init_array) - PROVIDE (__init_array_end = .); - } - .fini_array : { - PROVIDE (__fini_array_start = .); - *(.fini_array) - PROVIDE (__fini_array_end = .); - } .ctors : { PROVIDE (__ctors_start = .); - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) + KEEP (*(SORT(.preinit_array*))) + KEEP (*(SORT(.init_array*))) + KEEP (*(SORT(.ctors*))) PROVIDE (__ctors_end = .); } .dtors : { PROVIDE (__dtors_start = .); - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) + KEEP (*(SORT(.fini_array*))) + KEEP (*(SORT(.dtors*))) PROVIDE (__dtors_end = .); } diff --git a/com32/lib/dhcppack.c b/com32/lib/dhcppack.c new file mode 100644 index 00000000..a08583c4 --- /dev/null +++ b/com32/lib/dhcppack.c @@ -0,0 +1,166 @@ +#include <stdlib.h> +#include <errno.h> +#include <string.h> +// #include <arpa/inet.h> +#include <netinet/in.h> + +// #include "dhcp.h" +#include <dhcp.h> + +/* + * Pack DHCP options into an option field, without overload support. + * On return, len contains the number of active bytes, and the full + * field is zero-padded. + * + * Options which are successfully placed have their length zeroed out. + */ +static int dhcp_pack_field_zero(void *field, size_t *len, + struct dhcp_option opt[256]) +{ + int i; + size_t xlen, plen; + const uint8_t *p; + uint8_t *q = field; + size_t spc = *len; + int err = 0; + + if (!*len) + return ENOSPC; + + for (i = 1; i < 255; i++) { + if (opt[i].len < 0) + continue; + + /* We need to handle the 0 case as well as > 255 */ + if (opt[i].len <= 255) + xlen = opt[i].len + 2; + else + xlen = opt[i].len + 2*((opt[i].len+254)/255); + + p = opt[i].data; + + if (xlen >= spc) { + /* This option doesn't fit... */ + err++; + continue; + } + + xlen = opt[i].len; + do { + *q++ = i; + *q++ = plen = xlen > 255 ? 255 : xlen; + if (plen) + memcpy(q, p, plen); + q += plen; + p += plen; + spc -= plen+2; + xlen -= plen; + } while (xlen); + + opt[i].len = -1; + } + + *q++ = 255; /* End marker */ + memset(q, 0, spc); /* Zero-pad the rest of the field */ + + *len = xlen = q - (uint8_t *)field; + return err; +} + +/* + * Pack DHCP options into an option field, without overload support. + * On return, len contains the number of active bytes, and the full + * field is zero-padded. + * + * Use this to encode encapsulated option fields. + */ +int dhcp_pack_field(void *field, size_t *len, + struct dhcp_option opt[256]) +{ + struct dhcp_option ox[256]; + + memcpy(ox, opt, sizeof ox); + return dhcp_pack_field_zero(field, len, ox); +} + +/* + * Pack DHCP options into a packet. + * Apply overloading if (and only if) the "file" or "sname" option + * doesn't fit in the respective dedicated fields. + */ +int dhcp_pack_packet(void *packet, size_t *len, + const struct dhcp_option opt[256]) +{ + struct dhcp_packet *pkt = packet; + size_t spc = *len; + uint8_t overload; + struct dhcp_option ox[256]; + uint8_t *q; + int err; + + if (spc < sizeof(struct dhcp_packet)) + return ENOSPC; /* Buffer impossibly small */ + + pkt->magic = htonl(DHCP_VENDOR_MAGIC); + + memcpy(ox, opt, sizeof ox); + + /* Figure out if we should do overloading or not */ + overload = 0; + + if (opt[67].len > 128) + overload |= 1; + else + ox[67].len = -1; + + if (opt[66].len > 64) + overload |= 2; + else + ox[66].len = -1; + + /* Kill any passed-in overload option */ + ox[52].len = -1; + + q = pkt->options; + spc -= 240; + + /* Force option 53 (DHCP packet type) first */ + if (ox[53].len == 1) { + *q++ = 53; + *q++ = 1; + *q++ = *(uint8_t *)ox[53].data; + spc -= 3; + ox[53].len = -1; + } + + /* Follow with the overload option, if applicable */ + if (overload) { + *q++ = 52; + *q++ = 1; + *q++ = overload; + spc -= 3; + } + + err = dhcp_pack_field_zero(q, &spc, ox); + *len = spc + (q-(uint8_t *)packet); + + if (overload & 1) { + spc = 128; + err = dhcp_pack_field_zero(pkt->file, &spc, ox); + } else { + memset(pkt->file, 0, 128); + if (opt[67].len > 0) + memcpy(pkt->file, opt[67].data, opt[67].len); + } + + if (overload & 2) { + spc = 64; + err = dhcp_pack_field_zero(pkt->sname, &spc, ox); + } else { + memset(pkt->sname, 0, 64); + if (opt[66].len > 0) + memcpy(pkt->sname, opt[66].data, opt[66].len); + } + + return err; +} diff --git a/com32/lib/dhcpunpack.c b/com32/lib/dhcpunpack.c new file mode 100644 index 00000000..248173a8 --- /dev/null +++ b/com32/lib/dhcpunpack.c @@ -0,0 +1,116 @@ +#define _GNU_SOURCE /* For strnlen() */ +#include <stdlib.h> +#include <errno.h> +#include <string.h> +// #include <arpa/inet.h> +#include <netinet/in.h> + +// #include "dhcp.h" +#include <dhcp.h> + +/* + * Unpack DHCP options from a field. Assumes opt is pre-initalized + * (to all zero in the common case.) + */ +int dhcp_unpack_field(const void *field, size_t len, + struct dhcp_option opt[256]) +{ + const uint8_t *p = field; + int err = 0; + + while (len > 1) { + uint8_t op; + size_t xlen; + + op = *p++; len--; + if (op == 0) + continue; + else if (op == 255) + break; + + xlen = *p++; len--; + if (xlen > len) + break; + if (opt[op].len < 0) + opt[op].len = 0; + if (xlen) { + opt[op].data = realloc(opt[op].data, + opt[op].len + xlen + 1); + if (!opt[op].data) { + err = ENOMEM; + continue; + } + memcpy((char *)opt[op].data + opt[op].len, p, xlen); + opt[op].len += xlen; + /* Null-terminate as a courtesy to users */ + *((char *)opt[op].data + opt[op].len) = 0; + p += xlen; + len -= xlen; + } + } + + return err; +} + +/* + * Unpack a DHCP packet, with overload support. Do not use this + * to unpack an encapsulated option set. + */ +int dhcp_unpack_packet(const void *packet, size_t len, + struct dhcp_option opt[256]) +{ + const struct dhcp_packet *pkt = packet; + int err; + uint8_t overload; + int i; + + if (len < 240 || pkt->magic != htonl(DHCP_VENDOR_MAGIC)) + return EINVAL; /* Bogus packet */ + + for (i = 0; i < 256; i++) { + opt[i].len = -1; /* Option not present */ + opt[i].data = NULL; + } + + err = dhcp_unpack_field(pkt->options, len-240, opt); + + overload = 0; + if (opt[52].len == 1) { + overload = *(uint8_t *)opt[52].data; + free(opt[52].data); + opt[52].len = -1; + opt[52].data = NULL; + } + + if (overload & 1) { + err |= dhcp_unpack_field(pkt->file, 128, opt); + } else { + opt[67].len = strnlen((const char *)pkt->file, 128); + if (opt[67].len) { + opt[67].data = malloc(opt[67].len + 1); + if (opt[67].data) { + memcpy(opt[67].data, pkt->file, opt[67].len); + *((char *)opt[67].data + opt[67].len) = 0; + } else { + err |= ENOMEM; + } + } + } + + if (overload & 2) { + err |= dhcp_unpack_field(pkt->sname, 64, opt); + } else { + opt[66].len = strnlen((const char *)pkt->sname, 64); + if (opt[66].len) { + opt[66].data = malloc(opt[66].len + 1); + if (opt[66].data) { + memcpy(opt[66].data, pkt->file, opt[66].len); + *((char *)opt[66].data + opt[66].len) = 0; + } else { + err |= ENOMEM; + } + } + } + + return err; +} diff --git a/com32/lib/pci/scan.c b/com32/lib/pci/scan.c index e0974f98..fe00fc25 100644 --- a/com32/lib/pci/scan.c +++ b/com32/lib/pci/scan.c @@ -579,14 +579,14 @@ void free_pci_domain(struct pci_domain *domain) free(func->dev_info); free(func); } - free(slot); } + free(slot); } - free(bus); } + free(bus); } - free(domain); } + free(domain); } } diff --git a/com32/lib/strreplace.c b/com32/lib/strreplace.c new file mode 100644 index 00000000..d59efe0e --- /dev/null +++ b/com32/lib/strreplace.c @@ -0,0 +1,58 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2011 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include <string.h> +#include <stdlib.h> + +char *strreplace(const char *string, const char *string_to_replace, + const char *string_to_insert) +{ + char *token = NULL; + char *out = NULL; + + size_t slen, srlen, silen; + + token = strstr(string, string_to_replace); + if (!token) + return strdup(string); + + slen = strlen(string); + srlen = strlen(string_to_replace); + silen = strlen(string_to_insert); + + out = malloc(slen - srlen + silen + 1); + if (!out) + return NULL; + + memcpy(out, string, token - string); + memcpy(out + (token - string), string_to_insert, silen); + memcpy(out + (token - string) + silen, token + srlen, + slen - srlen - (token - string) + 1); + + return out; +} diff --git a/com32/lib/syslinux/disk.c b/com32/lib/syslinux/disk.c new file mode 100644 index 00000000..d6409af6 --- /dev/null +++ b/com32/lib/syslinux/disk.c @@ -0,0 +1,534 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin + * Copyright (C) 2010 Shao Miller + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/** + * @file disk.c + * + * Deal with disks and partitions + */ + +#include <dprintf.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslinux/disk.h> + +/** + * Call int 13h, but with retry on failure. Especially floppies need this. + * + * @v inreg CPU register settings upon INT call + * @v outreg CPU register settings returned by INT call + * @ret (int) 0 upon success, -1 upon failure + */ +int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg) +{ + int retry = 6; /* Number of retries */ + com32sys_t tmpregs; + + if (!outreg) + outreg = &tmpregs; + + while (retry--) { + __intcall(0x13, inreg, outreg); + if (!(outreg->eflags.l & EFLAGS_CF)) + return 0; /* CF=0, OK */ + } + + return -1; /* Error */ +} + +/** + * Query disk parameters and EBIOS availability for a particular disk. + * + * @v disk The INT 0x13 disk drive number to process + * @v diskinfo The structure to save the queried params to + * @ret (int) 0 upon success, -1 upon failure + */ +int disk_get_params(int disk, struct disk_info *const diskinfo) +{ + static com32sys_t inreg, outreg; + struct disk_ebios_eparam *eparam = __com32.cs_bounce; + + memset(diskinfo, 0, sizeof *diskinfo); + diskinfo->disk = disk; + diskinfo->bps = SECTOR; + + /* Get EBIOS support */ + memset(&inreg, 0, sizeof inreg); + inreg.eax.b[1] = 0x41; + inreg.ebx.w[0] = 0x55aa; + inreg.edx.b[0] = disk; + inreg.eflags.b[0] = 0x3; /* CF set */ + + __intcall(0x13, &inreg, &outreg); + + if (!(outreg.eflags.l & EFLAGS_CF) && + outreg.ebx.w[0] == 0xaa55 && (outreg.ecx.b[0] & 1)) { + diskinfo->ebios = 1; + } + + /* Get extended disk parameters if ebios == 1 */ + if (diskinfo->ebios) { + memset(&inreg, 0, sizeof inreg); + inreg.eax.b[1] = 0x48; + inreg.edx.b[0] = disk; + inreg.esi.w[0] = OFFS(eparam); + inreg.ds = SEG(eparam); + + memset(eparam, 0, sizeof *eparam); + eparam->len = sizeof *eparam; + + __intcall(0x13, &inreg, &outreg); + + if (!(outreg.eflags.l & EFLAGS_CF)) { + diskinfo->lbacnt = eparam->lbacnt; + if (eparam->bps) + diskinfo->bps = eparam->bps; + /* + * don't think about using geometry data returned by + * 48h, as it can differ from 08h a lot ... + */ + } + } + /* + * Get disk parameters the old way - really only useful for hard + * disks, but if we have a partitioned floppy it's actually our best + * chance... + */ + memset(&inreg, 0, sizeof inreg); + inreg.eax.b[1] = 0x08; + inreg.edx.b[0] = disk; + + __intcall(0x13, &inreg, &outreg); + + if (outreg.eflags.l & EFLAGS_CF) + return diskinfo->ebios ? 0 : -1; + + diskinfo->spt = 0x3f & outreg.ecx.b[0]; + diskinfo->head = 1 + outreg.edx.b[1]; + diskinfo->cyl = 1 + (outreg.ecx.b[1] | ((outreg.ecx.b[0] & 0xc0u) << 2)); + + if (diskinfo->spt) + diskinfo->cbios = 1; /* Valid geometry */ + else { + diskinfo->head = 1; + diskinfo->spt = 1; + diskinfo->cyl = 1; + } + + if (!diskinfo->lbacnt) + diskinfo->lbacnt = diskinfo->cyl * diskinfo->head * diskinfo->spt; + + return 0; +} + +/** + * Get disk block(s) and return a malloc'd buffer. + * + * @v diskinfo The disk drive to read from + * @v lba The logical block address to begin reading at + * @v count The number of sectors to read + * @ret data An allocated buffer with the read data + * + * Uses the disk number and information from diskinfo. Read count sectors + * from drive, starting at lba. Return a new buffer, or NULL upon failure. + */ +void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba, + uint8_t count) +{ + com32sys_t inreg; + struct disk_ebios_dapa *dapa = __com32.cs_bounce; + void *buf = (char *)__com32.cs_bounce + diskinfo->bps; + void *data; + uint32_t maxcnt; + + maxcnt = (__com32.cs_bounce_size - diskinfo->bps) / diskinfo->bps; + if (!count || count > maxcnt || lba + count > diskinfo->lbacnt) + return NULL; + + memset(&inreg, 0, sizeof inreg); + + if (diskinfo->ebios) { + dapa->len = sizeof(*dapa); + dapa->count = count; + dapa->off = OFFS(buf); + dapa->seg = SEG(buf); + dapa->lba = lba; + + inreg.esi.w[0] = OFFS(dapa); + inreg.ds = SEG(dapa); + inreg.edx.b[0] = diskinfo->disk; + inreg.eax.b[1] = 0x42; /* Extended read */ + } else { + unsigned int c, h, s, t; + /* + * if we passed lba + count check and we get here, that means that + * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus + * 32bits are perfectly enough and lbacnt corresponds to cylinder + * boundary + */ + s = lba % diskinfo->spt; + t = lba / diskinfo->spt; + h = t % diskinfo->head; + c = t / diskinfo->head; + + inreg.eax.b[0] = count; + inreg.eax.b[1] = 0x02; /* Read */ + inreg.ecx.b[1] = c; + inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1); + inreg.edx.b[1] = h; + inreg.edx.b[0] = diskinfo->disk; + inreg.ebx.w[0] = OFFS(buf); + inreg.es = SEG(buf); + } + + if (disk_int13_retry(&inreg, NULL)) + return NULL; + + data = malloc(count * diskinfo->bps); + if (data) + memcpy(data, buf, count * diskinfo->bps); + return data; +} + +/** + * Write disk block(s). + * + * @v diskinfo The disk drive to write to + * @v lba The logical block address to begin writing at + * @v data The data to write + * @v count The number of sectors to write + * @ret (int) 0 upon success, -1 upon failure + * + * Uses the disk number and information from diskinfo. + * Write sector(s) to a disk drive, starting at lba. + */ +int disk_write_sectors(const struct disk_info *const diskinfo, uint64_t lba, + const void *data, uint8_t count) +{ + com32sys_t inreg; + struct disk_ebios_dapa *dapa = __com32.cs_bounce; + void *buf = (char *)__com32.cs_bounce + diskinfo->bps; + uint32_t maxcnt; + + maxcnt = (__com32.cs_bounce_size - diskinfo->bps) / diskinfo->bps; + if (!count || count > maxcnt || lba + count > diskinfo->lbacnt) + return -1; + + memcpy(buf, data, count * diskinfo->bps); + memset(&inreg, 0, sizeof inreg); + + if (diskinfo->ebios) { + dapa->len = sizeof(*dapa); + dapa->count = count; + dapa->off = OFFS(buf); + dapa->seg = SEG(buf); + dapa->lba = lba; + + inreg.esi.w[0] = OFFS(dapa); + inreg.ds = SEG(dapa); + inreg.edx.b[0] = diskinfo->disk; + inreg.eax.b[1] = 0x43; /* Extended write */ + } else { + unsigned int c, h, s, t; + /* + * if we passed lba + count check and we get here, that means that + * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus + * 32bits are perfectly enough and lbacnt corresponds to cylinder + * boundary + */ + s = lba % diskinfo->spt; + t = lba / diskinfo->spt; + h = t % diskinfo->head; + c = t / diskinfo->head; + + inreg.eax.b[0] = count; + inreg.eax.b[1] = 0x03; /* Write */ + inreg.ecx.b[1] = c; + inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1); + inreg.edx.b[1] = h; + inreg.edx.b[0] = diskinfo->disk; + inreg.ebx.w[0] = OFFS(buf); + inreg.es = SEG(buf); + } + + if (disk_int13_retry(&inreg, NULL)) + return -1; + + return 0; /* ok */ +} + +/** + * Write disk blocks and verify they were written. + * + * @v diskinfo The disk drive to write to + * @v lba The logical block address to begin writing at + * @v buf The data to write + * @v count The number of sectors to write + * @ret rv 0 upon success, -1 upon failure + * + * Uses the disk number and information from diskinfo. + * Writes sectors to a disk drive starting at lba, then reads them back + * to verify they were written correctly. + */ +int disk_write_verify_sectors(const struct disk_info *const diskinfo, + uint64_t lba, const void *buf, uint8_t count) +{ + char *rb; + int rv; + + rv = disk_write_sectors(diskinfo, lba, buf, count); + if (rv) + return rv; /* Write failure */ + rb = disk_read_sectors(diskinfo, lba, count); + if (!rb) + return -1; /* Readback failure */ + rv = memcmp(buf, rb, count * diskinfo->bps); + free(rb); + return rv ? -1 : 0; +} + +/** + * Dump info about a DOS partition entry + * + * @v part The 16-byte partition entry to examine + */ +void disk_dos_part_dump(const struct disk_dos_part_entry *const part) +{ + (void)part; + dprintf("Partition status _____ : 0x%.2x\n" + "Partition CHS start\n" + " Cylinder ___________ : 0x%.4x (%u)\n" + " Head _______________ : 0x%.2x (%u)\n" + " Sector _____________ : 0x%.2x (%u)\n" + "Partition type _______ : 0x%.2x\n" + "Partition CHS end\n" + " Cylinder ___________ : 0x%.4x (%u)\n" + " Head _______________ : 0x%.2x (%u)\n" + " Sector _____________ : 0x%.2x (%u)\n" + "Partition LBA start __ : 0x%.8x (%u)\n" + "Partition LBA count __ : 0x%.8x (%u)\n" + "-------------------------------\n", + part->active_flag, + chs_cylinder(part->start), + chs_cylinder(part->start), + chs_head(part->start), + chs_head(part->start), + chs_sector(part->start), + chs_sector(part->start), + part->ostype, + chs_cylinder(part->end), + chs_cylinder(part->end), + chs_head(part->end), + chs_head(part->end), + chs_sector(part->end), + chs_sector(part->end), + part->start_lba, part->start_lba, part->length, part->length); +} + +/* Trivial error message output */ +static inline void error(const char *msg) +{ + fputs(msg, stderr); +} + +/** + * This walk-map effectively reverses the little-endian + * portions of a GPT disk/partition GUID for a string representation. + * There might be a better header for this... + */ +static const char guid_le_walk_map[] = { + 3, -1, -1, -1, 0, + 5, -1, 0, + 3, -1, 0, + 2, 1, 0, + 1, 1, 1, 1, 1, 1 +}; + +/** + * Fill a buffer with a textual GUID representation. + * + * @v buf Points to a minimum array of 37 chars + * @v id The GUID to represent as text + * + * The buffer must be >= char[37] and will be populated + * with an ASCII NUL C string terminator. + * Example: 11111111-2222-3333-4444-444444444444 + * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB + */ +void guid_to_str(char *buf, const struct guid *const id) +{ + unsigned int i = 0; + const char *walker = (const char *)id; + + while (i < sizeof(guid_le_walk_map)) { + walker += guid_le_walk_map[i]; + if (!guid_le_walk_map[i]) + *buf = '-'; + else { + *buf = ((*walker & 0xF0) >> 4) + '0'; + if (*buf > '9') + *buf += 'A' - '9' - 1; + buf++; + *buf = (*walker & 0x0F) + '0'; + if (*buf > '9') + *buf += 'A' - '9' - 1; + } + buf++; + i++; + } + *buf = 0; +} + +/** + * Create a GUID structure from a textual GUID representation. + * + * @v buf Points to a GUID string to parse + * @v id Points to a GUID to be populated + * @ret (int) Returns 0 upon success, -1 upon failure + * + * The input buffer must be >= 32 hexadecimal chars and be + * terminated with an ASCII NUL. Returns non-zero on failure. + * Example: 11111111-2222-3333-4444-444444444444 + * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB + */ +int str_to_guid(const char *buf, struct guid *const id) +{ + char guid_seq[sizeof(struct guid) * 2]; + unsigned int i = 0; + char *walker = (char *)id; + + while (*buf && i < sizeof(guid_seq)) { + switch (*buf) { + /* Skip these three characters */ + case '{': + case '}': + case '-': + break; + default: + /* Copy something useful to the temp. sequence */ + if ((*buf >= '0') && (*buf <= '9')) + guid_seq[i] = *buf - '0'; + else if ((*buf >= 'A') && (*buf <= 'F')) + guid_seq[i] = *buf - 'A' + 10; + else if ((*buf >= 'a') && (*buf <= 'f')) + guid_seq[i] = *buf - 'a' + 10; + else { + /* Or not */ + error("Illegal character in GUID!\n"); + return -1; + } + i++; + } + buf++; + } + /* Check for insufficient valid characters */ + if (i < sizeof(guid_seq)) { + error("Too few GUID characters!\n"); + return -1; + } + buf = guid_seq; + i = 0; + while (i < sizeof(guid_le_walk_map)) { + if (!guid_le_walk_map[i]) + i++; + walker += guid_le_walk_map[i]; + *walker = *buf << 4; + buf++; + *walker |= *buf; + buf++; + i++; + } + return 0; +} + +/** + * Display GPT partition details. + * + * @v gpt_part The GPT partition entry to display + */ +void disk_gpt_part_dump(const struct disk_gpt_part_entry *const gpt_part) +{ + unsigned int i; + char guid_text[37]; + + dprintf("----------------------------------\n" + "GPT part. LBA first __ : 0x%.16llx\n" + "GPT part. LBA last ___ : 0x%.16llx\n" + "GPT part. attribs ____ : 0x%.16llx\n" + "GPT part. name _______ : '", + gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs); + for (i = 0; i < sizeof(gpt_part->name); i++) { + if (gpt_part->name[i]) + dprintf("%c", gpt_part->name[i]); + } + dprintf("'"); + guid_to_str(guid_text, &gpt_part->type); + dprintf("GPT part. type GUID __ : {%s}\n", guid_text); + guid_to_str(guid_text, &gpt_part->uid); + dprintf("GPT part. unique ID __ : {%s}\n", guid_text); +} + +/** + * Display GPT header details. + * + * @v gpt The GPT header to display + */ +void disk_gpt_header_dump(const struct disk_gpt_header *const gpt) +{ + char guid_text[37]; + + printf("GPT sig ______________ : '%8.8s'\n" + "GPT major revision ___ : 0x%.4x\n" + "GPT minor revision ___ : 0x%.4x\n" + "GPT header size ______ : 0x%.8x\n" + "GPT header checksum __ : 0x%.8x\n" + "GPT reserved _________ : '%4.4s'\n" + "GPT LBA current ______ : 0x%.16llx\n" + "GPT LBA alternative __ : 0x%.16llx\n" + "GPT LBA first usable _ : 0x%.16llx\n" + "GPT LBA last usable __ : 0x%.16llx\n" + "GPT LBA part. table __ : 0x%.16llx\n" + "GPT partition count __ : 0x%.8x\n" + "GPT partition size ___ : 0x%.8x\n" + "GPT part. table chksum : 0x%.8x\n", + gpt->sig, + gpt->rev.fields.major, + gpt->rev.fields.minor, + gpt->hdr_size, + gpt->chksum, + gpt->reserved1, + gpt->lba_cur, + gpt->lba_alt, + gpt->lba_first_usable, + gpt->lba_last_usable, + gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum); + guid_to_str(guid_text, &gpt->disk_guid); + printf("GPT disk GUID ________ : {%s}\n", guid_text); +} diff --git a/com32/lib/syslinux/load_linux.c b/com32/lib/syslinux/load_linux.c index 45cd6965..856141f8 100644 --- a/com32/lib/syslinux/load_linux.c +++ b/com32/lib/syslinux/load_linux.c @@ -38,6 +38,7 @@ #include <inttypes.h> #include <string.h> #include <minmax.h> +#include <errno.h> #include <suffix_number.h> #include <syslinux/align.h> #include <syslinux/linux.h> @@ -180,13 +181,16 @@ static int map_initramfs(struct syslinux_movelist **fraglist, } int syslinux_boot_linux(void *kernel_buf, size_t kernel_size, - struct initramfs *initramfs, char *cmdline) + struct initramfs *initramfs, + struct setup_data *setup_data, + char *cmdline) { struct linux_header hdr, *whdr; size_t real_mode_size, prot_mode_size; addr_t real_mode_base, prot_mode_base; addr_t irf_size; size_t cmdline_size, cmdline_offset; + struct setup_data *sdp; struct syslinux_rm_regs regs; struct syslinux_movelist *fraglist = NULL; struct syslinux_memmap *mmap = NULL; @@ -449,6 +453,49 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size, } } + if (setup_data) { + uint64_t *prev_ptr = &whdr->setup_data; + + for (sdp = setup_data->next; sdp != setup_data; sdp = sdp->next) { + struct syslinux_memmap *ml; + const addr_t align_mask = 15; /* Header is 16 bytes */ + addr_t best_addr = 0; + size_t size = sdp->hdr.len + sizeof(sdp->hdr); + + if (!sdp->data || !sdp->hdr.len) + continue; + + if (hdr.version < 0x0209) { + /* Setup data not supported */ + errno = ENXIO; /* Kind of arbitrary... */ + goto bail; + } + + for (ml = amap; ml->type != SMT_END; ml = ml->next) { + addr_t adj_start = (ml->start + align_mask) & ~align_mask; + addr_t adj_end = ml->next->start & ~align_mask; + + if (ml->type == SMT_FREE && adj_end - adj_start >= size) + best_addr = (adj_end - size) & ~align_mask; + } + + if (!best_addr) + goto bail; + + *prev_ptr = best_addr; + prev_ptr = &sdp->hdr.next; + + if (syslinux_add_memmap(&amap, best_addr, size, SMT_ALLOC)) + goto bail; + if (syslinux_add_movelist(&fraglist, best_addr, + (addr_t)&sdp->hdr, sizeof sdp->hdr)) + goto bail; + if (syslinux_add_movelist(&fraglist, best_addr + sizeof sdp->hdr, + (addr_t)sdp->data, sdp->hdr.len)) + goto bail; + } + } + /* Set up the registers on entry */ memset(®s, 0, sizeof regs); regs.es = regs.ds = regs.ss = regs.fs = regs.gs = real_mode_base >> 4; diff --git a/com32/lib/syslinux/setup_data.c b/com32/lib/syslinux/setup_data.c new file mode 100644 index 00000000..a36c5b61 --- /dev/null +++ b/com32/lib/syslinux/setup_data.c @@ -0,0 +1,47 @@ +#include <stdlib.h> +#include <syslinux/linux.h> +#include <syslinux/loadfile.h> + +struct setup_data *setup_data_init(void) +{ + struct setup_data *setup_data; + + setup_data = zalloc(sizeof(*setup_data)); + if (!setup_data) + return NULL; + + setup_data->prev = setup_data->next = setup_data; + return setup_data; +} + +int setup_data_add(struct setup_data *head, uint32_t type, + const void *data, size_t data_len) +{ + struct setup_data *setup_data; + + setup_data = zalloc(sizeof(*setup_data)); + if (!setup_data) + return -1; + + setup_data->data = data; + setup_data->hdr.len = data_len; + setup_data->hdr.type = type; + setup_data->prev = head->prev; + setup_data->next = head; + head->prev->next = setup_data; + head->prev = setup_data; + + return 0; +} + +int setup_data_load(struct setup_data *head, uint32_t type, + const char *filename) +{ + void *data; + size_t len; + + if (loadfile(filename, &data, &len)) + return -1; + + return setup_data_add(head, type, data, len); +} diff --git a/com32/lua/src/Makefile b/com32/lua/src/Makefile index 14b0e33d..f03f7a7f 100644 --- a/com32/lua/src/Makefile +++ b/com32/lua/src/Makefile @@ -44,6 +44,7 @@ LIBLUA_OBJS += dmi.o LIBLUA_OBJS += cpu.o LIBLUA_OBJS += pci.o LIBLUA_OBJS += vesa.o +LIBLUA_OBJS += dhcp.o CFLAGS += -DLUA_ANSI diff --git a/com32/lua/src/cpu.c b/com32/lua/src/cpu.c index 8a246e3d..6ef4e5a3 100644 --- a/com32/lua/src/cpu.c +++ b/com32/lua/src/cpu.c @@ -9,13 +9,13 @@ #include"lualib.h" #include"cpuid.h" -static void add_string_item(lua_State *L, const char *item, const char *value_str) { +void add_string_item(lua_State *L, const char *item, const char *value_str) { lua_pushstring(L,item); lua_pushstring(L,value_str); lua_settable(L,-3); } -static void add_int_item(lua_State *L, const char *item, int value_int) { +void add_int_item(lua_State *L, const char *item, int value_int) { lua_pushstring(L,item); lua_pushnumber(L,value_int); lua_settable(L,-3); diff --git a/com32/lua/src/dhcp.c b/com32/lua/src/dhcp.c new file mode 100644 index 00000000..af948131 --- /dev/null +++ b/com32/lua/src/dhcp.c @@ -0,0 +1,358 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007 H. Peter Anvin - All Rights Reserved + * Copyright 2011 Timothy J Gleason <timmgleason_at_gmail.com> - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * dhcp.c + * + * Adds DHCPINFO functionality to the lua.c32 binary + * + * gettable() returns a table of the BOOTP message fields returned by + * the DHCP server for use in a Lua pxeboot script + * See http://tools.ietf.org/html/rfc1542 + * + * lua key value RFC key + * ----------------------------------------------------------------------- + * opcode op message opcode + * hardware.type htype Hardware address type + * hardware.length hlen Hardware address length + * hops hops Used by relay agents + * transaction.id xid transaction id + * elapsed.seconds secs Secs elapsed since client boot + * flags flags DHCP Flags field + * client.ip.addr ciaddr client IP addr + * your.ip.addr yiaddr 'Your' IP addr. (from server) + * server.ip.addr siaddr Boot server IP addr + * gateway.ip.addr giaddr Relay agent IP addr + * client.mac chaddr Client hardware addr + * server.hostname sname Optl. boot server hostname + * boot.file file boot file name (ascii path) + * magic.cookie cookie Magic cookie + * + * getoptions() returns a table of the DHCP Options field of the BOOTP + * message returned by the DHCP server for use in a Lua pxeboot script. + * Many of the options are reurned formatted in as strings in a standard, + * recognizable format, such as IP addresses. + * + * 1, 2, and 4 byte numerical options are returned as integers. + * + * Other Options with non-standard formats are returned as strings of the + * raw binary number that was returned by the DHCP server and must be decoded + * in a Lua script + * + * The Options table returns the Option code as the key except where there + * are multiple values returned. In those cases, an extra key increment number + * is added to allow individual access to each Option value. + * + * lua key value value Name + * ----------------------------------------------------------------------- + * 1 Subnet Mask + * 6.1 DNS Server [element 1] + * 6.2 DNS Server [element 2] + * 6.3 DNS Server [element 3] + * 209 PXE Configuration File + * 21.1 Policy Filter [element 1] + * 21.2 Policy Filter [element 2] + * + * Options that can have a list of values, but contain only one (like Option 6) + * will not return with .sub key values. + * + * Usage: + t = dhcp.gettable() + + for k,v in pairs(t) do + print(k.." : "..v) + end + */ + +#include <stdio.h> +#include "dhcp.h" +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +#include <syslinux/pxe.h> + +#define STR_BUF_SIZE 129 /* Sized to accomdate File field in BOOTP message */ + +void +ip_address_list(lua_State *L, uint8_t* value, uint8_t len, uint8_t option ) +{ + static char op_name[64]; + static char op_value[255]; + int loop; + + loop = len/4; + + if ( loop == 1) { + sprintf(op_name, "%u", option); + lua_pushstring(L, op_name); + sprintf(op_value, "%u.%u.%u.%u", value[0], value[1], value[2], value[3]); + lua_pushstring(L, op_value); + lua_settable(L,-3); + } else { + for (int done = 0; done < loop; done++){ + sprintf(op_name, "%u.%d", option, done+1); + lua_pushstring(L, op_name); + sprintf(op_value, "%u.%u.%u.%u", value[0+(done*4)], value[1+(done*4)], value[2+(done*4)], value[3+(done*4)]); + lua_pushstring(L, op_value); + lua_settable(L,-3); + } + } + +} + +static int dhcp_getoptions(lua_State *L) +{ + void* dhcpdata = 0; + dhcp_t* dhcp = 0; + size_t dhcplen = 0; + + /* Append the DHCP info */ + if (pxe_get_cached_info(PXENV_PACKET_TYPE_DHCP_ACK, + &dhcpdata, &dhcplen)) + { + return 0; + } + + dhcp = (dhcp_t*)dhcpdata; + + lua_newtable(L); + + int done = 0; + uint8_t* ptr = (uint8_t*)&dhcp->options; + uint8_t len; + uint8_t option; + uint8_t* value; + static char op_name[64]; + + do { + option = *ptr++; + len = *ptr++; + value = ptr; + ptr += len; + switch (option) { +// IP Address formatted lists, including singles + case 1: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 16: + case 21: /* Policy Filters - address/mask */ + case 28: + case 32: + case 33: /* Static routes - destination/router */ + case 41: + case 42: + case 44: + case 45: + case 47: + case 48: + case 49: + case 50: + case 51: + case 54: + case 65: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + ip_address_list(L, value, len, option); + break; +// 4byte options - numerical + case 2: + case 24: + case 35: + case 38: + case 58: + case 59: + case 211: + sprintf(op_name, "%u", option); + lua_pushstring(L, op_name); + lua_pushinteger(L, ntohl(*(long*)value)); + lua_settable(L,-3); + break; +// 2byte options -numerical + case 13: + case 22: + case 26: + case 57: + sprintf(op_name, "%u", option); + lua_pushstring(L, op_name); + lua_pushinteger(L, ntohs(*(short*)value)); + lua_settable(L,-3); + break; +// 1byte options - numerical + case 19: + case 20: + case 23: + case 27: + case 29: + case 30: + case 31: + case 34: + case 36: + case 37: + case 39: + case 46: + case 52: + case 53: + sprintf(op_name, "%u", option); + lua_pushstring(L, op_name); + lua_pushinteger(L, *value); + lua_settable(L,-3); + break; + case 255: + done = 1; + break; + default: + sprintf(op_name, "%u", option); + lua_pushstring(L, op_name); + lua_pushlstring(L, (const char*)value, len); + lua_settable(L,-3); + break; + } + + } while (!done); + + return 1; +} + +static int dhcp_gettable(lua_State *L) +{ + void* dhcpdata = 0; + dhcp_t* dhcp = 0; + size_t dhcplen = 0; + static char dhcp_arg[STR_BUF_SIZE]; + + /* Append the DHCP info */ + if (pxe_get_cached_info(PXENV_PACKET_TYPE_DHCP_ACK, + &dhcpdata, &dhcplen)) + { + return 0; + } + + dhcp = (dhcp_t*)dhcpdata; + + + lua_newtable(L); + + lua_pushstring(L, "opcode"); + lua_pushinteger(L, dhcp->op); + lua_settable(L,-3); + + lua_pushstring(L, "hardware.type"); + lua_pushinteger(L, dhcp->htype); + lua_settable(L,-3); + + lua_pushstring(L, "hardware.length"); + lua_pushinteger(L, dhcp->hlen); + lua_settable(L,-3); + + lua_pushstring(L, "hops"); + lua_pushinteger(L, dhcp->hops); + lua_settable(L,-3); + + lua_pushstring(L, "transaction.id"); + lua_pushinteger(L, ntohl(dhcp->xid)); + lua_settable(L,-3); + + lua_pushstring(L, "elapsed.seconds"); + lua_pushinteger(L, ntohs(dhcp->secs)); + lua_settable(L,-3); + + lua_pushstring(L, "flags"); + lua_pushinteger(L, ntohs(dhcp->flags)); + lua_settable(L,-3); + + sprintf(dhcp_arg, "%u.%u.%u.%u", dhcp->ciaddr[0], dhcp->ciaddr[1], dhcp->ciaddr[2], dhcp->ciaddr[3]); + lua_pushstring(L, "client.ip.addr"); + lua_pushstring(L, dhcp_arg); + lua_settable(L,-3); + + sprintf(dhcp_arg, "%u.%u.%u.%u", dhcp->yiaddr[0], dhcp->yiaddr[1], dhcp->yiaddr[2], dhcp->yiaddr[3]); + lua_pushstring(L, "your.ip.addr"); + lua_pushstring(L, dhcp_arg); + lua_settable(L,-3); + + sprintf(dhcp_arg, "%u.%u.%u.%u", dhcp->siaddr[0], dhcp->siaddr[1], dhcp->siaddr[2], dhcp->siaddr[3]); + lua_pushstring(L, "server.ip.addr"); + lua_pushstring(L, dhcp_arg); + lua_settable(L,-3); + + sprintf(dhcp_arg, "%u.%u.%u.%u", dhcp->giaddr[0], dhcp->giaddr[1], dhcp->giaddr[2], dhcp->giaddr[3]); + lua_pushstring(L, "gateway.ip.addr"); + lua_pushstring(L, dhcp_arg); + lua_settable(L,-3); + + sprintf(dhcp_arg, "%02X:%02X:%02X:%02X:%02X:%02X", + dhcp->chaddr[0], dhcp->chaddr[1], dhcp->chaddr[2], + dhcp->chaddr[3], dhcp->chaddr[4], dhcp->chaddr[5]); + lua_pushstring(L, "client.mac"); + lua_pushstring(L, dhcp_arg); + lua_settable(L,-3); + + snprintf(dhcp_arg, STR_BUF_SIZE, "%s", dhcp->sname); + dhcp_arg[STR_BUF_SIZE-1] = 0; /* Guarantee for lua_pushstring that dhcp_arg is 0 terminated /*/ + lua_pushstring(L, "server.hostname"); + lua_pushstring(L, dhcp_arg); + lua_settable(L,-3); + + snprintf(dhcp_arg, STR_BUF_SIZE, "%s", dhcp->file); + dhcp_arg[STR_BUF_SIZE-1] = 0; /* Guarantee for lua_pushstring that dhcp_arg is 0 terminated /*/ + lua_pushstring(L, "boot.file"); + lua_pushstring(L, dhcp_arg); + lua_settable(L,-3); + + sprintf(dhcp_arg, "%u.%u.%u.%u", dhcp->cookie[0], dhcp->cookie[1], dhcp->cookie[2], dhcp->cookie[3]); + lua_pushstring(L, "magic.cookie"); + lua_pushstring(L, dhcp_arg); + lua_settable(L,-3); + + return 1; +} + +static const luaL_reg dhcplib[] = { + {"gettable", dhcp_gettable}, + {"getoptions", dhcp_getoptions}, + {NULL, NULL} +}; + +LUALIB_API int luaopen_dhcp (lua_State *L) { + luaL_openlib(L, LUA_DHCPLIBNAME, dhcplib, 0); + return 1; +} diff --git a/com32/lua/src/dhcp.h b/com32/lua/src/dhcp.h new file mode 100644 index 00000000..a398cfc1 --- /dev/null +++ b/com32/lua/src/dhcp.h @@ -0,0 +1,49 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007 H. Peter Anvin - All Rights Reserved + * Copyright 2011 Timothy J Gleason <timmgleason_at_gmail.com> - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include <stdint.h> + +typedef struct dhcp { + uint8_t op; /* message opcode */ + uint8_t htype; /* Hardware address type */ + uint8_t hlen; /* Hardware address length */ + uint8_t hops; /* Used by relay agents */ + uint32_t xid; /* transaction id */ + uint16_t secs; /* Secs elapsed since client boot */ + uint16_t flags; /* DHCP Flags field */ + uint8_t ciaddr[4]; /* client IP addr */ + uint8_t yiaddr[4]; /* 'Your' IP addr. (from server) */ + uint8_t siaddr[4]; /* Boot server IP addr */ + uint8_t giaddr[4]; /* Relay agent IP addr */ + uint8_t chaddr[16]; /* Client hardware addr */ + uint8_t sname[64]; /* Optl. boot server hostname */ + uint8_t file[128]; /* boot file name (ascii path) */ + uint8_t cookie[4]; /* Magic cookie */ + uint8_t options[1020]; /* Options */ +} dhcp_t; + diff --git a/com32/lua/src/dmi.c b/com32/lua/src/dmi.c index c8329d33..984fb60c 100644 --- a/com32/lua/src/dmi.c +++ b/com32/lua/src/dmi.c @@ -9,275 +9,487 @@ #include "lualib.h" #include "dmi/dmi.h" -static int dmi_gettable(lua_State *L) +extern void add_string_item(lua_State*, const char*, const char*); +extern void add_int_item(lua_State*, const char*, int); +typedef int (*table_fn)(lua_State*, s_dmi*); + +/* Add a Lua_String entry to the table on stack + xxx_P is the poiter version (i.e., pBase is a pointer) + xxx_S is the staic version (i.e., Base is the struct) +*/ +#define LUA_ADD_STR_P(pLua_state, pBase, Field) \ + add_string_item(pLua_state, #Field, pBase->Field); +#define LUA_ADD_STR_S(pLua_state, Base, Field) \ + add_string_item(pLua_state, #Field, Base.Field); + +/* Add a Lua_Number entry to the table on stack + xxx_P is the poiter version (i.e., pBase is a pointer) + xxx_S is the staic version (i.e., Base is the struct) +*/ +#define LUA_ADD_NUM_P(pLua_state, pBase, Field) \ + add_int_item(pLua_state, #Field, pBase->Field); +#define LUA_ADD_NUM_S(pLua_state, Base, Field) \ + add_int_item(pLua_state, #Field, Base.Field); + +/* Add a sub-DMI table to the table on stack + All (*table_fn)() have to be named as get_<tabel_name>_table() for this + macro to work. For example, for the bios subtable, the table_fn is + get_bios_table() and the subtable name is "bios". + All (*table_fn)() have to return 1 if a subtable is created on the stack + or 0 if the subtable is not created (no corresponding dim subtable found). +*/ +#define LUA_ADD_TABLE(pLua_state, pDmi, tb_name) \ + add_dmi_sub_table(pLua_state, pDmi, #tb_name, get_ ## tb_name ## _table); + + +static void add_dmi_sub_table(lua_State *L, s_dmi *dmi_ptr, char *table_name, + table_fn get_table_fn) { - s_dmi dmi; - - lua_newtable(L); - - if ( ! dmi_iterate(&dmi) ) { - printf("No DMI Structure found\n"); - return -1; + if (get_table_fn(L, dmi_ptr)) { /* only adding it when it is there */ + lua_pushstring(L, table_name); + lua_insert(L, -2); + lua_settable(L,-3); } +} - parse_dmitable(&dmi); - - /* bios */ - lua_pushstring(L, "bios.vendor"); - lua_pushstring(L, dmi.bios.vendor); - lua_settable(L,-3); - - lua_pushstring(L, "bios.version"); - lua_pushstring(L, dmi.bios.version); - lua_settable(L,-3); - - lua_pushstring(L, "bios.release_date"); - lua_pushstring(L, dmi.bios.release_date); - lua_settable(L,-3); - lua_pushstring(L, "bios.bios_revision"); - lua_pushstring(L, dmi.bios.bios_revision); - lua_settable(L,-3); +void get_bool_table(lua_State *L, const char *str_table[], int n_elem, + bool *bool_table) +{ + int i; + for (i = 0; i < n_elem; i++) { + if (!str_table[i] || !*str_table[i]) /* aviod NULL/empty string */ + continue; + + lua_pushstring(L, str_table[i]); + lua_pushboolean(L, bool_table[i]); + lua_settable(L,-3); + } +} - lua_pushstring(L, "bios.firmware_revision"); - lua_pushstring(L, dmi.bios.firmware_revision); - lua_settable(L,-3); - lua_pushstring(L, "bios.address"); - lua_pushnumber(L, dmi.bios.address); - lua_settable(L,-3); +/* +** {====================================================== +** DMI subtables +** ======================================================= +*/ +static int get_bios_table(lua_State *L, s_dmi *dmi_ptr) +{ + s_bios *bios = &dmi_ptr->bios; - lua_pushstring(L, "bios.runtime_size"); - lua_pushnumber(L, dmi.bios.runtime_size); + if (!bios->filled) + return 0; + /* bios */ + lua_newtable(L); + LUA_ADD_STR_P(L, bios, vendor) + LUA_ADD_STR_P(L, bios, version) + LUA_ADD_STR_P(L, bios, release_date) + LUA_ADD_STR_P(L, bios, bios_revision) + LUA_ADD_STR_P(L, bios, firmware_revision) + LUA_ADD_NUM_P(L, bios, address) + LUA_ADD_NUM_P(L, bios, runtime_size) + LUA_ADD_STR_P(L, bios, runtime_size_unit) + LUA_ADD_NUM_P(L, bios, rom_size) + LUA_ADD_STR_P(L, bios, rom_size_unit) + + /* bios characteristics */ + lua_pushstring(L, "chars"); + lua_newtable(L); + get_bool_table(L, bios_charac_strings, + sizeof(s_characteristics)/sizeof(bool), + (bool *)(&bios->characteristics)); + get_bool_table(L, bios_charac_x1_strings, + sizeof(s_characteristics_x1)/sizeof(bool), + (bool *)(&bios->characteristics_x1)); + get_bool_table(L, bios_charac_x2_strings, + sizeof(s_characteristics_x2)/sizeof(bool), + (bool *)(&bios->characteristics_x2)); lua_settable(L,-3); - lua_pushstring(L, "bios.runtime_size_unit"); - lua_pushstring(L, dmi.bios.runtime_size_unit); - lua_settable(L,-3); + return 1; +} - lua_pushstring(L, "bios.rom_size"); - lua_pushnumber(L, dmi.bios.rom_size); - lua_settable(L,-3); - lua_pushstring(L, "bios.rom_size_unit"); - lua_pushstring(L, dmi.bios.rom_size_unit); - lua_settable(L,-3); +static int get_system_table(lua_State *L, s_dmi *dmi_ptr) +{ + s_system *system = &dmi_ptr->system; + if (!system->filled) + return 0; /* system */ - lua_pushstring(L, "system.manufacturer"); - lua_pushstring(L, dmi.system.manufacturer); - lua_settable(L,-3); - - lua_pushstring(L, "system.product_name"); - lua_pushstring(L, dmi.system.product_name); - lua_settable(L,-3); - - lua_pushstring(L, "system.version"); - lua_pushstring(L, dmi.system.version); - lua_settable(L,-3); - - lua_pushstring(L, "system.serial"); - lua_pushstring(L, dmi.system.serial); - lua_settable(L,-3); - - lua_pushstring(L, "system.uuid"); - lua_pushstring(L, dmi.system.uuid); - lua_settable(L,-3); + lua_newtable(L); + LUA_ADD_STR_P(L, system, manufacturer) + LUA_ADD_STR_P(L, system, product_name) + LUA_ADD_STR_P(L, system, version) + LUA_ADD_STR_P(L, system, serial) + LUA_ADD_STR_P(L, system, uuid) + LUA_ADD_STR_P(L, system, wakeup_type) + LUA_ADD_STR_P(L, system, sku_number) + LUA_ADD_STR_P(L, system, family) + LUA_ADD_STR_P(L, system, system_boot_status) + LUA_ADD_STR_P(L, system, configuration_options) + + /* system reset */ + if (system->system_reset.filled) { + lua_pushstring(L, "reset"); + lua_newtable(L); + LUA_ADD_NUM_S(L, system->system_reset, status) + LUA_ADD_NUM_S(L, system->system_reset, watchdog) + LUA_ADD_STR_S(L, system->system_reset, boot_option) + LUA_ADD_STR_S(L, system->system_reset, boot_option_on_limit) + LUA_ADD_STR_S(L, system->system_reset, reset_count) + LUA_ADD_STR_S(L, system->system_reset, reset_limit) + LUA_ADD_STR_S(L, system->system_reset, timer_interval) + LUA_ADD_STR_S(L, system->system_reset, timeout) + lua_settable(L,-3); + } - lua_pushstring(L, "system.wakeup_type"); - lua_pushstring(L, dmi.system.wakeup_type); - lua_settable(L,-3); + return 1; +} - lua_pushstring(L, "system.sku_number"); - lua_pushstring(L, dmi.system.sku_number); - lua_settable(L,-3); - lua_pushstring(L, "system.family"); - lua_pushstring(L, dmi.system.family); - lua_settable(L,-3); +static int get_base_board_table(lua_State *L, s_dmi *dmi_ptr) +{ + s_base_board *base_board = &dmi_ptr->base_board; + int n_dev = sizeof(base_board->devices_information) / + sizeof(base_board->devices_information[0]); + int i, j, has_dev; + if (!base_board->filled) + return 0; /* base_board */ - lua_pushstring(L, "base_board.manufacturer"); - lua_pushstring(L, dmi.base_board.manufacturer); - lua_settable(L,-3); - - lua_pushstring(L, "base_board.product_name"); - lua_pushstring(L, dmi.base_board.product_name); - lua_settable(L,-3); + lua_newtable(L); + LUA_ADD_STR_P(L, base_board, manufacturer) + LUA_ADD_STR_P(L, base_board, product_name) + LUA_ADD_STR_P(L, base_board, version) + LUA_ADD_STR_P(L, base_board, serial) + LUA_ADD_STR_P(L, base_board, asset_tag) + LUA_ADD_STR_P(L, base_board, location) + LUA_ADD_STR_P(L, base_board, type) + + /* base board features */ + lua_pushstring(L, "features"); + lua_newtable(L); + get_bool_table(L, base_board_features_strings, + sizeof(s_base_board_features)/sizeof(bool), + (bool *)(&base_board->features)); + lua_settable(L,-3); + + /* on-board devices */ + for (has_dev = 0, i = 0; i < n_dev; i++) + if (*base_board->devices_information[i].type) + has_dev++; + + if (has_dev) { + lua_pushstring(L, "devices"); + lua_newtable(L); + for (i = 0, j = 1; i < n_dev; i++) { + if (!*base_board->devices_information[i].type) /* empty device */ + continue; + + lua_pushinteger(L, j++); + lua_newtable(L); + LUA_ADD_STR_S(L, base_board->devices_information[i], type) + LUA_ADD_STR_S(L, base_board->devices_information[i], description) + LUA_ADD_NUM_S(L, base_board->devices_information[i], status) + lua_settable(L,-3); + } + lua_settable(L,-3); + } - lua_pushstring(L, "base_board.version"); - lua_pushstring(L, dmi.base_board.version); - lua_settable(L,-3); + return 1; +} - lua_pushstring(L, "base_board.serial"); - lua_pushstring(L, dmi.base_board.serial); - lua_settable(L,-3); - lua_pushstring(L, "base_board.asset_tag"); - lua_pushstring(L, dmi.base_board.asset_tag); - lua_settable(L,-3); +static int get_chassis_table(lua_State *L, s_dmi *dmi_ptr) +{ + s_chassis *chassis = &dmi_ptr->chassis; - lua_pushstring(L, "base_board.location"); - lua_pushstring(L, dmi.base_board.location); - lua_settable(L,-3); + if (!chassis->filled) + return 0; + /* chassis */ + lua_newtable(L); + LUA_ADD_STR_P(L, chassis, manufacturer) + LUA_ADD_STR_P(L, chassis, type) + LUA_ADD_STR_P(L, chassis, lock) + LUA_ADD_STR_P(L, chassis, version) + LUA_ADD_STR_P(L, chassis, serial) + LUA_ADD_STR_P(L, chassis, asset_tag) + LUA_ADD_STR_P(L, chassis, boot_up_state) + LUA_ADD_STR_P(L, chassis, power_supply_state) + LUA_ADD_STR_P(L, chassis, thermal_state) + LUA_ADD_STR_P(L, chassis, security_status) + LUA_ADD_STR_P(L, chassis, oem_information) + LUA_ADD_NUM_P(L, chassis, height) + LUA_ADD_NUM_P(L, chassis, nb_power_cords) - lua_pushstring(L, "base_board.type"); - lua_pushstring(L, dmi.base_board.type); - lua_settable(L,-3); + return 1; +} - /* chassis */ - lua_pushstring(L, "chassis.manufacturer"); - lua_pushstring(L, dmi.chassis.manufacturer); - lua_settable(L,-3); - lua_pushstring(L, "chassis.type"); - lua_pushstring(L, dmi.chassis.type); - lua_settable(L,-3); +static int get_processor_table(lua_State *L, s_dmi *dmi_ptr) +{ + s_processor *processor = &dmi_ptr->processor; + s_signature *signature = &processor->signature; - lua_pushstring(L, "chassis.lock"); - lua_pushstring(L, dmi.chassis.lock); + if (!processor->filled) + return 0; + /* processor */ + lua_newtable(L); + LUA_ADD_STR_P(L, processor, socket_designation) + LUA_ADD_STR_P(L, processor, type) + LUA_ADD_STR_P(L, processor, family) + LUA_ADD_STR_P(L, processor, manufacturer) + LUA_ADD_STR_P(L, processor, version) + LUA_ADD_NUM_P(L, processor, external_clock) + LUA_ADD_NUM_P(L, processor, max_speed) + LUA_ADD_NUM_P(L, processor, current_speed) + LUA_ADD_NUM_P(L, processor, voltage_mv) + LUA_ADD_STR_P(L, processor, status) + LUA_ADD_STR_P(L, processor, upgrade) + LUA_ADD_STR_P(L, processor, cache1) + LUA_ADD_STR_P(L, processor, cache2) + LUA_ADD_STR_P(L, processor, cache3) + LUA_ADD_STR_P(L, processor, serial) + LUA_ADD_STR_P(L, processor, part_number) + LUA_ADD_STR_P(L, processor, id) + LUA_ADD_NUM_P(L, processor, core_count) + LUA_ADD_NUM_P(L, processor, core_enabled) + LUA_ADD_NUM_P(L, processor, thread_count) + + /* processor signature */ + lua_pushstring(L, "signature"); + lua_newtable(L); + LUA_ADD_NUM_P(L, signature, type) + LUA_ADD_NUM_P(L, signature, family) + LUA_ADD_NUM_P(L, signature, model) + LUA_ADD_NUM_P(L, signature, stepping) + LUA_ADD_NUM_P(L, signature, minor_stepping) lua_settable(L,-3); - lua_pushstring(L, "chassis.version"); - lua_pushstring(L, dmi.chassis.version); + /* processor flags */ + lua_pushstring(L, "flags"); + lua_newtable(L); + get_bool_table(L, cpu_flags_strings, + sizeof(s_dmi_cpu_flags)/sizeof(bool), + (bool *)(&processor->cpu_flags)); lua_settable(L,-3); - lua_pushstring(L, "chassis.serial"); - lua_pushstring(L, dmi.chassis.serial); - lua_settable(L,-3); + return 1; +} - lua_pushstring(L, "chassis.asset_tag"); - lua_pushstring(L, dmi.chassis.asset_tag); - lua_settable(L,-3); - lua_pushstring(L, "chassis.boot_up_state"); - lua_pushstring(L, dmi.chassis.boot_up_state); - lua_settable(L,-3); +static int get_battery_table(lua_State *L, s_dmi *dmi_ptr) +{ + s_battery *battery = &dmi_ptr->battery; - lua_pushstring(L, "chassis.power_supply_state"); - lua_pushstring(L, dmi.chassis.power_supply_state); - lua_settable(L,-3); + if (!battery->filled) + return 0; + /* battery */ + lua_newtable(L); + LUA_ADD_STR_P(L, battery, location) + LUA_ADD_STR_P(L, battery, manufacturer) + LUA_ADD_STR_P(L, battery, manufacture_date) + LUA_ADD_STR_P(L, battery, serial) + LUA_ADD_STR_P(L, battery, name) + LUA_ADD_STR_P(L, battery, chemistry) + LUA_ADD_STR_P(L, battery, design_capacity) + LUA_ADD_STR_P(L, battery, design_voltage) + LUA_ADD_STR_P(L, battery, sbds) + LUA_ADD_STR_P(L, battery, sbds_serial) + LUA_ADD_STR_P(L, battery, maximum_error) + LUA_ADD_STR_P(L, battery, sbds_manufacture_date) + LUA_ADD_STR_P(L, battery, sbds_chemistry) + LUA_ADD_STR_P(L, battery, oem_info) - lua_pushstring(L, "chassis.thermal_state"); - lua_pushstring(L, dmi.chassis.thermal_state); - lua_settable(L,-3); + return 1; +} - lua_pushstring(L, "chassis.security_status"); - lua_pushstring(L, dmi.chassis.security_status); - lua_settable(L,-3); - lua_pushstring(L, "chassis.oem_information"); - lua_pushstring(L, dmi.chassis.oem_information); - lua_settable(L,-3); +static int get_memory_table(lua_State *L, s_dmi *dmi_ptr) +{ + s_memory *memory = dmi_ptr->memory; + int i, j, n_mem = dmi_ptr->memory_count; - lua_pushstring(L, "chassis.height"); - lua_pushnumber(L, dmi.chassis.height); - lua_settable(L,-3); + if (n_mem <= 0) /* no memory info */ + return 0; - lua_pushstring(L, "chassis.nb_power_cords"); - lua_pushnumber(L, dmi.chassis.nb_power_cords); - lua_settable(L,-3); + /* memory */ + lua_newtable(L); + for (j = 1, i = 0; i < n_mem; i++) { + if (!memory[i].filled) + continue; + + lua_pushinteger(L, j++); + lua_newtable(L); + LUA_ADD_STR_S(L, memory[i], manufacturer) + LUA_ADD_STR_S(L, memory[i], error) + LUA_ADD_STR_S(L, memory[i], total_width) + LUA_ADD_STR_S(L, memory[i], data_width) + LUA_ADD_STR_S(L, memory[i], size) + LUA_ADD_STR_S(L, memory[i], form_factor) + LUA_ADD_STR_S(L, memory[i], device_set) + LUA_ADD_STR_S(L, memory[i], device_locator) + LUA_ADD_STR_S(L, memory[i], bank_locator) + LUA_ADD_STR_S(L, memory[i], type) + LUA_ADD_STR_S(L, memory[i], type_detail) + LUA_ADD_STR_S(L, memory[i], speed) + LUA_ADD_STR_S(L, memory[i], serial) + LUA_ADD_STR_S(L, memory[i], asset_tag) + LUA_ADD_STR_S(L, memory[i], part_number) + lua_settable(L,-3); + } + return 1; +} - /* processor */ - lua_pushstring(L, "processor.socket_designation"); - lua_pushstring(L, dmi.processor.socket_designation); - lua_settable(L,-3); - lua_pushstring(L, "processor.type"); - lua_pushstring(L, dmi.processor.type); - lua_settable(L,-3); +static int get_memory_module_table(lua_State *L, s_dmi *dmi_ptr) +{ + s_memory_module *memory_module = dmi_ptr->memory_module; + int i, j, n_mem = dmi_ptr->memory_module_count; - lua_pushstring(L, "processor.family"); - lua_pushstring(L, dmi.processor.family); - lua_settable(L,-3); + if (n_mem <= 0) /* no memory module info */ + return 0; - lua_pushstring(L, "processor.manufacturer"); - lua_pushstring(L, dmi.processor.manufacturer); - lua_settable(L,-3); + /* memory module */ + lua_newtable(L); + for (j = 1, i = 0; i < n_mem; i++) { + if (!memory_module[i].filled) + continue; + + lua_pushinteger(L, j++); + lua_newtable(L); + LUA_ADD_STR_S(L, memory_module[i], socket_designation) + LUA_ADD_STR_S(L, memory_module[i], bank_connections) + LUA_ADD_STR_S(L, memory_module[i], speed) + LUA_ADD_STR_S(L, memory_module[i], type) + LUA_ADD_STR_S(L, memory_module[i], installed_size) + LUA_ADD_STR_S(L, memory_module[i], enabled_size) + LUA_ADD_STR_S(L, memory_module[i], error_status) + lua_settable(L,-3); + } + return 1; +} - lua_pushstring(L, "processor.version"); - lua_pushstring(L, dmi.processor.version); - lua_settable(L,-3); - lua_pushstring(L, "processor.external_clock"); - lua_pushnumber(L, dmi.processor.external_clock); - lua_settable(L,-3); +static int get_cache_table(lua_State *L, s_dmi *dmi_ptr) +{ + s_cache *cache = dmi_ptr->cache; + int i, n_cache = dmi_ptr->cache_count; - lua_pushstring(L, "processor.max_speed"); - lua_pushnumber(L, dmi.processor.max_speed); - lua_settable(L,-3); + if (n_cache <= 0) /* no cache info */ + return 0; - lua_pushstring(L, "processor.current_speed"); - lua_pushnumber(L, dmi.processor.current_speed); - lua_settable(L,-3); + /* memory */ + lua_newtable(L); + for (i = 0; i < n_cache; i++) { + lua_pushinteger(L, i + 1); + lua_newtable(L); + LUA_ADD_STR_S(L, cache[i], socket_designation) + LUA_ADD_STR_S(L, cache[i], configuration) + LUA_ADD_STR_S(L, cache[i], mode) + LUA_ADD_STR_S(L, cache[i], location) + LUA_ADD_NUM_S(L, cache[i], installed_size) + LUA_ADD_NUM_S(L, cache[i], max_size) + LUA_ADD_STR_S(L, cache[i], supported_sram_types) + LUA_ADD_STR_S(L, cache[i], installed_sram_types) + LUA_ADD_NUM_S(L, cache[i], speed) + LUA_ADD_STR_S(L, cache[i], error_correction_type) + LUA_ADD_STR_S(L, cache[i], system_type) + LUA_ADD_STR_S(L, cache[i], associativity) + lua_settable(L,-3); + } + return 1; +} - lua_pushstring(L, "processor.signature.type"); - lua_pushnumber(L, dmi.processor.signature.type); - lua_settable(L,-3); - lua_pushstring(L, "processor.signature.family"); - lua_pushnumber(L, dmi.processor.signature.family); - lua_settable(L,-3); +static int get_hardware_security_table(lua_State *L, s_dmi *dmi_ptr) +{ + if (!dmi_ptr->hardware_security.filled) + return 0; + /* hardware_security */ + lua_newtable(L); + LUA_ADD_STR_S(L, dmi_ptr->hardware_security, power_on_passwd_status) + LUA_ADD_STR_S(L, dmi_ptr->hardware_security, keyboard_passwd_status) + LUA_ADD_STR_S(L, dmi_ptr->hardware_security, administrator_passwd_status) + LUA_ADD_STR_S(L, dmi_ptr->hardware_security, front_panel_reset_status) - lua_pushstring(L, "processor.signature.model"); - lua_pushnumber(L, dmi.processor.signature.model); - lua_settable(L,-3); + return 1; +} - lua_pushstring(L, "processor.signature.stepping"); - lua_pushnumber(L, dmi.processor.signature.stepping); - lua_settable(L,-3); - lua_pushstring(L, "processor.signature.minor_stepping"); - lua_pushnumber(L, dmi.processor.signature.minor_stepping); - lua_settable(L,-3); +static int get_dmi_info_table(lua_State *L, s_dmi *dmi_ptr) +{ + dmi_table *dmitable = &dmi_ptr->dmitable; - lua_pushstring(L, "processor.voltage_mv"); - lua_pushnumber(L, dmi.processor.voltage_mv); - lua_settable(L,-3); + /* dmi info */ + lua_newtable(L); + LUA_ADD_NUM_P(L, dmitable, num) + LUA_ADD_NUM_P(L, dmitable, len) + LUA_ADD_NUM_P(L, dmitable, ver) + LUA_ADD_NUM_P(L, dmitable, base) + LUA_ADD_NUM_P(L, dmitable, major_version) + LUA_ADD_NUM_P(L, dmitable, minor_version) - lua_pushstring(L, "processor.status"); - lua_pushstring(L, dmi.processor.status); - lua_settable(L,-3); + return 1; +} - lua_pushstring(L, "processor.upgrade"); - lua_pushstring(L, dmi.processor.upgrade); - lua_settable(L,-3); - lua_pushstring(L, "processor.cache1"); - lua_pushstring(L, dmi.processor.cache1); - lua_settable(L,-3); +static int get_ipmi_table(lua_State *L, s_dmi *dmi_ptr) +{ + s_ipmi *ipmi = &dmi_ptr->ipmi; - lua_pushstring(L, "processor.cache2"); - lua_pushstring(L, dmi.processor.cache2); - lua_settable(L,-3); + if (!ipmi->filled) + return 0; + /* ipmi */ + lua_newtable(L); + LUA_ADD_STR_P(L, ipmi, interface_type) + LUA_ADD_NUM_P(L, ipmi, major_specification_version) + LUA_ADD_NUM_P(L, ipmi, minor_specification_version) + LUA_ADD_NUM_P(L, ipmi, I2C_slave_address) + LUA_ADD_NUM_P(L, ipmi, nv_address) + LUA_ADD_NUM_P(L, ipmi, base_address) + LUA_ADD_NUM_P(L, ipmi, irq) - lua_pushstring(L, "processor.cache3"); - lua_pushstring(L, dmi.processor.cache3); - lua_settable(L,-3); + return 1; +} +/* +** {====================================================== +** End of DMI subtables +** ======================================================= +*/ - lua_pushstring(L, "processor.serial"); - lua_pushstring(L, dmi.processor.serial); - lua_settable(L,-3); - lua_pushstring(L, "processor.part_number"); - lua_pushstring(L, dmi.processor.part_number); - lua_settable(L,-3); +static int dmi_gettable(lua_State *L) +{ + s_dmi dmi; - lua_pushstring(L, "processor.id"); - lua_pushstring(L, dmi.processor.id); - lua_settable(L,-3); + lua_newtable(L); - lua_pushstring(L, "processor.core_count"); - lua_pushnumber(L, dmi.processor.core_count); - lua_settable(L,-3); + if ( ! dmi_iterate(&dmi) ) { + printf("No DMI Structure found\n"); + return -1; + } - lua_pushstring(L, "processor.core_enabled"); - lua_pushnumber(L, dmi.processor.core_enabled); - lua_settable(L,-3); + parse_dmitable(&dmi); - lua_pushstring(L, "processor.thread_count"); - lua_pushnumber(L, dmi.processor.thread_count); - lua_settable(L,-3); + LUA_ADD_NUM_S(L, dmi, memory_module_count) + LUA_ADD_NUM_S(L, dmi, memory_count) + LUA_ADD_NUM_S(L, dmi, cache_count) + LUA_ADD_STR_S(L, dmi, oem_strings) + + LUA_ADD_TABLE(L, &dmi, bios) + LUA_ADD_TABLE(L, &dmi, system) + LUA_ADD_TABLE(L, &dmi, base_board) + LUA_ADD_TABLE(L, &dmi, chassis) + LUA_ADD_TABLE(L, &dmi, processor) + LUA_ADD_TABLE(L, &dmi, battery) + LUA_ADD_TABLE(L, &dmi, memory) + LUA_ADD_TABLE(L, &dmi, memory_module) + LUA_ADD_TABLE(L, &dmi, cache) + LUA_ADD_TABLE(L, &dmi, ipmi) + LUA_ADD_TABLE(L, &dmi, hardware_security) + LUA_ADD_TABLE(L, &dmi, dmi_info) /* set global variable: lua_setglobal(L, "dmitable"); */ diff --git a/com32/lua/src/linit.c b/com32/lua/src/linit.c index 6c7f63e4..6e978736 100644 --- a/com32/lua/src/linit.c +++ b/com32/lua/src/linit.c @@ -33,6 +33,7 @@ static const luaL_Reg lualibs[] = { {LUA_PCILIBNAME, luaopen_pci}, {LUA_SYSLINUXLIBNAME, luaopen_syslinux}, {LUA_VESALIBNAME, luaopen_vesa}, + {LUA_DHCPLIBNAME, luaopen_dhcp}, #endif {NULL, NULL} }; diff --git a/com32/lua/src/liolib.c b/com32/lua/src/liolib.c index 3f27395d..cf9dca22 100644 --- a/com32/lua/src/liolib.c +++ b/com32/lua/src/liolib.c @@ -18,12 +18,18 @@ #include "lauxlib.h" #include "lualib.h" - +#ifdef SYSLINUX + #define NO_TMP_FILE 1 + #define NO_READ_NUMBER 1 + #define NO_TEST_EOF 1 + #define NO_CLEAR_ERR 1 + #define NO_F_SEEK 1 + #define NO_F_SETVBUF 1 +#endif #define IO_INPUT 1 #define IO_OUTPUT 2 - static const char *const fnames[] = {"input", "output"}; @@ -180,7 +186,7 @@ static int io_popen (lua_State *L) { } -#ifndef SYSLINUX +#ifndef NO_TMP_FILE static int io_tmpfile (lua_State *L) { FILE **pf = newfile(L); *pf = tmpfile(); @@ -271,7 +277,7 @@ static int io_lines (lua_State *L) { ** ======================================================= */ -#ifndef SYSLINUX +#ifndef NO_READ_NUMBER /* No fscanf() and thus no read_number() */ static int read_number (lua_State *L, FILE *f) { lua_Number d; if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { @@ -283,8 +289,9 @@ static int read_number (lua_State *L, FILE *f) { return 0; /* read fails */ } } +#endif - +#ifndef NO_TEST_EOF /* no buffering -> no ungetc() -> no EOF test */ static int test_eof (lua_State *L, FILE *f) { int c = getc(f); ungetc(c, f); @@ -315,7 +322,6 @@ static int read_line (lua_State *L, FILE *f) { } } -#ifndef SYSLINUX /* Not used */ static int read_chars (lua_State *L, FILE *f, size_t n) { size_t rlen; /* how much to read */ size_t nr; /* number of chars actually read */ @@ -337,7 +343,9 @@ static int g_read (lua_State *L, FILE *f, int first) { int nargs = lua_gettop(L) - 1; int success; int n; +#ifndef NO_CLEAR_ERR clearerr(f); +#endif if (nargs == 0) { /* no arguments? */ success = read_line(L, f); n = first+1; /* to return 1 result */ @@ -348,14 +356,22 @@ static int g_read (lua_State *L, FILE *f, int first) { for (n = first; nargs-- && success; n++) { if (lua_type(L, n) == LUA_TNUMBER) { size_t l = (size_t)lua_tointeger(L, n); +#ifndef NO_TEST_EOF success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); +#else /* we don't have test_eof defined */ + success = (l == 0) ? 1 : read_chars(L, f, l); +#endif } else { const char *p = lua_tostring(L, n); luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); switch (p[1]) { case 'n': /* number */ +#ifndef NO_READ_NUMBER success = read_number(L, f); +#else + return luaL_argerror(L, n, "\"*number\" not supported"); +#endif break; case 'l': /* line */ success = read_line(L, f); @@ -388,7 +404,6 @@ static int io_read (lua_State *L) { static int f_read (lua_State *L) { return g_read(L, tofile(L), 2); } -#endif static int io_readline (lua_State *L) { @@ -441,7 +456,7 @@ static int f_write (lua_State *L) { return g_write(L, tofile(L), 2); } -#ifndef SYSLINUX +#ifndef NO_F_SEEK static int f_seek (lua_State *L) { static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; static const char *const modenames[] = {"set", "cur", "end", NULL}; @@ -456,8 +471,9 @@ static int f_seek (lua_State *L) { return 1; } } +#endif - +#ifndef NO_F_SETVBUF static int f_setvbuf (lua_State *L) { static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; static const char *const modenames[] = {"no", "full", "line", NULL}; @@ -488,8 +504,8 @@ static const luaL_Reg iolib[] = { {"open", io_open}, {"output", io_output}, {"popen", io_popen}, -#ifndef SYSLINUX {"read", io_read}, +#ifndef NO_TMP_FILE {"tmpfile", io_tmpfile}, #endif {"type", io_type}, @@ -502,9 +518,11 @@ static const luaL_Reg flib[] = { {"close", io_close}, {"flush", f_flush}, {"lines", f_lines}, -#ifndef SYSLINUX {"read", f_read}, +#ifndef NO_F_SEEK {"seek", f_seek}, +#endif +#ifndef NO_F_SETVBUF {"setvbuf", f_setvbuf}, #endif {"write", f_write}, diff --git a/com32/lua/src/lualib.h b/com32/lua/src/lualib.h index 0ae6ba75..40d1bf29 100644 --- a/com32/lua/src/lualib.h +++ b/com32/lua/src/lualib.h @@ -54,6 +54,9 @@ LUALIB_API int (luaopen_vesa) (lua_State *L); #define LUA_CPULIBNAME "cpu" LUALIB_API int (luaopen_cpu) (lua_State *L); + +#define LUA_DHCPLIBNAME "dhcp" +LUALIB_API int (luaopen_dhcp) (lua_State *L); #endif /* open all previous libraries */ diff --git a/com32/lua/src/syslinux.c b/com32/lua/src/syslinux.c index 9b207db7..afcdcaad 100644 --- a/com32/lua/src/syslinux.c +++ b/com32/lua/src/syslinux.c @@ -39,6 +39,7 @@ #include "syslinux/loadfile.h" #include "syslinux/linux.h" #include "syslinux/config.h" +#include "syslinux/reboot.h" int __parse_argv(char ***argv, const char *str); @@ -278,7 +279,7 @@ static int sl_boot_linux(lua_State * L) msleep(10000); */ - ret = syslinux_boot_linux(kernel_data, kernel_len, initramfs, newcmdline); + ret = syslinux_boot_linux(kernel_data, kernel_len, initramfs, NULL, newcmdline); printf("syslinux_boot_linux returned %d\n", ret); @@ -405,7 +406,36 @@ static int sl_boot_it(lua_State * L) (void)mem_limit; return syslinux_boot_linux(kernel->data, kernel->size, - initramfs, (char *)cmdline); + initramfs, NULL, (char *)cmdline); +} + +static int sl_config_file(lua_State * L) +{ + const char *config_file = syslinux_config_file(); + lua_pushstring(L, config_file); + return 1; +} + +static int sl_reboot(lua_State * L) +{ + int warm_boot = luaL_optint(L, 1, 0); + /* explicitly convert it to 1 or 0 */ + warm_boot = warm_boot? 1 : 0; + syslinux_reboot(warm_boot); + return 0; +} + +static int sl_ipappend_strs(lua_State * L) +{ + int i; + const struct syslinux_ipappend_strings *ip_strs = syslinux_ipappend_strings(); + lua_newtable(L); + for (i = 0; i < ip_strs->count; i++) { + lua_pushinteger(L, i + 1); + lua_pushstring(L, ip_strs->ptr[i]); + lua_settable(L,-3); + } + return 1; } static int sl_derivative(lua_State * L) @@ -459,6 +489,9 @@ static const luaL_reg syslinuxlib[] = { {"initramfs_load_archive", sl_initramfs_load_archive}, {"initramfs_add_file", sl_initramfs_add_file}, {"boot_it", sl_boot_it}, + {"config_file", sl_config_file}, + {"ipappend_strs", sl_ipappend_strs}, + {"reboot", sl_reboot}, {"derivative", sl_derivative}, {"version", sl_version}, {NULL, NULL} diff --git a/com32/mboot/mboot.c b/com32/mboot/mboot.c index 35450e03..10e6701c 100644 --- a/com32/mboot/mboot.c +++ b/com32/mboot/mboot.c @@ -198,7 +198,7 @@ int main(int argc, char *argv[]) } if (init_map()) - return 1; /* Failed to allocate intitial map */ + return 1; /* Failed to allocate initial map */ /* * Map the primary image. This should be done before mapping anything diff --git a/com32/menu/menumain.c b/com32/menu/menumain.c index 5b3f6bd1..8573901c 100644 --- a/com32/menu/menumain.c +++ b/com32/menu/menumain.c @@ -806,7 +806,7 @@ static const char *run_menu(void) while (entry < cm->nentries && is_disabled(cm->menu_entries[entry])) entry++; } - if (entry >= cm->nentries) { + if (entry >= cm->nentries - 1) { entry = cm->nentries - 1; while (entry > 0 && is_disabled(cm->menu_entries[entry])) entry--; diff --git a/com32/menu/readconfig.c b/com32/menu/readconfig.c index 0ac2564a..431017f6 100644 --- a/com32/menu/readconfig.c +++ b/com32/menu/readconfig.c @@ -392,7 +392,9 @@ static void record(struct menu *m, struct labeldata *ld, const char *append) break; } - if (ld->menudefault && me->action == MA_CMD) + if (ld->menudefault && (me->action == MA_CMD || + me->action == MA_GOTO || + me->action == MA_GOTO_UNRES)) m->defentry = m->nentries - 1; } diff --git a/com32/modules/Makefile b/com32/modules/Makefile index e9ce1d1f..f110e584 100644 --- a/com32/modules/Makefile +++ b/com32/modules/Makefile @@ -19,11 +19,12 @@ topdir = ../.. MAKEDIR = $(topdir)/mk include $(MAKEDIR)/com32.mk -MODULES = chain.c32 config.c32 ethersel.c32 dmitest.c32 cpuidtest.c32 \ +MODULES = 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 host.c32 ls.c32 gpxecmd.c32 \ - ifcpu.c32 cpuid.c32 cat.c32 pwd.c32 ifplop.c32 zzjson.c32 whichsys.c32 + ifcpu.c32 cpuid.c32 cat.c32 pwd.c32 ifplop.c32 zzjson.c32 \ + whichsys.c32 prdhcp.c32 pxechn.c32 kontron_wdt.c32 ifmemdsk.c32 TESTFILES = diff --git a/com32/modules/chain.c b/com32/modules/chain.c deleted file mode 100644 index 2ed0f14c..00000000 --- a/com32/modules/chain.c +++ /dev/null @@ -1,1932 +0,0 @@ -/* ----------------------------------------------------------------------- * - * - * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved - * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin - * Significant portions copyright (C) 2010 Shao Miller - * [partition iteration, GPT, "fs"] - * - * 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., 53 Temple Place Ste 330, - * Boston MA 02111-1307, USA; either version 2 of the License, or - * (at your option) any later version; incorporated herein by reference. - * - * ----------------------------------------------------------------------- */ - -/* - * chain.c - * - * Chainload a hard disk (currently rather braindead.) - * - * Usage: chain [options] - * chain hd<disk#> [<partition>] [options] - * chain fd<disk#> [options] - * chain mbr:<id> [<partition>] [options] - * chain guid:<guid> [<partition>] [options] - * chain label:<label> [<partition>] [options] - * chain boot [<partition>] [options] - * - * For example, "chain msdos=io.sys" will load DOS from the current Syslinux - * filesystem. "chain hd0 1" will boot the first partition on the first hard - * disk. - * - * When none of the "hdX", "fdX", "mbr:", "guid:", "label:", "boot" or "fs" - * options are specified, the default behaviour is equivalent to "boot". - * "boot" means to use the current Syslinux drive, and you can also specify - * a partition. - * - * The mbr: syntax means search all the hard disks until one with a - * specific MBR serial number (bytes 440-443) is found. - * - * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.) - * - * "fs" will use the current Syslinux filesystem as the boot drive/partition. - * When booting from PXELINUX, you will most likely wish to specify a disk. - * - * Options: - * - * file=<loader> - * loads the file <loader> **from the Syslinux filesystem** - * instead of loading the boot sector. - * - * seg=<segment>[:<offset>][{+@}entry] - * loads at <segment>:<offset> and jumps to <seg>:<entry> instead - * of the default 0000:7C00. <offset> and <entry> default to 0 and +0 - * repectively. If <entry> start with + (rather than @) then the - * entry point address is added to the offset. - * - * isolinux=<loader> - * chainload another version/build of the ISOLINUX bootloader and patch - * the loader with appropriate parameters in memory. - * This avoids the need for the -eltorito-alt-boot parameter of mkisofs, - * when you want more than one ISOLINUX per CD/DVD. - * - * ntldr=<loader> - * equivalent to seg=0x2000 file=<loader> sethidden, - * used with WinNT's loaders - * - * cmldr=<loader> - * used with Recovery Console of Windows NT/2K/XP. - * same as ntldr=<loader> & "cmdcons\0" written to - * the system name field in the bootsector - * - * freedos=<loader> - * equivalent to seg=0x60 file=<loader> sethidden, - * used with FreeDOS' kernel.sys. - * - * msdos=<loader> - * pcdos=<loader> - * equivalent to seg=0x70 file=<loader> sethidden, - * used with DOS' io.sys. - * - * drmk=<loader> - * Similar to msdos=<loader> but prepares the special options - * for the Dell Real Mode Kernel. - * - * grub=<loader> - * same as seg=0x800 file=<loader> & jumping to seg 0x820, - * used with GRUB Legacy stage2 files. - * - * grubcfg=<filename> - * set an alternative config filename in stage2 of Grub Legacy, - * only applicable in combination with "grub=<loader>". - * - * grldr=<loader> - * pass the partition number to GRUB4DOS, - * used with GRUB4DOS' grldr. - * - * swap - * if the disk is not fd0/hd0, install a BIOS stub which swaps - * the drive numbers. - * - * hide - * change type of primary partitions with IDs 01, 04, 06, 07, - * 0b, 0c, or 0e to 1x, except for the selected partition, which - * is converted the other way. - * - * sethidden - * update the "hidden sectors" (partition offset) field in a - * FAT/NTFS boot sector. - * - * keeppxe - * keep the PXE and UNDI stacks in memory (PXELINUX only). - * - * freeldr=<loader> - * loads ReactOS' FreeLdr.sys to 0:8000 and jumps to the PE entry-point - */ - -#include <com32.h> -#include <stdlib.h> -#include <stdio.h> -#include <ctype.h> -#include <string.h> -#include <console.h> -#include <minmax.h> -#include <stdbool.h> -#include <dprintf.h> -#include <syslinux/loadfile.h> -#include <syslinux/bootrm.h> -#include <syslinux/config.h> -#include <syslinux/video.h> - -#define SECTOR 512 /* bytes/sector */ - -static struct options { - const char *loadfile; - uint16_t keeppxe; - uint16_t seg; - uint16_t offs; - uint16_t entry; - bool isolinux; - bool cmldr; - bool grub; - bool grldr; - const char *grubcfg; - bool swap; - bool hide; - bool sethidden; - bool drmk; -} opt; - -struct data_area { - void *data; - addr_t base; - addr_t size; -}; - -static inline void error(const char *msg) -{ - fputs(msg, stderr); -} - -/* - * Call int 13h, but with retry on failure. Especially floppies need this. - */ -static int int13_retry(const com32sys_t * inreg, com32sys_t * outreg) -{ - int retry = 6; /* Number of retries */ - com32sys_t tmpregs; - - if (!outreg) - outreg = &tmpregs; - - while (retry--) { - __intcall(0x13, inreg, outreg); - if (!(outreg->eflags.l & EFLAGS_CF)) - return 0; /* CF=0, OK */ - } - - return -1; /* Error */ -} - -/* - * Query disk parameters and EBIOS availability for a particular disk. - */ -struct diskinfo { - int disk; - int ebios; /* EBIOS supported on this disk */ - int cbios; /* CHS geometry is valid */ - int head; - int sect; -} disk_info; - -static int get_disk_params(int disk) -{ - static com32sys_t getparm, parm, getebios, ebios; - - disk_info.disk = disk; - disk_info.ebios = disk_info.cbios = 0; - - /* Get EBIOS support */ - getebios.eax.w[0] = 0x4100; - getebios.ebx.w[0] = 0x55aa; - getebios.edx.b[0] = disk; - getebios.eflags.b[0] = 0x3; /* CF set */ - - __intcall(0x13, &getebios, &ebios); - - if (!(ebios.eflags.l & EFLAGS_CF) && - ebios.ebx.w[0] == 0xaa55 && (ebios.ecx.b[0] & 1)) { - disk_info.ebios = 1; - } - - /* Get disk parameters -- really only useful for - hard disks, but if we have a partitioned floppy - it's actually our best chance... */ - getparm.eax.b[1] = 0x08; - getparm.edx.b[0] = disk; - - __intcall(0x13, &getparm, &parm); - - if (parm.eflags.l & EFLAGS_CF) - return disk_info.ebios ? 0 : -1; - - disk_info.head = parm.edx.b[1] + 1; - disk_info.sect = parm.ecx.b[0] & 0x3f; - if (disk_info.sect == 0) { - disk_info.sect = 1; - } else { - disk_info.cbios = 1; /* Valid geometry */ - } - - return 0; -} - -/* - * Get a disk block and return a malloc'd buffer. - * Uses the disk number and information from disk_info. - */ -struct ebios_dapa { - uint16_t len; - uint16_t count; - uint16_t off; - uint16_t seg; - uint64_t lba; -}; - -/* Read count sectors from drive, starting at lba. Return a new buffer */ -static void *read_sectors(uint64_t lba, uint8_t count) -{ - com32sys_t inreg; - struct ebios_dapa *dapa = __com32.cs_bounce; - void *buf = (char *)__com32.cs_bounce + SECTOR; - void *data; - - if (!count) - /* Silly */ - return NULL; - - memset(&inreg, 0, sizeof inreg); - - if (disk_info.ebios) { - dapa->len = sizeof(*dapa); - dapa->count = count; - dapa->off = OFFS(buf); - dapa->seg = SEG(buf); - dapa->lba = lba; - - inreg.esi.w[0] = OFFS(dapa); - inreg.ds = SEG(dapa); - inreg.edx.b[0] = disk_info.disk; - inreg.eax.b[1] = 0x42; /* Extended read */ - } else { - unsigned int c, h, s, t; - - if (!disk_info.cbios) { - /* We failed to get the geometry */ - - if (lba) - return NULL; /* Can only read MBR */ - - s = h = c = 0; - } else { - s = lba % disk_info.sect; - t = lba / disk_info.sect; /* Track = head*cyl */ - h = t % disk_info.head; - c = t / disk_info.head; - } - - if (s >= 63 || h >= 256 || c >= 1024) - return NULL; - - inreg.eax.b[0] = count; - inreg.eax.b[1] = 0x02; /* Read */ - inreg.ecx.b[1] = c; - inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s + 1); - inreg.edx.b[1] = h; - inreg.edx.b[0] = disk_info.disk; - inreg.ebx.w[0] = OFFS(buf); - inreg.es = SEG(buf); - } - - if (int13_retry(&inreg, NULL)) - return NULL; - - data = malloc(count * SECTOR); - if (data) - memcpy(data, buf, count * SECTOR); - return data; -} - -static int write_sector(unsigned int lba, const void *data) -{ - com32sys_t inreg; - struct ebios_dapa *dapa = __com32.cs_bounce; - void *buf = (char *)__com32.cs_bounce + SECTOR; - - memcpy(buf, data, SECTOR); - memset(&inreg, 0, sizeof inreg); - - if (disk_info.ebios) { - dapa->len = sizeof(*dapa); - dapa->count = 1; /* 1 sector */ - dapa->off = OFFS(buf); - dapa->seg = SEG(buf); - dapa->lba = lba; - - inreg.esi.w[0] = OFFS(dapa); - inreg.ds = SEG(dapa); - inreg.edx.b[0] = disk_info.disk; - inreg.eax.w[0] = 0x4300; /* Extended write */ - } else { - unsigned int c, h, s, t; - - if (!disk_info.cbios) { - /* We failed to get the geometry */ - - if (lba) - return -1; /* Can only write MBR */ - - s = h = c = 0; - } else { - s = lba % disk_info.sect; - t = lba / disk_info.sect; /* Track = head*cyl */ - h = t % disk_info.head; - c = t / disk_info.head; - } - - if (s >= 63 || h >= 256 || c >= 1024) - return -1; - - inreg.eax.w[0] = 0x0301; /* Write one sector */ - inreg.ecx.b[1] = c; - inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s + 1); - inreg.edx.b[1] = h; - inreg.edx.b[0] = disk_info.disk; - inreg.ebx.w[0] = OFFS(buf); - inreg.es = SEG(buf); - } - - if (int13_retry(&inreg, NULL)) - return -1; - - return 0; /* ok */ -} - -static int write_verify_sector(unsigned int lba, const void *buf) -{ - char *rb; - int rv; - - rv = write_sector(lba, buf); - if (rv) - return rv; /* Write failure */ - rb = read_sectors(lba, 1); - if (!rb) - return -1; /* Readback failure */ - rv = memcmp(buf, rb, SECTOR); - free(rb); - return rv ? -1 : 0; -} - -/* - * CHS (cylinder, head, sector) value extraction macros. - * Taken from WinVBlock. Does not expand to an lvalue -*/ -#define chs_head(chs) chs[0] -#define chs_sector(chs) (chs[1] & 0x3F) -#define chs_cyl_high(chs) (((uint16_t)(chs[1] & 0xC0)) << 2) -#define chs_cyl_low(chs) ((uint16_t)chs[2]) -#define chs_cylinder(chs) (chs_cyl_high(chs) | chs_cyl_low(chs)) -typedef uint8_t chs[3]; - -/* A DOS partition table entry */ -struct part_entry { - uint8_t active_flag; /* 0x80 if "active" */ - chs start; - uint8_t ostype; - chs end; - uint32_t start_lba; - uint32_t length; -} __attribute__ ((packed)); - -static void mbr_part_dump(const struct part_entry *part) -{ - (void)part; - dprintf("Partition status _____ : 0x%.2x\n" - "Partition CHS start\n" - " Cylinder ___________ : 0x%.4x (%u)\n" - " Head _______________ : 0x%.2x (%u)\n" - " Sector _____________ : 0x%.2x (%u)\n" - "Partition type _______ : 0x%.2x\n" - "Partition CHS end\n" - " Cylinder ___________ : 0x%.4x (%u)\n" - " Head _______________ : 0x%.2x (%u)\n" - " Sector _____________ : 0x%.2x (%u)\n" - "Partition LBA start __ : 0x%.8x (%u)\n" - "Partition LBA count __ : 0x%.8x (%u)\n" - "-------------------------------\n", - part->active_flag, - chs_cylinder(part->start), - chs_cylinder(part->start), - chs_head(part->start), - chs_head(part->start), - chs_sector(part->start), - chs_sector(part->start), - part->ostype, - chs_cylinder(part->end), - chs_cylinder(part->end), - chs_head(part->end), - chs_head(part->end), - chs_sector(part->end), - chs_sector(part->end), - part->start_lba, part->start_lba, part->length, part->length); -} - -/* A DOS MBR */ -struct mbr { - char code[440]; - uint32_t disk_sig; - char pad[2]; - struct part_entry table[4]; - uint16_t sig; -} __attribute__ ((packed)); -static const uint16_t mbr_sig_magic = 0xAA55; - -/* Search for a specific drive, based on the MBR signature; bytes 440-443 */ -static int find_disk(uint32_t mbr_sig) -{ - int drive; - bool is_me; - struct mbr *mbr; - - for (drive = 0x80; drive <= 0xff; drive++) { - if (get_disk_params(drive)) - continue; /* Drive doesn't exist */ - if (!(mbr = read_sectors(0, 1))) - continue; /* Cannot read sector */ - is_me = (mbr->disk_sig == mbr_sig); - free(mbr); - if (is_me) - return drive; - } - return -1; -} - -/* Forward declaration */ -struct disk_part_iter; - -/* Partition-/scheme-specific routine returning the next partition */ -typedef struct disk_part_iter *(*disk_part_iter_func) (struct disk_part_iter * - part); - -/* Contains details for a partition under examination */ -struct disk_part_iter { - /* The block holding the table we are part of */ - char *block; - /* The LBA for the beginning of data */ - uint64_t lba_data; - /* The partition number, as determined by our heuristic */ - int index; - /* The DOS partition record to pass, if applicable */ - const struct part_entry *record; - /* Function returning the next available partition */ - disk_part_iter_func next; - /* Partition-/scheme-specific details */ - union { - /* MBR specifics */ - int mbr_index; - /* EBR specifics */ - struct { - /* The first extended partition's start LBA */ - uint64_t lba_extended; - /* Any applicable parent, or NULL */ - struct disk_part_iter *parent; - /* The parent extended partition index */ - int parent_index; - } ebr; - /* GPT specifics */ - struct { - /* Real (not effective) index in the partition table */ - int index; - /* Current partition GUID */ - const struct guid *part_guid; - /* Current partition label */ - const char *part_label; - /* Count of entries in GPT */ - int parts; - /* Partition record size */ - uint32_t size; - } gpt; - } private; -}; - -static struct disk_part_iter *next_ebr_part(struct disk_part_iter *part) -{ - const struct part_entry *ebr_table; - const struct part_entry *parent_table = - ((const struct mbr *)part->private.ebr.parent->block)->table; - static const struct part_entry phony = {.start_lba = 0 }; - uint64_t ebr_lba; - - /* Don't look for a "next EBR" the first time around */ - if (part->private.ebr.parent_index >= 0) - /* Look at the linked list */ - ebr_table = ((const struct mbr *)part->block)->table + 1; - /* Do we need to look for an extended partition? */ - if (part->private.ebr.parent_index < 0 || !ebr_table->start_lba) { - /* Start looking for an extended partition in the MBR */ - while (++part->private.ebr.parent_index < 4) { - uint8_t type = parent_table[part->private.ebr.parent_index].ostype; - - if ((type == 0x05) || (type == 0x0F) || (type == 0x85)) - break; - } - if (part->private.ebr.parent_index == 4) - /* No extended partitions found */ - goto out_finished; - part->private.ebr.lba_extended = - parent_table[part->private.ebr.parent_index].start_lba; - ebr_table = &phony; - } - /* Load next EBR */ - ebr_lba = ebr_table->start_lba + part->private.ebr.lba_extended; - free(part->block); - part->block = read_sectors(ebr_lba, 1); - if (!part->block) { - error("Could not load EBR!\n"); - goto err_ebr; - } - ebr_table = ((const struct mbr *)part->block)->table; - dprintf("next_ebr_part:\n"); - mbr_part_dump(ebr_table); - - /* - * Sanity check entry: must not extend outside the - * extended partition. This is necessary since some OSes - * put crap in some entries. - */ - { - const struct mbr *mbr = - (const struct mbr *)part->private.ebr.parent->block; - const struct part_entry *extended = - mbr->table + part->private.ebr.parent_index; - - if (ebr_table[0].start_lba >= extended->start_lba + extended->length) { - dprintf("Insane logical partition!\n"); - goto err_insane; - } - } - /* Success */ - part->lba_data = ebr_table[0].start_lba + ebr_lba; - dprintf("Partition %d logical lba %"PRIu64"\n", - part->index, part->lba_data); - part->index++; - part->record = ebr_table; - return part; - -err_insane: - - free(part->block); - part->block = NULL; -err_ebr: - -out_finished: - free(part->private.ebr.parent->block); - free(part->private.ebr.parent); - free(part->block); - free(part); - return NULL; -} - -static struct disk_part_iter *next_mbr_part(struct disk_part_iter *part) -{ - struct disk_part_iter *ebr_part; - /* Look at the partition table */ - struct part_entry *table = ((struct mbr *)part->block)->table; - - /* Look for data partitions */ - while (++part->private.mbr_index < 4) { - uint8_t type = table[part->private.mbr_index].ostype; - - if (type == 0x00 || type == 0x05 || type == 0x0F || type == 0x85) - /* Skip empty or extended partitions */ - continue; - if (!table[part->private.mbr_index].length) - /* Empty */ - continue; - break; - } - /* If we're currently the last partition, it's time for EBR processing */ - if (part->private.mbr_index == 4) { - /* Allocate another iterator for extended partitions */ - ebr_part = malloc(sizeof(*ebr_part)); - if (!ebr_part) { - error("Could not allocate extended partition iterator!\n"); - goto err_alloc; - } - /* Setup EBR iterator parameters */ - ebr_part->block = NULL; - ebr_part->index = 4; - ebr_part->record = NULL; - ebr_part->next = next_ebr_part; - ebr_part->private.ebr.parent = part; - /* Trigger an initial EBR load */ - ebr_part->private.ebr.parent_index = -1; - /* The EBR iterator is responsible for freeing us */ - return next_ebr_part(ebr_part); - } - dprintf("next_mbr_part:\n"); - mbr_part_dump(table + part->private.mbr_index); - - /* Update parameters to reflect this new partition. Re-use iterator */ - part->lba_data = table[part->private.mbr_index].start_lba; - dprintf("Partition %d primary lba %"PRIu64"\n", - part->private.mbr_index, part->lba_data); - part->index = part->private.mbr_index + 1; - part->record = table + part->private.mbr_index; - return part; - - free(ebr_part); -err_alloc: - - free(part->block); - free(part); - return NULL; -} - -/* - * GUID - * Be careful with endianness, you must adjust it yourself - * iff you are directly using the fourth data chunk - */ -struct guid { - uint32_t data1; - uint16_t data2; - uint16_t data3; - uint64_t data4; -} __attribute__ ((packed)); - - /* - * This walk-map effectively reverses the little-endian - * portions of the GUID in the output text - */ -static const char guid_le_walk_map[] = { - 3, -1, -1, -1, 0, - 5, -1, 0, - 3, -1, 0, - 2, 1, 0, - 1, 1, 1, 1, 1, 1 -}; - -#if DEBUG -/* - * Fill a buffer with a textual GUID representation. - * The buffer must be >= char[37] and will be populated - * with an ASCII NUL C string terminator. - * Example: 11111111-2222-3333-4444-444444444444 - * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB - */ -static void guid_to_str(char *buf, const struct guid *id) -{ - unsigned int i = 0; - const char *walker = (const char *)id; - - while (i < sizeof(guid_le_walk_map)) { - walker += guid_le_walk_map[i]; - if (!guid_le_walk_map[i]) - *buf = '-'; - else { - *buf = ((*walker & 0xF0) >> 4) + '0'; - if (*buf > '9') - *buf += 'A' - '9' - 1; - buf++; - *buf = (*walker & 0x0F) + '0'; - if (*buf > '9') - *buf += 'A' - '9' - 1; - } - buf++; - i++; - } - *buf = 0; -} -#endif - -/* - * Create a GUID structure from a textual GUID representation. - * The input buffer must be >= 32 hexadecimal chars and be - * terminated with an ASCII NUL. Returns non-zero on failure. - * Example: 11111111-2222-3333-4444-444444444444 - * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB - */ -static int str_to_guid(const char *buf, struct guid *id) -{ - char guid_seq[sizeof(struct guid) * 2]; - unsigned int i = 0; - char *walker = (char *)id; - - while (*buf && i < sizeof(guid_seq)) { - switch (*buf) { - /* Skip these three characters */ - case '{': - case '}': - case '-': - break; - default: - /* Copy something useful to the temp. sequence */ - if ((*buf >= '0') && (*buf <= '9')) - guid_seq[i] = *buf - '0'; - else if ((*buf >= 'A') && (*buf <= 'F')) - guid_seq[i] = *buf - 'A' + 10; - else if ((*buf >= 'a') && (*buf <= 'f')) - guid_seq[i] = *buf - 'a' + 10; - else { - /* Or not */ - error("Illegal character in GUID!\n"); - return -1; - } - i++; - } - buf++; - } - /* Check for insufficient valid characters */ - if (i < sizeof(guid_seq)) { - error("Too few GUID characters!\n"); - return -1; - } - buf = guid_seq; - i = 0; - while (i < sizeof(guid_le_walk_map)) { - if (!guid_le_walk_map[i]) - i++; - walker += guid_le_walk_map[i]; - *walker = *buf << 4; - buf++; - *walker |= *buf; - buf++; - i++; - } - return 0; -} - -/* A GPT partition */ -struct gpt_part { - struct guid type; - struct guid uid; - uint64_t lba_first; - uint64_t lba_last; - uint64_t attribs; - char name[72]; -} __attribute__ ((packed)); - -static void gpt_part_dump(const struct gpt_part *gpt_part) -{ -#ifdef DEBUG - unsigned int i; - char guid_text[37]; - - dprintf("----------------------------------\n" - "GPT part. LBA first __ : 0x%.16llx\n" - "GPT part. LBA last ___ : 0x%.16llx\n" - "GPT part. attribs ____ : 0x%.16llx\n" - "GPT part. name _______ : '", - gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs); - for (i = 0; i < sizeof(gpt_part->name); i++) { - if (gpt_part->name[i]) - dprintf("%c", gpt_part->name[i]); - } - dprintf("'"); - guid_to_str(guid_text, &gpt_part->type); - dprintf("GPT part. type GUID __ : {%s}\n", guid_text); - guid_to_str(guid_text, &gpt_part->uid); - dprintf("GPT part. unique ID __ : {%s}\n", guid_text); -#endif - (void)gpt_part; -} - -/* A GPT header */ -struct gpt { - char sig[8]; - union { - struct { - uint16_t minor; - uint16_t major; - } fields __attribute__ ((packed)); - uint32_t uint32; - char raw[4]; - } rev __attribute__ ((packed)); - uint32_t hdr_size; - uint32_t chksum; - char reserved1[4]; - uint64_t lba_cur; - uint64_t lba_alt; - uint64_t lba_first_usable; - uint64_t lba_last_usable; - struct guid disk_guid; - uint64_t lba_table; - uint32_t part_count; - uint32_t part_size; - uint32_t table_chksum; - char reserved2[1]; -} __attribute__ ((packed)); -static const char gpt_sig_magic[] = "EFI PART"; - -#if DEBUG -static void gpt_dump(const struct gpt *gpt) -{ - char guid_text[37]; - - printf("GPT sig ______________ : '%8.8s'\n" - "GPT major revision ___ : 0x%.4x\n" - "GPT minor revision ___ : 0x%.4x\n" - "GPT header size ______ : 0x%.8x\n" - "GPT header checksum __ : 0x%.8x\n" - "GPT reserved _________ : '%4.4s'\n" - "GPT LBA current ______ : 0x%.16llx\n" - "GPT LBA alternative __ : 0x%.16llx\n" - "GPT LBA first usable _ : 0x%.16llx\n" - "GPT LBA last usable __ : 0x%.16llx\n" - "GPT LBA part. table __ : 0x%.16llx\n" - "GPT partition count __ : 0x%.8x\n" - "GPT partition size ___ : 0x%.8x\n" - "GPT part. table chksum : 0x%.8x\n", - gpt->sig, - gpt->rev.fields.major, - gpt->rev.fields.minor, - gpt->hdr_size, - gpt->chksum, - gpt->reserved1, - gpt->lba_cur, - gpt->lba_alt, - gpt->lba_first_usable, - gpt->lba_last_usable, - gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum); - guid_to_str(guid_text, &gpt->disk_guid); - printf("GPT disk GUID ________ : {%s}\n", guid_text); -} -#endif - -static struct disk_part_iter *next_gpt_part(struct disk_part_iter *part) -{ - const struct gpt_part *gpt_part = NULL; - - while (++part->private.gpt.index < part->private.gpt.parts) { - gpt_part = - (const struct gpt_part *)(part->block + - (part->private.gpt.index * - part->private.gpt.size)); - if (!gpt_part->lba_first) - continue; - break; - } - /* Were we the last partition? */ - if (part->private.gpt.index == part->private.gpt.parts) { - goto err_last; - } - part->lba_data = gpt_part->lba_first; - part->private.gpt.part_guid = &gpt_part->uid; - part->private.gpt.part_label = gpt_part->name; - /* Update our index */ - part->index = part->private.gpt.index + 1; - gpt_part_dump(gpt_part); - - /* In a GPT scheme, we re-use the iterator */ - return part; - -err_last: - free(part->block); - free(part); - - return NULL; -} - -static struct disk_part_iter *get_first_partition(struct disk_part_iter *part) -{ - const struct gpt *gpt_candidate; - - /* - * Ignore any passed partition iterator. The caller should - * have passed NULL. Allocate a new partition iterator - */ - part = malloc(sizeof(*part)); - if (!part) { - error("Count not allocate partition iterator!\n"); - goto err_alloc_iter; - } - /* Read MBR */ - part->block = read_sectors(0, 2); - if (!part->block) { - error("Could not read two sectors!\n"); - goto err_read_mbr; - } - /* Check for an MBR */ - if (((struct mbr *)part->block)->sig != mbr_sig_magic) { - error("No MBR magic!\n"); - goto err_mbr; - } - /* Establish a pseudo-partition for the MBR (index 0) */ - part->index = 0; - part->record = NULL; - part->private.mbr_index = -1; - part->next = next_mbr_part; - /* Check for a GPT disk */ - gpt_candidate = (const struct gpt *)(part->block + SECTOR); - if (!memcmp(gpt_candidate->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) { - /* LBA for partition table */ - uint64_t lba_table; - - /* It looks like one */ - /* TODO: Check checksum. Possibly try alternative GPT */ -#if DEBUG - puts("Looks like a GPT disk."); - gpt_dump(gpt_candidate); -#endif - /* TODO: Check table checksum (maybe) */ - /* Note relevant GPT details */ - part->next = next_gpt_part; - part->private.gpt.index = -1; - part->private.gpt.parts = gpt_candidate->part_count; - part->private.gpt.size = gpt_candidate->part_size; - lba_table = gpt_candidate->lba_table; - gpt_candidate = NULL; - /* Load the partition table */ - free(part->block); - part->block = - read_sectors(lba_table, - ((part->private.gpt.size * part->private.gpt.parts) + - SECTOR - 1) / SECTOR); - if (!part->block) { - error("Could not read GPT partition list!\n"); - goto err_gpt_table; - } - } - /* Return the pseudo-partition's next partition, which is real */ - return part->next(part); - -err_gpt_table: - -err_mbr: - - free(part->block); - part->block = NULL; -err_read_mbr: - - free(part); -err_alloc_iter: - - return NULL; -} - -/* - * Search for a specific drive/partition, based on the GPT GUID. - * We return the disk drive number if found, as well as populating the - * boot_part pointer with the matching partition, if applicable. - * If no matching partition is found or the GUID is a disk GUID, - * boot_part will be populated with NULL. If not matching disk is - * found, we return -1. - */ -static int find_by_guid(const struct guid *gpt_guid, - struct disk_part_iter **boot_part) -{ - int drive; - bool is_me; - struct gpt *header; - - for (drive = 0x80; drive <= 0xff; drive++) { - if (get_disk_params(drive)) - continue; /* Drive doesn't exist */ - if (!(header = read_sectors(1, 1))) - continue; /* Cannot read sector */ - if (memcmp(&header->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) { - /* Not a GPT disk */ - free(header); - continue; - } -#if DEBUG - gpt_dump(header); -#endif - is_me = !memcmp(&header->disk_guid, gpt_guid, sizeof(*gpt_guid)); - free(header); - if (!is_me) { - /* Check for a matching partition */ - boot_part[0] = get_first_partition(NULL); - while (boot_part[0]) { - is_me = - !memcmp(boot_part[0]->private.gpt.part_guid, gpt_guid, - sizeof(*gpt_guid)); - if (is_me) - break; - boot_part[0] = boot_part[0]->next(boot_part[0]); - } - } else - boot_part[0] = NULL; - if (is_me) - return drive; - } - return -1; -} - -/* - * Search for a specific partition, based on the GPT label. - * We return the disk drive number if found, as well as populating the - * boot_part pointer with the matching partition, if applicable. - * If no matching partition is found, boot_part will be populated with - * NULL and we return -1. - */ -static int find_by_label(const char *label, struct disk_part_iter **boot_part) -{ - int drive; - bool is_me; - - for (drive = 0x80; drive <= 0xff; drive++) { - if (get_disk_params(drive)) - continue; /* Drive doesn't exist */ - /* Check for a GPT disk */ - boot_part[0] = get_first_partition(NULL); - if (!(boot_part[0]->next == next_gpt_part)) { - /* Not a GPT disk */ - while (boot_part[0]) { - /* Run through until the end */ - boot_part[0] = boot_part[0]->next(boot_part[0]); - } - continue; - } - /* Check for a matching partition */ - while (boot_part[0]) { - char gpt_label[sizeof(((struct gpt_part *) NULL)->name)]; - const char *gpt_label_scanner = - boot_part[0]->private.gpt.part_label; - int j = 0; - - /* Re-write the GPT partition label as ASCII */ - while (gpt_label_scanner < - boot_part[0]->private.gpt.part_label + sizeof(gpt_label)) { - if ((gpt_label[j] = *gpt_label_scanner)) - j++; - gpt_label_scanner++; - } - if ((is_me = !strcmp(label, gpt_label))) - break; - boot_part[0] = boot_part[0]->next(boot_part[0]); - } - if (is_me) - return drive; - } - - return -1; -} - -static void do_boot(struct data_area *data, int ndata, - struct syslinux_rm_regs *regs) -{ - uint16_t *const bios_fbm = (uint16_t *) 0x413; - addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */ - struct syslinux_memmap *mmap; - struct syslinux_movelist *mlist = NULL; - addr_t endimage; - uint8_t driveno = regs->edx.b[0]; - uint8_t swapdrive = driveno & 0x80; - int i; - - mmap = syslinux_memory_map(); - - if (!mmap) { - error("Cannot read system memory map\n"); - return; - } - - endimage = 0; - for (i = 0; i < ndata; i++) { - if (data[i].base + data[i].size > endimage) - endimage = data[i].base + data[i].size; - } - if (endimage > dosmem) - goto too_big; - - for (i = 0; i < ndata; i++) { - if (syslinux_add_movelist(&mlist, data[i].base, - (addr_t) data[i].data, data[i].size)) - goto enomem; - } - - if (opt.swap && driveno != swapdrive) { - static const uint8_t swapstub_master[] = { - /* The actual swap code */ - 0x53, /* 00: push bx */ - 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */ - 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */ - 0x5b, /* 08: pop bx */ - 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */ - 0x90, 0x90, /* 0E: nop; nop */ - /* Code to install this in the right location */ - /* Entry with DS = CS; ES = SI = 0; CX = 256 */ - 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */ - 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */ - 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */ - 0x4f, /* 1F: dec di */ - 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */ - 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */ - 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */ - 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */ - 0x8e, 0xc7, /* 32: mov es,di */ - 0x31, 0xff, /* 34: xor di,di */ - 0xf3, 0x66, 0xa5, /* 36: rep movsd */ - 0xbe, 0, 0, /* 39: mov si,0 */ - 0xbf, 0, 0, /* 3C: mov di,0 */ - 0x8e, 0xde, /* 3F: mov ds,si */ - 0x8e, 0xc7, /* 41: mov es,di */ - 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */ - 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */ - 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */ - 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */ - /* pad out to segment boundary */ - 0x90, 0x90, /* 5A: ... */ - 0x90, 0x90, 0x90, 0x90, /* 5C: ... */ - }; - static uint8_t swapstub[1024]; - uint8_t *p; - - /* Note: we can't rely on either INT 13h nor the dosmem - vector to be correct at this stage, so we have to use an - installer stub to put things in the right place. - Round the installer location to a 1K boundary so the only - possible overlap is the identity mapping. */ - endimage = (endimage + 1023) & ~1023; - - /* Create swap stub */ - memcpy(swapstub, swapstub_master, sizeof swapstub_master); - *(uint16_t *) & swapstub[0x3a] = regs->ds; - *(uint16_t *) & swapstub[0x3d] = regs->es; - *(uint32_t *) & swapstub[0x45] = regs->ecx.l; - *(uint32_t *) & swapstub[0x4b] = regs->esi.l; - *(uint32_t *) & swapstub[0x51] = regs->edi.l; - *(uint16_t *) & swapstub[0x56] = regs->ip; - *(uint16_t *) & swapstub[0x58] = regs->cs; - p = &swapstub[sizeof swapstub_master]; - - /* Mapping table; start out with identity mapping everything */ - for (i = 0; i < 256; i++) - p[i] = i; - - /* And the actual swap */ - p[driveno] = swapdrive; - p[swapdrive] = driveno; - - /* Adjust registers */ - regs->ds = regs->cs = endimage >> 4; - regs->es = regs->esi.l = 0; - regs->ecx.l = sizeof swapstub >> 2; - regs->ip = 0x10; /* Installer offset */ - regs->ebx.b[0] = regs->edx.b[0] = swapdrive; - - if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub, - sizeof swapstub)) - goto enomem; - - endimage += sizeof swapstub; - } - - /* Tell the shuffler not to muck with this area... */ - syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED); - - /* Force text mode */ - syslinux_force_text_mode(); - - fputs("Booting...\n", stdout); - syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs); - error("Chainboot failed!\n"); - return; - -too_big: - error("Loader file too large\n"); - return; - -enomem: - error("Out of memory\n"); - return; -} - -static int hide_unhide(struct mbr *mbr, int part) -{ - int i; - struct part_entry *pt; - const uint16_t mask = - (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1 - << - 0x0c) - | (1 << 0x0e); - uint8_t t; - bool write_back = false; - - for (i = 1; i <= 4; i++) { - pt = mbr->table + i - 1; - t = pt->ostype; - if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) { - /* It's a hideable partition type */ - if (i == part) - t &= ~0x10; /* unhide */ - else - t |= 0x10; /* hide */ - } - if (t != pt->ostype) { - write_back = true; - pt->ostype = t; - } - } - - if (write_back) - return write_verify_sector(0, mbr); - - return 0; /* ok */ -} - -static uint32_t get_file_lba(const char *filename) -{ - com32sys_t inregs; - uint32_t lba; - - /* Start with clean registers */ - memset(&inregs, 0, sizeof(com32sys_t)); - - /* Put the filename in the bounce buffer */ - strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size); - - /* Call comapi_open() which returns a structure pointer in SI - * to a structure whose first member happens to be the LBA. - */ - inregs.eax.w[0] = 0x0006; - inregs.esi.w[0] = OFFS(__com32.cs_bounce); - inregs.es = SEG(__com32.cs_bounce); - __com32.cs_intcall(0x22, &inregs, &inregs); - - if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) { - return 0; /* Filename not found */ - } - - /* Since the first member is the LBA, we simply cast */ - lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0])); - - /* Clean the registers for the next call */ - memset(&inregs, 0, sizeof(com32sys_t)); - - /* Put the filename in the bounce buffer */ - strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size); - - /* Call comapi_close() to free the structure */ - inregs.eax.w[0] = 0x0008; - inregs.esi.w[0] = OFFS(__com32.cs_bounce); - inregs.es = SEG(__com32.cs_bounce); - __com32.cs_intcall(0x22, &inregs, &inregs); - - return lba; -} - -static void usage(void) -{ - static const char usage[] = "\ -Usage: chain.c32 [options]\n\ - chain.c32 hd<disk#> [<partition>] [options]\n\ - chain.c32 fd<disk#> [options]\n\ - chain.c32 mbr:<id> [<partition>] [options]\n\ - chain.c32 guid:<guid> [<partition>] [options]\n\ - chain.c32 label:<label> [<partition>] [options]\n\ - chain.c32 boot [<partition>] [options]\n\ - chain.c32 fs [options]\n\ -Options: file=<loader> Load and execute file, instead of boot sector\n\ - isolinux=<loader> Load another version of ISOLINUX\n\ - ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\ - cmldr=<loader> Load Recovery Console of Windows NT/2K/XP/2003\n\ - freedos=<loader> Load FreeDOS KERNEL.SYS\n\ - freeldr=<loader> Load ReactOS' FREELDR.SYS\n\ - msdos=<loader> Load MS-DOS IO.SYS\n\ - pcdos=<loader> Load PC-DOS IBMBIO.COM\n\ - drmk=<loader> Load DRMK DELLBIO.BIN\n\ - grub=<loader> Load GRUB Legacy stage2\n\ - grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\ - grldr=<loader> Load GRUB4DOS grldr\n\ - seg=<seg> Jump to <seg>:0000, instead of 0000:7C00\n\ - seg=<seg>[:<offs>][{+@}<entry>] also specified offset and entrypoint\n\ - swap Swap drive numbers, if bootdisk is not fd0/hd0\n\ - hide Hide primary partitions, except selected partition\n\ - sethidden Set the FAT/NTFS hidden sectors field\n\ - keeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\ -See syslinux/com32/modules/chain.c for more information\n"; - error(usage); -} - -int main(int argc, char *argv[]) -{ - struct mbr *mbr = NULL; - char *p; - struct disk_part_iter *cur_part = NULL; - struct syslinux_rm_regs regs; - char *drivename, *partition; - int hd, drive, whichpart = 0; /* MBR by default */ - int i; - uint64_t fs_lba = 0; /* Syslinux partition */ - uint32_t file_lba = 0; - struct guid gpt_guid; - unsigned char *isolinux_bin; - uint32_t *checksum, *chkhead, *chktail; - struct data_area data[3]; - int ndata = 0; - addr_t load_base; - static const char cmldr_signature[8] = "cmdcons"; - - openconsole(&dev_null_r, &dev_stdcon_w); - - drivename = "boot"; - partition = NULL; - - /* Prepare the register set */ - memset(®s, 0, sizeof regs); - - opt.seg = 0; - opt.offs = 0x7c00; - opt.entry = 0x7c00; - - for (i = 1; i < argc; i++) { - if (!strncmp(argv[i], "file=", 5)) { - opt.loadfile = argv[i] + 5; - } else if (!strncmp(argv[i], "seg=", 4)) { - uint32_t v; - bool add_entry = true; - char *ep, *p = argv[i] + 4; - - v = strtoul(p, &ep, 0); - if (ep == p || v < 0x50 || v > 0x9f000) { - error("seg: Invalid segment\n"); - goto bail; - } - opt.seg = v; - opt.offs = opt.entry = 0; - if (*ep == ':') { - p = ep+1; - v = strtoul(p, &ep, 0); - if (ep == p) { - error("seg: Invalid offset\n"); - goto bail; - } - opt.offs = v; - } - if (*ep == '@' || *ep == '+') { - add_entry = (*ep == '+'); - p = ep+1; - v = strtoul(p, &ep, 0); - if (ep == p) { - error("seg: Invalid entry point\n"); - goto bail; - } - opt.entry = v; - } - if (add_entry) - opt.entry += opt.offs; - } else if (!strncmp(argv[i], "isolinux=", 9)) { - opt.loadfile = argv[i] + 9; - opt.isolinux = true; - } else if (!strncmp(argv[i], "ntldr=", 6)) { - opt.seg = 0x2000; /* NTLDR wants this address */ - opt.offs = opt.entry = 0; - opt.loadfile = argv[i] + 6; - opt.sethidden = true; - } else if (!strncmp(argv[i], "cmldr=", 6)) { - opt.seg = 0x2000; /* CMLDR wants this address */ - opt.offs = opt.entry = 0; - opt.loadfile = argv[i] + 6; - opt.cmldr = true; - opt.sethidden = true; - } else if (!strncmp(argv[i], "freedos=", 8)) { - opt.seg = 0x60; /* FREEDOS wants this address */ - opt.offs = opt.entry = 0; - opt.loadfile = argv[i] + 8; - opt.sethidden = true; - } else if (!strncmp(argv[i], "freeldr=", 8)) { - opt.loadfile = argv[i] + 8; - opt.sethidden = true; - /* The FreeLdr PE wants to be at 0:8000 */ - opt.seg = 0; - opt.offs = 0x8000; - /* TODO: Properly parse the PE. Right now, this is hard-coded */ - opt.entry = 0x8100; - } else if (!strncmp(argv[i], "msdos=", 6) || - !strncmp(argv[i], "pcdos=", 6)) { - opt.seg = 0x70; /* MS-DOS 2.0+ wants this address */ - opt.offs = opt.entry = 0; - opt.loadfile = argv[i] + 6; - opt.sethidden = true; - } else if (!strncmp(argv[i], "drmk=", 5)) { - opt.seg = 0x70; /* DRMK wants this address */ - opt.offs = opt.entry = 0; - opt.loadfile = argv[i] + 5; - opt.sethidden = true; - opt.drmk = true; - } else if (!strncmp(argv[i], "grub=", 5)) { - opt.seg = 0x800; /* stage2 wants this address */ - opt.offs = opt.entry = 0; - opt.loadfile = argv[i] + 5; - opt.grub = true; - } else if (!strncmp(argv[i], "grubcfg=", 8)) { - opt.grubcfg = argv[i] + 8; - } else if (!strncmp(argv[i], "grldr=", 6)) { - opt.loadfile = argv[i] + 6; - opt.grldr = true; - } else if (!strcmp(argv[i], "swap")) { - opt.swap = true; - } else if (!strcmp(argv[i], "noswap")) { - opt.swap = false; - } else if (!strcmp(argv[i], "hide")) { - opt.hide = true; - } else if (!strcmp(argv[i], "nohide")) { - opt.hide = false; - } else if (!strcmp(argv[i], "keeppxe")) { - opt.keeppxe = 3; - } else if (!strcmp(argv[i], "sethidden")) { - opt.sethidden = true; - } else if (!strcmp(argv[i], "nosethidden")) { - opt.sethidden = false; - } else if (((argv[i][0] == 'h' || argv[i][0] == 'f') - && argv[i][1] == 'd') - || !strncmp(argv[i], "mbr:", 4) - || !strncmp(argv[i], "mbr=", 4) - || !strncmp(argv[i], "guid:", 5) - || !strncmp(argv[i], "guid=", 5) - || !strncmp(argv[i], "uuid:", 5) - || !strncmp(argv[i], "uuid=", 5) - || !strncmp(argv[i], "label:", 6) - || !strncmp(argv[i], "label=", 6) - || !strcmp(argv[i], "boot") - || !strncmp(argv[i], "boot,", 5) - || !strcmp(argv[i], "fs")) { - drivename = argv[i]; - p = strchr(drivename, ','); - if (p) { - *p = '\0'; - partition = p + 1; - } else if (argv[i + 1] && argv[i + 1][0] >= '0' - && argv[i + 1][0] <= '9') { - partition = argv[++i]; - } - } else { - usage(); - goto bail; - } - } - - if (opt.grubcfg && !opt.grub) { - error("grubcfg=<filename> must be used together with grub=<loader>.\n"); - goto bail; - } - - /* - * Set up initial register values - */ - regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg; - regs.ip = opt.entry; - - /* - * For the special case of the standard 0:7C00 entry point, put - * the stack below; otherwise leave the stack pointer at the end - * of the segment (sp = 0). - */ - if (opt.seg == 0 && opt.offs == 0x7c00) - regs.esp.l = 0x7c00; - - - hd = 0; - if (!strncmp(drivename, "mbr", 3)) { - drive = find_disk(strtoul(drivename + 4, NULL, 0)); - if (drive == -1) { - error("Unable to find requested MBR signature\n"); - goto bail; - } - } else if (!strncmp(drivename, "guid", 4) || !strncmp(drivename, "uuid", 4)) { - if (str_to_guid(drivename + 5, &gpt_guid)) - goto bail; - drive = find_by_guid(&gpt_guid, &cur_part); - if (drive == -1) { - error("Unable to find requested GPT disk/partition\n"); - goto bail; - } - } else if (!strncmp(drivename, "label", 5)) { - if (!drivename[6]) { - error("No label specified.\n"); - goto bail; - } - drive = find_by_label(drivename + 6, &cur_part); - if (drive == -1) { - error("Unable to find requested partition by label\n"); - goto bail; - } - } else if ((drivename[0] == 'h' || drivename[0] == 'f') && - drivename[1] == 'd') { - hd = drivename[0] == 'h'; - drivename += 2; - drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0); - } else if (!strcmp(drivename, "boot") || !strcmp(drivename, "fs")) { - const union syslinux_derivative_info *sdi; - - sdi = syslinux_derivative_info(); - if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) - drive = 0x80; /* Boot drive not available */ - else - drive = sdi->disk.drive_number; - if (!strcmp(drivename, "fs") - && (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX - || sdi->c.filesystem == SYSLINUX_FS_EXTLINUX - || sdi->c.filesystem == SYSLINUX_FS_ISOLINUX)) - /* We should lookup the Syslinux partition number and use it */ - fs_lba = *sdi->disk.partoffset; - } else { - error("Unparsable drive specification\n"); - goto bail; - } - - /* DOS kernels want the drive number in BL instead of DL. Indulge them. */ - regs.ebx.b[0] = regs.edx.b[0] = drive; - - /* Get the disk geometry and disk access setup */ - if (get_disk_params(drive)) { - error("Cannot get disk parameters\n"); - goto bail; - } - - /* Get MBR */ - if (!(mbr = read_sectors(0, 1))) { - error("Cannot read Master Boot Record or sector 0\n"); - goto bail; - } - - if (partition) - whichpart = strtoul(partition, NULL, 0); - /* "guid:" or "label:" might have specified a partition */ - if (cur_part) - whichpart = cur_part->index; - - /* Boot the MBR by default */ - if (!cur_part && (whichpart || fs_lba)) { - /* Boot a partition, possibly the Syslinux partition itself */ - cur_part = get_first_partition(NULL); - while (cur_part) { - if ((cur_part->index == whichpart) - || (cur_part->lba_data == fs_lba)) - /* Found the partition to boot */ - break; - cur_part = cur_part->next(cur_part); - } - if (!cur_part) { - error("Requested partition not found!\n"); - goto bail; - } - whichpart = cur_part->index; - } - - if (!(drive & 0x80) && whichpart) { - error("Warning: Partitions of floppy devices may not work\n"); - } - - /* - * GRLDR of GRUB4DOS wants the partition number in DH: - * -1: whole drive (default) - * 0-3: primary partitions - * 4-*: logical partitions - */ - if (opt.grldr) - regs.edx.b[1] = whichpart - 1; - - if (opt.hide) { - if (whichpart < 1 || whichpart > 4) - error("WARNING: hide specified without a non-primary partition\n"); - if (hide_unhide(mbr, whichpart)) - error("WARNING: failed to write MBR for 'hide'\n"); - } - - /* Do the actual chainloading */ - load_base = (opt.seg << 4) + opt.offs; - - if (opt.loadfile) { - fputs("Loading the boot file...\n", stdout); - if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) { - error("Failed to load the boot file\n"); - goto bail; - } - data[ndata].base = load_base; - load_base = 0x7c00; /* If we also load a boot sector */ - - /* Create boot info table: needed when you want to chainload - another version of ISOLINUX (or another bootlaoder that needs - the -boot-info-table switch of mkisofs) - (will only work when run from ISOLINUX) */ - if (opt.isolinux) { - const union syslinux_derivative_info *sdi; - sdi = syslinux_derivative_info(); - - if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) { - /* Boot info table info (integers in little endian format) - - Offset Name Size Meaning - 8 bi_pvd 4 bytes LBA of primary volume descriptor - 12 bi_file 4 bytes LBA of boot file - 16 bi_length 4 bytes Boot file length in bytes - 20 bi_csum 4 bytes 32-bit checksum - 24 bi_reserved 40 bytes Reserved - - The 32-bit checksum is the sum of all the 32-bit words in the - boot file starting at byte offset 64. All linear block - addresses (LBAs) are given in CD sectors (normally 2048 bytes). - - LBA of primary volume descriptor should already be set to 16. - */ - - isolinux_bin = (unsigned char *)data[ndata].data; - - /* Get LBA address of bootfile */ - file_lba = get_file_lba(opt.loadfile); - - if (file_lba == 0) { - error("Failed to find LBA offset of the boot file\n"); - goto bail; - } - /* Set it */ - *((uint32_t *) & isolinux_bin[12]) = file_lba; - - /* Set boot file length */ - *((uint32_t *) & isolinux_bin[16]) = data[ndata].size; - - /* Calculate checksum */ - checksum = (uint32_t *) & isolinux_bin[20]; - chkhead = (uint32_t *) & isolinux_bin[64]; - chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3]; - *checksum = 0; - while (chkhead < chktail) - *checksum += *chkhead++; - - /* - * Deal with possible fractional dword at the end; - * this *should* never happen... - */ - if (data[ndata].size & 3) { - uint32_t xword = 0; - memcpy(&xword, chkhead, data[ndata].size & 3); - *checksum += xword; - } - } else { - error - ("The isolinux= option is only valid when run from ISOLINUX\n"); - goto bail; - } - } - - if (opt.grub) { - /* Layout of stage2 file (from byte 0x0 to 0x270) */ - struct grub_stage2_patch_area { - /* 0x0 to 0x205 */ - char unknown[0x206]; - /* 0x206: compatibility version number major */ - uint8_t compat_version_major; - /* 0x207: compatibility version number minor */ - uint8_t compat_version_minor; - - /* 0x208: install_partition variable */ - struct { - /* 0x208: sub-partition in sub-partition part2 */ - uint8_t part3; - /* 0x209: sub-partition in top-level partition */ - uint8_t part2; - /* 0x20a: top-level partiton number */ - uint8_t part1; - /* 0x20b: BIOS drive number (must be 0) */ - uint8_t drive; - } __attribute__ ((packed)) install_partition; - - /* 0x20c: deprecated (historical reason only) */ - uint32_t saved_entryno; - /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */ - uint8_t stage2_id; - /* 0x211: force LBA */ - uint8_t force_lba; - /* 0x212: version string (will probably be 0.97) */ - char version_string[5]; - /* 0x217: config filename */ - char config_file[89]; - /* 0x270: start of code (after jump from 0x200) */ - char codestart[1]; - } __attribute__ ((packed)) * stage2; - - if (data[ndata].size < sizeof(struct grub_stage2_patch_area)) { - error - ("The file specified by grub=<loader> is to small to be stage2 of GRUB Legacy.\n"); - goto bail; - } - - stage2 = data[ndata].data; - - /* - * Check the compatibility version number to see if we loaded a real - * stage2 file or a stage2 file that we support. - */ - if (stage2->compat_version_major != 3 - || stage2->compat_version_minor != 2) { - error - ("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary\n"); - goto bail; - } - - /* jump 0x200 bytes into the loadfile */ - regs.ip = 0x200; - - /* - * GRUB Legacy wants the partition number in the install_partition - * variable, located at offset 0x208 of stage2. - * When GRUB Legacy is loaded, it is located at memory address 0x8208. - * - * It looks very similar to the "boot information format" of the - * Multiboot specification: - * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format - * - * 0x208 = part3: sub-partition in sub-partition part2 - * 0x209 = part2: sub-partition in top-level partition - * 0x20a = part1: top-level partition number - * 0x20b = drive: BIOS drive number (must be 0) - * - * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at - * another location. - * - * Partition numbers always start from zero. - * Unused partition bytes must be set to 0xFF. - * - * We only care about top-level partition, so we only need to change - * "part1" to the appropriate value: - * -1: whole drive (default) (-1 = 0xFF) - * 0-3: primary partitions - * 4-*: logical partitions - */ - stage2->install_partition.part1 = whichpart - 1; - - /* - * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the - * config filename. The filename passed via grubcfg= will overwrite - * the default config filename "/boot/grub/menu.lst". - */ - if (opt.grubcfg) { - if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) { - error - ("The config filename length can't exceed 88 characters.\n"); - goto bail; - } - - strcpy((char *)stage2->config_file, opt.grubcfg); - } - } - - if (opt.drmk) { - /* DRMK entry is different than MS-DOS/PC-DOS */ - /* - * A new size, aligned to 16 bytes to ease use of ds:[bp+28]. - * We only really need 4 new, usable bytes at the end. - */ - int tsize = (data[ndata].size + 19) & 0xfffffff0; - const union syslinux_derivative_info *sdi; - - sdi = syslinux_derivative_info(); - /* We should lookup the Syslinux partition offset and use it */ - fs_lba = *sdi->disk.partoffset; - /* - * fs_lba should be verified against the disk as some DRMK - * variants will check and fail if it does not match - */ - dprintf(" fs_lba offset is %"PRIu64"\n", fs_lba); - /* DRMK only uses a DWORD */ - if (fs_lba > 0xffffffff) { - error - ("LBA very large; Only using lower 32 bits; DRMK will probably fail\n"); - } - regs.ss = regs.fs = regs.gs = 0; /* Used before initialized */ - if (!realloc(data[ndata].data, tsize)) { - error("Failed to realloc for DRMK\n"); - goto bail; /* We'll never make it */ - } - data[ndata].size = tsize; - /* ds:bp is assumed by DRMK to be the boot sector */ - /* offset 28 is the FAT HiddenSectors value */ - regs.ds = (tsize >> 4) + (opt.seg - 2); - /* "Patch" into tail of the new space */ - *(int *)(data[ndata].data + tsize - 4) = (int)(fs_lba & 0xffffffff); - } - - ndata++; - } - - if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) { - /* Actually read the boot sector */ - if (!cur_part) { - data[ndata].data = mbr; - } else if (!(data[ndata].data = read_sectors(cur_part->lba_data, 1))) { - error("Cannot read boot sector\n"); - goto bail; - } - data[ndata].size = SECTOR; - data[ndata].base = load_base; - - if (!opt.loadfile) { - const struct mbr *br = - (const struct mbr *)((char *)data[ndata].data + - data[ndata].size - sizeof(struct mbr)); - if (br->sig != mbr_sig_magic) { - error - ("Boot sector signature not found (unbootable disk/partition?)\n"); - goto bail; - } - } - /* - * To boot the Recovery Console of Windows NT/2K/XP we need to write - * the string "cmdcons\0" to memory location 0000:7C03. - * Memory location 0000:7C00 contains the bootsector of the partition. - */ - if (cur_part && opt.cmldr) { - memcpy((char *)data[ndata].data + 3, cmldr_signature, - sizeof cmldr_signature); - } - - /* - * Modify the hidden sectors (partition offset) copy in memory; - * this modifies the field used by FAT and NTFS filesystems, and - * possibly other boot loaders which use the same format. - */ - if (cur_part && opt.sethidden) { - *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data; - } - - ndata++; - } - - if (cur_part) { - if (cur_part->next == next_gpt_part) { - /* Do GPT hand-over, if applicable (as per syslinux/doc/gpt.txt) */ - struct part_entry *record; - /* Look at the GPT partition */ - const struct gpt_part *gp = (const struct gpt_part *) - (cur_part->block + - (cur_part->private.gpt.size * cur_part->private.gpt.index)); - /* Note the partition length */ - uint64_t lba_count = gp->lba_last - gp->lba_first + 1; - /* The length of the hand-over */ - int synth_size = - sizeof(struct part_entry) + sizeof(uint32_t) + - cur_part->private.gpt.size; - /* Will point to the partition record length in the hand-over */ - uint32_t *plen; - - /* Allocate the hand-over record */ - record = malloc(synth_size); - if (!record) { - error("Could not build GPT hand-over record!\n"); - goto bail; - } - /* Synthesize the record */ - memset(record, 0, synth_size); - record->active_flag = 0x80; - record->ostype = 0xED; - /* All bits set by default */ - record->start_lba = ~(uint32_t) 0; - record->length = ~(uint32_t) 0; - /* If these fit the precision, pass them on */ - if (cur_part->lba_data < record->start_lba) - record->start_lba = cur_part->lba_data; - if (lba_count < record->length) - record->length = lba_count; - /* Next comes the GPT partition record length */ - plen = (uint32_t *) (record + 1); - plen[0] = cur_part->private.gpt.size; - /* Next comes the GPT partition record copy */ - memcpy(plen + 1, gp, plen[0]); - cur_part->record = record; - - regs.eax.l = 0x54504721; /* '!GPT' */ - data[ndata].base = 0x7be; - data[ndata].size = synth_size; - data[ndata].data = (void *)record; - ndata++; - regs.esi.w[0] = 0x7be; - - dprintf("GPT handover:\n"); - mbr_part_dump(record); - gpt_part_dump((struct gpt_part *)(plen + 1)); - } else if (cur_part->record) { - /* MBR handover protocol */ - static struct part_entry handover_record; - - handover_record = *cur_part->record; - handover_record.start_lba = cur_part->lba_data; - - data[ndata].base = 0x7be; - data[ndata].size = sizeof handover_record; - data[ndata].data = &handover_record; - ndata++; - regs.esi.w[0] = 0x7be; - - dprintf("MBR handover:\n"); - mbr_part_dump(&handover_record); - } - } - - do_boot(data, ndata, ®s); - -bail: - if (cur_part) { - free(cur_part->block); - free((void *)cur_part->record); - } - free(cur_part); - free(mbr); - return 255; -} diff --git a/com32/modules/ifcpu.c b/com32/modules/ifcpu.c index a28acc4e..0aa63327 100644 --- a/com32/modules/ifcpu.c +++ b/com32/modules/ifcpu.c @@ -49,11 +49,12 @@ static void usage(void) " dry-run : just do the detection, don't boot \n" "\n" "cpu_features could be:\n" - " 64 : Processor is x86_64 compatible (lm cpu flag)\n" - " hvm : Processor features hardware virtualization (hvm or svm cpu flag)\n" - " multicore : Processor must be multi-core \n" - " smp : System must be multi-processor \n" - " pae : Processor features Physical Address Extension (PAE)\n" + " 64 : Processor is x86_64 compatible (lm cpu flag)\n" + " hvm : Processor features hardware virtualization (hvm or svm cpu flag)\n" + " multicore : Processor must be multi-core \n" + " smp : System must be multi-processor \n" + " pae : Processor features Physical Address Extension (PAE)\n" + " hypervisor : Processor is running under an hypervisor\n" "\n" "if you want to match many cpu features, just separate them with a single space.\n"); } @@ -114,30 +115,34 @@ int main(int argc, char *argv[]) args[n++] = &argv[i + 1]; } else if (!strcmp(argv[i], "64")) { if (debug) - printf(" 64bit : %s on this system\n", + printf(" 64bit : %s on this system\n", show_bool(cpu.flags.lm)); hardware_matches = cpu.flags.lm && hardware_matches; } else if (!strcmp(argv[i], "pae")) { if (debug) - printf(" pae : %s on this system\n", + printf(" pae : %s on this system\n", show_bool(cpu.flags.pae)); hardware_matches = cpu.flags.pae && hardware_matches; } else if (!strcmp(argv[i], "hvm")) { if (debug) - printf(" hvm : %s on this system\n", + printf(" hvm : %s on this system\n", show_bool((cpu.flags.vmx || cpu.flags.svm))); hardware_matches = (cpu.flags.vmx || cpu.flags.svm) && hardware_matches; } else if (!strcmp(argv[i], "multicore")) { if (debug) - printf(" multicore : %d cores on this system\n", cpu.num_cores); + printf(" multicore : %d cores on this system\n", cpu.num_cores); if (cpu.num_cores > 1) multicore = true; hardware_matches = multicore && hardware_matches; } else if (!strcmp(argv[i], "smp")) { if (debug) - printf(" smp : %s on this system\n", show_bool(cpu.flags.smp)); + printf(" smp : %s on this system\n", show_bool(cpu.flags.smp)); hardware_matches = cpu.flags.smp && hardware_matches; + } else if (!strcmp(argv[i], "hypervisor")) { + if (debug) + printf(" hypervisor : %s on this system\n", show_bool(cpu.flags.hypervisor)); + hardware_matches = cpu.flags.hypervisor && hardware_matches; } else if (!strcmp(argv[i], "dry-run")) { dryrun = true; } else if (!strcmp(argv[i], "debug")) { diff --git a/com32/modules/kontron_wdt.c b/com32/modules/kontron_wdt.c new file mode 100644 index 00000000..4e1d2535 --- /dev/null +++ b/com32/modules/kontron_wdt.c @@ -0,0 +1,414 @@ +/* + * kempld_wdt.c - Kontron PLD watchdog driver + * + * Copyright (c) 2010 Kontron Embedded Modules GmbH + * Author: Michael Brunner <michael.brunner@kontron.com> + * Author: Erwan Velu <erwan.velu@zodiacaerospace.com> + * + * Note: From the PLD watchdog point of view timeout and pretimeout are + * defined differently than in the kernel. + * First the pretimeout stage runs out before the timeout stage gets + * active. This has to be kept in mind. + * + * Kernel/API: P-----| pretimeout + * |-----------------------T timeout + * Watchdog: |-----------------P pretimeout_stage + * |-----T timeout_stage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <string.h> +#include <sys/io.h> +#include <unistd.h> +#include <syslinux/boot.h> +#include <stdio.h> +#include <stdlib.h> +#include <console.h> +#include "kontron_wdt.h" + +struct kempld_device_data pld; +struct kempld_watchdog_data wdt; +uint8_t status; +char default_label[255]; + +/* Default Timeout is 60sec */ +#define TIMEOUT 60 +#define PRETIMEOUT 0 + +#define do_div(n,base) ({ \ + int __res; \ + __res = ((unsigned long) n) % (unsigned) base; \ + n = ((unsigned long) n) / (unsigned) base; \ + __res; }) + + +/* Basic Wrappers to get code as less changed as possible */ +void iowrite8(uint8_t val, uint16_t addr) { outb(val,addr); } +void iowrite16(uint16_t val, uint16_t addr) { outw(val,addr); } +void iowrite32(uint32_t val, uint16_t addr) { outl(val,addr);} +uint8_t ioread8(uint16_t addr) { return inb(addr);} +uint16_t ioread16(uint16_t addr) { return inw(addr);} +uint32_t ioread32(uint32_t addr) { return inl(addr);} + + +/** + * kempld_set_index - change the current register index of the PLD + * @pld: kempld_device_data structure describing the PLD + * @index: register index on the chip + * + * This function changes the register index of the PLD. + */ +void kempld_set_index(struct kempld_device_data *pld, uint8_t index) +{ + if (pld->last_index != index) { + iowrite8(index, pld->io_index); + pld->last_index = index; + } +} + + +uint8_t kempld_read8(struct kempld_device_data *pld, uint8_t index) { + kempld_set_index(pld, index); + return ioread8(pld->io_data); +} + + +void kempld_write8(struct kempld_device_data *pld, uint8_t index, uint8_t data) { + kempld_set_index(pld, index); + iowrite8(data, pld->io_data); +} + + +uint16_t kempld_read16(struct kempld_device_data *pld, uint8_t index) +{ + return kempld_read8(pld, index) | kempld_read8(pld, index+1) << 8; +} + + +void kempld_write16(struct kempld_device_data *pld, uint8_t index, uint16_t data) +{ + kempld_write8(pld, index, (uint8_t)data); + kempld_write8(pld, index+1, (uint8_t)(data>>8)); +} + +uint32_t kempld_read32(struct kempld_device_data *pld, uint8_t index) +{ + return kempld_read16(pld, index) | kempld_read16(pld, index+2) << 16; +} + +void kempld_write32(struct kempld_device_data *pld, uint8_t index, uint32_t data) +{ + kempld_write16(pld, index, (uint16_t)data); + kempld_write16(pld, index+2, (uint16_t)(data>>16)); +} + +static void kempld_release_mutex(struct kempld_device_data *pld) +{ + iowrite8(pld->last_index | KEMPLD_MUTEX_KEY, pld->io_index); +} + +void init_structure(void) { + /* set default values for the case we start the watchdog or change + * the configuration */ + memset(&wdt,0,sizeof(wdt)); + memset(&pld,0,sizeof(pld)); + memset(&default_label,0,sizeof(default_label)); + wdt.timeout = TIMEOUT; + wdt.pretimeout = PRETIMEOUT; + wdt.pld = &pld; + + pld.io_base=KEMPLD_IOPORT; + pld.io_index=KEMPLD_IOPORT; + pld.io_data=KEMPLD_IODATA; + pld.pld_clock=33333333; +} + +static int kempld_probe(void) { + /* Check for empty IO space */ + int ret=0; + uint8_t index_reg = ioread8(pld.io_index); + if ((index_reg == 0xff) && (ioread8(pld.io_data) == 0xff)) { + ret = 1; + goto err_empty_io; + } + printf("Kempld structure found at 0x%X (data @ 0x%X)\n",pld.io_base,pld.io_data); + return 0; + +err_empty_io: + printf("No IO Found !\n"); + return ret; +} + +static int kempld_wdt_probe_stages(struct kempld_watchdog_data *wdt) +{ + struct kempld_device_data *pld = wdt->pld; + int i, ret; + uint32_t timeout; + uint32_t timeout_mask; + struct kempld_watchdog_stage *stage; + + wdt->stages = 0; + wdt->timeout_stage = NULL; + wdt->pretimeout_stage = NULL; + + for (i = 0; i < KEMPLD_WDT_MAX_STAGES; i++) { + + timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(i)); + kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(i), 0x00000000); + timeout_mask = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(i)); + kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(i), timeout); + + if (timeout_mask != 0xffffffff) { + stage = malloc(sizeof(struct kempld_watchdog_stage)); + if (stage == NULL) { + ret = -1; + goto err_alloc_stages; + } + stage->num = i; + stage->timeout_mask = ~timeout_mask; + wdt->stage[i] = stage; + wdt->stages++; + + /* assign available stages to timeout and pretimeout */ + if (wdt->stages == 1) + wdt->timeout_stage = stage; + else if (wdt->stages == 2) { + wdt->pretimeout_stage = wdt->timeout_stage; + wdt->timeout_stage = stage; + } + } else { + wdt->stage[i] = NULL; + } + } + + return 0; +err_alloc_stages: + kempld_release_mutex(pld); + printf("Cannot allocate stages\n"); + return ret; +} + +static int kempld_wdt_keepalive(struct kempld_watchdog_data *wdt) +{ + struct kempld_device_data *pld = wdt->pld; + + kempld_write8(pld, KEMPLD_WDT_KICK, 'K'); + + return 0; +} + +static int kempld_wdt_setstageaction(struct kempld_watchdog_data *wdt, + struct kempld_watchdog_stage *stage, + int action) +{ + struct kempld_device_data *pld = wdt->pld; + uint8_t stage_cfg; + + if (stage == NULL) + return -1; + + stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num)); + stage_cfg &= ~KEMPLD_WDT_STAGE_CFG_ACTION_MASK; + stage_cfg |= (action & KEMPLD_WDT_STAGE_CFG_ACTION_MASK); + if (action == KEMPLD_WDT_ACTION_RESET) + stage_cfg |= KEMPLD_WDT_STAGE_CFG_ASSERT; + else + stage_cfg &= ~KEMPLD_WDT_STAGE_CFG_ASSERT; + + kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->num), stage_cfg); + stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num)); + + return 0; +} + +static int kempld_wdt_setstagetimeout(struct kempld_watchdog_data *wdt, + struct kempld_watchdog_stage *stage, + int timeout) +{ + struct kempld_device_data *pld = wdt->pld; + uint8_t stage_cfg; + uint8_t prescaler; + uint64_t stage_timeout64; + uint32_t stage_timeout; + + if (stage == NULL) + return -1; + + prescaler = KEMPLD_WDT_PRESCALER_21BIT; + + stage_timeout64 = ((uint64_t)timeout*pld->pld_clock); + do_div(stage_timeout64, KEMPLD_PRESCALER(prescaler)); + stage_timeout = stage_timeout64 & stage->timeout_mask; + + if (stage_timeout64 != (uint64_t)stage_timeout) + return -1; + + stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num)); + stage_cfg &= ~KEMPLD_WDT_STAGE_CFG_PRESCALER_MASK; + stage_cfg |= KEMPLD_WDT_STAGE_CFG_SET_PRESCALER(prescaler); + kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->num), stage_cfg); + kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->num), + stage_timeout); + + return 0; +} + + +static int kempld_wdt_settimeout(struct kempld_watchdog_data *wdt) +{ + int stage_timeout; + int stage_pretimeout; + int ret; + if ((wdt->timeout <= 0) || + (wdt->pretimeout < 0) || + (wdt->pretimeout > wdt->timeout)) { + ret = -1; + goto err_check_values; + } + + if ((wdt->pretimeout == 0) || (wdt->pretimeout_stage == NULL)) { + if (wdt->pretimeout != 0) + printf("No pretimeout stage available, only enabling reset!\n"); + stage_pretimeout = 0; + stage_timeout = wdt->timeout; + } else { + stage_pretimeout = wdt->timeout - wdt->pretimeout; + stage_timeout = wdt->pretimeout; + } + + if (stage_pretimeout != 0) { + ret = kempld_wdt_setstageaction(wdt, wdt->pretimeout_stage, + KEMPLD_WDT_ACTION_NMI); + } else if ((stage_pretimeout == 0) + && (wdt->pretimeout_stage != NULL)) { + ret = kempld_wdt_setstageaction(wdt, wdt->pretimeout_stage, + KEMPLD_WDT_ACTION_NONE); + } else + ret = 0; + if (ret) + goto err_setstage; + + if (stage_pretimeout != 0) { + ret = kempld_wdt_setstagetimeout(wdt, wdt->pretimeout_stage, + stage_pretimeout); + if (ret) + goto err_setstage; + } + + ret = kempld_wdt_setstageaction(wdt, wdt->timeout_stage, + KEMPLD_WDT_ACTION_RESET); + if (ret) + goto err_setstage; + + ret = kempld_wdt_setstagetimeout(wdt, wdt->timeout_stage, + stage_timeout); + if (ret) + goto err_setstage; + + return 0; +err_setstage: +err_check_values: + return ret; +} + +static int kempld_wdt_start(struct kempld_watchdog_data *wdt) +{ + struct kempld_device_data *pld = wdt->pld; + uint8_t status; + + status = kempld_read8(pld, KEMPLD_WDT_CFG); + status |= KEMPLD_WDT_CFG_ENABLE; + kempld_write8(pld, KEMPLD_WDT_CFG, status); + status = kempld_read8(pld, KEMPLD_WDT_CFG); + + /* check if the watchdog was enabled */ + if (!(status & KEMPLD_WDT_CFG_ENABLE)) + return -1; + + return 0; +} + +/* A regular configuration file looks like + + LABEL WDT + COM32 wdt.c32 + APPEND timeout=120 default_label=local +*/ +void detect_parameters(const int argc, const char *argv[]) { + for (int i = 1; i < argc; i++) { + /* Override the timeout if specified on the cmdline */ + if (!strncmp(argv[i], "timeout=", 8)) { + wdt.timeout=atoi(argv[i]+8); + } else + /* Define which boot entry shall be used */ + if (!strncmp(argv[i], "default_label=", 14)) { + strlcpy(default_label, argv[i] + 14, sizeof(default_label)); + } + } +} + +int main(int argc, const char *argv[]) { + int ret=0; + openconsole(&dev_rawcon_r, &dev_stdcon_w); + init_structure(); + detect_parameters(argc,argv); + kempld_probe(); + + /* probe how many usable stages we have */ + if (kempld_wdt_probe_stages(&wdt)) { + printf("Cannot Probe Stages\n"); + return -1; + } + + /* Useless but who knows */ + wdt.ident.firmware_version = KEMPLD_WDT_REV_GET(kempld_read8(&pld, KEMPLD_WDT_REV)); + + status = kempld_read8(&pld, KEMPLD_WDT_CFG); + /* kick the watchdog if it is already enabled, otherwise start it */ + if (status & KEMPLD_WDT_CFG_ENABLE) { + /* Maybye the BIOS did setup a first timer + * in this case, let's enforce the timeout + * to be sure we do have the proper value */ + kempld_wdt_settimeout(&wdt); + kempld_wdt_keepalive(&wdt); + } else { + ret = kempld_wdt_settimeout(&wdt); + if (ret) { + printf("Unable to setup timeout !\n"); + goto booting; + } + + ret = kempld_wdt_start(&wdt); + if (ret) { + printf("Unable to start watchdog !\n"); + goto booting; + } + + } + + printf("Watchog armed ! Rebooting in %d seconds if no feed occurs !\n",wdt.timeout); + +booting: + /* Release Mutex to let Linux's Driver taking control */ + kempld_release_mutex(&pld); + + /* Let's boot the default entry if specified */ + if (strlen(default_label)>0) { + printf("Executing default label = '%s'\n",default_label); + syslinux_run_command(default_label); + } else { + return ret; + } +} diff --git a/com32/modules/kontron_wdt.h b/com32/modules/kontron_wdt.h new file mode 100644 index 00000000..e916de30 --- /dev/null +++ b/com32/modules/kontron_wdt.h @@ -0,0 +1,117 @@ +/* + * kempld_wdt.h - Kontron PLD watchdog driver definitions + * + * Copyright (c) 2010 Kontron Embedded Modules GmbH + * Author: Michael Brunner <michael.brunner@kontron.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _KEMPLD_WDT_H_ +#define _KEMPLD_WDT_H_ +#include <stdint.h> + +#define KEMPLD_IOPORT 0x0a80 +#define KEMPLD_IODATA (KEMPLD_IOPORT+1) + +#define KEMPLD_MUTEX_KEY 0x80 + +/* watchdog register definitions */ +#define KEMPLD_WDT_KICK 0x16 +#define KEMPLD_WDT_REV 0x16 +#define KEMPLD_WDT_REV_GET(x) (x & 0xf) +#define KEMPLD_WDT_CFG 0x17 +#define KEMPLD_WDT_CFG_STAGE_TIMEOUT_OCCURED(x) (1<<x) +#define KEMPLD_WDT_CFG_ENABLE_LOCK 0x8 +#define KEMPLD_WDT_CFG_ENABLE 0x10 +#define KEMPLD_WDT_CFG_AUTO_RELOAD 0x40 +#define KEMPLD_WDT_CFG_GLOBAL_LOCK 0x80 +#define KEMPLD_WDT_STAGE_CFG(x) (0x18+x) +#define KEMPLD_WDT_STAGE_CFG_ACTION_MASK 0x7 +#define KEMPLD_WDT_STAGE_CFG_GET_ACTION(x) (x & 0x7) +#define KEMPLD_WDT_STAGE_CFG_ASSERT 0x8 +#define KEMPLD_WDT_STAGE_CFG_PRESCALER_MASK 0x30 +#define KEMPLD_WDT_STAGE_CFG_GET_PRESCALER(x) ((x & 0x30)>>4) +#define KEMPLD_WDT_STAGE_CFG_SET_PRESCALER(x) ((x & 0x30)<<4) +#define KEMPLD_WDT_STAGE_TIMEOUT(x) (0x1b+x*4) +#define KEMPLD_WDT_MAX_STAGES 3 + +#define KEMPLD_WDT_ACTION_NONE 0x0 +#define KEMPLD_WDT_ACTION_RESET 0x1 +#define KEMPLD_WDT_ACTION_NMI 0x2 +#define KEMPLD_WDT_ACTION_SMI 0x3 +#define KEMPLD_WDT_ACTION_SCI 0x4 +#define KEMPLD_WDT_ACTION_DELAY 0x5 + +#define KEMPLD_WDT_PRESCALER_21BIT 0x0 +#define KEMPLD_WDT_PRESCALER_17BIT 0x1 +#define KEMPLD_WDT_PRESCALER_12BIT 0x2 + +const int kempld_prescaler_bits[] = { 21, 17, 12 }; + +struct kempld_watchdog_stage { + int num; + uint32_t timeout_mask; +}; + +/** + * struct kempld_device_data - Internal representation of the PLD device + * @io_base: Pointer to the IO memory + * @io_index: Pointer to the IO index register + * @io_data: Pointer to the IO data register + * @pld_clock: PLD clock frequency + * @lock: PLD spin-lock + * @lock_flags: PLD spin-lock flags + * @have_mutex: Bool value that indicates if mutex is aquired + * @last_index: Last written index value + * @rscr: Kernel resource structure + * @dev: Pointer to kernel device structure + * @info: KEMPLD info structure + */ +struct kempld_device_data { + uint16_t io_base; + uint16_t io_index; + uint16_t io_data; + uint32_t pld_clock; +/* spinlock_t lock; + unsigned long lock_flags; */ + int have_mutex; + uint8_t last_index; +/* struct resource rscr; + struct device *dev; + struct kempld_info info;*/ +}; + +struct watchdog_info { + uint32_t options; /* Options the card/driver supports */ + uint32_t firmware_version; /* Firmware version of the card */ + uint8_t identity[32]; /* Identity of the board */ +}; + +struct kempld_watchdog_data { + unsigned int revision; + int timeout; + int pretimeout; + unsigned long is_open; + unsigned long expect_close; + int stages; + struct kempld_watchdog_stage *timeout_stage; + struct kempld_watchdog_stage *pretimeout_stage; + struct kempld_device_data *pld; + struct kempld_watchdog_stage *stage[KEMPLD_WDT_MAX_STAGES]; + struct watchdog_info ident; +}; + +#endif /* _KEMPLD_WDT_H_ */ +#define KEMPLD_PRESCALER(x) (0xffffffff>>(32-kempld_prescaler_bits[x])) diff --git a/com32/modules/linux.c b/com32/modules/linux.c index b902ebc5..76443f91 100644 --- a/com32/modules/linux.c +++ b/com32/modules/linux.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- * * * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved - * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * Copyright 2009-2012 Intel Corporation; author: H. Peter Anvin * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -38,6 +38,7 @@ * Usage: linux.c32 [-dhcpinfo] kernel arguments... */ +#include <errno.h> #include <stdbool.h> #include <stdlib.h> #include <stdio.h> @@ -108,10 +109,31 @@ static char *make_cmdline(char **argv) return cmdline; } +static int setup_data_file(struct setup_data *setup_data, + uint32_t type, const char *filename, + bool opt_quiet) +{ + if (!opt_quiet) + printf("Loading %s... ", filename); + + if (setup_data_load(setup_data, type, filename)) { + if (opt_quiet) + printf("Loading %s ", filename); + printf("failed\n"); + return -1; + } + + if (!opt_quiet) + printf("ok\n"); + + return 0; +} + int main(int argc, char *argv[]) { const char *kernel_name; struct initramfs *initramfs; + struct setup_data *setup_data; char *cmdline; char *boot_image; void *kernel_data; @@ -120,7 +142,7 @@ int main(int argc, char *argv[]) bool opt_quiet = false; void *dhcpdata; size_t dhcplen; - char **argp, *arg, *p; + char **argp, **argl, *arg, *p; openconsole(&dev_null_r, &dev_stdcon_w); @@ -144,9 +166,12 @@ int main(int argc, char *argv[]) kernel_name = arg; + errno = 0; boot_image = malloc(strlen(kernel_name) + 12); - if (!boot_image) + if (!boot_image) { + fprintf(stderr, "Error allocating BOOT_IMAGE string: "); goto bail; + } strcpy(boot_image, "BOOT_IMAGE="); strcpy(boot_image + 11, kernel_name); /* argp now points to the kernel name, and the command line follows. @@ -159,23 +184,30 @@ int main(int argc, char *argv[]) if (!opt_quiet) printf("Loading %s... ", kernel_name); + errno = 0; if (loadfile(kernel_name, &kernel_data, &kernel_len)) { if (opt_quiet) printf("Loading %s ", kernel_name); - printf("failed!\n"); + printf("failed: "); goto bail; } if (!opt_quiet) printf("ok\n"); + errno = 0; cmdline = make_cmdline(argp); - if (!cmdline) + if (!cmdline) { + fprintf(stderr, "make_cmdline() failed: "); goto bail; + } /* Initialize the initramfs chain */ + errno = 0; initramfs = initramfs_init(); - if (!initramfs) + if (!initramfs) { + fprintf(stderr, "initramfs_init() failed: "); goto bail; + } if ((arg = find_argument(argp, "initrd="))) { do { @@ -185,10 +217,11 @@ int main(int argc, char *argv[]) if (!opt_quiet) printf("Loading %s... ", arg); + errno = 0; if (initramfs_load_archive(initramfs, arg)) { if (opt_quiet) printf("Loading %s ", kernel_name); - printf("failed!\n"); + printf("failed: "); goto bail; } if (!opt_quiet) @@ -202,15 +235,57 @@ int main(int argc, char *argv[]) /* Append the DHCP info */ if (opt_dhcpinfo && !pxe_get_cached_info(PXENV_PACKET_TYPE_DHCP_ACK, &dhcpdata, &dhcplen)) { + errno = 0; if (initramfs_add_file(initramfs, dhcpdata, dhcplen, dhcplen, - "/dhcpinfo.dat", 0, 0755)) + "/dhcpinfo.dat", 0, 0755)) { + fprintf(stderr, "Unable to add DHCP info: "); goto bail; + } + } + + /* Handle dtb and eventually other setup data */ + setup_data = setup_data_init(); + if (!setup_data) + goto bail; + + for (argl = argv; (arg = *argl); argl++) { + if (!memcmp(arg, "dtb=", 4)) { + if (setup_data_file(setup_data, SETUP_DTB, arg+4, opt_quiet)) + goto bail; + } else if (!memcmp(arg, "blob.", 5)) { + uint32_t type; + char *ep; + + type = strtoul(arg + 5, &ep, 10); + if (ep[0] != '=' || !ep[1]) + continue; + + if (!type) + continue; + + if (setup_data_file(setup_data, type, ep+1, opt_quiet)) + goto bail; + } } /* This should not return... */ - syslinux_boot_linux(kernel_data, kernel_len, initramfs, cmdline); + errno = 0; + syslinux_boot_linux(kernel_data, kernel_len, initramfs, + setup_data, cmdline); + fprintf(stderr, "syslinux_boot_linux() failed: "); bail: - fprintf(stderr, "Kernel load failure (insufficient memory?)\n"); + switch(errno) { + case ENOENT: + fprintf(stderr, "File not found\n"); + break; + case ENOMEM: + fprintf(stderr, "Out of memory\n"); + break; + default: + fprintf(stderr, "Error %d\n", errno); + break; + } + fprintf(stderr, "%s: Boot aborted!\n", progname); return 1; } diff --git a/com32/modules/prdhcp.c b/com32/modules/prdhcp.c new file mode 100644 index 00000000..e1785a03 --- /dev/null +++ b/com32/modules/prdhcp.c @@ -0,0 +1,164 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2010-2011 Gene Cumm - All Rights Reserved + * + * Portions from chain.c: + * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin + * Significant portions copyright (C) 2010 Shao Miller + * [partition iteration, GPT, "fs"] + * + * 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., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * prdhcp.c + * + * Print the contents of the 3 DHCP packets + */ + +#include <stdio.h> +#include <stdlib.h> +#include <consoles.h> +#include <console.h> +#include <errno.h> +#include <string.h> +#include <syslinux/config.h> +#include <syslinux/loadfile.h> +#include <syslinux/bootrm.h> +#include <syslinux/video.h> +#include <com32.h> +#include <stdint.h> +#include <syslinux/pxe.h> +#include <sys/gpxe.h> +#include <unistd.h> +#include <getkey.h> + +#define DEBUG 0 + +#define dprintf0(f, ...) ((void)0) + +#ifdef DEBUG +# define dpressanykey pressanykey +# define dprintf printf +# define dprint_pxe_bootp_t print_pxe_bootp_t +# define dprint_pxe_vendor_blk print_pxe_vendor_blk +# define dprint_pxe_vendor_raw print_pxe_vendor_raw +#else +# define dpressanykey(void) ((void)0) +# define dprintf(f, ...) ((void)0) +# define dprint_pxe_bootp_t(p, l) ((void)0) +# define dprint_pxe_vendor_blk(p, l) ((void)0) +# define dprint_pxe_vendor_raw(p, l) ((void)0) +#endif + +#define dprintf_opt_cp dprintf0 +#define dprintf_opt_inj dprintf + + +const char app_name_str[] = "prdhcp.c32"; + + +int pressanykey(void) { + int inc; + + printf("Press any key to continue. "); + inc = KEY_NONE; + while (inc == KEY_NONE) + inc = get_key(stdin, 6000); + puts(""); + return inc; +} + +void print_pxe_vendor_blk(pxe_bootp_t *p, size_t len) +{ + int i, vlen, oplen, j; + uint8_t *d; + uint32_t magic; + if (!p) { + printf(" packet pointer is null\n"); + return; + } + vlen = len - ((void *)&(p->vendor) - (void *)p); + printf(" Vendor Data: Len=%d", vlen); + d = p->vendor.d; + /* Print only 256 characters of the vendor/option data */ + /* + print_pxe_vendor_raw(p, (len - vlen) + 256); + vlen = 0; + */ + magic = ntohl(*((uint32_t *)d)); + printf(" Magic: %08X", ntohl(*((uint32_t *)d))); + if (magic != VM_RFC1048) /* Invalid DHCP packet */ + vlen = 0; + for (i = 4; i < vlen; i++) { + if (d[i]) /* Skip the padding */ + printf("\n @%03X-%3d", i, d[i]); + if (d[i] == 255) /* End of list */ + break; + if (d[i]) { + oplen = d[++i]; + printf(" l=%3d:", oplen); + for (j = (++i + oplen); i < vlen && i < j; i++) { + printf(" %02X", d[i]); + } + i--; + } + } + printf("\n"); +} + +void print_pxe_bootp_t(pxe_bootp_t *p, size_t len) +{ + if (!p) { + printf(" packet pointer is null\n"); + return; + } + printf(" op:%02X hw:%02X hl:%02X gh:%02X id:%08X se:%04X f:%04X" + " cip:%08X\n", p->opcode, p->Hardware, p->Hardlen, p->Gatehops, + ntohl(p->ident), ntohs(p->seconds), ntohs(p->Flags), ntohl(p->cip)); + printf(" yip:%08X sip:%08X gip:%08X", + ntohl(p->yip), ntohl(p->sip), ntohl(p->gip)); + printf(" caddr-%02X:%02X:%02X:%02X:%02X:%02X\n", p->CAddr[0], + p->CAddr[1], p->CAddr[2], p->CAddr[3], p->CAddr[4], p->CAddr[5]); + printf(" sName: '%s'\n", p->Sname); + printf(" bootfile: '%s'\n", p->bootfile); + print_pxe_vendor_blk(p, len); +} + +void print_dhcp_pkt_all(void) +{ + pxe_bootp_t *p; + size_t len; + int i; + int ptype[3] = {PXENV_PACKET_TYPE_DHCP_DISCOVER, PXENV_PACKET_TYPE_DHCP_ACK, PXENV_PACKET_TYPE_CACHED_REPLY}; + + for (i = 0; i < 3; i++) { + if (!pxe_get_cached_info(ptype[i], + (void **)&(p), &(len))) { + dprintf("Got packet #%d/%d\n", (i + 1), ptype[i]); + print_pxe_bootp_t(p, len); + pressanykey(); + } + } +} + +int main(void) +{ + int rv= -1; + const struct syslinux_version *sv; + + console_ansi_raw(); + sv = syslinux_version(); + if (sv->filesystem != SYSLINUX_FS_PXELINUX) { + printf("%s: May only run in PXELINUX\n", app_name_str); + return -2; + } + print_dhcp_pkt_all(); + return rv; +} diff --git a/com32/modules/pxechn.c b/com32/modules/pxechn.c new file mode 100644 index 00000000..26376900 --- /dev/null +++ b/com32/modules/pxechn.c @@ -0,0 +1,1161 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2010-2012 Gene Cumm - All Rights Reserved + * + * Portions from chain.c: + * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin + * Significant portions copyright (C) 2010 Shao Miller + * [partition iteration, GPT, "fs"] + * + * 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., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * pxechn.c + * + * PXE Chain Loader; Chain load to another PXE network boot program + * that may be on another host. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <consoles.h> +#include <console.h> +#include <errno.h> +#include <string.h> +#include <syslinux/config.h> +#include <syslinux/loadfile.h> +#include <syslinux/bootrm.h> +#include <syslinux/video.h> +#include <com32.h> +#include <stdint.h> +#include <syslinux/pxe.h> +#include <sys/gpxe.h> +#include <unistd.h> +#include <getkey.h> +#include <dhcp.h> +#include <limits.h> + + +#ifdef DEBUG +# define PXECHN_DEBUG 1 +#else +# define PXECHN_DEBUG 0 +#endif + +typedef union { + uint64_t q; + uint32_t l[2]; + uint16_t w[4]; + uint8_t b[8]; +} reg64_t; + +#define dprintf0(f, ...) ((void)0) + +#ifndef dprintf +# if (PXECHN_DEBUG > 0) +# define dprintf printf +# else +# define dprintf(f, ...) ((void)0) +# endif +#endif + +#if (PXECHN_DEBUG > 0) +# define dpressanykey pressanykey +# define dprint_pxe_bootp_t print_pxe_bootp_t +# define dprint_pxe_vendor_blk print_pxe_vendor_blk +# define dprint_pxe_vendor_raw print_pxe_vendor_raw +#else +# define dpressanykey(tm) ((void)0) +# define dprint_pxe_bootp_t(p, l) ((void)0) +# define dprint_pxe_vendor_blk(p, l) ((void)0) +# define dprint_pxe_vendor_raw(p, l) ((void)0) +#endif + +#define dprintf_opt_cp dprintf0 +#define dprintf_opt_inj dprintf0 +#define dprintf_pc_pa dprintf +#define dprintf_pc_so_s dprintf0 + +#define t_PXENV_RESTART_TFTP t_PXENV_TFTP_READ_FILE + +#define STACK_SPLIT 11 + +/* same as pxelinux.asm REBOOT_TIME */ +#define REBOOT_TIME 300 + +#define NUM_DHCP_OPTS 256 +#define DHCP_OPT_LEN_MAX 256 +#define PXE_VENDOR_RAW_PRN_MAX 0x7F +#define PXECHN_HOST_LEN 256 /* 63 bytes per label; 255 max total */ + +#define PXECHN_NUM_PKT_TYPE 3 +#define PXECHN_NUM_PKT_AVAIL 2*PXECHN_NUM_PKT_TYPE +#define PXECHN_PKT_TYPE_START PXENV_PACKET_TYPE_DHCP_DISCOVER + +#define PXECHN_FORCE_PKT1 0x80000000 +#define PXECHN_FORCE_PKT2 0x40000000 +#define PXECHN_FORCE_ALL (PXECHN_FORCE_PKT1 | PXECHN_FORCE_PKT2) +#define PXECHN_FORCE_ALL_1 0 +#define STRASINT_str ('s' + (('t' + ('r' << 8)) << 8)) + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +const char app_name_str[] = "pxechn.c32"; + +struct pxelinux_opt { + char *fn; /* Filename as passed to us */ + in_addr_t fip; /* fn's IP component */ + char *fp; /* fn's path component */ + in_addr_t gip; /* giaddr; Gateway/DHCP relay */ + uint32_t force; + uint32_t wait; /* Additional decision to wait before boot */ + int32_t wds; /* WDS option/level */ + in_addr_t sip; /* siaddr: Next Server IP Address */ + struct dhcp_option p[PXECHN_NUM_PKT_AVAIL]; + /* original _DHCP_DISCOVER, _DHCP_ACK, _CACHED_REPLY then modified packets */ + char host[PXECHN_HOST_LEN]; + struct dhcp_option opts[PXECHN_NUM_PKT_TYPE][NUM_DHCP_OPTS]; + char p_unpacked[PXECHN_NUM_PKT_TYPE]; +}; + + +/* from chain.c */ +struct data_area { + void *data; + addr_t base; + addr_t size; +}; + +/* From chain.c */ +static inline void error(const char *msg) +{ + fputs(msg, stderr); +} + +/* From chain.c */ +static void do_boot(struct data_area *data, int ndata, + struct syslinux_rm_regs *regs) +{ + uint16_t *const bios_fbm = (uint16_t *) 0x413; + addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */ + struct syslinux_memmap *mmap; + struct syslinux_movelist *mlist = NULL; + addr_t endimage; + int i; + + mmap = syslinux_memory_map(); + + if (!mmap) { + error("Cannot read system memory map\n"); + return; + } + + endimage = 0; + for (i = 0; i < ndata; i++) { + if (data[i].base + data[i].size > endimage) + endimage = data[i].base + data[i].size; + } + if (endimage > dosmem) + goto too_big; + + for (i = 0; i < ndata; i++) { + if (syslinux_add_movelist(&mlist, data[i].base, + (addr_t) data[i].data, data[i].size)) + goto enomem; + } + + + /* Tell the shuffler not to muck with this area... */ + syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED); + + /* Force text mode */ + syslinux_force_text_mode(); + + fputs("Booting...\n", stdout); + syslinux_shuffle_boot_rm(mlist, mmap, 3, regs); + error("Chainboot failed!\n"); + return; + +too_big: + error("Loader file too large\n"); + return; + +enomem: + error("Out of memory\n"); + return; +} + +void usage(void) +{ + printf("USAGE:\n" + " %s [OPTIONS]... _new-nbp_\n" + " %s -r _new-nbp_ (calls PXE stack PXENV_RESTART_TFTP)\n" + "OPTIONS:\n" + " [-c config] [-g gateway] [-p prefix] [-t reboot] [-u] [-w] [-W]" + " [-o opt.ty=val]\n\n", + app_name_str, app_name_str); +} + +void pxe_error(int ierr, const char *evt, const char *msg) +{ + if (msg) + printf("%s", msg); + else if (evt) + printf("Error while %s: ", evt); + printf("%d:%s\n", ierr, strerror(ierr)); +} + +int pressanykey(clock_t tm) { + int inc; + + printf("Press any key to continue. "); + inc = get_key(stdin, tm); + puts(""); + return inc; +} + +int dhcp_find_opt(pxe_bootp_t *p, size_t len, uint8_t opt) +{ + int rv = -1; + int i, vlen, oplen; + uint8_t *d; + uint32_t magic; + + if (!p) { + dprintf(" packet pointer is null\n"); + return rv; + } + vlen = len - ((void *)&(p->vendor) - (void *)p); + d = p->vendor.d; + magic = ntohl(*((uint32_t *)d)); + if (magic != VM_RFC1048) /* Invalid DHCP packet */ + vlen = 0; + for (i = 4; i < vlen; i++) { + if (d[i] == opt) { + dprintf("\n @%03X-%2d\n", i, d[i]); + rv = i; + break; + } + if (d[i] == ((NUM_DHCP_OPTS) - 1)) /* End of list */ + break; + if (d[i]) { /* Skip padding */ + oplen = d[++i]; + i += oplen; + } + } + return rv; +} + +void print_pxe_vendor_raw(pxe_bootp_t *p, size_t len) +{ + int i, vlen; + + if (!p) { + printf(" packet pointer is null\n"); + return; + } + vlen = len - ((void *)&(p->vendor) - (void *)p); + if (vlen > PXE_VENDOR_RAW_PRN_MAX) + vlen = PXE_VENDOR_RAW_PRN_MAX; + dprintf(" rawLen = %d", vlen); + for (i = 0; i < vlen; i++) { + if ((i & 0xf) == 0) + printf("\n %04X:", i); + printf(" %02X", p->vendor.d[i]); + } + printf("\n"); +} + +void print_pxe_vendor_blk(pxe_bootp_t *p, size_t len) +{ + int i, vlen, oplen, j; + uint8_t *d; + uint32_t magic; + if (!p) { + printf(" packet pointer is null\n"); + return; + } + vlen = len - ((void *)&(p->vendor) - (void *)p); + printf(" Vendor Data: Len=%d", vlen); + d = p->vendor.d; + magic = ntohl(*((uint32_t *)d)); + printf(" Magic: %08X", ntohl(*((uint32_t *)d))); + if (magic != VM_RFC1048) /* Invalid DHCP packet */ + vlen = 0; + for (i = 4; i < vlen; i++) { + if (d[i]) /* Skip the padding */ + printf("\n @%03X-%3d", i, d[i]); + if (d[i] == ((NUM_DHCP_OPTS) - 1)) /* End of list */ + break; + if (d[i]) { + oplen = d[++i]; + printf(" l=%3d:", oplen); + for (j = (++i + oplen); i < vlen && i < j; i++) { + printf(" %02X", d[i]); + } + i--; + } + } + printf("\n"); +} + +void print_pxe_bootp_t(pxe_bootp_t *p, size_t len) +{ + if (!p || len <= 0) { + printf(" packet pointer is null\n"); + return; + } + printf(" op:%02X hw:%02X hl:%02X gh:%02X id:%08X se:%04X f:%04X" + " cip:%08X\n", p->opcode, p->Hardware, p->Hardlen, p->Gatehops, + ntohl(p->ident), ntohs(p->seconds), ntohs(p->Flags), ntohl(p->cip)); + printf(" yip:%08X sip:%08X gip:%08X", + ntohl(p->yip), ntohl(p->sip), ntohl(p->gip)); + printf(" caddr-%02X:%02X:%02X:%02X:%02X:%02X\n", p->CAddr[0], + p->CAddr[1], p->CAddr[2], p->CAddr[3], p->CAddr[4], p->CAddr[5]); + printf(" sName: '%s'\n", p->Sname); + printf(" bootfile: '%s'\n", p->bootfile); + dprint_pxe_vendor_blk(p, len); +} + +void pxe_set_regs(struct syslinux_rm_regs *regs) +{ + com32sys_t tregs; + + regs->ip = 0x7C00; + /* Plan A uses SS:[SP + 4] */ + /* sdi->pxe.stack is a usable pointer, not something that can be nicely + and reliably split to SS:SP without causing issues */ + tregs.eax.l = 0x000A; + __intcall(0x22, &tregs, &tregs); + regs->ss = tregs.fs; + regs->esp.l = tregs.esi.w[0] + sizeof(tregs); + /* Plan B uses [ES:BX] */ + regs->es = tregs.es; + regs->ebx = tregs.ebx; + dprintf("\nsp:%04x ss:%04x es:%04x bx:%04x\n", regs->esp.w[0], + regs->ss, regs->es, regs->ebx.w[0]); + /* Zero out everything else just to be sure */ + regs->cs = regs->ds = regs->fs = regs->gs = 0; + regs->eax.l = regs->ecx.l = regs->edx.l = 0; +} + +int hostlen_limit(int len) +{ + return min(len, ((PXECHN_HOST_LEN) - 1)); +} + +//FIXME: To a library +/* Parse a filename into an IPv4 address and filename pointer + * returns Based on the interpretation of fn + * 0 regular file name + * 1 in format IP::FN + * 2 TFTP URL + * 3 HTTP URL + * 4 FTP URL + * 3 + 2^30 HTTPS URL + * -1 if fn is another URL type + */ +int pxechn_parse_fn(char fn[], in_addr_t *fip, char *host, char *fp[]) +{ + in_addr_t tip = 0; + char *csep, *ssep, *hsep; /* Colon, Slash separator positions */ + int hlen, plen; /* Hostname, protocol length */ + int rv = 0; + + csep = strchr(fn, ':'); + if (csep) { + if (csep[1] == ':') { /* assume IP::FN */ + *fp = &csep[2]; + rv = 1; + if (fn[0] != ':') { + hlen = hostlen_limit(csep - fn); + memcpy(host, fn, hlen); + host[hlen] = 0; + } + } else if ((csep[1] == '/') && (csep[2] == '/')) { + /* URL: proto://host:port/path/file */ + /* proto://[user[:passwd]@]host[:port]/path/file */ + ssep = strchr(csep + 3, '/'); + if (ssep) { + hlen = hostlen_limit(ssep - (csep + 3)); + *fp = ssep + 1; + } else { + hlen = hostlen_limit(strlen(csep + 3)); + } + memcpy(host, (csep + 3), hlen); + host[hlen] = 0; + plen = csep - fn; + if (strncmp(fn, "tftp", plen) == 0) + rv = 2; + else if (strncmp(fn, "http", plen) == 0) + rv = 3; + else if (strncmp(fn, "ftp", plen) == 0) + rv = 4; + else if (strncmp(fn, "https", plen) == 0) + rv = 3 + ( 1 << 30 ); + else + rv = -1; + } else { + csep = NULL; + } + } + if (!csep) { + *fp = fn; + } + if (host[0]) { + hsep = strchr(host, '@'); + if (!hsep) + hsep = host; + tip = pxe_dns(hsep); + } + if (tip != 0) + *fip = tip; + dprintf0(" host '%s'\n fp '%s'\n fip %08x\n", host, *fp, ntohl(*fip)); + return rv; +} + +void pxechn_opt_free(struct dhcp_option *opt) +{ + free(opt->data); + opt->len = -1; +} + +void pxechn_fill_pkt(struct pxelinux_opt *pxe, int ptype) +{ + int rv = -1; + int p1, p2; + if ((ptype < 0) || (ptype > PXECHN_NUM_PKT_TYPE)) + rv = -2; + p1 = ptype - PXECHN_PKT_TYPE_START; + p2 = p1 + PXECHN_NUM_PKT_TYPE; + if ((rv >= -1) && (!pxe_get_cached_info(ptype, + (void **)&(pxe->p[p1].data), (size_t *)&(pxe->p[p1].len)))) { + pxe->p[p2].data = malloc(2048); + if (pxe->p[p2].data) { + memcpy(pxe->p[p2].data, pxe->p[p1].data, pxe->p[p1].len); + pxe->p[p2].len = pxe->p[p1].len; + rv = 0; + dprint_pxe_bootp_t((pxe_bootp_t *)(pxe->p[p1].data), pxe->p[p1].len); + dpressanykey(INT_MAX); + } else { + printf("%s: ERROR: Unable to malloc() for second packet\n", app_name_str); + } + } else { + printf("%s: ERROR: Unable to retrieve first packet\n", app_name_str); + } + if (rv <= -1) { + pxechn_opt_free(&pxe->p[p1]); + } +} + +void pxechn_init(struct pxelinux_opt *pxe) +{ + /* Init for paranoia */ + pxe->fn = NULL; + pxe->fp = NULL; + pxe->force = 0; + pxe->wait = 0; + pxe->gip = 0; + pxe->wds = 0; + pxe->sip = 0; + pxe->host[0] = 0; + pxe->host[((NUM_DHCP_OPTS) - 1)] = 0; + for (int j = 0; j < PXECHN_NUM_PKT_TYPE; j++){ + for (int i = 0; i < NUM_DHCP_OPTS; i++) { + pxe->opts[j][i].data = NULL; + pxe->opts[j][i].len = -1; + } + pxe->p_unpacked[j] = 0; + pxe->p[j].data = NULL; + pxe->p[j+PXECHN_NUM_PKT_TYPE].data = NULL; + pxe->p[j].len = 0; + pxe->p[j+PXECHN_NUM_PKT_TYPE].len = 0; + } + pxechn_fill_pkt(pxe, PXENV_PACKET_TYPE_CACHED_REPLY); +} + +int pxechn_to_hex(char i) +{ + if (i >= '0' && i <= '9') + return (i - '0'); + if (i >= 'A' && i <= 'F') + return (i - 'A' + 10); + if (i >= 'a' && i <= 'f') + return (i - 'a' + 10); + if (i == 0) + return -1; + return -2; +} + +int pxechn_parse_2bhex(char ins[]) +{ + int ret = -2; + int n0 = -3, n1 = -3; + /* NULL pointer */ + if (!ins) { + ret = -1; + /* pxechn_to_hex can handle the NULL character by returning -1 and + breaking the execution of the statement chain */ + } else if (((n0 = pxechn_to_hex(ins[0])) >= 0) + && ((n1 = pxechn_to_hex(ins[1])) >= 0)) { + ret = (n0 * 16) + n1; + } else if (n0 == -1) { /* Leading NULL char */ + ret = -1; + } + return ret; +} + +int pxechn_optnum_ok(int optnum) +{ + if ((optnum > 0) && (optnum < ((NUM_DHCP_OPTS) - 1))) + return 1; + return 0; +} + +int pxechn_optnum_ok_notres(int optnum) +{ + if ((optnum <= 0) && (optnum >= ((NUM_DHCP_OPTS) - 1))) + return 0; + switch(optnum){ + case 66: case 67: + return 0; + break; + default: return 1; + } +} + +int pxechn_optlen_ok(int optlen) +{ + if ((optlen >= 0) && (optlen < ((DHCP_OPT_LEN_MAX) - 1))) + return 1; + return 0; +} + +int pxechn_setopt(struct dhcp_option *opt, void *data, int len) +{ + void *p; + if (!opt || !data) + return -1; + if (len < 0) { + return -3; + } + p = realloc(opt->data, len); + if (!p && len) { /* Allow for len=0 */ + pxechn_opt_free(opt); + return -2; + } + opt->data = p; + memcpy(opt->data, data, len); + opt->len = len; + return len; +} + +int pxechn_setopt_str(struct dhcp_option *opt, void *data) +{ + return pxechn_setopt(opt, data, strnlen(data, DHCP_OPT_LEN_MAX)); +} + +int pxechn_parse_int(char *data, char istr[], int tlen) +{ + int terr = errno; + + if ((tlen == 1) || (tlen == 2) || (tlen == 4)) { + errno = 0; + uint32_t optval = strtoul(istr, NULL, 0); + if (errno) + return -3; + errno = terr; + switch(tlen){ + case 1: + if (optval & 0xFFFFFF00) + return -4; + break; + case 2: + if (optval & 0xFFFF0000) + return -4; + optval = htons(optval); + break; + case 4: + optval = htonl(optval); + break; + } + memcpy(data, &optval, tlen); + } else if (tlen == 8) { + errno = 0; + uint64_t optval = strtoull(istr, NULL, 0); + if (errno) + return -3; + errno = terr; + optval = htonq(optval); + memcpy(data, &optval, tlen); + } else { + return -2; + } + return tlen; +} + +int pxechn_parse_hex_sep(char *data, char istr[], char sep) +{ + int len = 0; + int ipos = 0, ichar; + + if (!data || !istr) + return -1; + while ((istr[ipos]) && (len < DHCP_OPT_LEN_MAX)) { + dprintf(" %02X%02X", *((int *)(istr + ipos)) & 0xFF, *((int *)(istr + ipos +1)) & 0xFF); + ichar = pxechn_parse_2bhex(istr + ipos); + if (ichar >=0) { + data[len++] = ichar; + } else { + return -EINVAL; + } + if (!istr[ipos+2]){ + ipos += 2; + } else if (istr[ipos+2] != sep) { + return -(EINVAL + 1); + } else { + ipos += 3; + } + } + return len; +} + +int pxechn_parse_opttype(char istr[], int optnum) +{ + char *pos; + int tlen, type, tmask; + + if (!istr) + return -1; + pos = strchr(istr, '='); + if (!pos) + return -2; + if (istr[0] != '.') { + if (!pxechn_optnum_ok(optnum)) + return -3; + return -3; /* do lookup here */ + } else { + tlen = pos - istr - 1; + if ((tlen < 1) || (tlen > 4)) + return -4; + tmask = 0xFFFFFFFF >> (8 * (4 - tlen)); + type = (*(int*)(istr + 1)) & tmask; + } + return type; +} + +int pxechn_parse_setopt(struct dhcp_option opts[], struct dhcp_option *iopt, + char istr[]) +{ + int rv = 0, optnum, opttype; + char *cpos = NULL, *pos; + + if (!opts || !iopt || !(iopt->data)) + return -1; + if (!istr || !istr[0]) + return -2; + // -EINVAL; + optnum = strtoul(istr, &cpos, 0); + if (!pxechn_optnum_ok(optnum)) + return -3; + pos = strchr(cpos, '='); + if (!pos) + return -4; + opttype = pxechn_parse_opttype(cpos, optnum); + pos++; + switch(opttype) { + case 'b': + iopt->len = pxechn_parse_int(iopt->data, pos, 1); + break; + case 'l': + iopt->len = pxechn_parse_int(iopt->data, pos, 4); + break; + case 'q': + iopt->len = pxechn_parse_int(iopt->data, pos, 8); + break; + case 's': + case STRASINT_str: + iopt->len = strlen(pos); + if (iopt->len > DHCP_OPT_LEN_MAX) + iopt->len = DHCP_OPT_LEN_MAX; + memcpy(iopt->data, pos, iopt->len); + dprintf_pc_so_s("s.len=%d\trv=%d\n", iopt->len, rv); + break; + case 'w': + iopt->len = pxechn_parse_int(iopt->data, pos, 2); + break; + case 'x': + iopt->len = pxechn_parse_hex_sep(iopt->data, pos, ':'); + break; + default: + return -6; + break; + } + if (pxechn_optlen_ok(iopt->len)) { + rv = pxechn_setopt(&(opts[optnum]), (void *)(iopt->data), iopt->len); + } + if((opttype == 's') || (opttype == STRASINT_str)) + dprintf_pc_so_s("rv=%d\n", rv); + return rv; +} + +int pxechn_parse_force(const char istr[]) +{ + uint32_t rv = 0; + char *pos; + int terr = errno; + + errno = 0; + rv = strtoul(istr, &pos, 0); + if ((istr == pos ) || ((rv == ULONG_MAX) && (errno))) + rv = 0; + errno = terr; + return rv; +} + +int pxechn_uuid_set(struct pxelinux_opt *pxe) +{ + int ret = 0; + + if (!pxe->p_unpacked[0]) + ret = dhcp_unpack_packet((pxe_bootp_t *)(pxe->p[0].data), + pxe->p[0].len, pxe->opts[0]); + if (ret) { + error("Could not unpack packet\n"); + return -ret; /* dhcp_unpack_packet always returns positive errors */ + } + + if (pxe->opts[0][97].len >= 0 ) + pxechn_setopt(&(pxe->opts[2][97]), pxe->opts[0][97].data, pxe->opts[0][97].len); + return 1; + return 0; +} + +int pxechn_parse_args(int argc, char *argv[], struct pxelinux_opt *pxe, + struct dhcp_option opts[]) +{ + int arg, optnum, rv = 0; + char *p = NULL; + const char optstr[] = "c:f:g:o:p:St:uwW"; + struct dhcp_option iopt; + + if (pxe->p[5].data) + pxe->fip = ( (pxe_bootp_t *)(pxe->p[5].data) )->sip; + else + pxe->fip = 0; + /* Fill */ + pxe->fn = argv[0]; + pxechn_parse_fn(pxe->fn, &(pxe->fip), pxe->host, &(pxe->fp)); + pxechn_setopt_str(&(opts[67]), pxe->fp); + pxechn_setopt_str(&(opts[66]), pxe->host); + iopt.data = malloc(DHCP_OPT_LEN_MAX); + iopt.len = 0; + while ((rv >= 0) && (arg = getopt(argc, argv, optstr)) >= 0) { + dprintf_pc_pa(" Got arg '%c'/'%c' addr %08X val %s\n", arg == '?' ? optopt : arg, arg, (unsigned int)optarg, optarg ? optarg : ""); + switch(arg) { + case 'c': /* config */ + pxechn_setopt_str(&(opts[209]), optarg); + break; + case 'f': /* force */ + pxe->force = pxechn_parse_force(optarg); + break; + case 'g': /* gateway/DHCP relay */ + pxe->gip = pxe_dns(optarg); + break; + case 'n': /* native */ + break; + case 'o': /* option */ + rv = pxechn_parse_setopt(opts, &iopt, optarg); + break; + case 'p': /* prefix */ + pxechn_setopt_str(&(opts[210]), optarg); + break; + case 'S': /* sip from sName */ + pxe->sip = 1; + break; + case 't': /* timeout */ + optnum = strtoul(optarg, &p, 0); + if (p != optarg) { + optnum = htonl(optnum); + pxechn_setopt(&(opts[211]), (void *)(&optnum), 4); + } else { + rv = -3; + } + break; + case 'u': /* UUID: copy option 97 from packet 1 if present */ + pxechn_uuid_set(pxe); + break; + case 'w': /* wait */ + pxe->wait = 1; + break; + case 'W': /* WDS */ + pxe->wds = 1; + break; + case '?': + rv = -'?'; + default: + break; + } + if (rv >= 0) /* Clear it since getopt() doesn't guarentee it */ + optarg = NULL; + } + if (iopt.data) + pxechn_opt_free(&iopt); +/* FIXME: consider reordering the application of parsed command line options + such that the new nbp may be at the end */ + if (rv >= 0) { + rv = 0; + } else if (arg != '?') { + printf("Invalid argument for -%c: %s\n", arg, optarg); + } + dprintf("pxechn_parse_args rv=%d\n", rv); + return rv; +} + +int pxechn_args(int argc, char *argv[], struct pxelinux_opt *pxe) +{ + pxe_bootp_t *bootp0, *bootp1; + int ret = 0; + struct dhcp_option *opts; + char *str; + + opts = pxe->opts[2]; + /* Start filling packet #1 */ + bootp0 = (pxe_bootp_t *)(pxe->p[2].data); + bootp1 = (pxe_bootp_t *)(pxe->p[5].data); + + ret = dhcp_unpack_packet(bootp0, pxe->p[2].len, opts); + if (ret) { + error("Could not unpack packet\n"); + return -ret; + } + pxe->p_unpacked[2] = 1; + pxe->gip = bootp1->gip; + + ret = pxechn_parse_args(argc, argv, pxe, opts); + if (ret) + return ret; + if (pxe->sip > 0xFFFFFF) { /* a real IPv4 address */ + bootp1->sip = pxe->sip; + } else if ((pxe->sip == 1) + && (opts[66].len > 0)){ + /* unterminated? */ + if (strnlen(opts[66].data, opts[66].len) == (size_t)opts[66].len) { + str = malloc(opts[66].len + 1); + if (str) { + memcpy(str, opts[66].data, opts[66].len); + str[opts[66].len] = 0; + } + } else { + str = opts[66].data; + } + if (str) { + bootp1->sip = pxe_dns(str); + if (str != opts[66].data) + free(str); + } else { + bootp1->sip = pxe->fip; + } + } else { + bootp1->sip = pxe->fip; + } + bootp1->gip = pxe->gip; + + ret = dhcp_pack_packet(bootp1, (size_t *)&(pxe->p[5].len), opts); + if (ret) { + error("Could not pack packet\n"); + return -ret; /* dhcp_pack_packet always returns positive errors */ + } + return ret; +} + +/* dhcp_pkt2pxe: Copy packet to PXE's BC data for a ptype packet + * Input: + * p Packet data to copy + * len length of data to copy + * ptype Packet type to overwrite + */ +int dhcp_pkt2pxe(pxe_bootp_t *p, size_t len, int ptype) +{ + com32sys_t reg; + t_PXENV_GET_CACHED_INFO *ci; + void *cp; + int rv = -1; + + if (!(ci = lzalloc(sizeof(t_PXENV_GET_CACHED_INFO)))){ + dprintf("Unable to lzalloc() for PXE call structure\n"); + rv = 1; + goto ret; + } + ci->Status = PXENV_STATUS_FAILURE; + ci->PacketType = ptype; + memset(®, 0, sizeof(reg)); + reg.eax.w[0] = 0x0009; + reg.ebx.w[0] = PXENV_GET_CACHED_INFO; + reg.edi.w[0] = OFFS(ci); + reg.es = SEG(ci); + __intcall(0x22, ®, ®); + + if (ci->Status != PXENV_STATUS_SUCCESS) { + dprintf("PXE Get Cached Info failed: %d\n", ci->Status); + rv = 2; + goto ret; + } + + cp = MK_PTR(ci->Buffer.seg, ci->Buffer.offs); + if (!(memcpy(cp, p, len))) { + dprintf("Failed to copy packet\n"); + rv = 3; + goto ret; + } +ret: + lfree(ci); + return rv; +} + +int pxechn_mergeopt(struct pxelinux_opt *pxe, int d, int s) +{ + int ret = 0, i; + + if ((d >= PXECHN_NUM_PKT_TYPE) || (s >= PXECHN_NUM_PKT_TYPE) + || (d < 0) || (s < 0)) { + return -2; + } + if (!pxe->p_unpacked[s]) + ret = dhcp_unpack_packet(pxe->p[s].data, pxe->p[s].len, pxe->opts[s]); + if (ret) { + error("Could not unpack packet for merge\n"); + printf("Error %d (%d)\n", ret, EINVAL); + if (ret == EINVAL) { + if (pxe->p[s].len < 240) + printf("Packet %d is too short: %d (240)\n", s, pxe->p[s].len); + else if (((const struct dhcp_packet *)(pxe->p[s].data))->magic != htonl(DHCP_VENDOR_MAGIC)) + printf("Packet %d has no magic\n", s); + else + error("Unknown EINVAL error\n"); + } else { + error("Unknown error\n"); + } + return -ret; + } + for (i = 0; i < NUM_DHCP_OPTS; i++) { + if (pxe->opts[d][i].len <= -1) { + if (pxe->opts[s][i].len >= 0) + pxechn_setopt(&(pxe->opts[d][i]), pxe->opts[s][i].data, pxe->opts[s][i].len); + } + } + return 0; +} + +/* pxechn: Chainload to new PXE file ourselves + * Input: + * argc Count of arguments passed + * argv Values of arguments passed + * Returns 0 on success (which should never happen) + * 1 on loadfile() error + * 2 if DHCP Option 52 (Option Overload) used file field + * -1 on usage error + */ +int pxechn(int argc, char *argv[]) +{ + struct pxelinux_opt pxe; + pxe_bootp_t* p[(2 * PXECHN_NUM_PKT_TYPE)]; + int rv = 0; + int i; + struct data_area file; + struct syslinux_rm_regs regs; + + pxechn_init(&pxe); + for (i = 0; i < (2 * PXECHN_NUM_PKT_TYPE); i++) { + p[i] = (pxe_bootp_t *)(pxe.p[i].data); + } + + /* Parse arguments and patch packet 1 */ + rv = pxechn_args(argc, argv, &pxe); + dpressanykey(INT_MAX); + if (rv) + goto ret; + pxe_set_regs(®s); + /* Load the file late; it's the most time-expensive operation */ + printf("%s: Attempting to load '%s': ", app_name_str, pxe.fn); + if (loadfile(pxe.fn, &file.data, &file.size)) { + pxe_error(errno, NULL, NULL); + rv = -2; + goto ret; + } + puts("loaded."); + /* we'll be shuffling to the standard location of 7C00h */ + file.base = 0x7C00; + if ((pxe.wds) || + ((pxe.force) && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0))) { + printf("Forcing behavior %08X\n", pxe.force); + // P2 is the same as P3 if no PXE server present. + if ((pxe.wds) || + (pxe.force & PXECHN_FORCE_PKT2)) { + pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_DHCP_ACK); + rv = pxechn_mergeopt(&pxe, 2, 1); + if (rv) { + dprintf("Merge Option returned %d\n", rv); + } + rv = dhcp_pack_packet(p[5], (size_t *)&(pxe.p[5].len), pxe.opts[2]); + rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK); + } + if (pxe.force & PXECHN_FORCE_PKT1) { + puts("Unimplemented force option utilized"); + } + } + rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_CACHED_REPLY); + dprint_pxe_bootp_t(p[5], pxe.p[5].len); + if ((pxe.wds) || + ((pxe.force) && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0))) { + // printf("Forcing behavior %08X\n", pxe.force); + // P2 is the same as P3 if no PXE server present. + if ((pxe.wds) || + (pxe.force & PXECHN_FORCE_PKT2)) { + rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK); + } + } else if (pxe.force) { + printf("FORCE: bad argument %08X\n", pxe.force); + } + printf("\n...Ready to boot:\n"); + if (pxe.wait) { + pressanykey(INT_MAX); + } else { + dpressanykey(INT_MAX); + } + if (true) { + puts(" Attempting to boot..."); + do_boot(&file, 1, ®s); + } + /* If failed, copy backup back in and abort */ + dhcp_pkt2pxe(p[2], pxe.p[2].len, PXENV_PACKET_TYPE_CACHED_REPLY); + if (pxe.force && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0)) { + if (pxe.force & PXECHN_FORCE_PKT2) { + rv = dhcp_pkt2pxe(p[1], pxe.p[1].len, PXENV_PACKET_TYPE_DHCP_ACK); + } + } +ret: + return rv; +} + +/* pxe_restart: Restart the PXE environment with a new PXE file + * Input: + * ifn Name of file to chainload to in a format PXELINUX understands + * This must strictly be TFTP or relative file + */ +int pxe_restart(char *ifn) +{ + int rv = 0; + struct pxelinux_opt pxe; + com32sys_t reg; + t_PXENV_RESTART_TFTP *pxep; /* PXENV callback Parameter */ + + pxe.fn = ifn; + pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_CACHED_REPLY); + if (pxe.p[5].data) + pxe.fip = ( (pxe_bootp_t *)(pxe.p[5].data) )->sip; + else + pxe.fip = 0; + rv = pxechn_parse_fn(pxe.fn, &(pxe.fip), pxe.host, &(pxe.fp)); + if ((rv > 2) || (rv < 0)) { + printf("%s: ERROR: Unparsable filename argument: '%s'\n\n", app_name_str, pxe.fn); + goto ret; + } + printf(" Attempting to boot '%s'...\n\n", pxe.fn); + memset(®, 0, sizeof reg); + if (sizeof(t_PXENV_TFTP_READ_FILE) <= __com32.cs_bounce_size) { + pxep = __com32.cs_bounce; + memset(pxep, 0, sizeof(t_PXENV_RESTART_TFTP)); + } else if (!(pxep = lzalloc(sizeof(t_PXENV_RESTART_TFTP)))){ + dprintf("Unable to lzalloc() for PXE call structure\n"); + goto ret; + } + pxep->Status = PXENV_STATUS_SUCCESS; /* PXENV_STATUS_FAILURE */ + strcpy((char *)pxep->FileName, ifn); + pxep->BufferSize = 0x8000; + pxep->Buffer = (void *)0x7c00; + pxep->ServerIPAddress = pxe.fip; + dprintf("FN='%s' %08X %08X %08X %08X\n\n", (char *)pxep->FileName, + pxep->ServerIPAddress, (unsigned int)pxep, + pxep->BufferSize, (unsigned int)pxep->Buffer); + dprintf("PXENV_RESTART_TFTP status %d\n", pxep->Status); + reg.eax.w[0] = 0x0009; + reg.ebx.w[0] = PXENV_RESTART_TFTP; + reg.edi.w[0] = OFFS(pxep); + reg.es = SEG(pxep); + + __intcall(0x22, ®, ®); + + printf("PXENV_RESTART_TFTP returned %d\n", pxep->Status); + if (pxep != __com32.cs_bounce) + lfree(pxep); + +ret: + return rv; +} + +/* pxechn_gpxe: Use gPXE to chainload a new NBP + * Input: + * argc Count of arguments passed + * argv Values of arguments passed + * Returns 0 on success (which should never happen) + * 1 on loadfile() error + * -1 on usage error + */ +//FIXME:Implement +int pxechn_gpxe(int argc, char *argv[]) +{ + int rv = 0; + struct pxelinux_opt pxe; + + if (argc) { + printf("%s\n", argv[0]); + pxechn_args(argc, argv, &pxe); + } + return rv; +} + +int main(int argc, char *argv[]) +{ + int rv= -1; + int err; + const struct syslinux_version *sv; + + /* Initialization */ + err = errno; + console_ansi_raw(); /* sets errno = 9 (EBADF) */ + /* printf("%d %d\n", err, errno); */ + errno = err; + sv = syslinux_version(); + if (sv->filesystem != SYSLINUX_FS_PXELINUX) { + printf("%s: May only run in PXELINUX\n", app_name_str); + argc = 1; /* prevents further processing to boot */ + } + if (argc == 2) { + if ((strcasecmp(argv[1], "-h") == 0) || ((strcmp(argv[1], "-?") == 0)) + || (strcasecmp(argv[1], "--help") == 0)) { + argc = 1; + } else { + rv = pxechn(argc - 1, &argv[1]); + } + } else if (argc >= 3) { + if ((strcmp(argv[1], "-r") == 0)) { + if (argc == 3) + rv = pxe_restart(argv[2]); + } else { + rv = pxechn(argc - 1, &argv[1]); + } + } + if (rv <= -1 ) { + usage(); + rv = 1; + } + return rv; +} diff --git a/com32/tools/Makefile b/com32/tools/Makefile index 7badabd2..0161baf1 100644 --- a/com32/tools/Makefile +++ b/com32/tools/Makefile @@ -15,6 +15,8 @@ include $(MAKEDIR)/build.mk BINS = relocs +INCLUDES += -I./include + all : $(BINS) relocs : relocs.o diff --git a/com32/tools/include/tools/le_byteshift.h b/com32/tools/include/tools/le_byteshift.h new file mode 100644 index 00000000..c99d45a6 --- /dev/null +++ b/com32/tools/include/tools/le_byteshift.h @@ -0,0 +1,70 @@ +#ifndef _TOOLS_LE_BYTESHIFT_H +#define _TOOLS_LE_BYTESHIFT_H + +#include <linux/types.h> + +static inline __u16 __get_unaligned_le16(const __u8 *p) +{ + return p[0] | p[1] << 8; +} + +static inline __u32 __get_unaligned_le32(const __u8 *p) +{ + return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; +} + +static inline __u64 __get_unaligned_le64(const __u8 *p) +{ + return (__u64)__get_unaligned_le32(p + 4) << 32 | + __get_unaligned_le32(p); +} + +static inline void __put_unaligned_le16(__u16 val, __u8 *p) +{ + *p++ = val; + *p++ = val >> 8; +} + +static inline void __put_unaligned_le32(__u32 val, __u8 *p) +{ + __put_unaligned_le16(val >> 16, p + 2); + __put_unaligned_le16(val, p); +} + +static inline void __put_unaligned_le64(__u64 val, __u8 *p) +{ + __put_unaligned_le32(val >> 32, p + 4); + __put_unaligned_le32(val, p); +} + +static inline __u16 get_unaligned_le16(const void *p) +{ + return __get_unaligned_le16((const __u8 *)p); +} + +static inline __u32 get_unaligned_le32(const void *p) +{ + return __get_unaligned_le32((const __u8 *)p); +} + +static inline __u64 get_unaligned_le64(const void *p) +{ + return __get_unaligned_le64((const __u8 *)p); +} + +static inline void put_unaligned_le16(__u16 val, void *p) +{ + __put_unaligned_le16(val, p); +} + +static inline void put_unaligned_le32(__u32 val, void *p) +{ + __put_unaligned_le32(val, p); +} + +static inline void put_unaligned_le64(__u64 val, void *p) +{ + __put_unaligned_le64(val, p); +} + +#endif /* _TOOLS_LE_BYTESHIFT_H */ diff --git a/com32/tools/relocs.c b/com32/tools/relocs.c index 24742060..86fc7c50 100644 --- a/com32/tools/relocs.c +++ b/com32/tools/relocs.c @@ -13,12 +13,16 @@ #define USE_BSD #include <endian.h> #include <regex.h> -#include <sys/types.h> +#include <tools/le_byteshift.h> + +static void die(char *fmt, ...); #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static Elf32_Ehdr ehdr; static unsigned long reloc_count, reloc_idx; static unsigned long *relocs; +static unsigned long reloc16_count, reloc16_idx; +static unsigned long *relocs16; struct section { Elf32_Shdr shdr; @@ -29,60 +33,87 @@ struct section { }; static struct section *secs; -static void die(char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - exit(1); -} +enum symtype { + S_ABS, + S_REL, + S_SEG, + S_LIN, + S_NSYMTYPES +}; +static const char * const sym_regex_kernel[S_NSYMTYPES] = { /* * Following symbols have been audited. Don't warn user about * absolute relocations present w.r.t these symbols. */ + [S_ABS] = + "^(__.*_len|__.*_dwords)$", -/* True absolute relocations */ +/* + * These symbols are known to be relative, even if the linker marks them + * as absolute (typically defined outside any section in the linker script.) + */ + [S_REL] = + "^(__.*_start|__.*_end|_end|_[se](text|data))$", +}; -static const char safe_abs_regex[] = -"^(__.*_len|__.*_dwords)$"; -static regex_t safe_abs_regex_c; -static int is_safe_abs_reloc(const char *sym_name) -{ - return !regexec(&safe_abs_regex_c, sym_name, 0, NULL, 0); -} +static const char * const sym_regex_realmode[S_NSYMTYPES] = { +/* + * These are 16-bit segment symbols when compiling 16-bit code. + */ + [S_SEG] = + "^real_mode_seg$", -/* These are relative even though the linker marks them absolute */ +/* + * These are offsets belonging to segments, as opposed to linear addresses, + * when compiling 16-bit code. + */ + [S_LIN] = + "^pa_", +}; -static const char safe_rel_regex[] = -"^(__.*_start|__.*_end|_end|_[se](text|data))$"; -static regex_t safe_rel_regex_c; +static const char * const *sym_regex; -static int is_safe_rel_reloc(const char *sym_name) +static regex_t sym_regex_c[S_NSYMTYPES]; +static int is_reloc(enum symtype type, const char *sym_name) { - return !regexec(&safe_rel_regex_c, sym_name, 0, NULL, 0); + return sym_regex[type] && + !regexec(&sym_regex_c[type], sym_name, 0, NULL, 0); } -static void regex_init(void) +static void regex_init(int use_real_mode) { - char errbuf[128]; - int err; + char errbuf[128]; + int err; + int i; - err = regcomp(&safe_abs_regex_c, safe_abs_regex, - REG_EXTENDED|REG_NOSUB); - if (err) { - regerror(err, &safe_abs_regex_c, errbuf, sizeof errbuf); - die("%s", errbuf); - } + if (use_real_mode) + sym_regex = sym_regex_realmode; + else + sym_regex = sym_regex_kernel; - err = regcomp(&safe_rel_regex_c, safe_rel_regex, - REG_EXTENDED|REG_NOSUB); - if (err) { - regerror(err, &safe_rel_regex_c, errbuf, sizeof errbuf); - die("%s", errbuf); - } + for (i = 0; i < S_NSYMTYPES; i++) { + if (!sym_regex[i]) + continue; + + err = regcomp(&sym_regex_c[i], sym_regex[i], + REG_EXTENDED|REG_NOSUB); + + if (err) { + regerror(err, &sym_regex_c[i], errbuf, sizeof errbuf); + die("%s", errbuf); + } + } +} + +static void die(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + exit(1); } static const char *sym_type(unsigned type) @@ -153,13 +184,16 @@ static const char *rel_type(unsigned type) REL_TYPE(R_386_RELATIVE), REL_TYPE(R_386_GOTOFF), REL_TYPE(R_386_GOTPC), + REL_TYPE(R_386_8), + REL_TYPE(R_386_PC8), + REL_TYPE(R_386_16), + REL_TYPE(R_386_PC16), #undef REL_TYPE }; - const char *name = NULL; - if (type < ARRAY_SIZE(type_name)) + const char *name = "unknown type rel type name"; + if (type < ARRAY_SIZE(type_name) && type_name[type]) { name = type_name[type]; - if (!name) - name = "unknown"; + } return name; } @@ -189,7 +223,7 @@ static const char *sym_name(const char *sym_strtab, Elf32_Sym *sym) name = sym_strtab + sym->st_name; } else { - name = sec_name(secs[sym->st_shndx].shdr.sh_name); + name = sec_name(sym->st_shndx); } return name; } @@ -428,7 +462,7 @@ static void print_absolute_symbols(void) printf("\n"); } -static int print_absolute_relocs(FILE *f) +static void print_absolute_relocs(void) { int i, printed = 0; @@ -472,17 +506,18 @@ static int print_absolute_relocs(FILE *f) * Before warning check if this absolute symbol * relocation is harmless. */ - if (is_safe_abs_reloc(name) || - is_safe_rel_reloc(name)) + if (is_reloc(S_ABS, name) || is_reloc(S_REL, name)) continue; if (!printed) { - fprintf(f, "Unknown absolute relocations present\n"); - fprintf(f, "Offset Info Type Sym.Value Sym.Name\n"); + printf("WARNING: Absolute relocations" + " present\n"); + printf("Offset Info Type Sym.Value " + "Sym.Name\n"); printed = 1; } - fprintf(f, "%08x %08x %10s %08x %s\n", + printf("%08x %08x %10s %08x %s\n", rel->r_offset, rel->r_info, rel_type(ELF32_R_TYPE(rel->r_info)), @@ -492,12 +527,11 @@ static int print_absolute_relocs(FILE *f) } if (printed) - fputc('\n', f); - - return printed; + printf("\n"); } -static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym)) +static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym), + int use_real_mode) { int i; /* Walk through the relocations */ @@ -522,31 +556,71 @@ static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym)) Elf32_Rel *rel; Elf32_Sym *sym; unsigned r_type; + const char *symname; + int shn_abs; + rel = &sec->reltab[j]; sym = &sh_symtab[ELF32_R_SYM(rel->r_info)]; r_type = ELF32_R_TYPE(rel->r_info); - /* Don't visit relocations to absolute symbols */ - if (sym->st_shndx == SHN_ABS && - !is_safe_rel_reloc(sym_name(sym_strtab, sym))) - continue; + + shn_abs = sym->st_shndx == SHN_ABS; switch (r_type) { case R_386_NONE: case R_386_PC32: + case R_386_PC16: + case R_386_PC8: case R_386_GOTPC: case R_386_GOTOFF: case R_386_GOT32: case R_386_PLT32: - /* Relative relocations don't need to - be adjusted */ + /* + * NONE can be ignored and and PC relative + * relocations don't need to be adjusted. + */ break; + + case R_386_16: + symname = sym_name(sym_strtab, sym); + if (!use_real_mode) + goto bad; + if (shn_abs) { + if (is_reloc(S_ABS, symname)) + break; + else if (!is_reloc(S_SEG, symname)) + goto bad; + } else { + if (is_reloc(S_LIN, symname)) + goto bad; + else + break; + } + visit(rel, sym); + break; + case R_386_32: - /* Visit relocations that need adjustment */ + symname = sym_name(sym_strtab, sym); + if (shn_abs) { + if (is_reloc(S_ABS, symname)) + break; + else if (!is_reloc(S_REL, symname)) + goto bad; + } else { + if (use_real_mode && + !is_reloc(S_LIN, symname)) + break; + } visit(rel, sym); break; default: die("Unsupported relocation type: %s (%d)\n", rel_type(r_type), r_type); + break; + bad: + symname = sym_name(sym_strtab, sym); + die("Invalid %s %s relocation: %s\n", + shn_abs ? "absolute" : "relative", + rel_type(r_type), symname); } } } @@ -554,8 +628,12 @@ static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym)) static void count_reloc(Elf32_Rel *rel, Elf32_Sym *sym) { - (void)rel; (void)sym; - reloc_count += 1; + (void)sym; + + if (ELF32_R_TYPE(rel->r_info) == R_386_16) + reloc16_count++; + else + reloc_count++; } static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym) @@ -563,7 +641,10 @@ static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym) (void)sym; /* Remember the address that needs to be adjusted. */ - relocs[reloc_idx++] = rel->r_offset; + if (ELF32_R_TYPE(rel->r_info) == R_386_16) + relocs16[reloc16_idx++] = rel->r_offset; + else + relocs[reloc_idx++] = rel->r_offset; } static int cmp_relocs(const void *va, const void *vb) @@ -573,23 +654,41 @@ static int cmp_relocs(const void *va, const void *vb) return (*a == *b)? 0 : (*a > *b)? 1 : -1; } -static void emit_relocs(int as_text) +static int write32(unsigned int v, FILE *f) +{ + unsigned char buf[4]; + + put_unaligned_le32(v, buf); + return fwrite(buf, 1, 4, f) == 4 ? 0 : -1; +} + +static void emit_relocs(int as_text, int use_real_mode) { int i; /* Count how many relocations I have and allocate space for them. */ reloc_count = 0; - walk_relocs(count_reloc); + walk_relocs(count_reloc, use_real_mode); relocs = malloc(reloc_count * sizeof(relocs[0])); if (!relocs) { die("malloc of %d entries for relocs failed\n", reloc_count); } + + relocs16 = malloc(reloc16_count * sizeof(relocs[0])); + if (!relocs16) { + die("malloc of %d entries for relocs16 failed\n", + reloc16_count); + } /* Collect up the relocations */ reloc_idx = 0; - walk_relocs(collect_reloc); + walk_relocs(collect_reloc, use_real_mode); + + if (reloc16_count && !use_real_mode) + die("Segment relocations found but --realmode not specified\n"); /* Order the relocations for more efficient processing */ qsort(relocs, reloc_count, sizeof(relocs[0]), cmp_relocs); + qsort(relocs16, reloc16_count, sizeof(relocs16[0]), cmp_relocs); /* Print the relocations */ if (as_text) { @@ -598,61 +697,83 @@ static void emit_relocs(int as_text) */ printf(".section \".data.reloc\",\"a\"\n"); printf(".balign 4\n"); - for (i = 0; i < reloc_count; i++) { - printf("\t .long 0x%08lx\n", relocs[i]); + if (use_real_mode) { + printf("\t.long %lu\n", reloc16_count); + for (i = 0; i < reloc16_count; i++) + printf("\t.long 0x%08lx\n", relocs16[i]); + printf("\t.long %lu\n", reloc_count); + for (i = 0; i < reloc_count; i++) { + printf("\t.long 0x%08lx\n", relocs[i]); + } + } else { + for (i = 0; i < reloc_count; i++) { + printf("\t.long 0x%08lx\n", relocs[i]); + } + /* Print a stop */ + printf("\t.long 0x%08lx\n", (unsigned long)0); } + printf("\n"); } else { - unsigned char buf[4]; - /* Now print each relocation */ - for (i = 0; i < reloc_count; i++) { - buf[0] = (relocs[i] >> 0) & 0xff; - buf[1] = (relocs[i] >> 8) & 0xff; - buf[2] = (relocs[i] >> 16) & 0xff; - buf[3] = (relocs[i] >> 24) & 0xff; - fwrite(buf, 4, 1, stdout); + if (use_real_mode) { + write32(reloc16_count, stdout); + for (i = 0; i < reloc16_count; i++) + write32(relocs16[i], stdout); + write32(reloc_count, stdout); + + /* Now print each relocation */ + for (i = 0; i < reloc_count; i++) + write32(relocs[i], stdout); + } else { + /* Now print each relocation */ + for (i = 0; i < reloc_count; i++) { + write32(relocs[i], stdout); + } + + /* Print a stop */ + write32(0, stdout); } - /* Print a stop */ - memset(buf, 0, sizeof buf); - fwrite(buf, 4, 1, stdout); } } static void usage(void) { - die("relocs [--abs-syms |--abs-relocs | --text] vmlinux\n"); + die("relocs [--abs-syms|--abs-relocs|--text|--realmode] vmlinux\n"); } int main(int argc, char **argv) { int show_absolute_syms, show_absolute_relocs; - int as_text; + int as_text, use_real_mode; const char *fname; FILE *fp; int i; - int err = 0; show_absolute_syms = 0; show_absolute_relocs = 0; as_text = 0; + use_real_mode = 0; fname = NULL; for (i = 1; i < argc; i++) { char *arg = argv[i]; if (*arg == '-') { - if (strcmp(argv[1], "--abs-syms") == 0) { + if (strcmp(arg, "--abs-syms") == 0) { show_absolute_syms = 1; continue; } - - if (strcmp(argv[1], "--abs-relocs") == 0) { + if (strcmp(arg, "--abs-relocs") == 0) { show_absolute_relocs = 1; continue; } - else if (strcmp(argv[1], "--text") == 0) { + if (strcmp(arg, "--text") == 0) { as_text = 1; continue; } + if (strcmp(arg, "--realmode") == 0) { + use_real_mode = 1; + continue; + } } else if (!fname) { fname = arg; @@ -663,10 +784,7 @@ int main(int argc, char **argv) if (!fname) { usage(); } - - - regex_init(); - + regex_init(use_real_mode); fp = fopen(fname, "r"); if (!fp) { die("Cannot open %s: %s\n", @@ -682,10 +800,9 @@ int main(int argc, char **argv) return 0; } if (show_absolute_relocs) { - print_absolute_relocs(stdout); + print_absolute_relocs(); return 0; } - err = print_absolute_relocs(stderr); - emit_relocs(as_text); - return err; + emit_relocs(as_text, use_real_mode); + return 0; } diff --git a/core/bootsect.inc b/core/bootsect.inc index 6c204096..9e47e1a5 100644 --- a/core/bootsect.inc +++ b/core/bootsect.inc @@ -1,7 +1,7 @@ ;; ----------------------------------------------------------------------- ;; ;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved -;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; Copyright 2009-2012 Intel Corporation; author: H. Peter Anvin ;; ;; 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 @@ -38,6 +38,7 @@ SuperSize equ $+1 load_bootsec: mov edi,free_high_memory mov [trackbuf+4],edi ; Copy from this address + mov eax,0xA0000 ; Maximum load xor dx,dx ; No padding mov bx,abort_check ; Don't print dots, but allow abort call load_high @@ -45,6 +46,9 @@ load_bootsec: sub edi,free_high_memory mov [trackbuf+8],edi ; Save length + cmp edi,0xA0000-7C00h + ja bs_too_big + mov eax,7C00h ; Entry point mov [trackbuf],eax ; Copy to this address @@ -237,3 +241,13 @@ replace_stub: .csip equ $-4 section .text16 +bs_too_big: + call close + mov si,err_bs_too_big + jmp abort_load + + section .data16 +err_bs_too_big db "Too large for a bootstrap (need LINUX instead of KERNEL?)" + db CR, LF, 0 + + section .text16 diff --git a/core/comboot.inc b/core/comboot.inc index d6f670c9..61c8a3b7 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -651,21 +651,8 @@ comapi_cleanup: ret ; -; INT 22h AX=000Dh Clean up then replace bootstrap +; INT 22h AX=000Dh Obsolete ; -comapi_chainboot: - call comapi_cleanup - mov eax,P_EDI - mov [trackbuf+4],eax ; Copy from - mov eax,P_ECX - mov [trackbuf+8],eax ; Total bytes - mov eax,7C00h - mov [trackbuf],eax ; Copy to - push eax ; Entry point on stack - mov esi,P_ESI - mov edx,P_EBX - mov bx,P_DS - jmp replace_bootstrap_one ; ; INT 22h AX=000Eh Get configuration file name diff --git a/core/fs/btrfs/btrfs.c b/core/fs/btrfs/btrfs.c index b6a14e3b..aeb7614a 100644 --- a/core/fs/btrfs/btrfs.c +++ b/core/fs/btrfs/btrfs.c @@ -602,12 +602,15 @@ static void btrfs_get_fs_tree(struct fs_info *fs) do { do { struct btrfs_root_ref *ref; + int pathlen; if (btrfs_comp_keys_type(&search_key, &path.item.key)) break; ref = (struct btrfs_root_ref *)path.data; - if (!strcmp((char*)(ref + 1), SubvolName)) { + pathlen = path.item.size - sizeof(struct btrfs_root_ref); + + if (!strncmp((char*)(ref + 1), SubvolName, pathlen)) { subvol_ok = true; break; } diff --git a/core/fs/cache.c b/core/fs/cache.c index 0d7891be..3b21fc26 100644 --- a/core/fs/cache.c +++ b/core/fs/cache.c @@ -37,10 +37,10 @@ void cache_init(struct device *dev, int block_size_shift) dev->cache_head = head = (struct cache *) (data + (dev->cache_entries << block_size_shift)); - cache = dev->cache_head + 1; /* First cache descriptor */ + cache = head + 1; /* First cache descriptor */ head->prev = &cache[dev->cache_entries-1]; - head->next->prev = dev->cache_head; + head->prev->next = head; head->block = -1; head->data = NULL; diff --git a/core/fs/chdir.c b/core/fs/chdir.c index 9e8dfd2e..903cabce 100644 --- a/core/fs/chdir.c +++ b/core/fs/chdir.c @@ -1,6 +1,7 @@ #include <stdio.h> #include <stdbool.h> #include <string.h> +#include <dprintf.h> #include "fs.h" #include "cache.h" @@ -16,57 +17,70 @@ void pm_realpath(com32sys_t *regs) realpath(dst, src, FILENAME_MAX); } -#define EMIT(x) \ -do { \ - if (++n < bufsize) \ - *q++ = (x); \ -} while (0) - -static size_t join_paths(char *dst, size_t bufsize, - const char *s1, const char *s2) +static size_t copy_string(char *buf, size_t ix, size_t bufsize, const char *src) { - const char *list[2]; - int i; char c; - const char *p; - char *q = dst; - size_t n = 0; - bool slash = false; - - list[0] = s1; - list[1] = s2; - - for (i = 0; i < 2; i++) { - p = list[i]; - - while ((c = *p++)) { - if (c == '/') { - if (!slash) - EMIT(c); - slash = true; - } else { - EMIT(c); - slash = false; - } - } + + while ((c = *src++)) { + if (ix+1 < bufsize) + buf[ix] = c; + ix++; } - if (bufsize) - *q = '\0'; + if (ix < bufsize) + buf[ix] = '\0'; - return n; + return ix; +} + +static size_t generic_inode_to_path(struct inode *inode, char *dst, size_t bufsize) +{ + size_t s = 0; + + dprintf("inode %p name %s\n", inode, inode->name); + + if (inode->parent) { + if (!inode->name) /* Only the root should have no name */ + return -1; + + s = generic_inode_to_path(inode->parent, dst, bufsize); + if (s == (size_t)-1) + return s; /* Error! */ + + s = copy_string(dst, s, bufsize, "/"); + s = copy_string(dst, s, bufsize, inode->name); + } + + return s; } size_t realpath(char *dst, const char *src, size_t bufsize) { + int rv; + struct file *file; + size_t s; + + dprintf("realpath: input: %s\n", src); + if (this_fs->fs_ops->realpath) { - return this_fs->fs_ops->realpath(this_fs, dst, src, bufsize); + s = this_fs->fs_ops->realpath(this_fs, dst, src, bufsize); } else { - /* Filesystems with "common" pathname resolution */ - return join_paths(dst, bufsize, - src[0] == '/' ? "" : this_fs->cwd_name, - src); + rv = searchdir(src); + if (rv < 0) { + dprintf("realpath: searchpath failure\n"); + return -1; + } + + file = handle_to_file(rv); + s = generic_inode_to_path(file->inode, dst, bufsize); + if (s == 0) + s = copy_string(dst, 0, bufsize, "/"); + + _close_file(file); } + + dprintf("realpath: output: %s\n", dst); + return s; } int chdir(const char *src) @@ -74,6 +88,10 @@ int chdir(const char *src) int rv; struct file *file; char cwd_buf[CURRENTDIR_MAX]; + size_t s; + + dprintf("chdir: from %s (inode %p) add %s\n", + this_fs->cwd_name, this_fs->cwd, src); if (this_fs->fs_ops->chdir) return this_fs->fs_ops->chdir(this_fs, src); @@ -94,10 +112,20 @@ int chdir(const char *src) _close_file(file); /* Save the current working directory */ - realpath(cwd_buf, src, CURRENTDIR_MAX); + s = generic_inode_to_path(this_fs->cwd, cwd_buf, CURRENTDIR_MAX-1); /* Make sure the cwd_name ends in a slash, it's supposed to be a prefix */ - join_paths(this_fs->cwd_name, CURRENTDIR_MAX, cwd_buf, "/"); + if (s < 1 || cwd_buf[s-1] != '/') + cwd_buf[s++] = '/'; + + if (s >= CURRENTDIR_MAX) + s = CURRENTDIR_MAX - 1; + + cwd_buf[s++] = '\0'; + memcpy(this_fs->cwd_name, cwd_buf, s); + + dprintf("chdir: final %s (inode %p)\n", + this_fs->cwd_name, this_fs->cwd); return 0; } diff --git a/core/fs/ext2/ext2.c b/core/fs/ext2/ext2.c index 716670c6..7988faaf 100644 --- a/core/fs/ext2/ext2.c +++ b/core/fs/ext2/ext2.c @@ -164,6 +164,9 @@ static struct inode *ext2_iget_by_inr(struct fs_info *fs, uint32_t inr) struct inode *inode; e_inode = ext2_get_inode(fs, inr); + if (!e_inode) + return NULL; + if (!(inode = alloc_inode(fs, inr, sizeof(struct ext2_pvt_inode)))) return NULL; fill_inode(inode, e_inode); diff --git a/core/fs/fat/fat.c b/core/fs/fat/fat.c index d3079269..b08923cf 100644 --- a/core/fs/fat/fat.c +++ b/core/fs/fat/fat.c @@ -220,24 +220,30 @@ static sector_t next_sector(struct file *file) return sector; } -/* - * Mangle a filename pointed to by src into a buffer pointed to by dst; - * ends on encountering any whitespace. +/** + * mangle_name: + * + * Mangle a filename pointed to by src into a buffer pointed + * to by dst; ends on encountering any whitespace. + * dst is preserved. + * + * This verifies that a filename is < FILENAME_MAX characters, + * doesn't contain whitespace, zero-pads the output buffer, + * and removes redundant slashes. + * + * Unlike the generic version, this also converts backslashes to + * forward slashes. * */ static void vfat_mangle_name(char *dst, const char *src) { char *p = dst; + int i = FILENAME_MAX-1; char c; - int i = FILENAME_MAX -1; - /* - * Copy the filename, converting backslash to slash and - * collapsing duplicate separators. - */ while (not_whitespace(c = *src)) { - if (c == '\\') - c = '/'; + if (c == '\\') + c = '/'; if (c == '/') { if (src[1] == '/' || src[1] == '\\') { @@ -250,16 +256,13 @@ static void vfat_mangle_name(char *dst, const char *src) *dst++ = *src++; } - /* Strip terminal slashes or whitespace */ while (1) { if (dst == p) break; - if (*(dst-1) == '/' && dst-1 == p) /* it's the '/' case */ - break; - if (dst-2 == p && *(dst-2) == '.' && *(dst-1) == '.' ) /* the '..' case */ - break; - if ((*(dst-1) != '/') && (*(dst-1) != '.')) + if (dst[-1] != '/') break; + if ((dst[-1] == '/') && ((dst - 1) == p)) + break; dst--; i++; diff --git a/core/fs/fs.c b/core/fs/fs.c index ad2fb370..21f5dba0 100644 --- a/core/fs/fs.c +++ b/core/fs/fs.c @@ -37,6 +37,8 @@ void put_inode(struct inode *inode) while (inode && --inode->refcnt == 0) { struct inode *dead = inode; inode = inode->parent; + if (dead->name) + free((char *)dead->name); free(dead); } } @@ -207,6 +209,9 @@ int searchdir(const char *name) char *part, *p, echar; int symlink_count = MAX_SYMLINK_CNT; + dprintf("searchdir: %s root: %p cwd: %p\n", + name, this_fs->root, this_fs->cwd); + if (!(file = alloc_file())) goto err_no_close; file->fs = this_fs; @@ -305,6 +310,9 @@ int searchdir(const char *name) goto got_link; } + inode->name = strdup(part); + dprintf("path component: %s\n", inode->name); + inode->parent = parent; parent = NULL; @@ -349,6 +357,8 @@ int open_file(const char *name, struct com32_filedata *filedata) struct file *file; char mangled_name[FILENAME_MAX]; + dprintf("open_file %s\n", name); + mangle_name(mangled_name, name); rv = searchdir(mangled_name); @@ -376,6 +386,8 @@ void pm_open_file(com32sys_t *regs) const char *name = MK_PTR(regs->es, regs->esi.w[0]); char mangled_name[FILENAME_MAX]; + dprintf("pm_open_file %s\n", name); + mangle_name(mangled_name, name); rv = searchdir(mangled_name); if (rv < 0) { @@ -470,6 +482,7 @@ void fs_init(com32sys_t *regs) if (fs.fs_ops->iget_root) { fs.root = fs.fs_ops->iget_root(&fs); fs.cwd = get_inode(fs.root); + dprintf("init: root inode %p, cwd inode %p\n", fs.root, fs.cwd); } SectorShift = fs.sector_shift; diff --git a/core/fs/lib/searchconfig.c b/core/fs/lib/searchconfig.c index 24bfde31..f18836a8 100644 --- a/core/fs/lib/searchconfig.c +++ b/core/fs/lib/searchconfig.c @@ -25,7 +25,8 @@ int search_config(const char *search_directories[], const char *filenames[]) "%s%s%s", sd, (*sd && sd[strlen(sd)-1] == '/') ? "" : "/", sf); - realpath(ConfigName, confignamebuf, FILENAME_MAX); + if (realpath(ConfigName, confignamebuf, FILENAME_MAX) == (size_t)-1) + continue; regs.edi.w[0] = OFFS_WRT(ConfigName, 0); dprintf("Config search: %s\n", ConfigName); call16(core_open, ®s, ®s); diff --git a/core/fs/ntfs/ntfs.c b/core/fs/ntfs/ntfs.c new file mode 100644 index 00000000..500d0fd3 --- /dev/null +++ b/core/fs/ntfs/ntfs.c @@ -0,0 +1,1388 @@ +/* + * Copyright (C) 2011-2012 Paulo Alcantara <pcacjr@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* Note: No support for compressed files */ + +#include <dprintf.h> +#include <stdio.h> +#include <string.h> +#include <sys/dirent.h> +#include <cache.h> +#include <core.h> +#include <disk.h> +#include <fs.h> +#include <ilog2.h> +#include <klibc/compiler.h> +#include <ctype.h> + +#include "codepage.h" +#include "ntfs.h" +#include "runlist.h" + +static struct ntfs_readdir_state *readdir_state; + +/*** Function declarations */ +static f_mft_record_lookup ntfs_mft_record_lookup_3_0; +static f_mft_record_lookup ntfs_mft_record_lookup_3_1; + +/*** Function definitions */ + +/* Check if there are specific zero fields in an NTFS boot sector */ +static inline int ntfs_check_zero_fields(const struct ntfs_bpb *sb) +{ + return !sb->res_sectors && (!sb->zero_0[0] && !sb->zero_0[1] && + !sb->zero_0[2]) && !sb->zero_1 && !sb->zero_2 && + !sb->zero_3; +} + +static inline int ntfs_check_sb_fields(const struct ntfs_bpb *sb) +{ + return ntfs_check_zero_fields(sb) && + (!memcmp(sb->oem_name, "NTFS ", 8) || + !memcmp(sb->oem_name, "MSWIN4.0", 8) || + !memcmp(sb->oem_name, "MSWIN4.1", 8)); +} + +static inline struct inode *new_ntfs_inode(struct fs_info *fs) +{ + struct inode *inode; + + inode = alloc_inode(fs, 0, sizeof(struct ntfs_inode)); + if (!inode) + malloc_error("inode structure"); + + return inode; +} + +static void ntfs_fixups_writeback(struct fs_info *fs, struct ntfs_record *nrec) +{ + uint16_t *usa; + uint16_t usa_no; + uint16_t usa_count; + uint16_t *blk; + + dprintf("in %s()\n", __func__); + + if (nrec->magic != NTFS_MAGIC_FILE && nrec->magic != NTFS_MAGIC_INDX) + return; + + /* get the Update Sequence Array offset */ + usa = (uint16_t *)((uint8_t *)nrec + nrec->usa_ofs); + /* get the Update Sequence Array Number and skip it */ + usa_no = *usa++; + /* get the Update Sequene Array count */ + usa_count = nrec->usa_count - 1; /* exclude the USA number */ + /* make it to point to the last two bytes of the RECORD's first sector */ + blk = (uint16_t *)((uint8_t *)nrec + SECTOR_SIZE(fs) - 2); + + while (usa_count--) { + if (*blk != usa_no) + break; + + *blk = *usa++; + blk = (uint16_t *)((uint8_t *)blk + SECTOR_SIZE(fs)); + } +} + +/* read content from cache */ +static int ntfs_read(struct fs_info *fs, void *buf, size_t len, uint64_t count, + block_t *blk, uint64_t *blk_offset, + uint64_t *blk_next_offset, uint64_t *lcn) +{ + uint8_t *data; + uint64_t offset = *blk_offset; + const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift; + const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs); + uint64_t bytes; + uint64_t lbytes; + uint64_t loffset; + uint64_t k; + + dprintf("in %s()\n", __func__); + + if (count > len) + goto out; + + data = (uint8_t *)get_cache(fs->fs_dev, *blk); + if (!data) + goto out; + + if (!offset) + offset = (*lcn << clust_byte_shift) % blk_size; + + dprintf("LCN: 0x%X\n", *lcn); + dprintf("offset: 0x%X\n", offset); + + bytes = count; /* bytes to copy */ + lbytes = blk_size - offset; /* bytes left to copy */ + if (lbytes >= bytes) { + /* so there's room enough, then copy the whole content */ + memcpy(buf, data + offset, bytes); + loffset = offset; + offset += count; + } else { + dprintf("bytes: %u\n", bytes); + dprintf("bytes left: %u\n", lbytes); + /* otherwise, let's copy it partially... */ + k = 0; + while (bytes) { + memcpy(buf + k, data + offset, lbytes); + bytes -= lbytes; + loffset = offset; + offset += lbytes; + k += lbytes; + if (offset >= blk_size) { + /* then fetch a new FS block */ + data = (uint8_t *)get_cache(fs->fs_dev, ++*blk); + if (!data) + goto out; + + lbytes = bytes; + loffset = offset; + offset = 0; + } + } + } + + if (loffset >= blk_size) + loffset = 0; /* it must be aligned on a block boundary */ + + *blk_offset = loffset; + + if (blk_next_offset) + *blk_next_offset = offset; + + *lcn += blk_size / count; /* update LCN */ + + return 0; + +out: + return -1; +} + +static struct ntfs_mft_record *ntfs_mft_record_lookup_3_0(struct fs_info *fs, + uint32_t file, block_t *blk) +{ + const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size; + uint8_t *buf; + const block_t mft_blk = NTFS_SB(fs)->mft_blk; + block_t cur_blk; + block_t right_blk; + uint64_t offset; + uint64_t next_offset; + const uint32_t mft_record_shift = ilog2(mft_record_size); + const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift; + uint64_t lcn; + int err; + struct ntfs_mft_record *mrec; + + dprintf("in %s()\n", __func__); + + buf = (uint8_t *)malloc(mft_record_size); + if (!buf) + malloc_error("uint8_t *"); + + /* determine MFT record's LCN and block number */ + lcn = NTFS_SB(fs)->mft_lcn + (file << mft_record_shift >> clust_byte_shift); + cur_blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs)) - mft_blk; + offset = (file << mft_record_shift) % BLOCK_SIZE(fs); + for (;;) { + right_blk = cur_blk + mft_blk; + err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &right_blk, + &offset, &next_offset, &lcn); + if (err) { + printf("Error while reading from cache.\n"); + break; + } + + ntfs_fixups_writeback(fs, (struct ntfs_record *)buf); + + mrec = (struct ntfs_mft_record *)buf; + /* check if it has a valid magic number */ + if (mrec->magic == NTFS_MAGIC_FILE) { + if (blk) + *blk = cur_blk; /* update record starting block */ + + return mrec; /* found MFT record */ + } + + if (next_offset >= BLOCK_SIZE(fs)) { + /* try the next FS block */ + offset = 0; + cur_blk = right_blk - mft_blk + 1; + } else { + /* there's still content to fetch in the current block */ + cur_blk = right_blk - mft_blk; + offset = next_offset; /* update FS block offset */ + } + } + + free(buf); + + return NULL; +} + +static struct ntfs_mft_record *ntfs_mft_record_lookup_3_1(struct fs_info *fs, + uint32_t file, block_t *blk) +{ + const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size; + uint8_t *buf; + const block_t mft_blk = NTFS_SB(fs)->mft_blk; + block_t cur_blk; + block_t right_blk; + uint64_t offset; + uint64_t next_offset; + const uint32_t mft_record_shift = ilog2(mft_record_size); + const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift; + uint64_t lcn; + int err; + struct ntfs_mft_record *mrec; + + dprintf("in %s()\n", __func__); + + buf = (uint8_t *)malloc(mft_record_size); + if (!buf) + malloc_error("uint8_t *"); + + lcn = NTFS_SB(fs)->mft_lcn + (file << mft_record_shift >> clust_byte_shift); + cur_blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs)) - mft_blk; + offset = (file << mft_record_shift) % BLOCK_SIZE(fs); + for (;;) { + right_blk = cur_blk + NTFS_SB(fs)->mft_blk; + err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &right_blk, + &offset, &next_offset, &lcn); + if (err) { + printf("Error while reading from cache.\n"); + break; + } + + ntfs_fixups_writeback(fs, (struct ntfs_record *)buf); + + mrec = (struct ntfs_mft_record *)buf; + /* Check if the NTFS 3.1 MFT record number matches */ + if (mrec->magic == NTFS_MAGIC_FILE && mrec->mft_record_no == file) { + if (blk) + *blk = cur_blk; /* update record starting block */ + + return mrec; /* found MFT record */ + } + + if (next_offset >= BLOCK_SIZE(fs)) { + /* try the next FS block */ + offset = 0; + cur_blk = right_blk - NTFS_SB(fs)->mft_blk + 1; + } else { + /* there's still content to fetch in the current block */ + cur_blk = right_blk - NTFS_SB(fs)->mft_blk; + offset = next_offset; /* update FS block offset */ + } + } + + free(buf); + + return NULL; +} + +static bool ntfs_filename_cmp(const char *dname, struct ntfs_idx_entry *ie) +{ + const uint16_t *entry_fn; + uint8_t entry_fn_len; + unsigned i; + + dprintf("in %s()\n", __func__); + + entry_fn = ie->key.file_name.file_name; + entry_fn_len = ie->key.file_name.file_name_len; + + if (strlen(dname) != entry_fn_len) + return false; + + /* Do case-sensitive compares for Posix file names */ + if (ie->key.file_name.file_name_type == FILE_NAME_POSIX) { + for (i = 0; i < entry_fn_len; i++) + if (entry_fn[i] != dname[i]) + return false; + } else { + for (i = 0; i < entry_fn_len; i++) + if (tolower(entry_fn[i]) != tolower(dname[i])) + return false; + } + + return true; +} + +static inline uint8_t *mapping_chunk_init(struct ntfs_attr_record *attr, + struct mapping_chunk *chunk, + uint32_t *offset) +{ + memset(chunk, 0, sizeof *chunk); + *offset = 0U; + + return (uint8_t *)attr + attr->data.non_resident.mapping_pairs_offset; +} + +/* Parse data runs. + * + * return 0 on success or -1 on failure. + */ +static int parse_data_run(const void *stream, uint32_t *offset, + uint8_t *attr_len, struct mapping_chunk *chunk) +{ + uint8_t *buf; /* Pointer to the zero-terminated byte stream */ + uint8_t count; /* The count byte */ + uint8_t v, l; /* v is the number of changed low-order VCN bytes; + * l is the number of changed low-order LCN bytes + */ + uint8_t *byte; + int byte_shift = 8; + int mask; + uint8_t val; + int64_t res; + + (void)attr_len; + + dprintf("in %s()\n", __func__); + + chunk->flags &= ~MAP_MASK; + + buf = (uint8_t *)stream + *offset; + if (buf > attr_len || !*buf) { + chunk->flags |= MAP_END; /* we're done */ + return 0; + } + + if (!*offset) + chunk->flags |= MAP_START; /* initial chunk */ + + count = *buf; + v = count & 0x0F; + l = count >> 4; + + if (v > 8 || l > 8) /* more than 8 bytes ? */ + goto out; + + byte = (uint8_t *)buf + v; + count = v; + + res = 0LL; + while (count--) { + val = *byte--; + mask = val >> (byte_shift - 1); + res = (res << byte_shift) | ((val + mask) ^ mask); + } + + chunk->len = res; /* get length data */ + + byte = (uint8_t *)buf + v + l; + count = l; + + mask = 0xFFFFFFFF; + res = 0LL; + if (*byte & 0x80) + res |= (int64_t)mask; /* sign-extend it */ + + while (count--) + res = (res << byte_shift) | *byte--; + + chunk->lcn += res; + /* are VCNS from cur_vcn to next_vcn - 1 unallocated ? */ + if (!chunk->lcn) + chunk->flags |= MAP_UNALLOCATED; + else + chunk->flags |= MAP_ALLOCATED; + + *offset += v + l + 1; + + return 0; + +out: + return -1; +} + +static struct ntfs_mft_record * +ntfs_attr_list_lookup(struct fs_info *fs, struct ntfs_attr_record *attr, + uint32_t type, struct ntfs_mft_record *mrec) +{ + uint8_t *attr_len; + struct mapping_chunk chunk; + uint32_t offset; + uint8_t *stream; + int err; + const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs); + uint8_t buf[blk_size]; + uint64_t blk_offset; + int64_t vcn; + int64_t lcn; + int64_t last_lcn; + block_t blk; + struct ntfs_attr_list_entry *attr_entry; + uint32_t len = 0; + struct ntfs_mft_record *retval; + uint64_t start_blk = 0; + + dprintf("in %s()\n", __func__); + + if (attr->non_resident) + goto handle_non_resident_attr; + + attr_entry = (struct ntfs_attr_list_entry *) + ((uint8_t *)attr + attr->data.resident.value_offset); + len = attr->data.resident.value_len; + for (; (uint8_t *)attr_entry < (uint8_t *)attr + len; + attr_entry = (struct ntfs_attr_list_entry *)((uint8_t *)attr_entry + + attr_entry->length)) { + dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%X\n", + attr_entry->type); + if (attr_entry->type == type) + goto found; /* We got the attribute! :-) */ + } + + printf("No attribute found.\n"); + goto out; + +handle_non_resident_attr: + attr_len = (uint8_t *)attr + attr->len; + stream = mapping_chunk_init(attr, &chunk, &offset); + do { + err = parse_data_run(stream, &offset, attr_len, &chunk); + if (err) { + printf("parse_data_run()\n"); + goto out; + } + + if (chunk.flags & MAP_UNALLOCATED) + continue; + if (chunk.flags & MAP_END) + break; + if (chunk.flags & MAP_ALLOCATED) { + vcn = 0; + lcn = chunk.lcn; + while (vcn < chunk.len) { + blk = (lcn + vcn) << NTFS_SB(fs)->clust_byte_shift >> + BLOCK_SHIFT(fs); + blk_offset = 0; + last_lcn = lcn; + lcn += vcn; + err = ntfs_read(fs, buf, blk_size, blk_size, &blk, + &blk_offset, NULL, (uint64_t *)&lcn); + if (err) { + printf("Error while reading from cache.\n"); + goto out; + } + + attr_entry = (struct ntfs_attr_list_entry *)&buf; + len = attr->data.non_resident.data_size; + for (; (uint8_t *)attr_entry < (uint8_t *)&buf[0] + len; + attr_entry = (struct ntfs_attr_list_entry *) + ((uint8_t *)attr_entry + attr_entry->length)) { + dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%x\n", + attr_entry->type); + if (attr_entry->type == type) + goto found; /* We got the attribute! :-) */ + } + + lcn = last_lcn; /* restore original LCN */ + /* go to the next VCN */ + vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift)); + } + } + } while (!(chunk.flags & MAP_END)); + + printf("No attribute found.\n"); + +out: + return NULL; + +found: + /* At this point we have the attribute we were looking for. Now we + * will look for the MFT record that stores information about this + * attribute. + */ + + /* Check if the attribute type we're looking for is in the same + * MFT record. If so, we do not need to look it up again - return it. + */ + if (mrec->mft_record_no == attr_entry->mft_ref) + return mrec; + + retval = NTFS_SB(fs)->mft_record_lookup(fs, attr_entry->mft_ref, + &start_blk); + if (!retval) { + printf("No MFT record found!\n"); + goto out; + } + + /* return the found MFT record */ + return retval; +} + +static struct ntfs_attr_record * +__ntfs_attr_lookup(struct fs_info *fs, uint32_t type, + struct ntfs_mft_record **mrec) +{ + struct ntfs_mft_record *_mrec = *mrec; + struct ntfs_attr_record *attr; + struct ntfs_attr_record *attr_list_attr; + + dprintf("in %s()\n", __func__); + + if (!_mrec || type == NTFS_AT_END) + goto out; + +again: + attr_list_attr = NULL; + + attr = (struct ntfs_attr_record *)((uint8_t *)_mrec + _mrec->attrs_offset); + /* walk through the file attribute records */ + for (;; attr = (struct ntfs_attr_record *)((uint8_t *)attr + attr->len)) { + if (attr->type == NTFS_AT_END) + break; + + if (attr->type == NTFS_AT_ATTR_LIST) { + dprintf("MFT record #%lu has an $ATTRIBUTE_LIST attribute.\n", + _mrec->mft_record_no); + attr_list_attr = attr; + continue; + } + + if (attr->type == type) + break; + } + + /* if the record has an $ATTRIBUTE_LIST attribute associated + * with it, then we need to look for the wanted attribute in + * it as well. + */ + if (attr->type == NTFS_AT_END && attr_list_attr) { + struct ntfs_mft_record *retval; + + retval = ntfs_attr_list_lookup(fs, attr_list_attr, type, _mrec); + if (!retval) + goto out; + + _mrec = retval; + goto again; + } else if (attr->type == NTFS_AT_END && !attr_list_attr) { + attr = NULL; + } + + return attr; + +out: + return NULL; +} + +static inline struct ntfs_attr_record * +ntfs_attr_lookup(struct fs_info *fs, uint32_t type, + struct ntfs_mft_record **mmrec, + struct ntfs_mft_record *mrec) +{ + struct ntfs_mft_record *_mrec = mrec; + struct ntfs_mft_record *other = *mmrec; + struct ntfs_attr_record *retval = NULL; + + if (mrec == other) + return __ntfs_attr_lookup(fs, type, &other); + + retval = __ntfs_attr_lookup(fs, type, &_mrec); + if (!retval) { + _mrec = other; + retval = __ntfs_attr_lookup(fs, type, &other); + if (!retval) + other = _mrec; + } else if (retval && (_mrec != mrec)) { + other = _mrec; + } + + return retval; +} + +static inline enum dirent_type get_inode_mode(struct ntfs_mft_record *mrec) +{ + return mrec->flags & MFT_RECORD_IS_DIRECTORY ? DT_DIR : DT_REG; +} + +static int index_inode_setup(struct fs_info *fs, unsigned long mft_no, + struct inode *inode) +{ + uint64_t start_blk = 0; + struct ntfs_mft_record *mrec, *lmrec; + struct ntfs_attr_record *attr; + enum dirent_type d_type; + uint8_t *attr_len; + struct mapping_chunk chunk; + int err; + uint8_t *stream; + uint32_t offset; + + dprintf("in %s()\n", __func__); + + mrec = NTFS_SB(fs)->mft_record_lookup(fs, mft_no, &start_blk); + if (!mrec) { + printf("No MFT record found.\n"); + goto out; + } + + lmrec = mrec; + + NTFS_PVT(inode)->mft_no = mft_no; + NTFS_PVT(inode)->seq_no = mrec->seq_no; + + NTFS_PVT(inode)->start_cluster = start_blk >> NTFS_SB(fs)->clust_shift; + NTFS_PVT(inode)->here = start_blk; + + d_type = get_inode_mode(mrec); + if (d_type == DT_DIR) { /* directory stuff */ + dprintf("Got a directory.\n"); + attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec); + if (!attr) { + printf("No attribute found.\n"); + goto out; + } + + /* check if we have a previous allocated state structure */ + if (readdir_state) { + free(readdir_state); + readdir_state = NULL; + } + + /* allocate our state structure */ + readdir_state = malloc(sizeof *readdir_state); + if (!readdir_state) + malloc_error("ntfs_readdir_state structure"); + + readdir_state->mft_no = mft_no; + /* obviously, the ntfs_readdir() caller will start from INDEX root */ + readdir_state->in_idx_root = true; + } else if (d_type == DT_REG) { /* file stuff */ + dprintf("Got a file.\n"); + attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec); + if (!attr) { + printf("No attribute found.\n"); + goto out; + } + + NTFS_PVT(inode)->non_resident = attr->non_resident; + NTFS_PVT(inode)->type = attr->type; + + if (!attr->non_resident) { + NTFS_PVT(inode)->data.resident.offset = + (uint32_t)((uint8_t *)attr + attr->data.resident.value_offset); + inode->size = attr->data.resident.value_len; + } else { + attr_len = (uint8_t *)attr + attr->len; + + stream = mapping_chunk_init(attr, &chunk, &offset); + NTFS_PVT(inode)->data.non_resident.rlist = NULL; + for (;;) { + err = parse_data_run(stream, &offset, attr_len, &chunk); + if (err) { + printf("parse_data_run()\n"); + goto out; + } + + if (chunk.flags & MAP_UNALLOCATED) + continue; + if (chunk.flags & MAP_END) + break; + if (chunk.flags & MAP_ALLOCATED) { + /* append new run to the runlist */ + runlist_append(&NTFS_PVT(inode)->data.non_resident.rlist, + (struct runlist_element *)&chunk); + /* update for next VCN */ + chunk.vcn += chunk.len; + } + } + + if (runlist_is_empty(NTFS_PVT(inode)->data.non_resident.rlist)) { + printf("No mapping found\n"); + goto out; + } + + inode->size = attr->data.non_resident.initialized_size; + } + } + + inode->mode = d_type; + + free(mrec); + + return 0; + +out: + free(mrec); + + return -1; +} + +static struct inode *ntfs_index_lookup(const char *dname, struct inode *dir) +{ + struct fs_info *fs = dir->fs; + struct ntfs_mft_record *mrec, *lmrec; + block_t blk; + uint64_t blk_offset; + struct ntfs_attr_record *attr; + struct ntfs_idx_root *ir; + struct ntfs_idx_entry *ie; + const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs); + uint8_t buf[blk_size]; + struct ntfs_idx_allocation *iblk; + int err; + uint8_t *stream; + uint8_t *attr_len; + struct mapping_chunk chunk; + uint32_t offset; + int64_t vcn; + int64_t lcn; + int64_t last_lcn; + struct inode *inode; + + dprintf("in %s()\n", __func__); + + mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(dir)->mft_no, NULL); + if (!mrec) { + printf("No MFT record found.\n"); + goto out; + } + + lmrec = mrec; + attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec); + if (!attr) { + printf("No attribute found.\n"); + goto out; + } + + ir = (struct ntfs_idx_root *)((uint8_t *)attr + + attr->data.resident.value_offset); + ie = (struct ntfs_idx_entry *)((uint8_t *)&ir->index + + ir->index.entries_offset); + for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) { + /* bounds checks */ + if ((uint8_t *)ie < (uint8_t *)mrec || + (uint8_t *)ie + sizeof(struct ntfs_idx_entry_header) > + (uint8_t *)&ir->index + ir->index.index_len || + (uint8_t *)ie + ie->len > + (uint8_t *)&ir->index + ir->index.index_len) + goto index_err; + + /* last entry cannot contain a key. it can however contain + * a pointer to a child node in the B+ tree so we just break out + */ + if (ie->flags & INDEX_ENTRY_END) + break; + + if (ntfs_filename_cmp(dname, ie)) + goto found; + } + + /* check for the presence of a child node */ + if (!(ie->flags & INDEX_ENTRY_NODE)) { + printf("No child node, aborting...\n"); + goto out; + } + + /* then descend into child node */ + + attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec); + if (!attr) { + printf("No attribute found.\n"); + goto out; + } + + if (!attr->non_resident) { + printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n"); + goto out; + } + + attr_len = (uint8_t *)attr + attr->len; + stream = mapping_chunk_init(attr, &chunk, &offset); + do { + err = parse_data_run(stream, &offset, attr_len, &chunk); + if (err) + break; + + if (chunk.flags & MAP_UNALLOCATED) + continue; + + if (chunk.flags & MAP_ALLOCATED) { + dprintf("%d cluster(s) starting at 0x%08llX\n", chunk.len, + chunk.lcn); + + vcn = 0; + lcn = chunk.lcn; + while (vcn < chunk.len) { + blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift << + SECTOR_SHIFT(fs) >> BLOCK_SHIFT(fs); + + blk_offset = 0; + last_lcn = lcn; + lcn += vcn; + err = ntfs_read(fs, &buf, blk_size, blk_size, &blk, + &blk_offset, NULL, (uint64_t *)&lcn); + if (err) { + printf("Error while reading from cache.\n"); + goto not_found; + } + + ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf); + + iblk = (struct ntfs_idx_allocation *)&buf; + if (iblk->magic != NTFS_MAGIC_INDX) { + printf("Not a valid INDX record.\n"); + goto not_found; + } + + ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index + + iblk->index.entries_offset); + for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + + ie->len)) { + /* bounds checks */ + if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie + + sizeof(struct ntfs_idx_entry_header) > + (uint8_t *)&iblk->index + iblk->index.index_len || + (uint8_t *)ie + ie->len > + (uint8_t *)&iblk->index + iblk->index.index_len) + goto index_err; + + /* last entry cannot contain a key */ + if (ie->flags & INDEX_ENTRY_END) + break; + + if (ntfs_filename_cmp(dname, ie)) + goto found; + } + + lcn = last_lcn; /* restore the original LCN */ + /* go to the next VCN */ + vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift)); + } + } + } while (!(chunk.flags & MAP_END)); + +not_found: + dprintf("Index not found\n"); + +out: + free(mrec); + + return NULL; + +found: + dprintf("Index found\n"); + inode = new_ntfs_inode(fs); + err = index_inode_setup(fs, ie->data.dir.indexed_file, inode); + if (err) { + printf("Error in index_inode_setup()\n"); + free(inode); + goto out; + } + + free(mrec); + + return inode; + +index_err: + printf("Corrupt index. Aborting lookup...\n"); + goto out; +} + +/* Convert an UTF-16LE LFN to OEM LFN */ +static uint8_t ntfs_cvt_filename(char *filename, + const struct ntfs_idx_entry *ie) +{ + const uint16_t *entry_fn; + uint8_t entry_fn_len; + unsigned i; + + entry_fn = ie->key.file_name.file_name; + entry_fn_len = ie->key.file_name.file_name_len; + + for (i = 0; i < entry_fn_len; i++) + filename[i] = (char)entry_fn[i]; + + filename[i] = '\0'; + + return entry_fn_len; +} + +static int ntfs_next_extent(struct inode *inode, uint32_t lstart) +{ + struct fs_info *fs = inode->fs; + struct ntfs_sb_info *sbi = NTFS_SB(fs); + sector_t pstart = 0; + struct runlist *rlist; + struct runlist *ret; + const uint32_t sec_size = SECTOR_SIZE(fs); + const uint32_t sec_shift = SECTOR_SHIFT(fs); + + dprintf("in %s()\n", __func__); + + if (!NTFS_PVT(inode)->non_resident) { + pstart = (sbi->mft_blk + NTFS_PVT(inode)->here) << BLOCK_SHIFT(fs) >> + sec_shift; + inode->next_extent.len = (inode->size + sec_size - 1) >> sec_shift; + } else { + rlist = NTFS_PVT(inode)->data.non_resident.rlist; + + if (!lstart || lstart >= NTFS_PVT(inode)->here) { + if (runlist_is_empty(rlist)) + goto out; /* nothing to do ;-) */ + + ret = runlist_remove(&rlist); + + NTFS_PVT(inode)->here = + ((ret->run.len << sbi->clust_byte_shift) >> sec_shift); + + pstart = ret->run.lcn << sbi->clust_shift; + inode->next_extent.len = + ((ret->run.len << sbi->clust_byte_shift) + sec_size - 1) >> + sec_shift; + + NTFS_PVT(inode)->data.non_resident.rlist = rlist; + + free(ret); + ret = NULL; + } + } + + inode->next_extent.pstart = pstart; + + return 0; + +out: + return -1; +} + +static uint32_t ntfs_getfssec(struct file *file, char *buf, int sectors, + bool *have_more) +{ + uint8_t non_resident; + uint32_t ret; + struct fs_info *fs = file->fs; + struct inode *inode = file->inode; + struct ntfs_mft_record *mrec, *lmrec; + struct ntfs_attr_record *attr; + char *p; + + dprintf("in %s()\n", __func__); + + non_resident = NTFS_PVT(inode)->non_resident; + + ret = generic_getfssec(file, buf, sectors, have_more); + if (!ret) + return ret; + + if (!non_resident) { + mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, + NULL); + if (!mrec) { + printf("No MFT record found.\n"); + goto out; + } + + lmrec = mrec; + attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec); + if (!attr) { + printf("No attribute found.\n"); + goto out; + } + + p = (char *)((uint8_t *)attr + attr->data.resident.value_offset); + + /* p now points to the data offset, so let's copy it into buf */ + memcpy(buf, p, inode->size); + + ret = inode->size; + + free(mrec); + } + + return ret; + +out: + free(mrec); + + return 0; +} + +static inline bool is_filename_printable(const char *s) +{ + return s && (*s != '.' && *s != '$'); +} + +static int ntfs_readdir(struct file *file, struct dirent *dirent) +{ + struct fs_info *fs = file->fs; + struct inode *inode = file->inode; + struct ntfs_mft_record *mrec, *lmrec; + block_t blk; + uint64_t blk_offset; + const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs); + struct ntfs_attr_record *attr; + struct ntfs_idx_root *ir; + uint32_t count; + int len; + struct ntfs_idx_entry *ie = NULL; + uint8_t buf[BLOCK_SIZE(fs)]; + struct ntfs_idx_allocation *iblk; + int err; + uint8_t *stream; + uint8_t *attr_len; + struct mapping_chunk chunk; + uint32_t offset; + int64_t vcn; + int64_t lcn; + char filename[NTFS_MAX_FILE_NAME_LEN + 1]; + + dprintf("in %s()\n", __func__); + + mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, NULL); + if (!mrec) { + printf("No MFT record found.\n"); + goto out; + } + + lmrec = mrec; + attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec); + if (!attr) { + printf("No attribute found.\n"); + goto out; + } + + ir = (struct ntfs_idx_root *)((uint8_t *)attr + + attr->data.resident.value_offset); + + if (!file->offset && readdir_state->in_idx_root) { + file->offset = (uint32_t)((uint8_t *)&ir->index + + ir->index.entries_offset); + } + +idx_root_next_entry: + if (readdir_state->in_idx_root) { + ie = (struct ntfs_idx_entry *)(uint8_t *)file->offset; + if (ie->flags & INDEX_ENTRY_END) { + file->offset = 0; + readdir_state->in_idx_root = false; + readdir_state->idx_blks_count = 1; + readdir_state->entries_count = 0; + readdir_state->last_vcn = 0; + goto descend_into_child_node; + } + + file->offset = (uint32_t)((uint8_t *)ie + ie->len); + len = ntfs_cvt_filename(filename, ie); + if (!is_filename_printable(filename)) + goto idx_root_next_entry; + + goto done; + } + +descend_into_child_node: + if (!(ie->flags & INDEX_ENTRY_NODE)) + goto out; + + attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec); + if (!attr) + goto out; + + if (!attr->non_resident) { + printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n"); + goto out; + } + + attr_len = (uint8_t *)attr + attr->len; + +next_run: + stream = mapping_chunk_init(attr, &chunk, &offset); + count = readdir_state->idx_blks_count; + while (count--) { + err = parse_data_run(stream, &offset, attr_len, &chunk); + if (err) { + printf("Error while parsing data runs.\n"); + goto out; + } + + if (chunk.flags & MAP_UNALLOCATED) + break; + if (chunk.flags & MAP_END) + goto out; + } + + if (chunk.flags & MAP_UNALLOCATED) { + readdir_state->idx_blks_count++; + goto next_run; + } + +next_vcn: + vcn = readdir_state->last_vcn; + if (vcn >= chunk.len) { + readdir_state->last_vcn = 0; + readdir_state->idx_blks_count++; + goto next_run; + } + + lcn = chunk.lcn; + blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift << SECTOR_SHIFT(fs) >> + BLOCK_SHIFT(fs); + + blk_offset = 0; + err = ntfs_read(fs, &buf, blk_size, blk_size, &blk, &blk_offset, NULL, + (uint64_t *)&lcn); + if (err) { + printf("Error while reading from cache.\n"); + goto not_found; + } + + ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf); + + iblk = (struct ntfs_idx_allocation *)&buf; + if (iblk->magic != NTFS_MAGIC_INDX) { + printf("Not a valid INDX record.\n"); + goto not_found; + } + +idx_block_next_entry: + ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index + + iblk->index.entries_offset); + count = readdir_state->entries_count; + for ( ; count--; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) { + /* bounds checks */ + if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie + + sizeof(struct ntfs_idx_entry_header) > + (uint8_t *)&iblk->index + iblk->index.index_len || + (uint8_t *)ie + ie->len > + (uint8_t *)&iblk->index + iblk->index.index_len) + goto index_err; + + /* last entry cannot contain a key */ + if (ie->flags & INDEX_ENTRY_END) { + /* go to the next VCN */ + readdir_state->last_vcn += (blk_size / (1 << + NTFS_SB(fs)->clust_byte_shift)); + readdir_state->entries_count = 0; + goto next_vcn; + } + } + + readdir_state->entries_count++; + + /* Need to check if this entry has INDEX_ENTRY_END flag set. If + * so, then it won't contain a indexed_file file, so continue the + * lookup on the next VCN/LCN (if any). + */ + if (ie->flags & INDEX_ENTRY_END) + goto next_vcn; + + len = ntfs_cvt_filename(filename, ie); + if (!is_filename_printable(filename)) + goto idx_block_next_entry; + + goto done; + +out: + readdir_state->in_idx_root = true; + + free(mrec); + + return -1; + +done: + dirent->d_ino = ie->data.dir.indexed_file; + dirent->d_off = file->offset; + dirent->d_reclen = offsetof(struct dirent, d_name) + len + 1; + + free(mrec); + + mrec = NTFS_SB(fs)->mft_record_lookup(fs, ie->data.dir.indexed_file, NULL); + if (!mrec) { + printf("No MFT record found.\n"); + goto out; + } + + dirent->d_type = get_inode_mode(mrec); + memcpy(dirent->d_name, filename, len + 1); + + free(mrec); + + return 0; + +not_found: + printf("Index not found\n"); + goto out; + +index_err: + printf("Corrupt index. Aborting lookup...\n"); + goto out; +} + +static inline struct inode *ntfs_iget(const char *dname, struct inode *parent) +{ + return ntfs_index_lookup(dname, parent); +} + +static struct inode *ntfs_iget_root(struct fs_info *fs) +{ + uint64_t start_blk; + struct ntfs_mft_record *mrec, *lmrec; + struct ntfs_attr_record *attr; + struct ntfs_vol_info *vol_info; + struct inode *inode; + int err; + + dprintf("in %s()\n", __func__); + + /* Fetch the $Volume MFT record */ + start_blk = 0; + mrec = NTFS_SB(fs)->mft_record_lookup(fs, FILE_Volume, &start_blk); + if (!mrec) { + printf("Could not fetch $Volume MFT record!\n"); + goto err_mrec; + } + + lmrec = mrec; + + /* Fetch the volume information attribute */ + attr = ntfs_attr_lookup(fs, NTFS_AT_VOL_INFO, &mrec, lmrec); + if (!attr) { + printf("Could not find volume info attribute!\n"); + goto err_attr; + } + + /* Note NTFS version and choose version-dependent functions */ + vol_info = (void *)((char *)attr + attr->data.resident.value_offset); + NTFS_SB(fs)->major_ver = vol_info->major_ver; + NTFS_SB(fs)->minor_ver = vol_info->minor_ver; + if (vol_info->major_ver == 3 && vol_info->minor_ver == 0) + NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_0; + else if (vol_info->major_ver == 3 && vol_info->minor_ver == 1 && + mrec->mft_record_no == FILE_Volume) + NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_1; + + /* Free MFT record */ + free(mrec); + mrec = NULL; + + inode = new_ntfs_inode(fs); + inode->fs = fs; + + err = index_inode_setup(fs, FILE_root, inode); + if (err) + goto err_setup; + + NTFS_PVT(inode)->start = NTFS_PVT(inode)->here; + + return inode; + +err_setup: + + free(inode); +err_attr: + + free(mrec); +err_mrec: + + return NULL; +} + +/* Initialize the filesystem metadata and return blk size in bits */ +static int ntfs_fs_init(struct fs_info *fs) +{ + int read_count; + struct ntfs_bpb ntfs; + struct ntfs_sb_info *sbi; + struct disk *disk = fs->fs_dev->disk; + uint8_t mft_record_shift; + + dprintf("in %s()\n", __func__); + + read_count = disk->rdwr_sectors(disk, &ntfs, 0, 1, 0); + if (!read_count) + return -1; + + if (!ntfs_check_sb_fields(&ntfs)) + return -1; + + SECTOR_SHIFT(fs) = disk->sector_shift; + + /* Note: ntfs.clust_per_mft_record can be a negative number. + * If negative, it represents a shift count, else it represents + * a multiplier for the cluster size. + */ + mft_record_shift = ntfs.clust_per_mft_record < 0 ? + -ntfs.clust_per_mft_record : + ilog2(ntfs.sec_per_clust) + SECTOR_SHIFT(fs) + + ilog2(ntfs.clust_per_mft_record); + + SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs); + + sbi = malloc(sizeof *sbi); + if (!sbi) + malloc_error("ntfs_sb_info structure"); + + fs->fs_info = sbi; + + sbi->clust_shift = ilog2(ntfs.sec_per_clust); + sbi->clust_byte_shift = sbi->clust_shift + SECTOR_SHIFT(fs); + sbi->clust_mask = ntfs.sec_per_clust - 1; + sbi->clust_size = ntfs.sec_per_clust << SECTOR_SHIFT(fs); + sbi->mft_record_size = 1 << mft_record_shift; + sbi->clust_per_idx_record = ntfs.clust_per_idx_record; + + BLOCK_SHIFT(fs) = ilog2(ntfs.clust_per_idx_record) + sbi->clust_byte_shift; + BLOCK_SIZE(fs) = 1 << BLOCK_SHIFT(fs); + + sbi->mft_lcn = ntfs.mft_lclust; + sbi->mft_blk = ntfs.mft_lclust << sbi->clust_shift << SECTOR_SHIFT(fs) >> + BLOCK_SHIFT(fs); + /* 16 MFT entries reserved for metadata files (approximately 16 KiB) */ + sbi->mft_size = mft_record_shift << sbi->clust_shift << 4; + + sbi->clusters = ntfs.total_sectors << SECTOR_SHIFT(fs) >> sbi->clust_shift; + if (sbi->clusters > 0xFFFFFFFFFFF4ULL) + sbi->clusters = 0xFFFFFFFFFFF4ULL; + + /* + * Assume NTFS version 3.0 to begin with. If we find that the + * volume is a different version later on, we will adjust at + * that time. + */ + sbi->major_ver = 3; + sbi->minor_ver = 0; + sbi->mft_record_lookup = ntfs_mft_record_lookup_3_0; + + /* Initialize the cache */ + cache_init(fs->fs_dev, BLOCK_SHIFT(fs)); + + return BLOCK_SHIFT(fs); +} + +const struct fs_ops ntfs_fs_ops = { + .fs_name = "ntfs", + .fs_flags = FS_USEMEM | FS_THISIND, + .fs_init = ntfs_fs_init, + .searchdir = NULL, + .getfssec = ntfs_getfssec, + .close_file = generic_close_file, + .mangle_name = generic_mangle_name, + .load_config = generic_load_config, + .readdir = ntfs_readdir, + .iget_root = ntfs_iget_root, + .iget = ntfs_iget, + .next_extent = ntfs_next_extent, +}; diff --git a/core/fs/ntfs/ntfs.h b/core/fs/ntfs/ntfs.h new file mode 100644 index 00000000..721a78d7 --- /dev/null +++ b/core/fs/ntfs/ntfs.h @@ -0,0 +1,485 @@ +/* + * Copyright (C) 2011-2012 Paulo Alcantara <pcacjr@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "runlist.h" + +#ifndef _NTFS_H_ +#define _NTFS_H_ + +struct ntfs_bpb { + uint8_t jmp_boot[3]; + char oem_name[8]; + uint16_t sector_size; + uint8_t sec_per_clust; + uint16_t res_sectors; + uint8_t zero_0[3]; + uint16_t zero_1; + uint8_t media; + uint16_t zero_2; + uint16_t unused_0; + uint16_t unused_1; + uint32_t unused_2; + uint32_t zero_3; + uint32_t unused_3; + uint64_t total_sectors; + uint64_t mft_lclust; + uint64_t mft_mirr_lclust; + int8_t clust_per_mft_record; + uint8_t unused_4[3]; + uint8_t clust_per_idx_record; + uint8_t unused_5[3]; + uint64_t vol_serial; + uint32_t unused_6; + + uint8_t pad[428]; /* padding to a sector boundary (512 bytes) */ +} __attribute__((__packed__)); + +/* Function type for an NTFS-version-dependent MFT record lookup */ +struct ntfs_mft_record; +typedef struct ntfs_mft_record *f_mft_record_lookup(struct fs_info *, + uint32_t, block_t *); + +struct ntfs_sb_info { + block_t mft_blk; /* The first MFT record block */ + uint64_t mft_lcn; /* LCN of the first MFT record */ + unsigned mft_size; /* The MFT size in sectors */ + uint64_t mft_record_size; /* MFT record size in bytes */ + + uint8_t clust_per_idx_record; /* Clusters per Index Record */ + + unsigned long long clusters; /* Total number of clusters */ + + unsigned clust_shift; /* Based on sectors */ + unsigned clust_byte_shift; /* Based on bytes */ + unsigned clust_mask; + unsigned clust_size; + + uint8_t major_ver; /* Major version from $Volume */ + uint8_t minor_ver; /* Minor version from $Volume */ + + /* NTFS-version-dependent MFT record lookup function to use */ + f_mft_record_lookup *mft_record_lookup; +} __attribute__((__packed__)); + +/* The NTFS in-memory inode structure */ +struct ntfs_inode { + int64_t initialized_size; + int64_t allocated_size; + unsigned long mft_no; /* Number of the mft record / inode */ + uint16_t seq_no; /* Sequence number of the mft record */ + uint32_t type; /* Attribute type of this inode */ + uint8_t non_resident; + union { /* Non-resident $DATA attribute */ + struct { /* Used only if non_resident flags isn't set */ + uint32_t offset; /* Data offset */ + } resident; + struct { /* Used only if non_resident is set */ + struct runlist *rlist; + } non_resident; + } data; + uint32_t start_cluster; /* Starting cluster address */ + sector_t start; /* Starting sector */ + sector_t offset; /* Current sector offset */ + sector_t here; /* Sector corresponding to offset */ +}; + +/* This is structure is used to keep a state for ntfs_readdir() callers. + * As NTFS stores directory entries in a complex way, this is structure + * ends up saving a state required to find out where we must start from + * for the next ntfs_readdir() call. + */ +struct ntfs_readdir_state { + unsigned long mft_no; /* MFT record number */ + bool in_idx_root; /* It's true if we're still in the INDEX root */ + uint32_t idx_blks_count; /* Number of read INDX blocks */ + uint32_t entries_count; /* Number of read INDEX entries */ + int64_t last_vcn; /* Last VCN of the INDX block */ +}; + +enum { + MAP_UNSPEC, + MAP_START = 1 << 0, + MAP_END = 1 << 1, + MAP_ALLOCATED = 1 << 2, + MAP_UNALLOCATED = 1 << 3, + MAP_MASK = 0x0000000F, +}; + +struct mapping_chunk { + uint64_t vcn; + int64_t lcn; + uint64_t len; + uint32_t flags; +}; + +/* System defined attributes (32-bit) + * Each attribute type has a corresponding attribute name (in Unicode) + */ +enum { + NTFS_AT_UNUSED = 0x00, + NTFS_AT_STANDARD_INFORMATION = 0x10, + NTFS_AT_ATTR_LIST = 0x20, + NTFS_AT_FILENAME = 0x30, + NTFS_AT_OBJ_ID = 0x40, + NTFS_AT_SECURITY_DESCP = 0x50, + NTFS_AT_VOL_NAME = 0x60, + NTFS_AT_VOL_INFO = 0x70, + NTFS_AT_DATA = 0x80, + NTFS_AT_INDEX_ROOT = 0x90, + NTFS_AT_INDEX_ALLOCATION = 0xA0, + NTFS_AT_BITMAP = 0xB0, + NTFS_AT_REPARSE_POINT = 0xC0, + NTFS_AT_EA_INFO = 0xD0, + NTFS_AT_EA = 0xE0, + NTFS_AT_PROPERTY_SET = 0xF0, + NTFS_AT_LOGGED_UTIL_STREAM = 0x100, + NTFS_AT_FIRST_USER_DEFINED_ATTR = 0x1000, + NTFS_AT_END = 0xFFFFFFFF, +}; + +/* NTFS File Permissions (also called attributes in DOS terminology) */ +enum { + NTFS_FILE_ATTR_READONLY = 0x00000001, + NTFS_FILE_ATTR_HIDDEN = 0x00000002, + NTFS_FILE_ATTR_SYSTEM = 0x00000004, + NTFS_FILE_ATTR_DIRECTORY = 0x00000010, + NTFS_FILE_ATTR_ARCHIVE = 0x00000020, + NTFS_FILE_ATTR_DEVICE = 0x00000040, + NTFS_FILE_ATTR_NORMAL = 0x00000080, + NTFS_FILE_ATTR_TEMPORARY = 0x00000100, + NTFS_FILE_ATTR_SPARSE_FILE = 0x00000200, + NTFS_FILE_ATTR_REPARSE_POINT = 0x00000400, + NTFS_FILE_ATTR_COMPRESSED = 0x00000800, + NTFS_FILE_ATTR_OFFLINE = 0x00001000, + NTFS_FILE_ATTR_NOT_CONTENT_INDEXED = 0x00002000, + NTFS_FILE_ATTR_ENCRYPTED = 0x00004000, + NTFS_FILE_ATTR_VALID_FLAGS = 0x00007FB7, + NTFS_FILE_ATTR_VALID_SET_FLAGS = 0x000031A7, + NTFS_FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT = 0x10000000, + NTFS_FILE_ATTR_DUP_VIEW_INDEX_PRESENT = 0x20000000, +}; + +/* + * Magic identifiers present at the beginning of all ntfs record containing + * records (like mft records for example). + */ +enum { + /* Found in $MFT/$DATA */ + NTFS_MAGIC_FILE = 0x454C4946, /* MFT entry */ + NTFS_MAGIC_INDX = 0x58444E49, /* Index buffer */ + NTFS_MAGIC_HOLE = 0x454C4F48, + + /* Found in $LogFile/$DATA */ + NTFS_MAGIC_RSTR = 0x52545352, + NTFS_MAGIC_RCRD = 0x44524352, + /* Found in $LogFile/$DATA (May be found in $MFT/$DATA, also ?) */ + NTFS_MAGIC_CHKDSK = 0x444B4843, + /* Found in all ntfs record containing records. */ + NTFS_MAGIC_BAAD = 0x44414142, + NTFS_MAGIC_EMPTY = 0xFFFFFFFF, /* Record is empty */ +}; + +struct ntfs_record { + uint32_t magic; + uint16_t usa_ofs; + uint16_t usa_count; +} __attribute__((__packed__)) NTFS_RECORD; + +/* The $MFT metadata file types */ +enum ntfs_system_file { + FILE_MFT = 0, + FILE_MFTMirr = 1, + FILE_LogFile = 2, + FILE_Volume = 3, + FILE_AttrDef = 4, + FILE_root = 5, + FILE_Bitmap = 6, + FILE_Boot = 7, + FILE_BadClus = 8, + FILE_Secure = 9, + FILE_UpCase = 10, + FILE_Extend = 11, + FILE_reserved12 = 12, + FILE_reserved13 = 13, + FILE_reserved14 = 14, + FILE_reserved15 = 15, + FILE_reserved16 = 16, +}; + +enum { + MFT_RECORD_IN_USE = 0x0001, + MFT_RECORD_IS_DIRECTORY = 0x0002, +} __attribute__((__packed__)); + +struct ntfs_mft_record { + uint32_t magic; + uint16_t usa_ofs; + uint16_t usa_count; + uint64_t lsn; + uint16_t seq_no; + uint16_t link_count; + uint16_t attrs_offset; + uint16_t flags; /* MFT record flags */ + uint32_t bytes_in_use; + uint32_t bytes_allocated; + uint64_t base_mft_record; + uint16_t next_attr_instance; + uint16_t reserved; + uint32_t mft_record_no; +} __attribute__((__packed__)); /* 48 bytes */ + +/* This is the version without the NTFS 3.1+ specific fields */ +struct ntfs_mft_record_old { + uint32_t magic; + uint16_t usa_ofs; + uint16_t usa_count; + uint64_t lsn; + uint16_t seq_no; + uint16_t link_count; + uint16_t attrs_offset; + uint16_t flags; /* MFT record flags */ + uint32_t bytes_in_use; + uint32_t bytes_allocated; + uint64_t base_mft_record; + uint16_t next_attr_instance; +} __attribute__((__packed__)); /* 42 bytes */ + +enum { + ATTR_DEF_INDEXABLE = 0x02, + ATTR_DEF_MULTIPLE = 0x04, + ATTR_DEF_NOT_ZERO = 0x08, + ATTR_DEF_INDEXED_UNIQUE = 0x10, + ATTR_DEF_NAMED_UNIQUE = 0x20, + ATTR_DEF_RESIDENT = 0x40, + ATTR_DEF_ALWAYS_LOG = 0x80, +}; + +struct ntfs_attr_record { + uint32_t type; /* Attr. type code */ + uint32_t len; + uint8_t non_resident; + uint8_t name_len; + uint16_t name_offset; + uint16_t flags; /* Attr. flags */ + uint16_t instance; + union { + struct { /* Resident attribute */ + uint32_t value_len; + uint16_t value_offset; + uint8_t flags; /* Flags of resident attributes */ + int8_t reserved; + } __attribute__((__packed__)) resident; + struct { /* Non-resident attributes */ + uint64_t lowest_vcn; + uint64_t highest_vcn; + uint16_t mapping_pairs_offset; + uint8_t compression_unit; + uint8_t reserved[5]; + int64_t allocated_size; + int64_t data_size; /* Byte size of the attribute value. + * Note: it can be larger than + * allocated_size if attribute value is + * compressed or sparse. + */ + int64_t initialized_size; + int64_t compressed_size; + } __attribute__((__packed__)) non_resident; + } __attribute__((__packed__)) data; +} __attribute__((__packed__)); + +/* Attribute: Attribute List (0x20) + * Note: it can be either resident or non-resident + */ +struct ntfs_attr_list_entry { + uint32_t type; + uint16_t length; + uint8_t name_length; + uint8_t name_offset; + uint64_t lowest_vcn; + uint64_t mft_ref; + uint16_t instance; + uint16_t name[0]; +} __attribute__((__packed__)); + +#define NTFS_MAX_FILE_NAME_LEN 255 + +/* Possible namespaces for filenames in ntfs (8-bit) */ +enum { + FILE_NAME_POSIX = 0x00, + FILE_NAME_WIN32 = 0x01, + FILE_NAME_DOS = 0x02, + FILE_NAME_WIN32_AND_DOS = 0x03, +} __attribute__((__packed__)); + +/* Attribute: Filename (0x30) + * Note: always resident + */ +struct ntfs_filename_attr { + uint64_t parent_directory; + int64_t ctime; + int64_t atime; + int64_t mtime; + int64_t rtime; + uint64_t allocated_size; + uint64_t data_size; + uint32_t file_attrs; + union { + struct { + uint16_t packed_ea_size; + uint16_t reserved; /* reserved for alignment */ + } __attribute__((__packed__)) ea; + struct { + uint32_t reparse_point_tag; + } __attribute__((__packed__)) rp; + } __attribute__((__packed__)) type; + uint8_t file_name_len; + uint8_t file_name_type; + uint16_t file_name[0]; /* File name in Unicode */ +} __attribute__((__packed__)); + +/* Attribute: Volume Name (0x60) + * Note: always resident + * Note: Present only in FILE_volume + */ +struct ntfs_vol_name { + uint16_t name[0]; /* The name of the volume in Unicode */ +} __attribute__((__packed__)); + +/* Attribute: Volume Information (0x70) + * Note: always resident + * Note: present only in FILE_Volume + */ +struct ntfs_vol_info { + uint64_t reserved; + uint8_t major_ver; + uint8_t minor_ver; + uint16_t flags; /* Volume flags */ +} __attribute__((__packed__)); + +/* Attribute: Data attribute (0x80) + * Note: can be either resident or non-resident + */ +struct ntfs_data_attr { + uint8_t data[0]; +} __attribute__((__packed__)); + +/* Index header flags (8-bit) */ +enum { + SMALL_INDEX = 0, + LARGE_INDEX = 1, + LEAF_NODE = 0, + INDEX_NODE = 1, + NODE_MASK = 1, +} __attribute__((__packed__)); + +/* Header for the indexes, describing the INDEX_ENTRY records, which + * follow the struct ntfs_idx_header. + */ +struct ntfs_idx_header { + uint32_t entries_offset; + uint32_t index_len; + uint32_t allocated_size; + uint8_t flags; /* Index header flags */ + uint8_t reserved[3]; /* Align to 8-byte boundary */ +} __attribute__((__packed__)); + +/* Attribute: Index Root (0x90) + * Note: always resident + */ +struct ntfs_idx_root { + uint32_t type; /* It is $FILE_NAME for directories, zero for view indexes. + * No other values allowed. + */ + uint32_t collation_rule; + uint32_t index_block_size; + uint8_t clust_per_index_block; + uint8_t reserved[3]; + struct ntfs_idx_header index; +} __attribute__((__packed__)); + +/* Attribute: Index allocation (0xA0) + * Note: always non-resident, of course! :-) + */ +struct ntfs_idx_allocation { + uint32_t magic; + uint16_t usa_ofs; /* Update Sequence Array offsets */ + uint16_t usa_count; /* Update Sequence Array number in bytes */ + int64_t lsn; + int64_t index_block_vcn; /* Virtual cluster number of the index block */ + struct ntfs_idx_header index; +} __attribute__((__packed__)); + +enum { + INDEX_ENTRY_NODE = 1, + INDEX_ENTRY_END = 2, + /* force enum bit width to 16-bit */ + INDEX_ENTRY_SPACE_FILTER = 0xFFFF, +} __attribute__((__packed__)); + +struct ntfs_idx_entry_header { + union { + struct { /* Only valid when INDEX_ENTRY_END is not set */ + uint64_t indexed_file; + } __attribute__((__packed__)) dir; + struct { /* Used for views/indexes to find the entry's data */ + uint16_t data_offset; + uint16_t data_len; + uint32_t reservedV; + } __attribute__((__packed__)) vi; + } __attribute__((__packed__)) data; + uint16_t len; + uint16_t key_len; + uint16_t flags; /* Index entry flags */ + uint16_t reserved; /* Align to 8-byte boundary */ +} __attribute__((__packed__)); + +struct ntfs_idx_entry { + union { + struct { /* Only valid when INDEX_ENTRY_END is not set */ + uint64_t indexed_file; + } __attribute__((__packed__)) dir; + struct { /* Used for views/indexes to find the entry's data */ + uint16_t data_offset; + uint16_t data_len; + uint32_t reservedV; + } __attribute__((__packed__)) vi; + } __attribute__((__packed__)) data; + uint16_t len; + uint16_t key_len; + uint16_t flags; /* Index entry flags */ + uint16_t reserved; /* Align to 8-byte boundary */ + union { + struct ntfs_filename_attr file_name; + //SII_INDEX_KEY sii; + //SDH_INDEX_KEY sdh; + //GUID object_id; + //REPARSE_INDEX_KEY reparse; + //SID sid; + uint32_t owner_id; + } __attribute__((__packed__)) key; +} __attribute__((__packed__)); + +static inline struct ntfs_sb_info *NTFS_SB(struct fs_info *fs) +{ + return fs->fs_info; +} + +#define NTFS_PVT(i) ((struct ntfs_inode *)((i)->pvt)) + +#endif /* _NTFS_H_ */ diff --git a/core/fs/ntfs/runlist.h b/core/fs/ntfs/runlist.h new file mode 100644 index 00000000..4938696b --- /dev/null +++ b/core/fs/ntfs/runlist.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011 Paulo Alcantara <pcacjr@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _RUNLIST_H_ +#define _RUNLIST_H_ + +struct runlist_element { + uint64_t vcn; + int64_t lcn; + uint64_t len; +}; + +struct runlist { + struct runlist_element run; + struct runlist *next; +}; + +static struct runlist *tail; + +static inline bool runlist_is_empty(struct runlist *rlist) +{ + return !rlist; +} + +static inline struct runlist *runlist_alloc(void) +{ + struct runlist *rlist; + + rlist = malloc(sizeof *rlist); + if (!rlist) + malloc_error("runlist structure"); + + rlist->next = NULL; + + return rlist; +} + +static inline void runlist_append(struct runlist **rlist, + struct runlist_element *elem) +{ + struct runlist *n = runlist_alloc(); + + n->run = *elem; + + if (runlist_is_empty(*rlist)) { + *rlist = n; + tail = n; + } else { + tail->next = n; + tail = n; + } +} + +static inline struct runlist *runlist_remove(struct runlist **rlist) +{ + struct runlist *ret; + + if (runlist_is_empty(*rlist)) + return NULL; + + ret = *rlist; + *rlist = ret->next; + + return ret; +} + +#endif /* _RUNLIST_H_ */ diff --git a/core/include/fs.h b/core/include/fs.h index ecd148da..e1f5733c 100644 --- a/core/include/fs.h +++ b/core/include/fs.h @@ -95,6 +95,7 @@ struct extent { struct inode { struct fs_info *fs; /* The filesystem this inode is associated with */ struct inode *parent; /* Parent directory, if any */ + const char *name; /* Name, valid for generic path search only */ int refcnt; int mode; /* FILE , DIR or SYMLINK */ uint32_t size; diff --git a/core/ldlinux.asm b/core/ldlinux.asm index f62f55b2..a2f859d0 100644 --- a/core/ldlinux.asm +++ b/core/ldlinux.asm @@ -37,6 +37,8 @@ ROOT_FS_OPS: dd vfat_fs_ops extern ext2_fs_ops dd ext2_fs_ops + extern ntfs_fs_ops + dd ntfs_fs_ops extern btrfs_fs_ops dd btrfs_fs_ops dd 0 diff --git a/core/syslinux.ld b/core/syslinux.ld index 40a01394..11adbcb8 100644 --- a/core/syslinux.ld +++ b/core/syslinux.ld @@ -280,8 +280,9 @@ SECTIONS __ctors_lma = __ctors_vma + __text_lma - __text_vma; .ctors : AT(__ctors_lma) { __ctors_start = .; - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) + KEEP (*(SORT(.preinit_array*))) + KEEP (*(SORT(.init_array*))) + KEEP (*(SORT(.ctors*))) __ctors_end = .; } @@ -289,8 +290,8 @@ SECTIONS __dtors_lma = __dtors_vma + __text_lma - __text_vma; .dtors : AT(__dtors_lma) { __dtors_start = .; - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) + KEEP (*(SORT(.fini_array*))) + KEEP (*(SORT(.dtors*))) __dtors_end = .; } diff --git a/diag/geodsp/Makefile b/diag/geodsp/Makefile index 119ccbc7..55160859 100644 --- a/diag/geodsp/Makefile +++ b/diag/geodsp/Makefile @@ -33,11 +33,15 @@ CFLAGS = -g -O all: $(BTARGET) # Higher compression levels result in larger files -%.img.xz: %.bin mk-lba-img - ./mk-lba-img < $< | $(XZ) -0f > $@ || ( rm -f $@ ; false ) +%.img.xz: %.bin mk-lba-img.pl + $(PERL) mk-lba-img $< | $(XZ) -0 > $@ || ( rm -f $@ ; false ) -%.img.gz: %.bin mk-lba-img - ./mk-lba-img < $< | $(GZIP) -9 > $@ || ( rm -f $@ ; false ) +%.img.gz: %.bin mk-lba-img.pl + $(PERL) mk-lba-img $< | $(GZIPPROG) -9 > $@ || ( rm -f $@ ; false ) + +# in case someone really wants these without needing a decompressor +%.img: %.bin mk-lba-img.pl + $(PERL) mk-lba-img $< > $@ || ( rm -f $@ ; false ) %.bin: %.asm $(coredir)/writehex.inc $(coredir)/macros.inc $(coredir)/diskboot.inc $(NASM) $(NASMOPT) -o $@ -l $(@:.bin=.lst) $< diff --git a/diag/geodsp/mk-lba-img.pl b/diag/geodsp/mk-lba-img.pl new file mode 100755 index 00000000..59ef4f0f --- /dev/null +++ b/diag/geodsp/mk-lba-img.pl @@ -0,0 +1,94 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2011 Gene Cumm +## +## 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., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## mk-lba-img.pl +## +## Make an image where each sector contains the LBA of the sector with +## a head of an input file. +## + +# use bytes; + +use constant SECTOR_SIZE => 512; +use constant LBA_SIZE => 8; +use constant LONG_SIZE => 4; +use constant NUM_SECTORS => (256*63+1); +# use constant NUM_SECTORS => 5; +use constant DEBUG => 1; + +# sub dprint +# { +# if (DEBUG) { +# print($_); +# } +# } + +($ifilen, $ofilen) = @ARGV; + +if ((!defined($ifilen)) || ($ifilen eq "-")) { # + print(STDERR "Using stdin\n"); + $IFILE = STDIN; +} else { + open($IFILE, '<', $ifilen) or die "open:$!"; + print(STDERR "Using $ifilen\n"); +} + +binmode($ifile); + +if (!defined($ofilen)) { + $OFILE = STDOUT; +} else { + open($OFILE, '>', $ofilen) or die "open:$!"; + print(STDERR "Using $ofilen\n"); +} + +binmode($OFILE); + +# $pk0 = pack('L', 0); +$n_long = (SECTOR_SIZE/LONG_SIZE); +$n_lba = (SECTOR_SIZE/LBA_SIZE); + +$len=0; +while ( read($IFILE, $ch, 1) ) { + print($OFILE $ch); + $len++; +} +$tail = (SECTOR_SIZE - ($len % SECTOR_SIZE)) % SECTOR_SIZE; +$ch = pack("C", 0); +print("Len: $len\ttail: $tail\n"); +for ($i=0; $i<$tail; $i++) { + print($OFILE $ch); +} + +$st = ($len + $tail) / SECTOR_SIZE; + +for ($i=$st; $i<(NUM_SECTORS); $i++) { + @ia = (); + for ($j=0; $j< $n_lba; $j++) { + push(@ia, $i, 0); + } + @ipk = pack("L[$n_long]", @ia); + # There is a 64-bit INT conversion but it normally isn't usable + # on a 32-bit platform + print($OFILE @ipk); # Gently simulate a 64-bit LBA +} + +if (defined($ifilen) && (!($ifilen eq "-"))) { + close($IFILE); +} + +if (defined($ofilen)) { + close($OFILE); +} + +exit 0; diff --git a/diag/mbr/README b/diag/mbr/README index fb7a7dd8..96b67c6c 100644 --- a/diag/mbr/README +++ b/diag/mbr/README @@ -5,11 +5,13 @@ handoff.bin Show the data that the BIOS/MBR hands off to an MBR/VBR. +++ USAGE +++ +NOTE: in the examples, mbr.bin, /dev/hda and /dev/hda1 are used as generic representations. + Writing out an MBR is straight forward (it is assumed below that /dev/hda is the target raw device and /dev/hda1 is the target partition): dd conv=notrunc bs=440 count=1 if=mbr.bin of=/dev/hda -Writing a VBR to match Syslinux requires more work as it must have a jump and be offset into the partition: +Writing a VBR to match Syslinux requires more work as it must have a jump and be offset into the partition (and as a result the code must be compaible with this offset): echo -en "\0353\0130\0220" |dd conv=notrunc bs=1 count=3 of=/dev/hda1 dd conv=notrunc bs=2 count=210 seek=45 if=mbr.bin of=/dev/hda1 diff --git a/diag/mbr/handoff.S b/diag/mbr/handoff.S index 7af3fdeb..ab8582b7 100644 --- a/diag/mbr/handoff.S +++ b/diag/mbr/handoff.S @@ -43,11 +43,11 @@ * Install instructions (assuming your target is /dev/dev; file or block device): * * MBR: - * dd conv=notrunc bs=440 count=1 if=mbr_ho.bin of=/dev/dev + * dd conv=notrunc bs=440 count=1 if=handoff.bin of=/dev/dev * * VBR/PBR (should work for FAT12/16/32, ext[234]fs, btrfs): * echo -en "\0353\0130\0220" |dd conv=notrunc bs=1 count=3 of=/dev/dev - * dd conv=notrunc bs=2 count=210 seek=45 if=mbr_ho.bin of=/dev/dev + * dd conv=notrunc bs=2 count=210 seek=45 if=handoff.bin of=/dev/dev */ // #define DEBUG_MARKER1 /* Insert markers in binary */ diff --git a/doc/chain.txt b/doc/chain.txt new file mode 100644 index 00000000..6dd0632d --- /dev/null +++ b/doc/chain.txt @@ -0,0 +1,327 @@ + chain.c32 documentation + +Although syslinux is capable of (very simple) native chainloading (through .bss +and .bs options - see doc/syslinux.txt), it also features a very roboust and +rich com32 module designed for such purpose. + +Chain module can perform few basic tasks: + +- load and jump to a sector +- load and jump to a file (also loading a sector for other purposes) +- prepare handover data to use by a file / boot sector +- fix different options in a file / sector / partition entries +- perform a "service-only" run + +It can chainload data from both GPT and DOS partitions, as well as boot the +first sector from a raw disk. + +In more details, the rough overview of code is as follows: + +1. Parse arguments. +2. Find drive and/or partition to boot from. +3. Perform partition-level patching - for example hiding, unhiding, fixing chs values, etc. +4. Load a file to boot from. +5. Load a sector to boot from, if it doesn't conflict with #5. +6. Prepare handover area, if it doesn't conflict with #5 & #6. +7. Prepare registers. +8. Patch loaded file if necessary. +9. Patch loaded sector if necessary. +10. Chainload. + +In most basic form, syslinux loads specified boot sector (or mbr, if not +specified) at 0:0x7c00, prepares handover area as a standard mbr would do, and +jumps to 0:0x7c00. + +A "service-only" run is possible when either: + +- 'break' is in effect + +or + +- 'nofile' and 'nomaps' (or 'nosect') are in effect + +This is useful for invocations such as: + +chain.c32 hdN M setbpb save break +chain.c32 hdN fixchs break +chain.c32 hdN unhideall break + +Please see respective options for more details. + + +Module invocation: + +chain [drive/partition] [options] + + DRIVE / PARTITION SPECIFICATION + +Drive can be specified as 'hd#', 'fd#', 'boot', 'mbr', or 'guid'. + +- 'mbr' will select a drive by a signature. +- 'guid' will select a drive by a guid +- 'boot' is the drive syslinux was booted from. This is the default value, if + nothing else is specified. +- 'hd#' and 'fd#' are standard ways to specify drive number as seen by bios, + starting from 0. + +Option 'guid' is shared with partition selection (see below). If you happened +to have non-unique guids, they are searched in disk0, partitions of disk0, +disk1 ... order. + +The priority of those options are the same as in the above list. + +If you specify the same value more than once, the last value will be used. + +'mbr' and 'guid' take extra parameter - you should use ':' or '=' as a +delimiter. + + +Partition can be specified as '#', 'guid', 'label' or 'fs'. + +- 'guid' option will select a partition by a guid (not a type guid !) +- 'label' will select a partition by a label (searching is done in + disk order) +- 'fs' will select a partition from which syslinux was executed +- '#' is the standard method. Partitions 1-4 are primary, 5+ logical, 0 = boot + MBR (default). + +The priority of those options are the same as in the above list. + +If you use a number to select a partition it should be specified after a drive +using space or comma as delimiters (after 'hd#', 'fd#', 'mbr', 'guid' or 'boot'). + + OPTIONS + file=<file> + *nofile + +It's often convenient to load a file directly and transfer control to it, +instead of the sector from the disk. Note, that the <file> must reside on +syslinux partition. + +If you choose this option without specifying any addresses explicitly (see +options 'sect=' and 'seg='), the file will cause sector to not be loaded at all +(as their memory placement would overlap). + + seg=<segment>:<offset>:<ip> + *seg=0:0x7c00:0x7c00 + +This triplet lets you alter the addresses a file will use. It's loaded at +<segment:offset>, the entry point is at <segment:ip>. When you chainload some +other bootloader or kernel, it's almost always mandatory. + +The defaults, if option is not specified, are 0:0x7c00:0x7c00 +If any of the fields are ommited (e.g. 0x2000::), they default to 0. + + sect=<segment>:<offset>:<ip> + nosect + *sect=0:0x7c00:0x7c00 + nosect sets: nomaps + +This triplet lets you alter the addresses a sector will use. It's loaded at +<segment:offset>, the entry point is at <segment:ip>. This option is mostly +used in tandem with 'file=' and 'seg=' options, as some loaders/kernels will +expect relocated sector at some particular address (e.g. DRKM). + +'nosect' will cause sector to not be loaded at all. In plenty cases, when a file +is being chainloaded, sector is not necessary. + +The defaults if option is not specified, are 0:0x7c00:0x7c00. +If some of the fields are ommited (e.g. 0x2000::), they default to 0. + + *maps + nomaps + +In some cases, it's useful to fix BPB values in NTFS/FATxx bootsectors and +evntually write them back, but otherwise boot sector itself is not necessary to +continue booting. 'nomaps' allows that - a sector will be loaded, but won't be +mmapped into real memory. Any overlap tests (vs. handover or file areas) are +not performed, being meaningless in such case. + + setbpb + *nosetbpb + +Microsoft side of the world is paritculary sensitive to certain BPB values. +Depending on the system and chainloading method (sector or file), some or all +of those fields must match reality - and after e.g. drive clonning or +when using usb stick in different computers - that is often not the case. + +The "reality" means: + +"hidden sectors" - valid offset of the partition from the beginning of the disk +"geometry" - valid disk geometry as reported by BIOS +"drive" - valid drive number + +This option will automatically determine the type of BPB and fix what is possible +to fix, relatively to detected BPB. If it's impossible to detect BPB, function +will do nothing. + + filebpb + *nofilebpb + +Chainloaded file can simply be an image of a sector. In such case, it could be +useful to also fix its BPB values. + + save + *nosave + +Fixing BPB values only in memory might not be enough. This option allows +writing of the corrected sector. You will probably want to use this option +together with 'setbpb'. + +- this option never applies to a loaded file +- chain module will not save anything to disk by default (besides options such + as hide or fixchs - so options related directly to partition entries) +- writing is only performed, if the values actually changed + + *hand + nohand + +By default, a handover area is always prepared if possible - meaning it doesn't +overlap with other areas. It's often not necessary though - usually, a +chainloaded file or kernel don't care about it anymore, so a user can disable +it explicitly with this option. + + hptr + *nohptr + +In case when both file and sector are loaded, ds:si and ds:bp will point to +sector address before the chainloading. This option lets user force those +registers to point to handover area. This is useful when both the file and the +sector are actually a sector's image and the sector is mmapped. + + swap + *noswap + +This option will install a tiny stub code used to swap drive numbers, if the +drive we use during chainloading is not fd0 or hd0. + + hide[all] + unhide[all] + *nohide + +In certain situations it's useful to hide partitions - for example to make sure +DOS gets C:. 'hide' will hide hidable primary partitions, except the one we're +booting from. Similary, 'hideall' will hide all hidable partitions, except the +one we're booting from. Hiding is performed only on the selected drive. Options +starting with 'un' will simply unhide every partition (primary ones or all). +Writing is only performed, if the os type values actually changed. + + fixchs + *nofixchs + +If you want to make a drive you're booting from totally compatible with current +BIOS, you can use this to fix all partitions' CHS numbers. Good to silence e.g. +FreeDOS complainig about 'logical CHS differs from physical' of sfdisk about +'found (...) expected (...). Functionally seems to be mostly cosmetic, as +Microsoft world - in cases it cares about geometry - generally sticks to values +written in bootsectors. And the rest of the world generally doesn't care about +them at all. Writing is only performed, if the values actually got changed. + + keepexe + *nokeepexe + +If you're booting over a network using pxelinux - this lets you keep UNDI +stacks in memory (pxelinux only). + + warn + *nowarn + +This option will wait for a keypress right before continuing the chainloading. +Useful to see warnings emited by the chain module. + + *nobreak + break + break sets: nofile nomaps nohand + +It is possible to trigger a "service-only" run - The chain module will do +everything requested as usual, but it will not perform the actual chainloading. +'break' option disables handover, file loading and sector mapping, as these +are pointless in such scenario (although file might be reenabled in some future +version, if writing to actual files becomes possible). Mainly useful for +options 'fixchs', '[un]hide[all]' and setbpb. + + isolinux=<file> + sets: file=<file> nohand nosect isolinux + +Chainload another version/build of the ISOLINUX bootloader and patch the loader +with appropriate parameters in memory. This avoids the need for the +-eltorito-alt-boot parameter of mkisofs, when you want more than one ISOLINUX +per CD/DVD. + + ntldr=<file> + sets: file=<file> seg=0x2000 setbpb nohand + +Prepares to load ntldr directly. You might want to add 'save' option to store +corrected BPB values. + + cmldr=<file> + sets: file=<file> seg=0x2000 setbpb nohand cmldr + +Prepares to load recovery console directly. In-memory copy of bootsector is +patched with "cmdcons\0". Remarks the same as in 'ntldr='. + + reactos=<file> + sets: file=<file> seg=0:0x8000:0x8100 setbpb nohand + +Prepares to load ReactOS's freeldr directly. You might want to add 'save' +option to store corrected BPB values. + + freedos=<file> + sets: file=<file> seg=0x60 sect=0x1FE0 setbpb nohand + +Prepares to load freedos kernel directly. You will likely want to add 'save' +option, as those kernels seem to require proper geometry written back to disk. +Sector address is chosen based on where freedos' bootsectors relocate themselves, +although it seems the kernel doesn't rely on it. + +You might also want to employ 'hide' option, if you have problems with properly +assigned C: drive. + + pcdos=<file> + msdos=<file> + sets: file=<file> seg=0x70 sect=0x8000 setbpb nohand + +Similary to 'freedos=', This prepares to load MSDOS 2.00 - 6.xx or derivatives. +Sector address is chosen arbitrarily. Otherwise comments as above. + + msdos7=<file> + sets: file=<file> seg=0x70::0x200 sect=0x8000 setbpb nohand + +Only for MSDOS 7+ versions (98se ~ 7.xx, Me ~ 8.xx). Comments as above. +TODO/TEST + + drmk=<file> + sets: file=<file> seg=0x70 sect=0x2000:0:0 setbpb nohand + +This is used for loading of *only* Dell's DOS derivatives. It does require boot +sector at 0x2000 and overall valid BPB values. As in other DOS-ish cases, +likely candidates for use are 'save' and 'hide'. + + grub=<file> [grubcfg=<config>] + sets: file=<file> seg=0x800::0x200 nohand nosect grub + +Chainloads grub legacy's stage2, performing additional corrections on the file +in memory. Additionally, alternate config file can be specified through +'grubcfg=' option + + grldr=<file> + sets: file=<file> nohand nosect grldr + +Chainloads GRUB4DOS grldr, performing additional corrections on the file +in memory. + + bss=<file> + sets: file=<file> nomaps setbpb bss + +This emulates syslinux's native BSS option. This loads both the file and the +sector, adjusts BPB values in the loaded sector, then copies all possible BPB +fields to the loaded file. Everything is made with reference to the selected +disk/partition. + + bs=<file> + sets: file=<file> nosect filebpb + +This emulates syslinux's native BS option. This loads the file and if possible +- adjusts its BPB values. Everything is made with reference to the selected +disk/partition. + diff --git a/doc/comboot.txt b/doc/comboot.txt index 04d5deb8..6e9d7ab5 100644 --- a/doc/comboot.txt +++ b/doc/comboot.txt @@ -560,29 +560,7 @@ AX=000Ch [2.00] Perform final cleanup meanings in future versions of Syslinux. -AX=000Dh [2.08] Cleanup and replace bootstrap code - Input: AX 000Dh - DX derivative-specific flags (see previous function) - EDI bootstrap code (linear address, can be in high memory) - ECX bootstrap code length in bytes (must fit in low mem) - EBX(!) initial value of EDX after bootstrap - ESI initial value of ESI after bootstrap - DS initial value of DS after bootstrap - Output: Does not return - - This routine performs final cleanup, then takes a piece of - code, copies it over the primary bootstrap at address 7C00h, - and jumps to it. This can be used to chainload boot sectors, - MBRs, bootstraps, etc. - - Normal boot sectors expect DL to contain the drive number, - and, for hard drives (DL >= 80h) DS:SI to contain a pointer to - the 16-byte partition table entry. The memory between - 600h-7FFh is available to put the partition table entry in. - - For PXELINUX, if the PXE stack is not unloaded, all registers - (except DS, ESI and EDX) and the stack will be set up as they - were set up by the PXE ROM. +AX=000Dh [2.08] Obsoleted in 3.80 AX=000Eh [2.11] Get configuration file name @@ -627,57 +605,7 @@ AX=0010h [3.00] Resolve hostname [PXELINUX] AX=0011h [3.05] Obsoleted in 3.80 -AX=0012h [3.50] Cleanup, shuffle and boot - Input: AX 0012h - DX derivative-specific flags (see function 000Ch) - ES:DI shuffle descriptor list (must be in low memory) - CX number of shuffle descriptors - EBX(!) initial value of EDX after bootstrap - ESI initial value of ESI after bootstrap - DS initial value of DS after bootstrap - EBP CS:IP of routine to jump to - Output: Does not return - (if CX is too large the routine returns with CF=1) - - This routine performs final cleanup, then performs a sequence - of copies, and jumps to a specified real mode entry point. - This is a more general version of function 000Dh, which can - also be used to load other types of programs. - - The copies must not touch memory below address 7C00h. - - ES:DI points to a list of CX descriptors each of the form: - - Offset Size Meaning - 0 dword destination address - 4 dword source address - 8 dword length in bytes - - The copies are overlap-safe, like memmove(). - - Starting in version 3.50, if the source address is -1 - (FFFFFFFFh) then the block specified by the destination - address and the length is set to all zero. - - Starting in version 3.50, if the destination address is -1 - (FFFFFFFFh) then the data block is loaded as a new set of - descriptors, and processing is continued (and unprocessed - descriptors are lost, this is thus typically only used as the - last descriptor in a block.) The block must still fit in the - internal descriptor buffer (see function 0011h), but can, of - course, itself chain another block. - - - Normal boot sectors expect DL to contain the drive number, - and, for hard drives (DL >= 80h) DS:SI to contain a pointer to - the 16-byte partition table entry. The memory between - 600h-7FFh is available to put the partition table entry in. - - For PXELINUX, if the PXE stack is not unloaded, all registers - (except DS, ESI and EDX) and the stack will be set up as they - were set up by the PXE ROM. - - This interface was probably broken before version 3.50. +AX=0012h [3.50] Obsoleted in 3.80 AX=0013h [3.08] Idle loop call diff --git a/doc/gpt.txt b/doc/gpt.txt index 09099320..2ef387a9 100644 --- a/doc/gpt.txt +++ b/doc/gpt.txt @@ -51,7 +51,7 @@ form: 4 1 0xED (partition type: synthetic) 5 3 CHS of partition end 8 4 Partition start LBA - 12 4 Partition end LBA + 12 4 Partition length in sectors 16 4 Length of the GPT entry 20 varies GPT partition entry diff --git a/doc/logo/LICENSE b/doc/logo/LICENSE new file mode 100644 index 00000000..e753087f --- /dev/null +++ b/doc/logo/LICENSE @@ -0,0 +1,5 @@ +The Syslinux logo is licensed under the Creative Commons +Attribution-ShareAlike 3.0 Unported License. To view a copy of this +license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send +a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain +View, California, 94041, USA. diff --git a/doc/menu.txt b/doc/menu.txt index 620527e6..8a999cd4 100644 --- a/doc/menu.txt +++ b/doc/menu.txt @@ -18,7 +18,7 @@ See menu/README for more information. +++ THE SIMPLE MENU SYSTEM +++ The simple menu system is a single module located at -com32/modules/vesamenu.c32 (graphical) or com32/modules/menu.c32 (text +com32/menu/vesamenu.c32 (graphical) or com32/menu/menu.c32 (text mode only). It uses the same configuration file as the regular Syslinux command line, and displays all the LABEL statements. diff --git a/doc/pxechn.txt b/doc/pxechn.txt new file mode 100644 index 00000000..7853d9ab --- /dev/null +++ b/doc/pxechn.txt @@ -0,0 +1,138 @@ += pxechn.c32(1) = +:doctype: manpage +:author: Gene Cumm +:email: gene.cumm@gmail.com +:revdate: 2012-09-16 + + +== NAME == +pxechn.c32 - Chainboot to new Network Boot Program (NBP) + + +== SYNOPSIS == +[verse] +*pxechn.c32* [-h | --help | -?] +*pxechn.c32* -r 'FILE' +*pxechn.c32* 'FILE' ['OPTIONS'] + + +== DESCRIPTION == +Chainboot to a new Network Boot Program (NBP) 'FILE' with options to +adjust PXE packet #3 (PXENV_PACKET_TYPE_CACHED_REPLY) to alter end +behavior. 'FILE' may be a filename, an IP::FN ( +192.168.1.1::path/to/file.0 ), or URL. 'FILE' is parsed to adjust the +DHCP 'sname' field/option 66 and 'file' field/option 67. +// but these may be override-able in the future. + + +== OPTIONS == +*-c* 'CONFIG':: + 'config' file for PXELINUX (DHCP Option 209). + +// *-f* 'MOD':: +// 'Force' behavior specified by modifier 'MOD' +// +// *-g* 'HOST':: +// Set 'gateway'/relay DHCP field to 'HOST'. Parsed by pxe_dns(). +// +*-h*, *--help*, *-?*:: + Print 'help'/usage information; invalid options will also cause + this. + +// *-n*:: +// Use 'native' methods, ignoring underlying gPXE/iPXE. +// +// *-N*:: +// Use 'non-native' methods to utilize gPXE/iPXE (if available). +// +*-o* 'OPT.TYPE=VALUE':: + Set 'option'. 'OPT' is in 'DECIMAL INPUT' format (below). 'TYPE' + specifies the output type and input syntax (listed here in quotes + and at present, 1 character). ''b'yte', ''w'ord'(2B), ''l'ong'(4B), + ''q'uad'(8B), character ''s'tring' and colon-separated 'he'x'' + string (case insensitive; bytes must have 2 digits and each byte + must be separated). byte, word, long and quad input values must + meet criteria for 'DECIMAL INPUT' + +*-p* 'PATH':: + 'path' option for PXELINUX (DHCP Option 210). + +*-r*:: + 'restart'. Call the PXE stack with PXENV_RESTART_TFTP. _Must_ be + the only option and before 'FILE'. + +*-S*:: + Set 'sip' based on sname field/option 66 (by direct IP if a + period-delimited address or otherwise DNS). + +*-t* 'SECONDS':: + 'timeout' option for PXELINUX (DHCP Option 211). + +// *-u*:: +// Copy 'UUID' (Option 97) if found in packet #1 + +*-w*:: + 'wait'. After loading, wait for user input before booting. + +*-W*:: + Enable 'WDS' (Windows Deployment Services) - specific options. + 'FILE' (or its overrides for DHCP fields siaddr and file) must point + at the WDS server. + *NOTE:* As of 2012-05-31, there is a known issue with gPXE/iPXE, at + least with undionly.kkpxe. +// PXELINUX asks gPXE/iPXE to unload, reverting to an underlying stack + + +== DECIMAL INPUT == +All parameters that are defaulted to decimal format are processed by +*strtoul*(3) with a base of 0 which allows alternate formats and finds a +suitable non-space separating character. + + +== EXAMPLES == +`pxechn.c32 http://myhost.dom.loc/path/nbp.0 -c myconfig`:: + Load nbp.0 and set PXELINUX config (option 209). + +`pxechn.c32 gpxelinux.0 -p http://10.1.1.4/tftp/ -w -c myconfig -o 15.s=domain.loc -o 6.x=0A:01:01:02:ac:17:4D:Ec -`:: + Load gpxelinux.0 from the current directory, set prefix, wait to + execute, set first config, set the domain name and 2 domain name + servers (case mixed to show insensitivity; 10.1.1.2 and + 172.23.77.236). + +`pxechn.c32 gpxelinux.0 -p http://10.1.1.4/tftp/ -w -o 0xA0.x=12:34:56:78 -x 197.x=00:d0:de:00`:: + Load gpxelinux.0 (relative to the current directory and not + altering sname/option 66), set the PXELINUX path prefix, wait after + loading, set option 160 to 0x12 0x34 0x56 0x78, and option 197 to + 0x00 0xD0 0xDE 0x00. + +`pxechn.c32 10.1.1.8:boot\x86\wdsnbp.com -W`:: + Load wdsnbp.com from 10.1.1.8 and copy DHCP Option 66 to DHCP + field sname if there's room. + +`pxechn.c32 10.1.1.4:boot\x86\wdsnbp.com -W -o 66.x=0a:01:01:08 -S`:: + Load wdsnbp.com from 10.1.1.4, point packets to 10.1.1.8 for use + with WDS, copy DHCP Option 66 to DHCP field sname if there's room + and decode this to an IPv4 address. + + +== NOTES == +Please note that some NBPs may ignore packet #3 by either not examining +it at all or by issuing its own DHCP DISCOVER/REQUEST, negating all DHCP +field/option modifications by pxechn.c32, including Microsoft Windows +Server 2008R2 WDS's wdsnbp.com. See also option '-W'. + +URL specifications in 'FILE' that include user/password before the host +will currently cause the siaddr field to not be set properly. + +The non-space constraint is due to how Syslinux variants parse the +command line as of 2012-09-16. + + +== AUTHOR == +{author} <{email}> + + +== COPYRIGHT == +Copyright \(C) 2012 {author}. Free use of this software is granted under +the terms of the GNU General Public License (GPL), version 2 (GPLv2) +(or, at your option, any later version). diff --git a/doc/syslinux.txt b/doc/syslinux.txt index 07d5df9a..033a1ec0 100644 --- a/doc/syslinux.txt +++ b/doc/syslinux.txt @@ -100,7 +100,7 @@ directory on the disk. There are two versions of the Linux installer; one in the "mtools" directory which requires no special privilege (other than write permission to the device where you are installing) but requires the -mtools program suite to be available, and one in the "unix" directory +mtools program suite to be available, and one in the "linux" directory which requires root privilege. diff --git a/dos/Makefile b/dos/Makefile index 5e5fc63f..f9420084 100644 --- a/dos/Makefile +++ b/dos/Makefile @@ -27,7 +27,7 @@ INCLUDES = -include code16.h -nostdinc -iwithprefix include \ -I. -I.. -I../libfat -I ../libinstaller -I ../libinstaller/getopt SRCS = syslinux.c \ - ../libinstaller/fat.c \ + ../libinstaller/fs.c \ ../libinstaller/syslxmod.c \ ../libinstaller/syslxopt.c \ ../libinstaller/setadv.c \ diff --git a/dos/syslinux.c b/dos/syslinux.c index b5fdfc52..fa4bf387 100644 --- a/dos/syslinux.c +++ b/dos/syslinux.c @@ -31,6 +31,7 @@ #include "sysexits.h" #include "syslxopt.h" #include "syslxint.h" +#include "syslxfs.h" char *program = "syslinux.com"; /* Name of program */ uint16_t dos_version; @@ -638,7 +639,7 @@ int main(int argc, char *argv[]) /* * Check to see that what we got was indeed an MS-DOS boot sector/superblock */ - if ((errmsg = syslinux_check_bootsect(sectbuf))) { + if ((errmsg = syslinux_check_bootsect(sectbuf, NULL))) { unlock_device(0); puts(errmsg); putchar('\n'); @@ -749,7 +750,7 @@ int main(int argc, char *argv[]) read_device(dev_fd, sectbuf, 1, 0); /* Copy the syslinux code into the boot sector */ - syslinux_make_bootsect(sectbuf); + syslinux_make_bootsect(sectbuf, VFAT); /* Write new boot sector */ if (opt.bootsecfile) { diff --git a/dosutil/Makefile b/dosutil/Makefile index 5746e86b..6bce6248 100644 --- a/dosutil/Makefile +++ b/dosutil/Makefile @@ -11,7 +11,7 @@ WCLOPT = -6 -osx -mt -bt=DOS -l=COM UPX = upx NASM = nasm -NASMOPT = -O9999 +NASMOPT = -Ox WCTARGETS = mdiskchk.com NSTARGETS = eltorito.sys copybs.com diff --git a/extlinux/Makefile b/extlinux/Makefile index 12213d88..6cde574e 100644 --- a/extlinux/Makefile +++ b/extlinux/Makefile @@ -11,7 +11,7 @@ ## ----------------------------------------------------------------------- ## -## Linux vfat, ext2/ext3/ext4 and btrfs installer +## Linux vfat, ntfs, ext2/ext3/ext4 and btrfs installer ## topdir = .. @@ -25,6 +25,7 @@ CFLAGS = $(GCCWARN) -Wno-sign-compare -D_FILE_OFFSET_BITS=64 \ LDFLAGS = SRCS = main.c \ + mountinfo.c \ ../libinstaller/syslxmod.c \ ../libinstaller/syslxopt.c \ ../libinstaller/syslxcom.c \ diff --git a/extlinux/btrfs.h b/extlinux/btrfs.h index be0c24ef..4e2cb317 100644 --- a/extlinux/btrfs.h +++ b/extlinux/btrfs.h @@ -8,8 +8,10 @@ #define BTRFS_SUPER_INFO_OFFSET (64 * 1024) #define BTRFS_SUPER_INFO_SIZE 4096 #define BTRFS_MAGIC "_BHRfS_M" +#define BTRFS_MAGIC_L 8 #define BTRFS_CSUM_SIZE 32 #define BTRFS_FSID_SIZE 16 +#define BTRFS_UUID_SIZE 16 typedef __u64 u64; typedef __u32 u32; @@ -46,17 +48,52 @@ struct btrfs_dir_item { } __attribute__ ((__packed__)); struct btrfs_super_block { - unsigned char csum[BTRFS_CSUM_SIZE]; - /* the first 3 fields must match struct btrfs_header */ - unsigned char fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ - u64 bytenr; /* this block number */ - u64 flags; - - /* allowed to be different from the btrfs_header from here own down */ - u64 magic; + uint8_t csum[32]; + uint8_t fsid[16]; + uint64_t bytenr; + uint64_t flags; + uint8_t magic[8]; + uint64_t generation; + uint64_t root; + uint64_t chunk_root; + uint64_t log_root; + uint64_t log_root_transid; + uint64_t total_bytes; + uint64_t bytes_used; + uint64_t root_dir_objectid; + uint64_t num_devices; + uint32_t sectorsize; + uint32_t nodesize; + uint32_t leafsize; + uint32_t stripesize; + uint32_t sys_chunk_array_size; + uint64_t chunk_root_generation; + uint64_t compat_flags; + uint64_t compat_ro_flags; + uint64_t incompat_flags; + uint16_t csum_type; + uint8_t root_level; + uint8_t chunk_root_level; + uint8_t log_root_level; + struct btrfs_dev_item { + uint64_t devid; + uint64_t total_bytes; + uint64_t bytes_used; + uint32_t io_align; + uint32_t io_width; + uint32_t sector_size; + uint64_t type; + uint64_t generation; + uint64_t start_offset; + uint32_t dev_group; + uint8_t seek_speed; + uint8_t bandwidth; + uint8_t uuid[16]; + uint8_t fsid[16]; + } __attribute__ ((__packed__)) dev_item; + uint8_t label[256]; } __attribute__ ((__packed__)); - #define BTRFS_IOCTL_MAGIC 0x94 #define BTRFS_VOL_NAME_MAX 255 #define BTRFS_PATH_NAME_MAX 4087 @@ -110,6 +147,23 @@ struct btrfs_ioctl_search_header { __u32 len; } __attribute__((may_alias)); +#define BTRFS_DEVICE_PATH_NAME_MAX 1024 +struct btrfs_ioctl_dev_info_args { + __u64 devid; /* in/out */ + __u8 uuid[BTRFS_UUID_SIZE]; /* in/out */ + __u64 bytes_used; /* out */ + __u64 total_bytes; /* out */ + __u64 unused[379]; /* pad to 4k */ + __u8 path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */ +}; + +struct btrfs_ioctl_fs_info_args { + __u64 max_id; /* out */ + __u64 num_devices; /* out */ + __u8 fsid[BTRFS_FSID_SIZE]; /* out */ + __u64 reserved[124]; /* pad to 1k */ +}; + #define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key)) /* * the buf is an array of search headers where @@ -123,5 +177,9 @@ struct btrfs_ioctl_search_args { #define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \ struct btrfs_ioctl_search_args) +#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \ + struct btrfs_ioctl_dev_info_args) +#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \ + struct btrfs_ioctl_fs_info_args) #endif diff --git a/extlinux/main.c b/extlinux/main.c index e574051b..f0d8e11b 100755..100644 --- a/extlinux/main.c +++ b/extlinux/main.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- * * * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved - * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin + * Copyright 2009-2012 Intel Corporation; author: H. Peter Anvin * * 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 @@ -14,7 +14,7 @@ /* * extlinux.c * - * Install the syslinux boot block on an fat, ext2/3/4 and btrfs filesystem + * Install the syslinux boot block on an fat, ntfs, ext2/3/4 and btrfs filesystem */ #define _GNU_SOURCE /* Enable everything */ @@ -45,11 +45,14 @@ #include "btrfs.h" #include "fat.h" +#include "ntfs.h" #include "../version.h" #include "syslxint.h" #include "syslxcom.h" /* common functions shared with extlinux and syslinux */ +#include "syslxfs.h" #include "setadv.h" #include "syslxopt.h" /* unified options */ +#include "mountinfo.h" #ifdef DEBUG # define dprintf printf @@ -73,7 +76,7 @@ static char subvol[BTRFS_SUBVOL_MAX]; /* * Get the size of a block device */ -uint64_t get_size(int devfd) +static uint64_t get_size(int devfd) { uint64_t bytes; uint32_t sects; @@ -207,14 +210,14 @@ ok: * * Returns the number of modified bytes in the boot file. */ -int patch_file_and_bootblock(int fd, const char *dir, int devfd) +static int patch_file_and_bootblock(int fd, const char *dir, int devfd) { struct stat dirst, xdst; struct hd_geometry geo; sector_t *sectp; uint64_t totalbytes, totalsectors; int nsect; - struct boot_sector *sbs; + struct fat_boot_sector *sbs; char *dirpath, *subpath, *xdirpath; int rv; @@ -271,7 +274,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd) early bootstrap share code with the FAT version. */ dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors); - sbs = (struct boot_sector *)syslinux_bootsect; + sbs = (struct fat_boot_sector *)syslinux_bootsect; totalsectors = totalbytes >> SECTOR_SHIFT; if (totalsectors >= 65536) { @@ -292,7 +295,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd) nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT; nsect += 2; /* Two sectors for the ADV */ sectp = alloca(sizeof(sector_t) * nsect); - if (fs_type == EXT2 || fs_type == VFAT) { + if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS) { if (sectmap(fd, sectp, nsect)) { perror("bmap"); exit(1); @@ -323,7 +326,8 @@ int install_bootblock(int fd, const char *device) { struct ext2_super_block sb; struct btrfs_super_block sb2; - struct boot_sector sb3; + struct fat_boot_sector sb3; + struct ntfs_boot_sector sb4; bool ok = false; if (fs_type == EXT2) { @@ -339,7 +343,7 @@ int install_bootblock(int fd, const char *device) perror("reading superblock"); return 1; } - if (sb2.magic == *(u64 *)BTRFS_MAGIC) + if (!memcmp(sb2.magic, BTRFS_MAGIC, BTRFS_MAGIC_L)) ok = true; } else if (fs_type == VFAT) { if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) { @@ -348,20 +352,39 @@ int install_bootblock(int fd, const char *device) } if (fat_check_sb_fields(&sb3)) ok = true; + } else if (fs_type == NTFS) { + if (xpread(fd, &sb4, sizeof(sb4), 0) != sizeof(sb4)) { + perror("reading ntfs superblock"); + return 1; + } + + if (ntfs_check_sb_fields(&sb4)) + ok = true; } if (!ok) { - fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n", + fprintf(stderr, "no fat, ntfs, ext2/3/4 or btrfs superblock found on %s\n", device); return 1; } if (fs_type == VFAT) { - struct boot_sector *sbs = (struct boot_sector *)syslinux_bootsect; - if (xpwrite(fd, &sbs->bsHead, bsHeadLen, 0) != bsHeadLen || - xpwrite(fd, &sbs->bsCode, bsCodeLen, - offsetof(struct boot_sector, bsCode)) != bsCodeLen) { + struct fat_boot_sector *sbs = (struct fat_boot_sector *)syslinux_bootsect; + if (xpwrite(fd, &sbs->FAT_bsHead, FAT_bsHeadLen, 0) != FAT_bsHeadLen || + xpwrite(fd, &sbs->FAT_bsCode, FAT_bsCodeLen, + offsetof(struct fat_boot_sector, FAT_bsCode)) != FAT_bsCodeLen) { perror("writing fat bootblock"); return 1; } + } else if (fs_type == NTFS) { + struct ntfs_boot_sector *sbs = + (struct ntfs_boot_sector *)syslinux_bootsect; + if (xpwrite(fd, &sbs->NTFS_bsHead, + NTFS_bsHeadLen, 0) != NTFS_bsHeadLen || + xpwrite(fd, &sbs->NTFS_bsCode, NTFS_bsCodeLen, + offsetof(struct ntfs_boot_sector, + NTFS_bsCode)) != NTFS_bsCodeLen) { + perror("writing ntfs bootblock"); + return 1; + } } else { if (xpwrite(fd, syslinux_bootsect, syslinux_bootsect_len, 0) != syslinux_bootsect_len) { @@ -513,35 +536,6 @@ static int test_issubvolume(char *path) } /* - * Get file handle for a file or dir - */ -static int open_file_or_dir(const char *fname) -{ - int ret; - struct stat st; - DIR *dirstream; - int fd; - - ret = stat(fname, &st); - if (ret < 0) { - return -1; - } - if (S_ISDIR(st.st_mode)) { - dirstream = opendir(fname); - if (!dirstream) { - return -2; - } - fd = dirfd(dirstream); - } else { - fd = open(fname, O_RDWR); - } - if (fd < 0) { - return -3; - } - return fd; -} - -/* * Get the default subvolume of a btrfs filesystem * rootdir: btrfs root dir * subvol: this function will save the default subvolume name here @@ -563,7 +557,7 @@ static char * get_default_subvol(char * rootdir, char * subvol) ret = test_issubvolume(rootdir); if (ret == 1) { - fd = open_file_or_dir(rootdir); + fd = open(rootdir, O_RDONLY); if (fd < 0) { fprintf(stderr, "ERROR: failed to open %s\n", rootdir); } @@ -752,9 +746,9 @@ static char * get_default_subvol(char * rootdir, char * subvol) return subvol; } -int install_file(const char *path, int devfd, struct stat *rst) +static int install_file(const char *path, int devfd, struct stat *rst) { - if (fs_type == EXT2 || fs_type == VFAT) + if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS) return ext2_fat_install_file(path, devfd, rst); else if (fs_type == BTRFS) return btrfs_install_file(path, devfd, rst); @@ -772,17 +766,33 @@ static void device_cleanup(void) /* Verify that a device fd and a pathname agree. Return 0 on valid, -1 on error. */ +static int validate_device_btrfs(int pathfd, int devfd); static int validate_device(const char *path, int devfd) { struct stat pst, dst; struct statfs sfs; + int pfd; + int rv = -1; + + pfd = open(path, O_RDONLY|O_DIRECTORY); + if (pfd < 0) + goto err; + + if (fstat(pfd, &pst) || fstat(devfd, &dst) || statfs(path, &sfs)) + goto err; - if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs)) - return -1; /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */ - if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC) - return 0; - return (pst.st_dev == dst.st_rdev) ? 0 : -1; + if (fs_type == BTRFS) { + if (sfs.f_type == BTRFS_SUPER_MAGIC) + rv = validate_device_btrfs(pfd, devfd); + } else { + rv = (pst.st_dev == dst.st_rdev) ? 0 : -1; + } + +err: + if (pfd >= 0) + close(pfd); + return rv; } #ifndef __KLIBC__ @@ -803,37 +813,46 @@ static const char *find_device(const char *mtab_file, dev_t dev) /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */ switch (fs_type) { case BTRFS: - if (!strcmp(mnt->mnt_type, "btrfs") && - !stat(mnt->mnt_dir, &dst) && - dst.st_dev == dev) { - if (!subvol[0]) { - get_default_subvol(mnt->mnt_dir, subvol); - } - done = true; - } - break; + if (!strcmp(mnt->mnt_type, "btrfs") && + !stat(mnt->mnt_dir, &dst) && + dst.st_dev == dev) { + if (!subvol[0]) + get_default_subvol(mnt->mnt_dir, subvol); + done = true; + } + break; case EXT2: - if ((!strcmp(mnt->mnt_type, "ext2") || - !strcmp(mnt->mnt_type, "ext3") || - !strcmp(mnt->mnt_type, "ext4")) && - !stat(mnt->mnt_fsname, &dst) && - dst.st_rdev == dev) { - done = true; - break; - } + if ((!strcmp(mnt->mnt_type, "ext2") || + !strcmp(mnt->mnt_type, "ext3") || + !strcmp(mnt->mnt_type, "ext4")) && + !stat(mnt->mnt_fsname, &dst) && + dst.st_rdev == dev) { + done = true; + break; + } case VFAT: - if ((!strcmp(mnt->mnt_type, "vfat")) && - !stat(mnt->mnt_fsname, &dst) && - dst.st_rdev == dev) { - done = true; - break; - } + if ((!strcmp(mnt->mnt_type, "vfat")) && + !stat(mnt->mnt_fsname, &dst) && + dst.st_rdev == dev) { + done = true; + break; + } + case NTFS: + if ((!strcmp(mnt->mnt_type, "fuseblk") /* ntfs-3g */ || + !strcmp(mnt->mnt_type, "ntfs")) && + !stat(mnt->mnt_fsname, &dst) && + dst.st_rdev == dev) { + done = true; + break; + } + + break; case NONE: break; } if (done) { - devname = strdup(mnt->mnt_fsname); - break; + devname = strdup(mnt->mnt_fsname); + break; } } endmntent(mtab); @@ -842,6 +861,158 @@ static const char *find_device(const char *mtab_file, dev_t dev) } #endif +/* + * On newer Linux kernels we can use sysfs to get a backwards mapping + * from device names to standard filenames + */ +static const char *find_device_sysfs(dev_t dev) +{ + char sysname[64]; + char linkname[PATH_MAX]; + ssize_t llen; + char *p, *q; + char *buf = NULL; + struct stat st; + + snprintf(sysname, sizeof sysname, "/sys/dev/block/%u:%u", + major(dev), minor(dev)); + + llen = readlink(sysname, linkname, sizeof linkname); + if (llen < 0 || llen >= sizeof linkname) + goto err; + + linkname[llen] = '\0'; + + p = strrchr(linkname, '/'); + p = p ? p+1 : linkname; /* Leave basename */ + + buf = q = malloc(strlen(p) + 6); + if (!buf) + goto err; + + memcpy(q, "/dev/", 5); + q += 5; + + while (*p) { + *q++ = (*p == '!') ? '/' : *p; + p++; + } + + *q = '\0'; + + if (!stat(buf, &st) && st.st_dev == dev) + return buf; /* Found it! */ + +err: + if (buf) + free(buf); + return NULL; +} + +static const char *find_device_mountinfo(const char *path, dev_t dev) +{ + const struct mountinfo *m; + struct stat st; + + m = find_mount(path, NULL); + + if (m->devpath[0] == '/' && m->dev == dev && + !stat(m->devpath, &st) && S_ISBLK(st.st_mode) && st.st_rdev == dev) + return m->devpath; + else + return NULL; +} + +static int validate_device_btrfs(int pfd, int dfd) +{ + struct btrfs_ioctl_fs_info_args fsinfo; + static struct btrfs_ioctl_dev_info_args devinfo; + struct btrfs_super_block sb2; + + if (ioctl(pfd, BTRFS_IOC_FS_INFO, &fsinfo)) + return -1; + + /* We do not support multi-device btrfs yet */ + if (fsinfo.num_devices != 1) + return -1; + + /* The one device will have the max devid */ + memset(&devinfo, 0, sizeof devinfo); + devinfo.devid = fsinfo.max_id; + if (ioctl(pfd, BTRFS_IOC_DEV_INFO, &devinfo)) + return -1; + + if (devinfo.path[0] != '/') + return -1; + + if (xpread(dfd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET) != sizeof sb2) + return -1; + + if (memcmp(sb2.magic, BTRFS_MAGIC, BTRFS_MAGIC_L)) + return -1; + + if (memcmp(sb2.fsid, fsinfo.fsid, sizeof fsinfo.fsid)) + return -1; + + if (sb2.num_devices != 1) + return -1; + + if (sb2.dev_item.devid != devinfo.devid) + return -1; + + if (memcmp(sb2.dev_item.uuid, devinfo.uuid, sizeof devinfo.uuid)) + return -1; + + if (memcmp(sb2.dev_item.fsid, fsinfo.fsid, sizeof fsinfo.fsid)) + return -1; + + return 0; /* It's good! */ +} + +static const char *find_device_btrfs(const char *path) +{ + int pfd, dfd; + struct btrfs_ioctl_fs_info_args fsinfo; + static struct btrfs_ioctl_dev_info_args devinfo; + const char *rv = NULL; + + pfd = dfd = -1; + + pfd = open(path, O_RDONLY); + if (pfd < 0) + goto err; + + if (ioctl(pfd, BTRFS_IOC_FS_INFO, &fsinfo)) + goto err; + + /* We do not support multi-device btrfs yet */ + if (fsinfo.num_devices != 1) + goto err; + + /* The one device will have the max devid */ + memset(&devinfo, 0, sizeof devinfo); + devinfo.devid = fsinfo.max_id; + if (ioctl(pfd, BTRFS_IOC_DEV_INFO, &devinfo)) + goto err; + + if (devinfo.path[0] != '/') + goto err; + + dfd = open((const char *)devinfo.path, O_RDONLY); + if (dfd < 0) + goto err; + + if (!validate_device_btrfs(pfd, dfd)) + rv = (const char *)devinfo.path; /* It's good! */ + +err: + if (pfd >= 0) + close(pfd); + if (dfd >= 0) + close(dfd); + return rv; +} + static const char *get_devname(const char *path) { const char *devname = NULL; @@ -856,34 +1027,57 @@ static const char *get_devname(const char *path) fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno)); return devname; } -#ifdef __KLIBC__ - /* klibc doesn't have getmntent and friends; instead, just create - a new device with the appropriate device type */ - snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u", - major(st.st_dev), minor(st.st_dev)); + if (opt.device) + devname = opt.device; - if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) { - fprintf(stderr, "%s: cannot create device %s\n", program, devname); - return devname; + if (!devname){ + if (fs_type == BTRFS) { + /* For btrfs try to get the device name from btrfs itself */ + devname = find_device_btrfs(path); + } } - atexit(device_cleanup); /* unlink the device node on exit */ - devname = devname_buf; + if (!devname) { + devname = find_device_mountinfo(path, st.st_dev); + } -#else +#ifdef __KLIBC__ + if (!devname) { + devname = find_device_sysfs(st.st_dev); + } + if (!devname) { + /* klibc doesn't have getmntent and friends; instead, just create + a new device with the appropriate device type */ + snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u", + major(st.st_dev), minor(st.st_dev)); + + if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) { + fprintf(stderr, "%s: cannot create device %s\n", program, devname); + return devname; + } + + atexit(device_cleanup); /* unlink the device node on exit */ + devname = devname_buf; + } - devname = find_device("/proc/mounts", st.st_dev); +#else + if (!devname) { + devname = find_device("/proc/mounts", st.st_dev); + } if (!devname) { /* Didn't find it in /proc/mounts, try /etc/mtab */ devname = find_device("/etc/mtab", st.st_dev); } if (!devname) { + devname = find_device_sysfs(st.st_dev); + fprintf(stderr, "%s: cannot find device for path %s\n", program, path); return devname; } fprintf(stderr, "%s is device %s\n", path, devname); + #endif return devname; } @@ -910,9 +1104,12 @@ static int open_device(const char *path, struct stat *st, const char **_devname) fs_type = BTRFS; else if (sfs.f_type == MSDOS_SUPER_MAGIC) fs_type = VFAT; + else if (sfs.f_type == NTFS_SB_MAGIC || + sfs.f_type == FUSE_SUPER_MAGIC /* ntfs-3g */) + fs_type = NTFS; if (!fs_type) { - fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n", + fprintf(stderr, "%s: not a fat, ntfs, ext2/3/4 or btrfs filesystem: %s\n", program, path); return -1; } @@ -959,7 +1156,7 @@ static int ext_read_adv(const char *path, int devfd, const char **namep) if (err == 2) /* ldlinux.sys does not exist */ err = read_adv(path, name = "extlinux.sys"); if (namep) - *namep = name; + *namep = name; return err; } } @@ -977,7 +1174,7 @@ static int ext_write_adv(const char *path, const char *cfg, int devfd) return write_adv(path, cfg); } -int install_loader(const char *path, int update_only) +static int install_loader(const char *path, int update_only) { struct stat st, fst; int devfd, rv; diff --git a/extlinux/mountinfo.c b/extlinux/mountinfo.c new file mode 100644 index 00000000..2be87580 --- /dev/null +++ b/extlinux/mountinfo.c @@ -0,0 +1,277 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2012 Intel Corporation; 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. + * + * ----------------------------------------------------------------------- */ + +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include "mountinfo.h" + +/* + * Parse /proc/self/mountinfo + */ +static int get_string(FILE *f, char *string_buf, size_t string_len, char *ec) +{ + int ch; + char *p = string_buf; + + for (;;) { + if (!string_len) + return -2; /* String too long */ + + ch = getc(f); + if (ch == EOF) { + return -1; /* Got EOF */ + } else if (ch == ' ' || ch == '\t' || ch == '\n') { + *ec = ch; + *p = '\0'; + return p - string_buf; + } else if (ch == '\\') { + /* Should always be followed by 3 octal digits in 000..377 */ + int oc = 0; + int i; + for (i = 0; i < 3; i++) { + ch = getc(f); + if (ch < '0' || ch > '7' || (i == 0 && ch > '3')) + return -1; /* Bad escape sequence */ + oc = (oc << 3) + (ch - '0'); + } + if (!oc) + return -1; /* We can't handle \000 */ + *p++ = oc; + string_len--; + } else { + *p++ = ch; + string_len--; + } + } +} + +static void free_mountinfo(struct mountinfo *m) +{ + struct mountinfo *nx; + + while (m) { + free((char *)m->root); + free((char *)m->path); + free((char *)m->fstype); + free((char *)m->devpath); + free((char *)m->mountopt); + nx = m->next; + free(m); + m = nx; + } +} + +static struct mountinfo *head = NULL, **tail = &head; + +static void parse_mountinfo(void) +{ + FILE *f; + struct mountinfo *m, *mm; + char string_buf[PATH_MAX*8]; + int n; + char ec, *ep; + unsigned int ma, mi; + + f = fopen("/proc/self/mountinfo", "r"); + if (!f) + return; + + for (;;) { + m = malloc(sizeof(struct mountinfo)); + if (!m) + break; + memset(m, 0, sizeof *m); + + n = get_string(f, string_buf, sizeof string_buf, &ec); + if (n < 0 || ec == '\n') + break; + + m->mountid = strtoul(string_buf, &ep, 10); + if (*ep) + break; + + n = get_string(f, string_buf, sizeof string_buf, &ec); + if (n < 0 || ec == '\n') + break; + + m->parentid = strtoul(string_buf, &ep, 10); + if (*ep) + break; + + n = get_string(f, string_buf, sizeof string_buf, &ec); + if (n < 0 || ec == '\n') + break; + + if (sscanf(string_buf, "%u:%u", &ma, &mi) != 2) + break; + + m->dev = makedev(ma, mi); + + n = get_string(f, string_buf, sizeof string_buf, &ec); + if (n < 1 || ec == '\n' || string_buf[0] != '/') + break; + + m->root = strdup(string_buf); + if (!m->root) + break; + + n = get_string(f, string_buf, sizeof string_buf, &ec); + if (n < 1 || ec == '\n' || string_buf[0] != '/') + break; + + m->path = strdup(string_buf); + m->pathlen = (n == 1) ? 0 : n; /* Treat / as empty */ + + /* Skip tagged attributes */ + do { + n = get_string(f, string_buf, sizeof string_buf, &ec); + if (n < 0 || ec == '\n') + goto quit; + } while (n != 1 || string_buf[0] != '-'); + + n = get_string(f, string_buf, sizeof string_buf, &ec); + if (n < 0 || ec == '\n') + break; + + m->fstype = strdup(string_buf); + if (!m->fstype) + break; + + n = get_string(f, string_buf, sizeof string_buf, &ec); + if (n < 0 || ec == '\n') + break; + + m->devpath = strdup(string_buf); + if (!m->devpath) + break; + + n = get_string(f, string_buf, sizeof string_buf, &ec); + if (n < 0) + break; + + m->mountopt = strdup(string_buf); + if (!m->mountopt) + break; + + /* Skip any previously unknown fields */ + while (ec != '\n' && ec != EOF) + ec = getc(f); + + *tail = m; + tail = &m->next; + } +quit: + fclose(f); + free_mountinfo(m); + + /* Create parent links */ + for (m = head; m; m = m->next) { + for (mm = head; mm; mm = mm->next) { + if (m->parentid == mm->mountid) { + m->parent = mm; + if (!strcmp(m->path, mm->path)) + mm->hidden = 1; /* Hidden under another mount */ + break; + } + } + } +} + +const struct mountinfo *find_mount(const char *path, char **subpath) +{ + static int done_init; + char *real_path; + const struct mountinfo *m, *best; + struct stat st; + int len, matchlen; + + if (!done_init) { + parse_mountinfo(); + done_init = 1; + } + + if (stat(path, &st)) + return NULL; + + real_path = realpath(path, NULL); + if (!real_path) + return NULL; + + /* + * Tricky business: we need the longest matching subpath + * which isn't a parent of the same subpath. + */ + len = strlen(real_path); + matchlen = 0; + best = NULL; + for (m = head; m; m = m->next) { + if (m->hidden) + continue; /* Hidden underneath another mount */ + + if (m->pathlen > len) + continue; /* Cannot possibly match */ + + if (m->pathlen < matchlen) + continue; /* No point in testing this one */ + + if (st.st_dev == m->dev && + !memcmp(m->path, real_path, m->pathlen) && + (real_path[m->pathlen] == '/' || real_path[m->pathlen] == '\0')) { + matchlen = m->pathlen; + best = m; + } + } + + if (best && subpath) { + if (real_path[best->pathlen] == '\0') + *subpath = strdup("/"); + else + *subpath = strdup(real_path + best->pathlen); + } + + return best; +} + +#ifdef TEST + +int main(int argc, char *argv[]) +{ + int i; + const struct mountinfo *m; + char *subpath; + + parse_mountinfo(); + + for (i = 1; i < argc; i++) { + m = find_mount(argv[i], &subpath); + if (!m) { + printf("%s: %s\n", argv[i], strerror(errno)); + continue; + } + + printf("%s -> %s @ %s(%u,%u):%s %s %s\n", + argv[i], subpath, m->devpath, major(m->dev), minor(m->dev), + m->root, m->fstype, m->mountopt); + printf("Usable device: %s\n", find_device(m->dev, m->devpath)); + free(subpath); + } + + return 0; +} + +#endif diff --git a/extlinux/mountinfo.h b/extlinux/mountinfo.h new file mode 100644 index 00000000..9cbcac12 --- /dev/null +++ b/extlinux/mountinfo.h @@ -0,0 +1,35 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2012 Intel Corporation; 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. + * + * ----------------------------------------------------------------------- */ + +#ifndef SYSLINUX_MOUNTINFO_H +#define SYSLINUX_MOUNTINFO_H + +#include <sys/types.h> + +struct mountinfo { + struct mountinfo *next; + struct mountinfo *parent; + const char *root; + const char *path; + const char *fstype; + const char *devpath; + const char *mountopt; + int mountid; + int parentid; + int pathlen; + int hidden; + dev_t dev; +}; + +const struct mountinfo *find_mount(const char *path, char **subpath); + +#endif /* SYSLINUX_MOUNTINFO_H */ diff --git a/extlinux/ntfs.h b/extlinux/ntfs.h new file mode 100644 index 00000000..d907d452 --- /dev/null +++ b/extlinux/ntfs.h @@ -0,0 +1,19 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2011 Paulo Alcantara <pcacjr@gmail.com> + * + * 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., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef _NTFS_H_ +#define _NTFS_H_ + +#define NTFS_SB_MAGIC 0x5346544E +#define FUSE_SUPER_MAGIC 0x65735546 + +#endif /* _NTFS_H_ */ diff --git a/libinstaller/ext2fs/ext2_fs.h b/libinstaller/ext2fs/ext2_fs.h new file mode 100644 index 00000000..e52c9e1f --- /dev/null +++ b/libinstaller/ext2fs/ext2_fs.h @@ -0,0 +1,856 @@ +/* + * linux/include/linux/ext2_fs.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/include/linux/minix_fs.h + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#ifndef _EXT2FS_EXT2_FS_H +#define _EXT2FS_EXT2_FS_H + +#include <linux/types.h> + +/* + * The second extended filesystem constants/structures + */ + +/* + * Define EXT2FS_DEBUG to produce debug messages + */ +#undef EXT2FS_DEBUG + +/* + * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files + */ +#define EXT2_PREALLOCATE +#define EXT2_DEFAULT_PREALLOC_BLOCKS 8 + +/* + * The second extended file system version + */ +#define EXT2FS_DATE "95/08/09" +#define EXT2FS_VERSION "0.5b" + +/* + * Special inode numbers + */ +#define EXT2_BAD_INO 1 /* Bad blocks inode */ +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT4_USR_QUOTA_INO 3 /* User quota inode */ +#define EXT4_GRP_QUOTA_INO 4 /* Group quota inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ +#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ +#define EXT2_JOURNAL_INO 8 /* Journal inode */ +#define EXT2_EXCLUDE_INO 9 /* The "exclude" inode, for snapshots */ +#define EXT4_REPLICA_INO 10 /* Used by non-upstream feature */ + +/* First non-reserved inode for old ext2 filesystems */ +#define EXT2_GOOD_OLD_FIRST_INO 11 + +/* + * The second extended file system magic number + */ +#define EXT2_SUPER_MAGIC 0xEF53 + +#ifdef __KERNEL__ +#define EXT2_SB(sb) (&((sb)->u.ext2_sb)) +#else +/* Assume that user mode programs are passing in an ext2fs superblock, not + * a kernel struct super_block. This will allow us to call the feature-test + * macros from user land. */ +#define EXT2_SB(sb) (sb) +#endif + +/* + * Maximal count of links to a file + */ +#define EXT2_LINK_MAX 65000 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */ +#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */ +#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE) +#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE) +#ifdef __KERNEL__ +#define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize) +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) +#define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->addr_per_block_bits) +#define EXT2_INODE_SIZE(s) (EXT2_SB(s)->s_inode_size) +#define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino) +#else +#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size) +#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino) +#endif +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32)) + +/* + * Macro-instructions used to manage allocation clusters + */ +#define EXT2_MIN_CLUSTER_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE +#define EXT2_MAX_CLUSTER_LOG_SIZE 29 /* 512MB */ +#define EXT2_MIN_CLUSTER_SIZE EXT2_MIN_BLOCK_SIZE +#define EXT2_MAX_CLUSTER_SIZE (1 << EXT2_MAX_CLUSTER_LOG_SIZE) +#define EXT2_CLUSTER_SIZE(s) (EXT2_MIN_BLOCK_SIZE << \ + (s)->s_log_cluster_size) +#define EXT2_CLUSTER_SIZE_BITS(s) ((s)->s_log_cluster_size + 10) + +/* + * Macro-instructions used to manage fragments + * + * Note: for backwards compatibility only, for the dump program. + * Ext2/3/4 will never support fragments.... + */ +#define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE +#define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE +#define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE +#define EXT2_FRAG_SIZE(s) EXT2_BLOCK_SIZE(s) +#define EXT2_FRAGS_PER_BLOCK(s) 1 + +/* + * ACL structures + */ +struct ext2_acl_header /* Header of Access Control Lists */ +{ + __u32 aclh_size; + __u32 aclh_file_count; + __u32 aclh_acle_count; + __u32 aclh_first_acle; +}; + +struct ext2_acl_entry /* Access Control List Entry */ +{ + __u32 acle_size; + __u16 acle_perms; /* Access permissions */ + __u16 acle_type; /* Type of entry */ + __u16 acle_tag; /* User or group identity */ + __u16 acle_pad1; + __u32 acle_next; /* Pointer on next entry for the */ + /* same inode or on next free entry */ +}; + +/* + * Structure of a blocks group descriptor + */ +struct ext2_group_desc +{ + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_flags; + __u32 bg_exclude_bitmap_lo; /* Exclude bitmap for snapshots */ + __u16 bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */ + __u16 bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */ + __u16 bg_itable_unused; /* Unused inodes count */ + __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/ +}; + +/* + * Structure of a blocks group descriptor + */ +struct ext4_group_desc +{ + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_flags; /* EXT4_BG_flags (INODE_UNINIT, etc) */ + __u32 bg_exclude_bitmap_lo; /* Exclude bitmap for snapshots */ + __u16 bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */ + __u16 bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */ + __u16 bg_itable_unused; /* Unused inodes count */ + __u16 bg_checksum; /* crc16(sb_uuid+group+desc) */ + __u32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */ + __u32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */ + __u32 bg_inode_table_hi; /* Inodes table block MSB */ + __u16 bg_free_blocks_count_hi;/* Free blocks count MSB */ + __u16 bg_free_inodes_count_hi;/* Free inodes count MSB */ + __u16 bg_used_dirs_count_hi; /* Directories count MSB */ + __u16 bg_itable_unused_hi; /* Unused inodes count MSB */ + __u32 bg_exclude_bitmap_hi; /* Exclude bitmap block MSB */ + __u16 bg_block_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+bitmap) MSB */ + __u16 bg_inode_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+bitmap) MSB */ + __u32 bg_reserved; +}; + +#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */ +#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */ +#define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */ + +/* + * Data structures used by the directory indexing feature + * + * Note: all of the multibyte integer fields are little endian. + */ + +/* + * Note: dx_root_info is laid out so that if it should somehow get + * overlaid by a dirent the two low bits of the hash version will be + * zero. Therefore, the hash version mod 4 should never be 0. + * Sincerely, the paranoia department. + */ +struct ext2_dx_root_info { + __u32 reserved_zero; + __u8 hash_version; /* 0 now, 1 at release */ + __u8 info_length; /* 8 */ + __u8 indirect_levels; + __u8 unused_flags; +}; + +#define EXT2_HASH_LEGACY 0 +#define EXT2_HASH_HALF_MD4 1 +#define EXT2_HASH_TEA 2 +#define EXT2_HASH_LEGACY_UNSIGNED 3 /* reserved for userspace lib */ +#define EXT2_HASH_HALF_MD4_UNSIGNED 4 /* reserved for userspace lib */ +#define EXT2_HASH_TEA_UNSIGNED 5 /* reserved for userspace lib */ + +#define EXT2_HASH_FLAG_INCOMPAT 0x1 + +struct ext2_dx_entry { + __u32 hash; + __u32 block; +}; + +struct ext2_dx_countlimit { + __u16 limit; + __u16 count; +}; + + +/* + * Macro-instructions used to manage group descriptors + */ +#define EXT2_MIN_DESC_SIZE 32 +#define EXT2_MIN_DESC_SIZE_64BIT 64 +#define EXT2_MAX_DESC_SIZE EXT2_MIN_BLOCK_SIZE +#define EXT2_DESC_SIZE(s) \ + ((EXT2_SB(s)->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ? \ + (s)->s_desc_size : EXT2_MIN_DESC_SIZE) + +#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group) +#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group) +#define EXT2_CLUSTERS_PER_GROUP(s) (EXT2_SB(s)->s_clusters_per_group) +#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s)) +/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */ +#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((((unsigned) 1 << 16) - 8) * \ + (EXT2_CLUSTER_SIZE(s) / \ + EXT2_BLOCK_SIZE(s))) +#define EXT2_MAX_CLUSTERS_PER_GROUP(s) (((unsigned) 1 << 16) - 8) +#define EXT2_MAX_INODES_PER_GROUP(s) (((unsigned) 1 << 16) - \ + EXT2_INODES_PER_BLOCK(s)) +#ifdef __KERNEL__ +#define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block) +#define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits) +#else +#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_DESC_SIZE(s)) +#endif + +/* + * Constants relative to the data blocks + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* + * Inode flags + */ +#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ +#define EXT2_UNRM_FL 0x00000002 /* Undelete */ +#define EXT2_COMPR_FL 0x00000004 /* Compress file */ +#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ +#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ +#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT2_DIRTY_FL 0x00000100 +#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */ +#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ +/* End compression flags --- maybe not all used */ +#define EXT2_BTREE_FL 0x00001000 /* btree format dir */ +#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ +#define EXT2_IMAGIC_FL 0x00002000 +#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ +#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ +#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ +#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ +#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ +#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ +#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ +/* EXT4_EOFBLOCKS_FL 0x00400000 was here */ +#define EXT4_SNAPFILE_FL 0x01000000 /* Inode is a snapshot */ +#define EXT4_SNAPFILE_DELETED_FL 0x04000000 /* Snapshot is being deleted */ +#define EXT4_SNAPFILE_SHRUNK_FL 0x08000000 /* Snapshot shrink has completed */ +#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ + +#define EXT2_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x004B80FF /* User modifiable flags */ + +/* + * ioctl commands + */ + +/* Used for online resize */ +struct ext2_new_group_input { + __u32 group; /* Group number for this data */ + __u32 block_bitmap; /* Absolute block number of block bitmap */ + __u32 inode_bitmap; /* Absolute block number of inode bitmap */ + __u32 inode_table; /* Absolute block number of inode table start */ + __u32 blocks_count; /* Total number of blocks in this group */ + __u16 reserved_blocks; /* Number of reserved blocks in this group */ + __u16 unused; /* Number of reserved GDT blocks in group */ +}; + +struct ext4_new_group_input { + __u32 group; /* Group number for this data */ + __u64 block_bitmap; /* Absolute block number of block bitmap */ + __u64 inode_bitmap; /* Absolute block number of inode bitmap */ + __u64 inode_table; /* Absolute block number of inode table start */ + __u32 blocks_count; /* Total number of blocks in this group */ + __u16 reserved_blocks; /* Number of reserved blocks in this group */ + __u16 unused; +}; + +#ifdef __GNU__ /* Needed for the Hurd */ +#define _IOT_ext2_new_group_input _IOT (_IOTS(__u32), 5, _IOTS(__u16), 2, 0, 0) +#endif + +#define EXT2_IOC_GETFLAGS _IOR('f', 1, long) +#define EXT2_IOC_SETFLAGS _IOW('f', 2, long) +#define EXT2_IOC_GETVERSION _IOR('v', 1, long) +#define EXT2_IOC_SETVERSION _IOW('v', 2, long) +#define EXT2_IOC_GETVERSION_NEW _IOR('f', 3, long) +#define EXT2_IOC_SETVERSION_NEW _IOW('f', 4, long) +#define EXT2_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long) +#define EXT2_IOC_GROUP_ADD _IOW('f', 8,struct ext2_new_group_input) +#define EXT4_IOC_GROUP_ADD _IOW('f', 8,struct ext4_new_group_input) +#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64) + +/* + * Structure of an inode on the disk + */ +struct ext2_inode { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Inode change time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ + union { + struct { + __u32 l_i_version; /* was l_i_reserved1 */ + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + } osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + __u32 i_generation; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_size_high; /* Formerly i_dir_acl, directory ACL */ + __u32 i_faddr; /* Fragment address */ + union { + struct { + __u16 l_i_blocks_hi; + __u16 l_i_file_acl_high; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u16 l_i_checksum_lo; /* crc32c(uuid+inum+inode) */ + __u16 l_i_reserved; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + } osd2; /* OS dependent 2 */ +}; + +/* + * Permanent part of an large inode on the disk + */ +struct ext2_inode_large { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Inode Change time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ + union { + struct { + __u32 l_i_version; /* was l_i_reserved1 */ + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + } osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + __u32 i_generation; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_size_high; /* Formerly i_dir_acl, directory ACL */ + __u32 i_faddr; /* Fragment address */ + union { + struct { + __u16 l_i_blocks_hi; + __u16 l_i_file_acl_high; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u16 l_i_checksum_lo; /* crc32c(uuid+inum+inode) */ + __u16 l_i_reserved; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + } osd2; /* OS dependent 2 */ + __u16 i_extra_isize; + __u16 i_checksum_hi; /* crc32c(uuid+inum+inode) */ + __u32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */ + __u32 i_mtime_extra; /* extra Modification time (nsec << 2 | epoch) */ + __u32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */ + __u32 i_crtime; /* File creation time */ + __u32 i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/ + __u32 i_version_hi; /* high 32 bits for 64-bit version */ +}; + +#define i_dir_acl i_size_high + +#if defined(__KERNEL__) || defined(__linux__) +#define i_reserved1 osd1.linux1.l_i_reserved1 +#define i_frag osd2.linux2.l_i_frag +#define i_fsize osd2.linux2.l_i_fsize +#define i_uid_low i_uid +#define i_gid_low i_gid +#define i_uid_high osd2.linux2.l_i_uid_high +#define i_gid_high osd2.linux2.l_i_gid_high +#else +#if defined(__GNU__) + +#define i_translator osd1.hurd1.h_i_translator +#define i_frag osd2.hurd2.h_i_frag; +#define i_fsize osd2.hurd2.h_i_fsize; +#define i_uid_high osd2.hurd2.h_i_uid_high +#define i_gid_high osd2.hurd2.h_i_gid_high +#define i_author osd2.hurd2.h_i_author + +#endif /* __GNU__ */ +#endif /* defined(__KERNEL__) || defined(__linux__) */ + +#define inode_uid(inode) ((inode).i_uid | (inode).osd2.linux2.l_i_uid_high << 16) +#define inode_gid(inode) ((inode).i_gid | (inode).osd2.linux2.l_i_gid_high << 16) +#define ext2fs_set_i_uid_high(inode,x) ((inode).osd2.linux2.l_i_uid_high = (x)) +#define ext2fs_set_i_gid_high(inode,x) ((inode).osd2.linux2.l_i_gid_high = (x)) + +/* + * File system states + */ +#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ +#define EXT2_ERROR_FS 0x0002 /* Errors detected */ +#define EXT3_ORPHAN_FS 0x0004 /* Orphans being recovered */ + +/* + * Misc. filesystem flags + */ +#define EXT2_FLAGS_SIGNED_HASH 0x0001 /* Signed dirhash in use */ +#define EXT2_FLAGS_UNSIGNED_HASH 0x0002 /* Unsigned dirhash in use */ +#define EXT2_FLAGS_TEST_FILESYS 0x0004 /* OK for use on development code */ +#define EXT2_FLAGS_IS_SNAPSHOT 0x0010 /* This is a snapshot image */ +#define EXT2_FLAGS_FIX_SNAPSHOT 0x0020 /* Snapshot inodes corrupted */ +#define EXT2_FLAGS_FIX_EXCLUDE 0x0040 /* Exclude bitmaps corrupted */ + +/* + * Mount flags + */ +#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ +#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ +#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ +#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ +#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ +#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ +#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ +#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ + +#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt +#define set_opt(o, opt) o |= EXT2_MOUNT_##opt +#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \ + EXT2_MOUNT_##opt) +/* + * Maximal mount counts between two filesystem checks + */ +#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ +#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ + +/* + * Behaviour when detecting errors + */ +#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ +#define EXT2_ERRORS_RO 2 /* Remount fs read-only */ +#define EXT2_ERRORS_PANIC 3 /* Panic */ +#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE + +#if (__GNUC__ >= 4) +#define ext4_offsetof(TYPE,MEMBER) __builtin_offsetof(TYPE,MEMBER) +#else +#define ext4_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/* + * Structure of the super block + */ +struct ext2_super_block { + __u32 s_inodes_count; /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + __u32 s_free_inodes_count; /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __u32 s_log_cluster_size; /* Allocation cluster size */ + __u32 s_blocks_per_group; /* # Blocks per group */ + __u32 s_clusters_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + __u32 s_wtime; /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_minor_rev_level; /* minor revision level */ + __u32 s_lastcheck; /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __u32 s_first_ino; /* First non-reserved inode */ + __u16 s_inode_size; /* size of inode structure */ + __u16 s_block_group_nr; /* block group # of this superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + __u32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_reserved_gdt_blocks; /* Per group table for online growth */ + /* + * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. + */ + __u8 s_journal_uuid[16]; /* uuid of journal superblock */ + __u32 s_journal_inum; /* inode number of journal file */ + __u32 s_journal_dev; /* device number of journal file */ + __u32 s_last_orphan; /* start of list of inodes to delete */ + __u32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_jnl_backup_type; /* Default type of journal backup */ + __u16 s_desc_size; /* Group desc. size: INCOMPAT_64BIT */ + __u32 s_default_mount_opts; + __u32 s_first_meta_bg; /* First metablock group */ + __u32 s_mkfs_time; /* When the filesystem was created */ + __u32 s_jnl_blocks[17]; /* Backup of the journal inode */ + __u32 s_blocks_count_hi; /* Blocks count high 32bits */ + __u32 s_r_blocks_count_hi; /* Reserved blocks count high 32 bits*/ + __u32 s_free_blocks_hi; /* Free blocks count */ + __u16 s_min_extra_isize; /* All inodes have at least # bytes */ + __u16 s_want_extra_isize; /* New inodes should reserve # bytes */ + __u32 s_flags; /* Miscellaneous flags */ + __u16 s_raid_stride; /* RAID stride */ + __u16 s_mmp_update_interval; /* # seconds to wait in MMP checking */ + __u64 s_mmp_block; /* Block for multi-mount protection */ + __u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/ + __u8 s_log_groups_per_flex; /* FLEX_BG group size */ + __u8 s_reserved_char_pad; + __u16 s_reserved_pad; /* Padding to next 32bits */ + __u64 s_kbytes_written; /* nr of lifetime kilobytes written */ + __u32 s_snapshot_inum; /* Inode number of active snapshot */ + __u32 s_snapshot_id; /* sequential ID of active snapshot */ + __u64 s_snapshot_r_blocks_count; /* reserved blocks for active + snapshot's future use */ + __u32 s_snapshot_list; /* inode number of the head of the on-disk snapshot list */ +#define EXT4_S_ERR_START ext4_offsetof(struct ext2_super_block, s_error_count) + __u32 s_error_count; /* number of fs errors */ + __u32 s_first_error_time; /* first time an error happened */ + __u32 s_first_error_ino; /* inode involved in first error */ + __u64 s_first_error_block; /* block involved of first error */ + __u8 s_first_error_func[32]; /* function where the error happened */ + __u32 s_first_error_line; /* line number where error happened */ + __u32 s_last_error_time; /* most recent time of an error */ + __u32 s_last_error_ino; /* inode involved in last error */ + __u32 s_last_error_line; /* line number where error happened */ + __u64 s_last_error_block; /* block involved of last error */ + __u8 s_last_error_func[32]; /* function where the error happened */ +#define EXT4_S_ERR_END ext4_offsetof(struct ext2_super_block, s_mount_opts) + __u8 s_mount_opts[64]; + __u32 s_usr_quota_inum; /* inode number of user quota file */ + __u32 s_grp_quota_inum; /* inode number of group quota file */ + __u32 s_overhead_blocks; /* overhead blocks/clusters in fs */ + __u32 s_reserved[108]; /* Padding to the end of the block */ + __u32 s_checksum; /* crc32c(superblock) */ +}; + +#define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START) + +/* + * Codes for operating systems + */ +#define EXT2_OS_LINUX 0 +#define EXT2_OS_HURD 1 +#define EXT2_OBSO_OS_MASIX 2 +#define EXT2_OS_FREEBSD 3 +#define EXT2_OS_LITES 4 + +/* + * Revision levels + */ +#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV +#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV + +#define EXT2_GOOD_OLD_INODE_SIZE 128 + +/* + * Journal inode backup types + */ +#define EXT3_JNL_BACKUP_BLOCKS 1 + +/* + * Feature set definitions + */ + +#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_compat & (mask) ) +#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_ro_compat & (mask) ) +#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_incompat & (mask) ) + +#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 +#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 +#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010 +#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 +#define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040 +/* #define EXT2_FEATURE_COMPAT_EXCLUDE_INODE 0x0080 not used, legacy */ +#define EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP 0x0100 + + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */ +#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 +#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 +#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 +#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 +#define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT 0x0080 +#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100 +#define EXT4_FEATURE_RO_COMPAT_BIGALLOC 0x0200 +#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400 +#define EXT4_FEATURE_RO_COMPAT_REPLICA 0x0800 + +#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 +#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 +#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400 +#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 + +#define EXT2_FEATURE_COMPAT_SUPP 0 +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ + EXT4_FEATURE_INCOMPAT_MMP) +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) + +/* + * Default values for user and/or group using reserved blocks + */ +#define EXT2_DEF_RESUID 0 +#define EXT2_DEF_RESGID 0 + +/* + * Default mount options + */ +#define EXT2_DEFM_DEBUG 0x0001 +#define EXT2_DEFM_BSDGROUPS 0x0002 +#define EXT2_DEFM_XATTR_USER 0x0004 +#define EXT2_DEFM_ACL 0x0008 +#define EXT2_DEFM_UID16 0x0010 +#define EXT3_DEFM_JMODE 0x0060 +#define EXT3_DEFM_JMODE_DATA 0x0020 +#define EXT3_DEFM_JMODE_ORDERED 0x0040 +#define EXT3_DEFM_JMODE_WBACK 0x0060 +#define EXT4_DEFM_NOBARRIER 0x0100 +#define EXT4_DEFM_BLOCK_VALIDITY 0x0200 +#define EXT4_DEFM_DISCARD 0x0400 +#define EXT4_DEFM_NODELALLOC 0x0800 + +/* + * Structure of a directory entry + */ +#define EXT2_NAME_LEN 255 + +struct ext2_dir_entry { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u16 name_len; /* Name length */ + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * The new version of the directory entry. Since EXT2 structures are + * stored in intel byte order, and the name_len field could never be + * bigger than 255 chars, it's safe to reclaim the extra byte for the + * file_type field. + */ +struct ext2_dir_entry_2 { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +#define EXT2_FT_UNKNOWN 0 +#define EXT2_FT_REG_FILE 1 +#define EXT2_FT_DIR 2 +#define EXT2_FT_CHRDEV 3 +#define EXT2_FT_BLKDEV 4 +#define EXT2_FT_FIFO 5 +#define EXT2_FT_SOCK 6 +#define EXT2_FT_SYMLINK 7 + +#define EXT2_FT_MAX 8 + +/* + * EXT2_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 4 + */ +#define EXT2_DIR_PAD 4 +#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) +#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ + ~EXT2_DIR_ROUND) + +/* + * This structure is used for multiple mount protection. It is written + * into the block number saved in the s_mmp_block field in the superblock. + * Programs that check MMP should assume that if SEQ_FSCK (or any unknown + * code above SEQ_MAX) is present then it is NOT safe to use the filesystem, + * regardless of how old the timestamp is. + * + * The timestamp in the MMP structure will be updated by e2fsck at some + * arbitary intervals (start of passes, after every few groups of inodes + * in pass1 and pass1b). There is no guarantee that e2fsck is updating + * the MMP block in a timely manner, and the updates it does are purely + * for the convenience of the sysadmin and not for automatic validation. + * + * Note: Only the mmp_seq value is used to determine whether the MMP block + * is being updated. The mmp_time, mmp_nodename, and mmp_bdevname + * fields are only for informational purposes for the administrator, + * due to clock skew between nodes and hostname HA service takeover. + */ +#define EXT4_MMP_MAGIC 0x004D4D50U /* ASCII for MMP */ +#define EXT4_MMP_SEQ_CLEAN 0xFF4D4D50U /* mmp_seq value for clean unmount */ +#define EXT4_MMP_SEQ_FSCK 0xE24D4D50U /* mmp_seq value when being fscked */ +#define EXT4_MMP_SEQ_MAX 0xE24D4D4FU /* maximum valid mmp_seq value */ + +struct mmp_struct { + __u32 mmp_magic; /* Magic number for MMP */ + __u32 mmp_seq; /* Sequence no. updated periodically */ + __u64 mmp_time; /* Time last updated */ + char mmp_nodename[64]; /* Node which last updated MMP block */ + char mmp_bdevname[32]; /* Bdev which last updated MMP block */ + __u16 mmp_check_interval; /* Changed mmp_check_interval */ + __u16 mmp_pad1; + __u32 mmp_pad2[227]; +}; + +/* + * Default interval for MMP update in seconds. + */ +#define EXT4_MMP_UPDATE_INTERVAL 5 + +/* + * Maximum interval for MMP update in seconds. + */ +#define EXT4_MMP_MAX_UPDATE_INTERVAL 300 + +/* + * Minimum interval for MMP checking in seconds. + */ +#define EXT4_MMP_MIN_CHECK_INTERVAL 5 + +#endif /* _EXT2FS_EXT2_FS_H */ diff --git a/libinstaller/fat.c b/libinstaller/fs.c index 9cde00c2..179629e9 100644 --- a/libinstaller/fat.c +++ b/libinstaller/fs.c @@ -1,7 +1,8 @@ /* ----------------------------------------------------------------------- * * - * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved - * Copyright 2009-2010 Intel Corporation; author H. Peter Anvin + * Copyright 1998-2011 H. Peter Anvin - All Rights Reserved + * Copyright 2009-2011 Intel Corporation; author H. Peter Anvin + * Copyright 2011 Paulo Alcantara <pcacjr@gmail.com> * * 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 @@ -12,7 +13,7 @@ * ----------------------------------------------------------------------- */ /* - * fat.c - Initial sanity check for FAT-based installers + * fs.c - Generic sanity check for FAT/NTFS-based installers */ #define _XOPEN_SOURCE 500 /* Required on glibc 2.x */ @@ -25,45 +26,41 @@ #include "syslinux.h" #include "syslxint.h" +#include "syslxcom.h" +#include "syslxfs.h" -void syslinux_make_bootsect(void *bs) +void syslinux_make_bootsect(void *bs, int fs_type) { - struct boot_sector *bootsect = bs; - const struct boot_sector *sbs = - (const struct boot_sector *)boot_sector; - - memcpy(&bootsect->bsHead, &sbs->bsHead, bsHeadLen); - memcpy(&bootsect->bsCode, &sbs->bsCode, bsCodeLen); + if (fs_type == VFAT) { + struct fat_boot_sector *bootsect = bs; + const struct fat_boot_sector *sbs = + (const struct fat_boot_sector *)boot_sector; + + memcpy(&bootsect->FAT_bsHead, &sbs->FAT_bsHead, FAT_bsHeadLen); + memcpy(&bootsect->FAT_bsCode, &sbs->FAT_bsCode, FAT_bsCodeLen); + } else if (fs_type == NTFS) { + struct ntfs_boot_sector *bootsect = bs; + const struct ntfs_boot_sector *sbs = + (const struct ntfs_boot_sector *)boot_sector; + + memcpy(&bootsect->NTFS_bsHead, &sbs->NTFS_bsHead, NTFS_bsHeadLen); + memcpy(&bootsect->NTFS_bsCode, &sbs->NTFS_bsCode, NTFS_bsCodeLen); + } } -/* - * Check to see that what we got was indeed an MS-DOS boot sector/superblock; - * Return NULL if OK and otherwise an error message; - */ -const char *syslinux_check_bootsect(const void *bs) +static const char *check_fat_bootsect(const void *bs, int *fs_type) { int sectorsize; + const struct fat_boot_sector *sectbuf = bs; long long sectors, fatsectors, dsectors; long long clusters; int rootdirents, clustersize; - const struct boot_sector *sectbuf = bs; - - /* Must be 0xF0 or 0xF8..0xFF */ - if (get_8(§buf->bsMedia) != 0xF0 && get_8(§buf->bsMedia) < 0xF8) - return "invalid media signature (not a FAT filesystem?)"; sectorsize = get_16(§buf->bsBytesPerSec); - if (sectorsize == SECTOR_SIZE) - ; /* ok */ - else if (sectorsize >= 512 && sectorsize <= 4096 && - (sectorsize & (sectorsize - 1)) == 0) - return "unsupported sectors size"; - else - return "impossible sector size"; clustersize = get_8(§buf->bsSecPerClust); if (clustersize == 0 || (clustersize & (clustersize - 1))) - return "impossible cluster size"; + return "impossible cluster size on an FAT volume"; sectors = get_16(§buf->bsSectors); sectors = sectors ? sectors : get_32(§buf->bsHugeSectors); @@ -79,16 +76,19 @@ const char *syslinux_check_bootsect(const void *bs) dsectors -= (rootdirents + sectorsize / 32 - 1) / sectorsize; if (dsectors < 0) - return "negative number of data sectors"; - - if (fatsectors == 0) - return "zero FAT sectors"; + return "negative number of data sectors on an FAT volume"; clusters = dsectors / clustersize; + fatsectors = get_16(§buf->bsFATsecs); + fatsectors = fatsectors ? fatsectors : get_32(§buf->bs32.FATSz32); + fatsectors *= get_8(§buf->bsFATs); + + if (!fatsectors) + return "zero FAT sectors"; + if (clusters < 0xFFF5) { /* FAT12 or FAT16 */ - if (!get_16(§buf->bsFATsecs)) return "zero FAT sectors (FAT12/16)"; @@ -100,10 +100,10 @@ const char *syslinux_check_bootsect(const void *bs) if (clusters < 0xFF5) return "less than 4084 clusters but claims FAT16"; } else if (!memcmp(§buf->bs16.FileSysType, "FAT32 ", 8)) { - return "less than 65525 clusters but claims FAT32"; + return "less than 65525 clusters but claims FAT32"; } else if (memcmp(§buf->bs16.FileSysType, "FAT ", 8)) { - static char fserr[] = - "filesystem type \"????????\" not supported"; + static char fserr[] = "filesystem type \"????????\" not " + "supported"; memcpy(fserr + 17, §buf->bs16.FileSysType, 8); return fserr; } @@ -119,8 +119,54 @@ const char *syslinux_check_bootsect(const void *bs) memcmp(§buf->bs32.FileSysType, "FAT32 ", 8)) return "missing FAT32 signature"; } else { - return "impossibly large number of clusters"; + return "impossibly large number of clusters on an FAT volume"; } + if (fs_type) + *fs_type = VFAT; + + return NULL; +} + +static const char *check_ntfs_bootsect(const void *bs, int *fs_type) +{ + const struct ntfs_boot_sector *sectbuf = bs; + + if (memcmp(§buf->bsOemName, "NTFS ", 8) && + memcmp(§buf->bsOemName, "MSWIN4.0", 8) && + memcmp(§buf->bsOemName, "MSWIN4.1", 8)) + return "unknown OEM name but claims NTFS"; + + if (fs_type) + *fs_type = NTFS; + return NULL; } + +const char *syslinux_check_bootsect(const void *bs, int *fs_type) +{ + uint8_t media_sig; + int sectorsize; + const struct fat_boot_sector *sectbuf = bs; + const char *retval; + + media_sig = get_8(§buf->bsMedia); + /* Must be 0xF0 or 0xF8-0xFF for FAT/NTFS volumes */ + if (media_sig != 0xF0 && media_sig < 0xF8) + return "invalid media signature (not an FAT/NTFS volume?)"; + + sectorsize = get_16(§buf->bsBytesPerSec); + if (sectorsize == SECTOR_SIZE) ; /* ok */ + else if (sectorsize >= 512 && sectorsize <= 4096 && + (sectorsize & (sectorsize - 1)) == 0) + return "unsupported sectors size"; + else + return "impossible sector size"; + + if (ntfs_check_zero_fields((struct ntfs_boot_sector *)bs)) + retval = check_ntfs_bootsect(bs, fs_type); + else + retval = check_fat_bootsect(bs, fs_type); + + return retval; +} diff --git a/libinstaller/linuxioctl.h b/libinstaller/linuxioctl.h index 7ef919a3..e2731c7c 100644 --- a/libinstaller/linuxioctl.h +++ b/libinstaller/linuxioctl.h @@ -9,22 +9,36 @@ #include <sys/ioctl.h> +#ifdef __linux__ + #define statfs _kernel_statfs /* HACK to deal with broken 2.4 distros */ #include <linux/fd.h> /* Floppy geometry */ #include <linux/hdreg.h> /* Hard disk geometry */ -#include <linux/fs.h> /* FIGETBSZ, FIBMAP, FS_IOC_FIEMAP */ -#include <linux/msdos_fs.h> /* FAT_IOCTL_SET_ATTRIBUTES */ +#include <linux/fs.h> /* FIGETBSZ, FIBMAP, FS_IOC_* */ #undef SECTOR_SIZE /* Defined in msdos_fs.h for no good reason */ #undef SECTOR_BITS -#include <linux/ext2_fs.h> /* EXT2_IOC_* */ + +#ifndef FS_IOC_GETFLAGS +/* Old kernel headers, these were once ext2-specific... */ +# include <linux/ext2_fs.h> /* EXT2_IOC_* */ + +# define FS_IOC_GETFLAGS EXT2_IOC_GETFLAGS +# define FS_IOC_SETFLAGS EXT2_IOC_SETFLAGS + +# define FS_IMMUTABLE_FL EXT2_IMMUTABLE_FL + +#else + +# include <ext2fs/ext2_fs.h> + +#endif #ifndef FAT_IOCTL_GET_ATTRIBUTES # define FAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, __u32) #endif - #ifndef FAT_IOCTL_SET_ATTRIBUTES # define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, __u32) #endif @@ -37,11 +51,13 @@ #undef statfs -#if defined(__linux__) && !defined(BLKGETSIZE64) +#ifndef BLKGETSIZE64 /* This takes a u64, but the size field says size_t. Someone screwed big. */ # define BLKGETSIZE64 _IOR(0x12,114,size_t) #endif #include <linux/loop.h> +#endif /* __linux__ */ + #endif /* LIBINSTALLER_LINUXIOCTL_H */ diff --git a/libinstaller/setadv.c b/libinstaller/setadv.c index 9cf6f188..214f7fc1 100644 --- a/libinstaller/setadv.c +++ b/libinstaller/setadv.c @@ -30,6 +30,7 @@ #include <errno.h> #include "syslxint.h" #include "syslxcom.h" +#include "syslxfs.h" unsigned char syslinux_adv[2 * ADV_SIZE]; diff --git a/libinstaller/syslinux.h b/libinstaller/syslinux.h index 710d30e5..8b86f881 100644 --- a/libinstaller/syslinux.h +++ b/libinstaller/syslinux.h @@ -40,10 +40,10 @@ extern const int syslinux_mbr_mtime; #define SECTOR_SIZE (1 << SECTOR_SHIFT) /* This takes a boot sector and merges in the syslinux fields */ -void syslinux_make_bootsect(void *); +void syslinux_make_bootsect(void *bs, int fs_type); /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */ -const char *syslinux_check_bootsect(const void *bs); +const char *syslinux_check_bootsect(const void *bs, int *fs_type); /* This patches the boot sector and ldlinux.sys based on a sector map */ typedef uint64_t sector_t; diff --git a/libinstaller/syslxcom.c b/libinstaller/syslxcom.c index 1de85aa5..57f13cda 100644 --- a/libinstaller/syslxcom.c +++ b/libinstaller/syslxcom.c @@ -30,8 +30,10 @@ #include <sys/types.h> #include <sys/mount.h> #include <sys/vfs.h> + #include "linuxioctl.h" #include "syslxcom.h" +#include "syslxfs.h" const char *program; @@ -121,9 +123,9 @@ void clear_attributes(int fd) { int flags; - if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) { - flags &= ~EXT2_IMMUTABLE_FL; - ioctl(fd, EXT2_IOC_SETFLAGS, &flags); + if (!ioctl(fd, FS_IOC_GETFLAGS, &flags)) { + flags &= ~FS_IMMUTABLE_FL; + ioctl(fd, FS_IOC_SETFLAGS, &flags); } break; } @@ -133,6 +135,8 @@ void clear_attributes(int fd) ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr); break; } + case NTFS: + break; default: break; } @@ -151,9 +155,9 @@ void set_attributes(int fd) { int flags; - if (st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) { - flags |= EXT2_IMMUTABLE_FL; - ioctl(fd, EXT2_IOC_SETFLAGS, &flags); + if (st.st_uid == 0 && !ioctl(fd, FS_IOC_GETFLAGS, &flags)) { + flags |= FS_IMMUTABLE_FL; + ioctl(fd, FS_IOC_SETFLAGS, &flags); } break; } @@ -163,6 +167,8 @@ void set_attributes(int fd) ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr); break; } + case NTFS: + break; default: break; } diff --git a/libinstaller/syslxcom.h b/libinstaller/syslxcom.h index bf186ca6..8b3b4614 100644 --- a/libinstaller/syslxcom.h +++ b/libinstaller/syslxcom.h @@ -3,15 +3,6 @@ #include "syslinux.h" -/* Global fs_type for handling fat, ext2/3/4 and btrfs */ -enum filesystem { - NONE, - EXT2, - BTRFS, - VFAT, -}; - -extern int fs_type; extern const char *program; ssize_t xpread(int fd, void *buf, size_t count, off_t offset); ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset); diff --git a/libinstaller/syslxfs.h b/libinstaller/syslxfs.h new file mode 100644 index 00000000..7a231461 --- /dev/null +++ b/libinstaller/syslxfs.h @@ -0,0 +1,26 @@ +/* + * Copyright 2011 Paulo Alcantara <pcacjr@gmail.com> + * + * 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., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef _SYSLXFS_H_ +#define _SYSLXFS_H_ + +/* Global fs_type for handling fat, ntfs, ext2/3/4 and btrfs */ +enum filesystem { + NONE, + EXT2, + BTRFS, + VFAT, + NTFS, +}; + +extern int fs_type; + +#endif /* _SYSLXFS_H_ */ diff --git a/libinstaller/syslxint.h b/libinstaller/syslxint.h index 7c9da516..e5428b79 100644 --- a/libinstaller/syslxint.h +++ b/libinstaller/syslxint.h @@ -2,6 +2,7 @@ * * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin + * Copyright 2011 Paulo Alcantara <pcacjr@gmail.com> * * 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 @@ -22,6 +23,17 @@ # define X86_MEM 0 #endif +#ifdef __GNUC__ +# ifdef __MINGW32__ + /* gcc 4.7 miscompiles packed structures in MS-bitfield mode */ +# define PACKED __attribute__((packed,gcc_struct)) +# else +# define PACKED __attribute__((packed)) +# endif +#else +# error "Need to define PACKED for this compiler" +#endif + /* * Access functions for littleendian numbers, possibly misaligned. */ @@ -189,10 +201,10 @@ struct ext_patch_area { struct syslinux_extent { uint64_t lba; uint16_t len; -} __attribute__((packed)); +} PACKED; /* FAT bootsector format, also used by other disk-based derivatives */ -struct boot_sector { +struct fat_boot_sector { uint8_t bsJump[3]; char bsOemName[8]; uint16_t bsBytesPerSec; @@ -217,7 +229,7 @@ struct boot_sector { char VolumeLabel[11]; char FileSysType[8]; uint8_t Code[442]; - } __attribute__ ((packed)) bs16; + } PACKED bs16; struct { uint32_t FATSz32; uint16_t ExtFlags; @@ -233,21 +245,76 @@ struct boot_sector { char VolumeLabel[11]; char FileSysType[8]; uint8_t Code[414]; - } __attribute__ ((packed)) bs32; - } __attribute__ ((packed)); + } PACKED bs32; + } PACKED; + + uint32_t bsMagic; + uint16_t bsForwardPtr; + uint16_t bsSignature; +} PACKED; + +/* NTFS bootsector format */ +struct ntfs_boot_sector { + uint8_t bsJump[3]; + char bsOemName[8]; + uint16_t bsBytesPerSec; + uint8_t bsSecPerClust; + uint16_t bsResSectors; + uint8_t bsZeroed_0[3]; + uint16_t bsZeroed_1; + uint8_t bsMedia; + uint16_t bsZeroed_2; + uint16_t bsUnused_0; + uint16_t bsUnused_1; + uint32_t bsUnused_2; + uint32_t bsZeroed_3; + uint32_t bsUnused_3; + uint64_t bsTotalSectors; + uint64_t bsMFTLogicalClustNr; + uint64_t bsMFTMirrLogicalClustNr; + uint8_t bsClustPerMFTrecord; + uint8_t bsUnused_4[3]; + uint8_t bsClustPerIdxBuf; + uint8_t bsUnused_5[3]; + uint64_t bsVolSerialNr; + uint32_t bsUnused_6; + + uint8_t Code[420]; uint32_t bsMagic; uint16_t bsForwardPtr; uint16_t bsSignature; -} __attribute__ ((packed)); +} PACKED; + +#define FAT_bsHead bsJump +#define FAT_bsHeadLen offsetof(struct fat_boot_sector, bsBytesPerSec) +#define FAT_bsCode bs32.Code /* The common safe choice */ +#define FAT_bsCodeLen (offsetof(struct fat_boot_sector, bsSignature) - \ + offsetof(struct fat_boot_sector, FAT_bsCode)) + +#define NTFS_bsHead bsJump +#define NTFS_bsHeadLen offsetof(struct ntfs_boot_sector, bsOemName) +#define NTFS_bsCode Code +#define NTFS_bsCodeLen (offsetof(struct ntfs_boot_sector, bsSignature) - \ + offsetof(struct ntfs_boot_sector, NTFS_bsCode)) -#define bsHead bsJump -#define bsHeadLen offsetof(struct boot_sector, bsBytesPerSec) -#define bsCode bs32.Code /* The common safe choice */ -#define bsCodeLen (offsetof(struct boot_sector, bsSignature) - \ - offsetof(struct boot_sector, bsCode)) +/* Check if there are specific zero fields in an NTFS boot sector */ +static inline int ntfs_check_zero_fields(const struct ntfs_boot_sector *sb) +{ + return !sb->bsResSectors && (!sb->bsZeroed_0[0] && !sb->bsZeroed_0[1] && + !sb->bsZeroed_0[2]) && !sb->bsZeroed_1 && !sb->bsZeroed_2 && + !sb->bsZeroed_3; +} + +static inline int ntfs_check_sb_fields(const struct ntfs_boot_sector *sb) +{ + return ntfs_check_zero_fields(sb) && + (!memcmp(sb->bsOemName, "NTFS ", 8) || + !memcmp(sb->bsOemName, "MSWIN4.0", 8) || + !memcmp(sb->bsOemName, "MSWIN4.1", 8)); +} -static inline int fat_check_sb_fields(const struct boot_sector *sb) +static inline int fat_check_sb_fields(const struct fat_boot_sector *sb) { return sb->bsResSectors && sb->bsFATs && (!memcmp(sb->bs16.FileSysType, "FAT12 ", 8) || diff --git a/libinstaller/syslxmod.c b/libinstaller/syslxmod.c index 8847b736..c706f2c2 100644 --- a/libinstaller/syslxmod.c +++ b/libinstaller/syslxmod.c @@ -96,8 +96,6 @@ static inline void *ptr(void *img, uint16_t *offset_p) * Returns the number of modified bytes in ldlinux.sys if successful, * otherwise -1. */ -#define NADV 2 - int syslinux_patch(const sector_t *sectp, int nsectors, int stupid, int raid_mode, const char *subdir, const char *subvol) @@ -109,7 +107,7 @@ int syslinux_patch(const sector_t *sectp, int nsectors, int nsect = ((boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT) + 2; uint32_t csum; int i, dw, nptrs; - struct boot_sector *sbs = (struct boot_sector *)boot_sector; + struct fat_boot_sector *sbs = (struct fat_boot_sector *)boot_sector; uint64_t *advptrs; if (nsectors < nsect) diff --git a/libinstaller/syslxopt.c b/libinstaller/syslxopt.c index 7ceb3ba2..b739752f 100644 --- a/libinstaller/syslxopt.c +++ b/libinstaller/syslxopt.c @@ -25,6 +25,7 @@ #include <sysexits.h> #include "../version.h" #include "syslxcom.h" +#include "syslxfs.h" #include "syslxopt.h" /* These are the options we can set their values */ @@ -65,6 +66,7 @@ const struct option long_options[] = { {"menu-save", 1, NULL, 'M'}, {"mbr", 0, NULL, 'm'}, /* DOS/Win32 only */ {"active", 0, NULL, 'a'}, /* DOS/Win32 only */ + {"device", 1, NULL, OPT_DEVICE}, {0, 0, 0, 0} }; @@ -86,7 +88,8 @@ void __attribute__ ((noreturn)) usage(int rv, enum syslinux_mode mode) /* Mounted fs installation (extlinux) */ /* Actually extlinux can also use -d to provide a directory too... */ fprintf(stderr, - "Usage: %s [options] directory\n", + "Usage: %s [options] directory\n" + " --device Force use of a specific block device (experts only)\n", program); break; @@ -209,6 +212,11 @@ void parse_options(int argc, char *argv[], enum syslinux_mode mode) case 'a': opt.activate_partition = 1; break; + case OPT_DEVICE: + if (mode != MODE_EXTLINUX) + usage(EX_USAGE, mode); + opt.device = optarg; + break; case 'v': fprintf(stderr, "%s " VERSION_STR " Copyright 1994-" YEAR_STR diff --git a/libinstaller/syslxopt.h b/libinstaller/syslxopt.h index bcbe0352..042301f6 100644 --- a/libinstaller/syslxopt.h +++ b/libinstaller/syslxopt.h @@ -24,6 +24,7 @@ enum long_only_opt { OPT_NONE, OPT_RESET_ADV, OPT_ONCE, + OPT_DEVICE, }; enum syslinux_mode { diff --git a/linux/Makefile b/linux/Makefile index 82bf1118..08a3ed49 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -11,7 +11,7 @@ ## ----------------------------------------------------------------------- ## -## Linux FAT installer +## Linux FAT/NTFS installer ## topdir = .. @@ -28,7 +28,7 @@ SRCS = syslinux.c \ ../libinstaller/syslxcom.c \ ../libinstaller/setadv.c \ ../libinstaller/advio.c \ - ../libinstaller/fat.c \ + ../libinstaller/fs.c \ ../libinstaller/syslxmod.c \ ../libinstaller/bootsect_bin.c \ ../libinstaller/ldlinux_bin.c diff --git a/linux/syslinux.c b/linux/syslinux.c index c7a9ecc4..4b13b7fe 100755 --- a/linux/syslinux.c +++ b/linux/syslinux.c @@ -69,6 +69,7 @@ #include <getopt.h> #include <sysexits.h> #include "syslxcom.h" +#include "syslxfs.h" #include "setadv.h" #include "syslxopt.h" /* unified options */ @@ -294,14 +295,14 @@ int main(int argc, char *argv[]) die("can't combine an offset with a block device"); } - fs_type = VFAT; xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); fsync(dev_fd); /* - * Check to see that what we got was indeed an MS-DOS boot sector/superblock + * Check to see that what we got was indeed an FAT/NTFS + * boot sector/superblock */ - if ((errmsg = syslinux_check_bootsect(sectbuf))) { + if ((errmsg = syslinux_check_bootsect(sectbuf, &fs_type))) { fprintf(stderr, "%s: %s\n", opt.device, errmsg); exit(1); } @@ -357,10 +358,17 @@ int main(int argc, char *argv[]) mntpath = mntname; } - if (do_mount(dev_fd, &mnt_cookie, mntpath, "vfat") && - do_mount(dev_fd, &mnt_cookie, mntpath, "msdos")) { - rmdir(mntpath); - die("mount failed"); + if (fs_type == VFAT) { + if (do_mount(dev_fd, &mnt_cookie, mntpath, "vfat") && + do_mount(dev_fd, &mnt_cookie, mntpath, "msdos")) { + rmdir(mntpath); + die("failed on mounting fat volume"); + } + } else if (fs_type == NTFS) { + if (do_mount(dev_fd, &mnt_cookie, mntpath, "ntfs-3g")) { + rmdir(mntpath); + die("failed on mounting ntfs volume"); + } } ldlinux_path = alloca(strlen(mntpath) + strlen(subdir) + 1); @@ -474,7 +482,7 @@ umount: xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); /* Copy the syslinux code into the boot sector */ - syslinux_make_bootsect(sectbuf); + syslinux_make_bootsect(sectbuf, fs_type); /* Write new boot sector */ xpwrite(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); diff --git a/man/extlinux.1 b/man/extlinux.1 index 3192122a..5daa4e52 100644 --- a/man/extlinux.1 +++ b/man/extlinux.1 @@ -45,8 +45,17 @@ Updates a previous \fBEXTLINUX\fP installation. .TP \fB\-z\fR, \fB\-\-zip\fR Force zipdrive geometry (-H 64 -S 32). +.TP +\fB\-\-device\fR=\fIdevicename\fR +Override the automatic detection of device names. This option is +intended for special environments only and should not be used by +normal users. Misuse of this option can cause disk corruption and +lost data. .SH FILES -The extlinux configuration file needs to be named extlinux.conf and needs to be stored in the extlinux installation directory. For more information about the contents of extlinux.conf, see syslinux(1) manpage, section files. +The extlinux configuration file needs to be named syslinux.cfg or +extlinux.conf and needs to be stored in the extlinux installation +directory. For more information about the contents of extlinux.conf, +see syslinux(1) manpage, section files. .SH BUGS I would appreciate hearing of any problems you have with \s-1SYSLINUX\s+1. I would also like to hear from you if you have successfully used \s-1SYSLINUX\s+1, diff --git a/mbr/altmbr.S b/mbr/altmbr.S index 1b609051..c66b4ddc 100644 --- a/mbr/altmbr.S +++ b/mbr/altmbr.S @@ -204,7 +204,7 @@ scan_partition_table: 5: decb (partition) jz boot - addw $16, %bx + addw $16, %si loopw 5b popw %cx /* %cx <- 4 */ diff --git a/memdisk/Makefile b/memdisk/Makefile index 5475b44d..b5cd52ce 100644 --- a/memdisk/Makefile +++ b/memdisk/Makefile @@ -20,7 +20,7 @@ INCLUDES = -I$(topdir)/com32/include CFLAGS += -D__MEMDISK__ -DDATE='"$(DATE)"' LDFLAGS = $(GCCOPT) -g NASM = nasm -NASMOPT = -O9999 +NASMOPT = -Ox NFLAGS = -dDATE='"$(DATE)"' NINCLUDE = diff --git a/memdisk/mstructs.h b/memdisk/mstructs.h index fecbff40..0b0dc07d 100644 --- a/memdisk/mstructs.h +++ b/memdisk/mstructs.h @@ -121,9 +121,9 @@ typedef union { uint8_t specify1; /* "First specify byte" */ uint8_t specify2; /* "Second specify byte" */ uint8_t delay; /* Delay until motor turn off */ - uint8_t sectors; /* Sectors/track */ - uint8_t bps; /* Bytes/sector (02h = 512) */ + + uint8_t sectors; /* Sectors/track */ uint8_t isgap; /* Length of intersector gap */ uint8_t dlen; /* Data length (0FFh) */ uint8_t fgap; /* Formatting gap */ diff --git a/mtools/Makefile b/mtools/Makefile index 3e172fd6..78cea1e2 100755 --- a/mtools/Makefile +++ b/mtools/Makefile @@ -8,7 +8,7 @@ CFLAGS = $(GCCWARN) -D_FILE_OFFSET_BITS=64 $(OPTFLAGS) $(INCLUDES) LDFLAGS = SRCS = syslinux.c \ - ../libinstaller/fat.c \ + ../libinstaller/fs.c \ ../libinstaller/syslxmod.c \ ../libinstaller/syslxopt.c \ ../libinstaller/setadv.c \ diff --git a/mtools/syslinux.c b/mtools/syslinux.c index ac189c61..c65021bb 100755 --- a/mtools/syslinux.c +++ b/mtools/syslinux.c @@ -41,6 +41,7 @@ #include "libfat.h" #include "setadv.h" #include "syslxopt.h" +#include "syslxfs.h" char *program; /* Name of program */ pid_t mypid; @@ -197,7 +198,7 @@ int main(int argc, char *argv[]) /* * Check to see that what we got was indeed an MS-DOS boot sector/superblock */ - if ((errmsg = syslinux_check_bootsect(sectbuf))) { + if ((errmsg = syslinux_check_bootsect(sectbuf, NULL))) { die(errmsg); } @@ -356,7 +357,7 @@ int main(int argc, char *argv[]) xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); /* Copy the syslinux code into the boot sector */ - syslinux_make_bootsect(sectbuf); + syslinux_make_bootsect(sectbuf, VFAT); /* Write new boot sector */ xpwrite(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); diff --git a/utils/Makefile b/utils/Makefile index 44cb54fb..be739935 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -51,7 +51,7 @@ isohdpfx.c: $(ISOHDPFX) isohdpfxarray.pl $(PERL) isohdpfxarray.pl $(ISOHDPFX) > $@ isohybrid: isohybrid.o isohdpfx.o - $(CC) $(LDFLAGS) -luuid -o $@ $^ + $(CC) $(LDFLAGS) -o $@ $^ -luuid gethostip: gethostip.o $(CC) $(LDFLAGS) -o $@ $^ diff --git a/utils/isohybrid.c b/utils/isohybrid.c index 1dcbaa11..97d43b86 100644 --- a/utils/isohybrid.c +++ b/utils/isohybrid.c @@ -54,7 +54,7 @@ enum { VERBOSE = 1 , EFI = 2 , MAC = 4}; uint16_t head = 64; /* 1 <= head <= 256 */ uint8_t sector = 32; /* 1 <= sector <= 63 */ -uint8_t entry = 1; /* partition number: 1 <= entry <= 4 */ +uint8_t entry = 0; /* partition number: 1 <= entry <= 4 */ uint8_t offset = 0; /* partition offset: 0 <= offset <= 64 */ uint16_t type = 0x17; /* partition type: 0 <= type <= 255 */ uint32_t id = 0; /* MBR: 0 <= id <= 0xFFFFFFFF(4294967296) */ @@ -300,6 +300,8 @@ check_option(int argc, char *argv[]) entry = strtoul(optarg, &err, 0); if (entry < 1 || entry > 4) errx(1, "invalid entry: `%s', 1 <= entry <= 4", optarg); + if (mode & MAC || mode & EFI) + errx(1, "setting an entry is unsupported with EFI or Mac"); break; case 'o': @@ -334,10 +336,14 @@ check_option(int argc, char *argv[]) case 'u': mode |= EFI; + if (entry) + errx(1, "setting an entry is unsupported with EFI or Mac"); break; case 'm': mode |= MAC; + if (entry) + errx(1, "setting an entry is unsupported with EFI or Mac"); break; case 'v': @@ -428,10 +434,10 @@ lendian_64(const uint64_t s) if (*(uint8_t *)&r) return s; - r = (s & 0x00000000000000FF) << 56 | (s & 0xFF00000000000000) >> 56 - | (s & 0x000000000000FF00) << 40 | (s & 0x00FF000000000000) >> 40 - | (s & 0x0000000000FF0000) << 24 | (s & 0x0000FF0000000000) >> 24 - | (s & 0x00000000FF000000) << 8 | (s & 0x000000FF00000000) >> 8; + r = (s & 0x00000000000000FFull) << 56 | (s & 0xFF00000000000000ull) >> 56 + | (s & 0x000000000000FF00ull) << 40 | (s & 0x00FF000000000000ull) >> 40 + | (s & 0x0000000000FF0000ull) << 24 | (s & 0x0000FF0000000000ull) >> 24 + | (s & 0x00000000FF000000ull) << 8 | (s & 0x000000FF00000000ull) >> 8; return r; } @@ -581,6 +587,12 @@ initialise_mbr(uint8_t *mbr) memcpy(mbr, afp_header, sizeof(afp_header)); } + if (!entry) + entry = 1; + + if (mode & EFI) + type = 0; + mbr += MBRSIZE; /* offset 432 */ tmp = lendian_int(de_lba * 4); @@ -633,6 +645,40 @@ initialise_mbr(uint8_t *mbr) tmp = lendian_int(psize); memcpy(&mbr[12], &tmp, sizeof(tmp)); } + if (i == 2 && (mode & EFI)) + { + mbr[0] = 0x0; + mbr[1] = 0xfe; + mbr[2] = 0xff; + mbr[3] = 0xff; + mbr[4] = 0xef; + mbr[5] = 0xfe; + mbr[6] = 0xff; + mbr[7] = 0xff; + + tmp = lendian_int(efi_lba * 4); + memcpy(&mbr[8], &tmp, sizeof(tmp)); + + tmp = lendian_int(efi_count); + memcpy(&mbr[12], &tmp, sizeof(tmp)); + } + if (i == 3 && (mode & MAC)) + { + mbr[0] = 0x0; + mbr[1] = 0xfe; + mbr[2] = 0xff; + mbr[3] = 0xff; + mbr[4] = 0x0; + mbr[5] = 0xfe; + mbr[6] = 0xff; + mbr[7] = 0xff; + + tmp = lendian_int(mac_lba * 4); + memcpy(&mbr[8], &tmp, sizeof(tmp)); + + tmp = lendian_int(mac_count); + memcpy(&mbr[12], &tmp, sizeof(tmp)); + } mbr += 16; } mbr[0] = 0x55; @@ -713,7 +759,7 @@ initialise_gpt(uint8_t *gpt, uint32_t current, uint32_t alternate, int primary) reverse_uuid(disk_uuid); } - header->signature = lendian_64(0x5452415020494645); + header->signature = lendian_64(0x5452415020494645ull); header->revision = lendian_int(0x010000); header->headerSize = lendian_int(0x5c); header->currentLBA = lendian_64(current); @@ -908,8 +954,7 @@ main(int argc, char *argv[]) if (!read_efi_section(buf)) { buf += 32; if (!read_efi_catalogue(buf, &efi_count, &efi_lba) && efi_lba) { - offset = 1; - type = 0xee; + offset = 0; } else { errx(1, "%s: invalid efi catalogue", argv[0]); } @@ -925,8 +970,7 @@ main(int argc, char *argv[]) if (!read_efi_section(buf)) { buf += 32; if (!read_efi_catalogue(buf, &mac_count, &mac_lba) && mac_lba) { - offset = 1; - type = 0xee; + offset = 0; } else { errx(1, "%s: invalid efi catalogue", argv[0]); } @@ -1 +1 @@ -4.05 2011 +4.06 2012 diff --git a/win/ntfssect.c b/win/ntfssect.c new file mode 100644 index 00000000..8c2bccaa --- /dev/null +++ b/win/ntfssect.c @@ -0,0 +1,355 @@ +/* -------------------------------------------------------------------------- * + * + * Copyright 2011 Shao Miller - 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., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +/**** + * ntfssect.c + * + * Fetch NTFS file cluster & sector information via Windows + * + * With special thanks to Mark Roddy for his article: + * http://www.wd-3.com/archive/luserland.htm + */ + +#include <windows.h> +#include <winioctl.h> +#include <stddef.h> +#include <string.h> + +#include "ntfssect.h" + +/*** Macros */ +#define M_ERR(msg) (NtfsSectLastErrorMessage = (msg)) + +/*** Function declarations */ +static DWORD NtfsSectGetVolumeHandle( + CHAR * VolumeName, + S_NTFSSECT_VOLINFO * VolumeInfo + ); +static DWORD NtfsSectGetVolumePartitionLba(S_NTFSSECT_VOLINFO * VolumeInfo); + +/*** Objects */ +CHAR * NtfsSectLastErrorMessage; + +/*** Function definitions */ +DWORD M_NTFSSECT_API NtfsSectGetFileVcnExtent( + HANDLE File, + LARGE_INTEGER * Vcn, + S_NTFSSECT_EXTENT * Extent + ) { + BOOL bad, ok; + DWORD output_size, rc; + STARTING_VCN_INPUT_BUFFER input; + RETRIEVAL_POINTERS_BUFFER output; + + bad = ( + File == INVALID_HANDLE_VALUE || + !Vcn || + Vcn->QuadPart < 0 || + !Extent + ); + if (bad) + return ERROR_INVALID_PARAMETER; + + input.StartingVcn = *Vcn; + ok = DeviceIoControl( + File, + FSCTL_GET_RETRIEVAL_POINTERS, + &input, + sizeof input, + &output, + sizeof output, + &output_size, + NULL + ); + rc = GetLastError(); + switch (rc) { + case NO_ERROR: + case ERROR_MORE_DATA: + Extent->FirstVcn = output.StartingVcn; + Extent->NextVcn = output.Extents[0].NextVcn; + Extent->FirstLcn = output.Extents[0].Lcn; + return ERROR_SUCCESS; + + case ERROR_HANDLE_EOF: + break; + + default: + M_ERR("NtfsSectGetFileVcnExtent(): Unknown status!"); + } + + return rc; + } + +/* Internal use only */ +static DWORD NtfsSectGetVolumeHandle( + CHAR * VolumeName, + S_NTFSSECT_VOLINFO * VolumeInfo + ) { + #define M_VOL_PREFIX "\\\\.\\" + CHAR volname[sizeof M_VOL_PREFIX - 1 + MAX_PATH + 1] = M_VOL_PREFIX; + CHAR * const volname_short = volname + sizeof M_VOL_PREFIX - 1; + CHAR * c; + DWORD rc; + + /* Prefix "\\.\" onto the passed volume name */ + strcpy(volname + sizeof M_VOL_PREFIX - 1, VolumeName); + + /* Find the last non-null character */ + for (c = volname_short; *c; ++c) + ; + + /* Remove trailing back-slash */ + if (c[-1] == '\\') + c[-1] = 0; + + /* Open the volume */ + VolumeInfo->Handle = CreateFile( + volname, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL + ); + rc = GetLastError(); + if (VolumeInfo->Handle == INVALID_HANDLE_VALUE) { + M_ERR("Unable to open volume handle!"); + goto err_handle; + } + + return ERROR_SUCCESS; + + CloseHandle(VolumeInfo->Handle); + err_handle: + + return rc; + } + +DWORD M_NTFSSECT_API NtfsSectGetVolumeInfo( + CHAR * VolumeName, + S_NTFSSECT_VOLINFO * VolumeInfo + ) { + S_NTFSSECT_XPFUNCS xp_funcs; + DWORD rc, free_clusts, total_clusts; + BOOL ok; + + if (!VolumeName || !VolumeInfo) + return ERROR_INVALID_PARAMETER; + + rc = NtfsSectGetVolumeHandle(VolumeName, VolumeInfo); + if (rc != ERROR_SUCCESS) + goto err_handle; + + rc = NtfsSectLoadXpFuncs(&xp_funcs); + if (rc != ERROR_SUCCESS) + goto err_xp_funcs; + + ok = xp_funcs.GetDiskFreeSpace( + VolumeName, + &VolumeInfo->SectorsPerCluster, + &VolumeInfo->BytesPerSector, + &free_clusts, + &total_clusts + ); + rc = GetLastError(); + if (!ok) { + M_ERR("GetDiskFreeSpace() failed!"); + goto err_freespace; + } + + rc = NtfsSectGetVolumePartitionLba(VolumeInfo); + if (rc != ERROR_SUCCESS) + goto err_lba; + + VolumeInfo->Size = sizeof *VolumeInfo; + rc = ERROR_SUCCESS; + + err_lba: + + err_freespace: + + NtfsSectUnloadXpFuncs(&xp_funcs); + err_xp_funcs: + + if (rc != ERROR_SUCCESS) { + CloseHandle(VolumeInfo->Handle); + VolumeInfo->Handle = INVALID_HANDLE_VALUE; + } + err_handle: + + return rc; + } + +DWORD M_NTFSSECT_API NtfsSectGetVolumeInfoFromFileName( + CHAR * FileName, + S_NTFSSECT_VOLINFO * VolumeInfo + ) { + S_NTFSSECT_XPFUNCS xp_funcs; + DWORD rc; + CHAR volname[MAX_PATH + 1]; + BOOL ok; + + if (!FileName || !VolumeInfo) + return ERROR_INVALID_PARAMETER; + + rc = NtfsSectLoadXpFuncs(&xp_funcs); + if (rc != ERROR_SUCCESS) { + goto err_xp_funcs; + } + + ok = xp_funcs.GetVolumePathName( + FileName, + volname, + sizeof volname + ); + rc = GetLastError(); + if (!ok) { + M_ERR("GetVolumePathName() failed!"); + goto err_volname; + } + + rc = NtfsSectGetVolumeInfo(volname, VolumeInfo); + + err_volname: + + NtfsSectUnloadXpFuncs(&xp_funcs); + err_xp_funcs: + + return rc; + } + +/* Internal use only */ +static DWORD NtfsSectGetVolumePartitionLba(S_NTFSSECT_VOLINFO * VolumeInfo) { + BOOL ok; + VOLUME_DISK_EXTENTS vol_disk_extents; + DWORD output_size, rc; + + ok = DeviceIoControl( + VolumeInfo->Handle, + IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, + NULL, + 0, + &vol_disk_extents, + sizeof vol_disk_extents, + &output_size, + NULL + ); + rc = GetLastError(); + if (!ok) { + M_ERR("Couldn't fetch volume disk extent(s)!"); + goto err_vol_disk_extents; + } + + if (vol_disk_extents.NumberOfDiskExtents != 1) { + M_ERR("Unsupported number of volume disk extents!"); + goto err_num_of_extents; + } + + VolumeInfo->PartitionLba.QuadPart = ( + vol_disk_extents.Extents[0].StartingOffset.QuadPart / + VolumeInfo->BytesPerSector + ); + + return ERROR_SUCCESS; + + err_num_of_extents: + + err_vol_disk_extents: + + return rc; + } + +DWORD M_NTFSSECT_API NtfsSectLcnToLba( + const S_NTFSSECT_VOLINFO * VolumeInfo, + const LARGE_INTEGER * Lcn, + LARGE_INTEGER * Lba + ) { + BOOL bad; + bad = ( + !VolumeInfo || + !VolumeInfo->BytesPerSector || + !VolumeInfo->SectorsPerCluster || + !Lcn || + Lcn->QuadPart < 0 || + !Lba + ); + if (bad) + return ERROR_INVALID_PARAMETER; + + Lba->QuadPart = ( + VolumeInfo->PartitionLba.QuadPart + + Lcn->QuadPart * + VolumeInfo->SectorsPerCluster + ); + return ERROR_SUCCESS; + } + +DWORD M_NTFSSECT_API NtfsSectLoadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs) { + DWORD rc; + + if (!XpFuncs) + return ERROR_INVALID_PARAMETER; + + XpFuncs->Size = sizeof *XpFuncs; + + XpFuncs->Kernel32 = LoadLibrary("kernel32.dll"); + rc = GetLastError(); + if (!XpFuncs->Kernel32) { + M_ERR("KERNEL32.DLL not found!"); + goto err; + } + + XpFuncs->GetVolumePathName = (F_KERNEL32_GETVOLUMEPATHNAME *) ( + GetProcAddress( + XpFuncs->Kernel32, + "GetVolumePathNameA" + ) + ); + rc = GetLastError(); + if (!XpFuncs->GetVolumePathName) { + M_ERR("GetVolumePathName() not found in KERNEL32.DLL!"); + goto err; + } + + XpFuncs->GetDiskFreeSpace = (F_KERNEL32_GETDISKFREESPACE *) ( + GetProcAddress( + XpFuncs->Kernel32, + "GetDiskFreeSpaceA" + ) + ); + rc = GetLastError(); + if (!XpFuncs->GetDiskFreeSpace) { + M_ERR("GetDiskFreeSpace() not found in KERNEL32.DLL!"); + goto err; + } + + return ERROR_SUCCESS; + + err: + NtfsSectUnloadXpFuncs(XpFuncs); + return rc; + } + +VOID M_NTFSSECT_API NtfsSectUnloadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs) { + if (!XpFuncs) + return; + + XpFuncs->GetDiskFreeSpace = NULL; + XpFuncs->GetVolumePathName = NULL; + if (XpFuncs->Kernel32) + FreeLibrary(XpFuncs->Kernel32); + XpFuncs->Kernel32 = NULL; + XpFuncs->Size = 0; + return; + } + diff --git a/win/ntfssect.h b/win/ntfssect.h new file mode 100644 index 00000000..246c26da --- /dev/null +++ b/win/ntfssect.h @@ -0,0 +1,152 @@ +/* -------------------------------------------------------------------------- * + * + * Copyright 2011 Shao Miller - 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., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ +#ifndef M_NTFSSECT_H_ + +/**** + * ntfssect.h + * + * Fetch NTFS file cluster & sector information via Windows + * + * With special thanks to Mark Roddy for his article: + * http://www.wd-3.com/archive/luserland.htm + */ + +/*** Macros */ +#define M_NTFSSECT_H_ +#define M_NTFSSECT_API + +/*** Object types */ + +/* An "extent;" a contiguous range of file data */ +typedef struct S_NTFSSECT_EXTENT_ S_NTFSSECT_EXTENT; + +/* Volume info relevant to file cluster & sector info */ +typedef struct S_NTFSSECT_VOLINFO_ S_NTFSSECT_VOLINFO; + +/* Stores function pointers to some Windows functions */ +typedef struct S_NTFSSECT_XPFUNCS_ S_NTFSSECT_XPFUNCS; + +/*** Function types */ + +/* The function type for Kernel32.dll's GetDiskFreeSpace() */ +typedef BOOL WINAPI F_KERNEL32_GETDISKFREESPACE( + LPCTSTR, + LPDWORD, + LPDWORD, + LPDWORD, + LPDWORD + ); + +/* The function type for Kernel32.dll's GetVolumePathName() */ +typedef BOOL WINAPI F_KERNEL32_GETVOLUMEPATHNAME(LPCTSTR, LPCTSTR, DWORD); + +/*** Function declarations */ + +/** + * Fetch the extent containing a particular VCN + * + * @v File + * @v Vcn + * @v Extent + * @ret DWORD + */ +DWORD M_NTFSSECT_API NtfsSectGetFileVcnExtent( + HANDLE File, + LARGE_INTEGER * Vcn, + S_NTFSSECT_EXTENT * Extent + ); + +/** + * Populate a volume info object + * + * @v VolumeName + * @v VolumeInfo + * @ret DWORD + */ +DWORD M_NTFSSECT_API NtfsSectGetVolumeInfo( + CHAR * VolumeName, + S_NTFSSECT_VOLINFO * VolumeInfo + ); + +/** + * Populate a volume info object + * + * @v FileName + * @v VolumeInfo + * @ret DWORD + */ +DWORD M_NTFSSECT_API NtfsSectGetVolumeInfoFromFileName( + CHAR * FileName, + S_NTFSSECT_VOLINFO * VolumeInfo + ); + +/** + * Convert a volume LCN to an absolute disk LBA + * + * @v VolumeInfo + * @v Lcn + * @v Lba + * @ret DWORD + */ +DWORD M_NTFSSECT_API NtfsSectLcnToLba( + const S_NTFSSECT_VOLINFO * VolumeInfo, + const LARGE_INTEGER * Lcn, + LARGE_INTEGER * Lba + ); + +/** + * Load some helper XP functions + * + * @v XpFuncs + * @ret DWORD + */ +DWORD M_NTFSSECT_API NtfsSectLoadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs); + +/** + * Unload some helper XP functions + * + * @v XpFuncs + * @ret DWORD + */ +VOID M_NTFSSECT_API NtfsSectUnloadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs); + +/*** Object declarations */ + +/** + * The last error message set by one of our functions. + * Obviously not per-thread + */ +extern CHAR * NtfsSectLastErrorMessage; + +/*** Struct/union definitions */ +struct S_NTFSSECT_EXTENT_ { + LARGE_INTEGER FirstVcn; + LARGE_INTEGER NextVcn; + LARGE_INTEGER FirstLcn; + }; + +struct S_NTFSSECT_VOLINFO_ { + DWORD Size; + HANDLE Handle; + DWORD BytesPerSector; + DWORD SectorsPerCluster; + LARGE_INTEGER PartitionLba; + }; + +struct S_NTFSSECT_XPFUNCS_ { + DWORD Size; + HMODULE Kernel32; + F_KERNEL32_GETVOLUMEPATHNAME * GetVolumePathName; + F_KERNEL32_GETDISKFREESPACE * GetDiskFreeSpace; + }; + +#endif /* M_NTFSSECT_H_ */ diff --git a/win/ntfstest.c b/win/ntfstest.c new file mode 100644 index 00000000..1fc27182 --- /dev/null +++ b/win/ntfstest.c @@ -0,0 +1,163 @@ +/* -------------------------------------------------------------------------- * + * + * Copyright 2011 Shao Miller - 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., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +/**** + * ntfstest.c + * + * (C) Shao Miller, 2011 + * + * Tests ntfssect.c functions + * + * With special thanks to Mark Roddy for his article: + * http://www.wd-3.com/archive/luserland.htm + */ +#include <windows.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ntfssect.h" + +/*** Object types */ + +/*** Function declarations */ +static void show_usage(void); +static void show_last_err(void); +static void show_err(DWORD); + +/*** Struct/union definitions */ + +/*** Function definitions */ + +/** Program entry-point */ +int main(int argc, char ** argv) { + int rc; + DWORD err; + S_NTFSSECT_VOLINFO vol_info; + HANDLE file; + LARGE_INTEGER vcn, lba; + S_NTFSSECT_EXTENT extent; + LONGLONG len; + BOOL ok; + + if (argc != 2) { + rc = EXIT_FAILURE; + show_usage(); + goto err_args; + } + + /* Get volume info */ + err = NtfsSectGetVolumeInfoFromFileName(argv[1], &vol_info); + if (err != ERROR_SUCCESS) { + show_err(err); + goto err_vol_info; + } + printf( + "Volume has %d bytes per sector, %d sectors per cluster\n", + vol_info.BytesPerSector, + vol_info.SectorsPerCluster + ); + + /* Open the file for reading */ + file = CreateFile( + argv[1], + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL + ); + if (file == INVALID_HANDLE_VALUE) { + rc = EXIT_FAILURE; + show_last_err(); + goto err_file; + } + + /* For each extent */ + for ( + vcn.QuadPart = 0; + NtfsSectGetFileVcnExtent(file, &vcn, &extent) == ERROR_SUCCESS; + vcn = extent.NextVcn + ) { + len = extent.NextVcn.QuadPart - extent.FirstVcn.QuadPart; + printf("Extent @ VCN #%lld,", extent.FirstVcn.QuadPart); + printf(" %lld clusters long:\n", len); + printf(" VCN #%lld -", extent.FirstVcn.QuadPart); + printf(" #%lld\n", extent.FirstVcn.QuadPart + len - 1); + printf(" LCN #%lld -", extent.FirstLcn.QuadPart); + printf(" #%lld\n", extent.FirstLcn.QuadPart + len - 1); + err = NtfsSectLcnToLba( + &vol_info, + &extent.FirstLcn, + &lba + ); + if (err == ERROR_SUCCESS) { + printf(" LBA #%lld -", lba.QuadPart); + printf( + " #%lld\n", + lba.QuadPart + len * vol_info.SectorsPerCluster + ); + } + continue; + } + + rc = EXIT_SUCCESS; + + CloseHandle(file); + err_file: + + CloseHandle(vol_info.Handle); + err_vol_info: + + err_args: + + return rc; + } + +/** Display usage */ +static void show_usage(void) { + static const char usage_text[] = "\ + File sector info . . . . . . . . . . . . . . . . . . . . Shao Miller, 2011\n\ +\n\ + Usage: NTFSTEST.EXE <filename>\n\ +\n\ + Attempts to dump cluster and sector info for <filename>.\n"; + + printf(usage_text); + return; + } + +static void show_last_err(void) { + show_err(GetLastError()); + return; + } + +/** Display an error */ +static void show_err(DWORD err_code) { + void * buf; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &buf, + 0, + NULL + ); + fprintf(stderr, "Error: %s\n", buf); + LocalFree(buf); + return; + } + diff --git a/win/ntfstest.rc b/win/ntfstest.rc new file mode 100644 index 00000000..462d21f1 --- /dev/null +++ b/win/ntfstest.rc @@ -0,0 +1,26 @@ + +1 VERSIONINFO +FILEVERSION 0,0,0,1 +PRODUCTVERSION 0,0,0,1 +FILEOS 0x40004 +FILETYPE 0x1 + { + BLOCK "StringFileInfo" + { + BLOCK "040904B0" + { + VALUE "CompanyName", "Shao Miller" + VALUE "FileDescription", "NTFS File Sector Info" + VALUE "FileVersion", "0.0.0.1 (Aug-12-2011)" + VALUE "InternalName", "NTFSTest" + VALUE "LegalCopyright", "© 2011 Shao Miller. All rights reserved." + VALUE "OriginalFilename", "NTFSTEST.EXE" + VALUE "ProductName", "Utilities" + VALUE "ProductVersion", "0.0.0.1" + } + } + } + +#if 0 +1 ICON "ntfstest.ico" +#endif diff --git a/win/syslinux.c b/win/syslinux.c index 0e833d8d..669450eb 100644 --- a/win/syslinux.c +++ b/win/syslinux.c @@ -27,6 +27,8 @@ #include "setadv.h" #include "sysexits.h" #include "syslxopt.h" +#include "syslxfs.h" +#include "ntfssect.h" #ifdef __GNUC__ # define noreturn void __attribute__((noreturn)) @@ -48,16 +50,16 @@ void error(char *msg); // The following struct should be in the ntddstor.h file, but I didn't have it. // mingw32 has <ddk/ntddstor.h>, but including that file causes all kinds // of other failures. mingw64 has it in <winioctl.h>. -#ifndef __x86_64__ -typedef struct _STORAGE_DEVICE_NUMBER { +// Thus, instead of STORAGE_DEVICE_NUMBER, use a lower-case private +// definition... +struct storage_device_number { DEVICE_TYPE DeviceType; ULONG DeviceNumber; ULONG PartitionNumber; -} STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER; -#endif +}; BOOL GetStorageDeviceNumberByHandle(HANDLE handle, - const STORAGE_DEVICE_NUMBER * sdn) + const struct storage_device_number *sdn) { BOOL result = FALSE; DWORD count; @@ -254,6 +256,7 @@ int main(int argc, char *argv[]) int ldlinux_sectors; uint32_t ldlinux_cluster; int nsectors; + int fs_type; if (!checkver()) { fprintf(stderr, @@ -326,8 +329,10 @@ int main(int argc, char *argv[]) exit(1); } - /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */ - if ((errmsg = syslinux_check_bootsect(sectbuf))) { + /* Check to see that what we got was indeed an FAT/NTFS + * boot sector/superblock + */ + if ((errmsg = syslinux_check_bootsect(sectbuf, &fs_type))) { fprintf(stderr, "%s\n", errmsg); exit(1); } @@ -379,6 +384,38 @@ int main(int argc, char *argv[]) ldlinux_sectors = (syslinux_ldlinux_len + 2 * ADV_SIZE + SECTOR_SIZE - 1) >> SECTOR_SHIFT; sectors = calloc(ldlinux_sectors, sizeof *sectors); + if (fs_type == NTFS) { + DWORD err; + S_NTFSSECT_VOLINFO vol_info; + LARGE_INTEGER vcn, lba, len; + S_NTFSSECT_EXTENT extent; + + err = NtfsSectGetVolumeInfo(drive_name + 4, &vol_info); + if (err != ERROR_SUCCESS) { + error("Could not fetch NTFS volume info"); + exit(1); + } + secp = sectors; + nsectors = 0; + for (vcn.QuadPart = 0; + NtfsSectGetFileVcnExtent(f_handle, &vcn, &extent) == ERROR_SUCCESS; + vcn = extent.NextVcn) { + err = NtfsSectLcnToLba(&vol_info, &extent.FirstLcn, &lba); + if (err != ERROR_SUCCESS) { + error("Could not translate LDLINUX.SYS LCN to disk LBA"); + exit(1); + } + lba.QuadPart -= vol_info.PartitionLba.QuadPart; + len.QuadPart = ((extent.NextVcn.QuadPart - + extent.FirstVcn.QuadPart) * + vol_info.SectorsPerCluster); + while (len.QuadPart-- && nsectors < ldlinux_sectors) { + *secp++ = lba.QuadPart++; + nsectors++; + } + } + goto map_done; + } fs = libfat_open(libfat_readfile, (intptr_t) d_handle); ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL); secp = sectors; @@ -390,6 +427,7 @@ int main(int argc, char *argv[]) s = libfat_nextsector(fs, s); } libfat_close(fs); +map_done: /* * Patch ldlinux.sys and the boot sector @@ -409,7 +447,7 @@ int main(int argc, char *argv[]) /* If desired, fix the MBR */ if (opt.install_mbr || opt.activate_partition) { - STORAGE_DEVICE_NUMBER sdn; + struct storage_device_number sdn; if (GetStorageDeviceNumberByHandle(d_handle, &sdn)) { if (!FixMBR(sdn.DeviceNumber, sdn.PartitionNumber, opt.install_mbr, opt.activate_partition)) { fprintf(stderr, @@ -472,7 +510,7 @@ int main(int argc, char *argv[]) } /* Make the syslinux boot sector */ - syslinux_make_bootsect(sectbuf); + syslinux_make_bootsect(sectbuf, fs_type); /* Write the syslinux boot sector into the boot sector */ if (opt.bootsecfile) { diff --git a/win32/Makefile b/win32/Makefile index d4133ff8..f960998a 100644 --- a/win32/Makefile +++ b/win32/Makefile @@ -47,9 +47,9 @@ WINCC_IS_GOOD := $(shell $(WINCC) $(WINCFLAGS) $(WINLDFLAGS) \ .SUFFIXES: .c .obj .lib .exe .i .s .S -SRCS = ../win/syslinux.c +SRCS = ../win/syslinux.c ../win/ntfssect.c OBJS = $(patsubst %.c,%.obj,$(notdir $(SRCS))) -LIBSRC = ../libinstaller/fat.c \ +LIBSRC = ../libinstaller/fs.c \ ../libinstaller/syslxmod.c \ ../libinstaller/syslxopt.c \ ../libinstaller/setadv.c \ diff --git a/win32/find-mingw32.sh b/win32/find-mingw32.sh index f79949c7..51dcdd7e 100755 --- a/win32/find-mingw32.sh +++ b/win32/find-mingw32.sh @@ -20,7 +20,8 @@ for prefix in \ i386-mingw32msvc- \ i486-mingw32msvc- \ i586-mingw32msvc- \ - i686-mingw32msvc-; do + i686-mingw32msvc- \ + i686-w64-mingw32-; do if "${prefix}${cc}" -v > /dev/null 2>&1; then echo "$prefix" exit 0 diff --git a/win32/ntfstest/Makefile b/win32/ntfstest/Makefile new file mode 100644 index 00000000..00e89cf9 --- /dev/null +++ b/win32/ntfstest/Makefile @@ -0,0 +1,87 @@ +## ----------------------------------------------------------------------- +## +## Copyright 1998-2008 H. Peter Anvin - All Rights Reserved +## Copyright 2010 Intel Corporation; author: H. Peter Anvin +## +## 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., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Makefile for Win32 NTFS file cluster test +# +# This is separated out mostly so we can have a different set of Makefile +# variables. +# + +OSTYPE = $(shell uname -msr) +ifeq ($(findstring CYGWIN,$(OSTYPE)),CYGWIN) +## Compiling on Cygwin +WINPREFIX := +WINCFLAGS := -mno-cygwin $(GCCWARN) -Os -fomit-frame-pointer -D_FILE_OFFSET_BITS=64 +WINLDFLAGS := -mno-cygwin -Os -s +else +## Compiling on some variant of MinGW +ifeq ($(findstring MINGW32,$(OSTYPE)),MINGW32) +WINPREFIX := +else +WINPREFIX := $(shell ../find-mingw32.sh gcc) +endif +WINCFLAGS := $(GCCWARN) -Wno-sign-compare -Os -fomit-frame-pointer \ + -D_FILE_OFFSET_BITS=64 +WINLDFLAGS := -Os -s +endif +WINCFLAGS += -I. -I../../win + +WINCC := $(WINPREFIX)gcc +WINAR := $(WINPREFIX)ar +WINRANLIB := $(WINPREFIX)ranlib +WINDRES := $(WINPREFIX)windres + +WINCC_IS_GOOD := $(shell $(WINCC) $(WINCFLAGS) $(WINLDFLAGS) \ + -o hello.exe ../../win/hello.c >/dev/null 2>&1 ; echo $$?) + +.SUFFIXES: .c .obj .lib .exe .i .s .S .rc .res + +SRCS = ../../win/ntfstest.c ../../win/ntfssect.c +RCS = ../../win/ntfstest.rc +OBJS = $(patsubst %.c,%.obj,$(notdir $(SRCS))) +RESS = $(patsubst %.rc,%.res,$(notdir $(RCS))) + +VPATH = .:../../win + +TARGETS = ntfstest.exe + +ifeq ($(WINCC_IS_GOOD),0) +all: $(TARGETS) +else +all: + rm -f $(TARGETS) +endif + +tidy dist: + -rm -f *.o *.obj *.lib *.i *.s *.a .*.d *.tmp *_bin.c hello.exe + +clean: tidy + +spotless: clean + -rm -f *~ $(TARGETS) + +ntfstest.exe: $(OBJS) $(RESS) + $(WINCC) $(WINLDFLAGS) -o $@ $^ + + +%.obj: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -c -o $@ $< +%.i: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -E -o $@ $< +%.s: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -S -o $@ $< +%.res: %.rc + $(WINDRES) -O COFF $< $@ + +-include .*.d *.tmp diff --git a/win64/Makefile b/win64/Makefile index 0bc746d5..fe60793c 100644 --- a/win64/Makefile +++ b/win64/Makefile @@ -37,9 +37,9 @@ WINCC_IS_GOOD := $(shell $(WINCC) $(WINCFLAGS) $(WINLDFLAGS) \ .SUFFIXES: .c .obj .lib .exe .i .s .S -SRCS = ../win/syslinux.c +SRCS = ../win/syslinux.c ../win/ntfssect.c OBJS = $(patsubst %.c,%.obj,$(notdir $(SRCS))) -LIBSRC = ../libinstaller/fat.c \ +LIBSRC = ../libinstaller/fs.c \ ../libinstaller/syslxmod.c \ ../libinstaller/syslxopt.c \ ../libinstaller/setadv.c \ diff --git a/win64/find-mingw64.sh b/win64/find-mingw64.sh index c45db562..c2e42cd7 100755 --- a/win64/find-mingw64.sh +++ b/win64/find-mingw64.sh @@ -12,6 +12,8 @@ for prefix in \ x86_64-mingw64msvc- \ x86_64-mingw32- \ x86_64-mingw32msvc- \ + x86_64-w64-mingw32- \ + x86_64-w64-mingw32msvc- \ amd64-pc-mingw64- \ amd64-pc-mingw64msvc- \ amd64-pc-mingw32- \ @@ -20,6 +22,8 @@ for prefix in \ amd64-mingw64msvc- \ amd64-mingw32- \ amd64-mingw32msvc- \ + amd64-w64-mingw32- \ + amd64-w64-mingw32msvc- \ ; do if "${prefix}${cc}" -v > /dev/null 2>&1; then echo "$prefix" diff --git a/win64/ntfstest/Makefile b/win64/ntfstest/Makefile new file mode 100644 index 00000000..5b975be5 --- /dev/null +++ b/win64/ntfstest/Makefile @@ -0,0 +1,76 @@ +## ----------------------------------------------------------------------- +## +## Copyright 1998-2008 H. Peter Anvin - All Rights Reserved +## Copyright 2010 Intel Corporation; author: H. Peter Anvin +## +## 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., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Makefile for Win64 NTFS file cluster test +# +# This is separated out mostly so we can have a different set of Makefile +# variables. +# + +OSTYPE = $(shell uname -msr) +# Don't know how to do a native compile here... +WINPREFIX := $(shell ../find-mingw64.sh gcc) +WINCFLAGS := $(GCCWARN) -Wno-sign-compare -Os -fomit-frame-pointer \ + -D_FILE_OFFSET_BITS=64 +WINLDFLAGS := -Os -s +WINCFLAGS += -I. -I../../win + +WINCC := $(WINPREFIX)gcc +WINAR := $(WINPREFIX)ar +WINRANLIB := $(WINPREFIX)ranlib +WINDRES := $(WINPREFIX)windres + +WINCC_IS_GOOD := $(shell $(WINCC) $(WINCFLAGS) $(WINLDFLAGS) \ + -o hello.exe ../../win/hello.c >/dev/null 2>&1 ; echo $$?) + +.SUFFIXES: .c .obj .lib .exe .i .s .S .rc .res + +SRCS = ../../win/ntfstest.c ../../win/ntfssect.c +RCS = ../../win/ntfstest.rc +OBJS = $(patsubst %.c,%.obj,$(notdir $(SRCS))) +RESS = $(patsubst %.rc,%.res,$(notdir $(RCS))) + +VPATH = .:../../win + +TARGETS = ntfstest64.exe + +ifeq ($(WINCC_IS_GOOD),0) +all: $(TARGETS) +else +all: + rm -f $(TARGETS) +endif + +tidy dist: + -rm -f *.o *.obj *.lib *.i *.s *.a .*.d *.tmp *_bin.c hello.exe + +clean: tidy + +spotless: clean + -rm -f *~ $(TARGETS) + +ntfstest64.exe: $(OBJS) $(RESS) + $(WINCC) $(WINLDFLAGS) -o $@ $^ + + +%.obj: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -c -o $@ $< +%.i: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -E -o $@ $< +%.s: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -S -o $@ $< +%.res: %.rc + $(WINDRES) -O COFF $< $@ + +-include .*.d *.tmp |