diff options
Diffstat (limited to 'com32/modules')
-rw-r--r-- | com32/modules/Makefile | 8 | ||||
-rw-r--r-- | com32/modules/chain.c | 1870 | ||||
-rw-r--r-- | com32/modules/elf.c | 16 | ||||
-rw-r--r-- | com32/modules/ethersel.c | 7 | ||||
-rw-r--r-- | com32/modules/ifcpu.c | 25 | ||||
-rw-r--r-- | com32/modules/ifmemdsk.c | 392 | ||||
-rw-r--r-- | com32/modules/kontron_wdt.c | 414 | ||||
-rw-r--r-- | com32/modules/kontron_wdt.h | 117 | ||||
-rw-r--r-- | com32/modules/linux.c | 95 | ||||
-rw-r--r-- | com32/modules/pcitest.c | 7 | ||||
-rw-r--r-- | com32/modules/pmload.c | 16 | ||||
-rw-r--r-- | com32/modules/prdhcp.c | 164 | ||||
-rw-r--r-- | com32/modules/pxechn.c | 1161 | ||||
-rw-r--r-- | com32/modules/zzjson.c | 101 |
14 files changed, 2466 insertions, 1927 deletions
diff --git a/com32/modules/Makefile b/com32/modules/Makefile index 2d479132..f110e584 100644 --- a/com32/modules/Makefile +++ b/com32/modules/Makefile @@ -16,13 +16,15 @@ ## topdir = ../.. -include ../MCONFIG +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 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 48f53ffd..00000000 --- a/com32/modules/chain.c +++ /dev/null @@ -1,1870 +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> - * loads at and jumps to <seg>:0000 instead of 0000:7C00. - * - * 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). - */ - -#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; - 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 %u\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 %u\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\ - 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=<segment> Jump to <seg>:0000, instead of 0000:7C00\n\ - swap Swap drive numbers, if bootdisk is not fd0/hd0\n\ - hide Hide primary partitions, except selected partition\n\ - 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); - - 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 segval = strtoul(argv[i] + 4, NULL, 0); - if (segval < 0x50 || segval > 0x9f000) { - error("Invalid segment\n"); - goto bail; - } - opt.seg = segval; - } 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.loadfile = argv[i] + 6; - opt.sethidden = true; - } else if (!strncmp(argv[i], "cmldr=", 6)) { - opt.seg = 0x2000; /* CMLDR wants this address */ - 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.loadfile = argv[i] + 8; - opt.sethidden = true; - } else if (!strncmp(argv[i], "msdos=", 6) || - !strncmp(argv[i], "pcdos=", 6)) { - opt.seg = 0x70; /* MS-DOS 2.0+ wants this address */ - opt.loadfile = argv[i] + 6; - opt.sethidden = true; - } else if (!strncmp(argv[i], "drmk=", 5)) { - opt.seg = 0x70; /* DRMK wants this address */ - 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.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; - } - - if (opt.seg) { - regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg; - } else { - regs.ip = 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 ? (opt.seg << 4) : 0x7c00; - - 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 %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"); - } - 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/elf.c b/com32/modules/elf.c index 182afa60..0ac45174 100644 --- a/com32/modules/elf.c +++ b/com32/modules/elf.c @@ -120,10 +120,8 @@ int boot_elf(void *ptr, size_t len, char **argv) if (!mmap || !amap) goto bail; -#if DEBUG dprintf("Initial memory map:\n"); - syslinux_dump_memmap(stdout, mmap); -#endif + syslinux_dump_memmap(mmap); ph = (Elf32_Phdr *) (cptr + eh->e_phoff); @@ -185,10 +183,8 @@ int boot_elf(void *ptr, size_t len, char **argv) if (!stack_frame) goto bail; -#if DEBUG dprintf("Right before syslinux_memmap_largest()...\n"); - syslinux_dump_memmap(stdout, amap); -#endif + syslinux_dump_memmap(amap); if (syslinux_memmap_largest(amap, SMT_FREE, &lstart, &llen)) goto bail; /* NO free memory?! */ @@ -239,16 +235,14 @@ int boot_elf(void *ptr, size_t len, char **argv) regs.eip = eh->e_entry; regs.esp = stack_pointer; -#if DEBUG dprintf("Final memory map:\n"); - syslinux_dump_memmap(stdout, mmap); + syslinux_dump_memmap(mmap); dprintf("Final available map:\n"); - syslinux_dump_memmap(stdout, amap); + syslinux_dump_memmap(amap); dprintf("Movelist:\n"); - syslinux_dump_movelist(stdout, ml); -#endif + syslinux_dump_movelist(ml); /* This should not return... */ fputs("Booting...\n", stdout); diff --git a/com32/modules/ethersel.c b/com32/modules/ethersel.c index 5c3cf02a..28dc62ca 100644 --- a/com32/modules/ethersel.c +++ b/com32/modules/ethersel.c @@ -38,12 +38,7 @@ #include <com32.h> #include <syslinux/boot.h> #include <syslinux/config.h> - -#ifdef DEBUG -# define dprintf printf -#else -# define dprintf(...) ((void)0) -#endif +#include <dprintf.h> #define MAX_LINE 512 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/ifmemdsk.c b/com32/modules/ifmemdsk.c new file mode 100644 index 00000000..cfed87f9 --- /dev/null +++ b/com32/modules/ifmemdsk.c @@ -0,0 +1,392 @@ +/* ----------------------------------------------------------------------- * + * + * 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., 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. + * + * ----------------------------------------------------------------------- */ + +/**** + * @file ifmemdsk.c + * + * This COM32 module detects if there are MEMDISKs established. + */ + +static const char usage_text[] = "\ +Usage:\n\ + ifmemdsk.c32 [<option> [...]] --info [<option> [...]]\n\ + ifmemdsk.c32 [<option> [...]] [<detected_cmd>] -- [<not_detected_cmd>]\n\ +\n\ +Options:\n\ + --info . . . . . Displays info about MEMDISK(s)\n\ + --safe-hooks . . Will scan INT 13h \"safe hook\" chain\n\ + --mbfts . . . . . Will scan memory for MEMDISK mBFTs\n\ + --no-sequential Suppresses probing all drive numbers\n\ +\n\ +If a MEMDISK is found, or if a particular MEMDISK is sought by the options\n\ +and is found, then the 'detected_cmd' action will be taken, else the\n\ +'not_detected_cmd' action will be taken.\n\ +\n"; + +#include <stdio.h> +#include <string.h> +#include <alloca.h> +#include <com32.h> +#include <console.h> +#include <syslinux/boot.h> + +/* Pull in MEMDISK common structures */ +#include "../../memdisk/mstructs.h" + +/*** Macros */ +#define M_GET_DRIVE_PARAMS (0x08) +#define M_SEGOFFTOPTR(seg, off) (((seg) << 4) + (off)) +#define M_INT13H M_SEGOFFTOPTR(0x0000, 0x0013 * 4) +#define M_FREEBASEMEM M_SEGOFFTOPTR(0x0040, 0x0013) +#define M_TOP M_SEGOFFTOPTR(0x9FFF, 0x0000) + +/*** Object types */ +typedef struct mdi s_mdi; +typedef real_addr_t u_segoff; +typedef struct safe_hook s_safe_hook; +typedef struct mBFT s_mbft; + +/*** Function types */ +typedef int f_find(void); + +/*** Function declarations */ +static const s_mdi * installation_check(int); +static f_find scan_drives; +static f_find walk_safe_hooks; +static const s_safe_hook * is_safe_hook(const void *); +static const s_mdi * is_memdisk_hook(const s_safe_hook *); +static f_find scan_mbfts; +static const s_mbft * is_mbft(const void *); +static f_find do_nothing; +static void memdisk_info(const s_mdi *); +static void boot_args(char **); +static const char * bootloadername(uint8_t); + +/*** Structure/union definitions */ + +/*** Objects */ +static int show_info = 0; + +/*** Function definitions */ + +int main(int argc, char ** argv) { + static f_find * do_scan_drives = scan_drives; + static f_find * do_walk_safe_hooks = do_nothing; + static f_find * do_scan_mbfts = do_nothing; + char ** detected_cmd; + char ** not_detected_cmd; + char ** cmd; + char ** cur_arg; + int show_usage; + int found; + + (void) argc; + + openconsole(&dev_null_r, &dev_stdcon_w); + + detected_cmd = NULL; + not_detected_cmd = NULL; + show_usage = 1; + for (cur_arg = argv + 1; *cur_arg; ++cur_arg) { + /* Check for command divider */ + if (!strcmp(*cur_arg, "--")) { + show_usage = 0; + *cur_arg = NULL; + not_detected_cmd = cur_arg + 1; + break; + } + + /* Check for '--info' */ + if (!strcmp(*cur_arg, "--info")) { + show_usage = 0; + show_info = 1; + continue; + } + + /* Other options */ + if (!strcmp(*cur_arg, "--no-sequential")) { + do_scan_drives = do_nothing; + continue; + } + + if (!strcmp(*cur_arg, "--safe-hooks")) { + do_walk_safe_hooks = walk_safe_hooks; + continue; + } + + if (!strcmp(*cur_arg, "--mbfts")) { + do_scan_mbfts = scan_mbfts; + continue; + } + + /* Check for invalid option */ + if (!memcmp(*cur_arg, "--", sizeof "--" - 1)) { + puts("Invalid option!"); + show_usage = 1; + break; + } + + /* Set 'detected_cmd' if it's null */ + if (!detected_cmd) + detected_cmd = cur_arg; + + continue; + } + + if (show_usage) { + fprintf(stderr, usage_text); + return 1; + } + + found = 0; + found += do_walk_safe_hooks(); + found += do_scan_mbfts(); + found += do_scan_drives(); + + cmd = found ? detected_cmd : not_detected_cmd; + if (cmd && *cmd) + boot_args(cmd); + + return 0; + } + +static const s_mdi * installation_check(int drive) { + com32sys_t params, results; + int found; + + /* Set parameters for INT 0x13 call */ + memset(¶ms, 0, sizeof params); + params.eax.w[0] = M_GET_DRIVE_PARAMS << 8; + params.edx.w[0] = drive; + /* 'ME' 'MD' 'IS' 'K?' */ + params.eax.w[1] = 0x454D; + params.ecx.w[1] = 0x444D; + params.edx.w[1] = 0x5349; + params.ebx.w[1] = 0x3F4B; + + /* Perform the call */ + __intcall(0x13, ¶ms, &results); + + /* Check result */ + found = ( + /* '!M' 'EM' 'DI' 'SK' */ + results.eax.w[1] == 0x4D21 && + results.ecx.w[1] == 0x4D45 && + results.edx.w[1] == 0x4944 && + results.ebx.w[1] == 0x4B53 + ); + + if (found) + return MK_PTR(results.es, results.edi.w[0]); + + return NULL; + } + +static int scan_drives(void) { + int found, drive; + const s_mdi * mdi; + + for (found = drive = 0; drive <= 0xFF; ++drive) { + mdi = installation_check(drive); + if (!mdi) + continue; + + memdisk_info(mdi); + ++found; + continue; + } + + return found; + } + +static int walk_safe_hooks(void) { + static const u_segoff * const int13 = (void *) M_INT13H; + const void * addr; + int found; + const s_safe_hook * hook; + const s_mdi * mdi; + + /* INT 0x13 vector */ + addr = MK_PTR(int13->seg_off.segment, int13->seg_off.offset); + found = 0; + while (addr) { + hook = is_safe_hook(addr); + if (!hook) + break; + + mdi = is_memdisk_hook(hook); + if (mdi) { + memdisk_info(mdi); + ++found; + } + + addr = MK_PTR( + hook->old_hook.seg_off.segment, + hook->old_hook.seg_off.offset + ); + continue; + } + return found; + } + +static const s_safe_hook * is_safe_hook(const void * addr) { + static const char magic[] = "$INT13SF"; + const s_safe_hook * const test = addr; + + if (memcmp(test->signature, magic, sizeof magic - 1)) + return NULL; + + return test; + } + +static const s_mdi * is_memdisk_hook(const s_safe_hook * hook) { + static const char magic[] = "MEMDISK"; + const s_mbft * mbft; + + if (memcmp(hook->vendor, magic, sizeof magic - 1)) + return NULL; + + /* An mBFT is always aligned */ + mbft = MK_PTR(hook->mbft >> 4, 0); + return &mbft->mdi; + } + +static int scan_mbfts(void) { + static const uint16_t * const free_base_mem = (void *) M_FREEBASEMEM; + static const void * const top = (void *) M_TOP; + const void * addr; + const s_mbft * mbft; + int found; + + found = 0; + for (addr = MK_PTR(*free_base_mem << 4, 0); addr < top; addr += 1 << 4) { + if (!(mbft = is_mbft(addr))) + continue; + + memdisk_info(&mbft->mdi); + ++found; + continue; + } + + return found; + } + +static const s_mbft * is_mbft(const void * addr) { + static const char magic[] = "mBFT"; + const s_mbft * const test = addr; + const uint8_t * ptr, * end; + uint8_t chksum; + + if (memcmp(test->acpi.signature, magic, sizeof magic - 1)) + return NULL; + + if (test->acpi.length != sizeof *test) + return NULL; + + end = (void *) (test + 1); + chksum = 0; + for (ptr = addr; ptr < end; ++ptr) + chksum += *ptr; + if (chksum) + return NULL; + + /* Looks like it's an mBFT! */ + return test; + } + +static int do_nothing(void) { + return 0; + } + +static void memdisk_info(const s_mdi * mdi) { + const char * cmdline; + + if (!show_info) + return; + + cmdline = MK_PTR( + mdi->cmdline.seg_off.segment, + mdi->cmdline.seg_off.offset + ); + printf( + "Found MEMDISK version %u.%02u:\n" + " diskbuf == 0x%08X, disksize == %u sectors\n" + " bootloaderid == 0x%02X (%s),\n" + " cmdline: %s\n", + mdi->version_major, + mdi->version_minor, + mdi->diskbuf, + mdi->disksize, + mdi->bootloaderid, + bootloadername(mdi->bootloaderid), + cmdline + ); + return; + } + +/* This function copyright H. Peter Anvin */ +static void boot_args(char **args) +{ + int len = 0, a = 0; + char **pp; + const char *p; + char c, *q, *str; + + for (pp = args; *pp; pp++) + len += strlen(*pp) + 1; + + q = str = alloca(len); + for (pp = args; *pp; pp++) { + p = *pp; + while ((c = *p++)) + *q++ = c; + *q++ = ' '; + a = 1; + } + q -= a; + *q = '\0'; + + if (!str[0]) + syslinux_run_default(); + else + syslinux_run_command(str); +} + +/* This function copyright H. Peter Anvin */ +static const char *bootloadername(uint8_t id) +{ + static const struct { + uint8_t id, mask; + const char *name; + } *lp, list[] = { + {0x00, 0xf0, "LILO"}, + {0x10, 0xf0, "LOADLIN"}, + {0x31, 0xff, "SYSLINUX"}, + {0x32, 0xff, "PXELINUX"}, + {0x33, 0xff, "ISOLINUX"}, + {0x34, 0xff, "EXTLINUX"}, + {0x30, 0xf0, "Syslinux family"}, + {0x40, 0xf0, "Etherboot"}, + {0x50, 0xf0, "ELILO"}, + {0x70, 0xf0, "GrUB"}, + {0x80, 0xf0, "U-Boot"}, + {0xA0, 0xf0, "Gujin"}, + {0xB0, 0xf0, "Qemu"}, + {0x00, 0x00, "unknown"} + }; + + for (lp = list;; lp++) { + if (((id ^ lp->id) & lp->mask) == 0) + return lp->name; + } +} + 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/pcitest.c b/com32/modules/pcitest.c index 672023ad..9921ee6d 100644 --- a/com32/modules/pcitest.c +++ b/com32/modules/pcitest.c @@ -39,12 +39,7 @@ #include <com32.h> #include <sys/pci.h> #include <stdbool.h> - -#ifdef DEBUG -# define dprintf printf -#else -# define dprintf(...) ((void)0) -#endif +#include <dprintf.h> char display_line = 0; #define moreprintf(...) \ diff --git a/com32/modules/pmload.c b/com32/modules/pmload.c index 3064a94f..4c01db08 100644 --- a/com32/modules/pmload.c +++ b/com32/modules/pmload.c @@ -81,10 +81,8 @@ int boot_raw(void *ptr, size_t len, addr_t where, char **argv) if (!mmap || !amap) goto bail; -#if DEBUG dprintf("Initial memory map:\n"); - syslinux_dump_memmap(stdout, mmap); -#endif + syslinux_dump_memmap(mmap); dprintf("Segment at 0x%08x len 0x%08x\n", where, len); @@ -119,10 +117,8 @@ int boot_raw(void *ptr, size_t len, addr_t where, char **argv) if (!stack_frame) goto bail; -#if DEBUG dprintf("Right before syslinux_memmap_largest()...\n"); - syslinux_dump_memmap(stdout, amap); -#endif + syslinux_dump_memmap(amap); if (syslinux_memmap_largest(amap, SMT_FREE, &lstart, &llen)) goto bail; /* NO free memory?! */ @@ -173,16 +169,14 @@ int boot_raw(void *ptr, size_t len, addr_t where, char **argv) regs.eip = where; regs.esp = stack_pointer; -#if DEBUG dprintf("Final memory map:\n"); - syslinux_dump_memmap(stdout, mmap); + syslinux_dump_memmap(mmap); dprintf("Final available map:\n"); - syslinux_dump_memmap(stdout, amap); + syslinux_dump_memmap(amap); dprintf("Movelist:\n"); - syslinux_dump_movelist(stdout, ml); -#endif + syslinux_dump_movelist(ml); /* This should not return... */ fputs("Booting...\n", stdout); 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/modules/zzjson.c b/com32/modules/zzjson.c new file mode 100644 index 00000000..e2516fa1 --- /dev/null +++ b/com32/modules/zzjson.c @@ -0,0 +1,101 @@ +/* + * Display directory contents + */ +#include <stdlib.h> +#include <stdio.h> +#include <console.h> +#include <string.h> +#include <com32.h> +#include <zzjson/zzjson.h> +#include <stdarg.h> + +static void myerror(void *ehandle, const char *format, ...) { + va_list ap; + fprintf(ehandle, "error: "); + va_start(ap, format); + vfprintf(ehandle, format, ap); + va_end(ap); + fputc('\n', ehandle); +} + + +int main(int argc, char *argv[]) +{ + openconsole(&dev_rawcon_r, &dev_stdcon_w); + (void) argc; + (void) argv; + ZZJSON *tmp; + ZZJSON_CONFIG config = { ZZJSON_VERY_STRICT, NULL, + (int(*)(void*)) fgetc, + NULL, + malloc, calloc, free, realloc, + stderr, myerror, stdout, + (int(*)(void*,const char*,...)) fprintf, + (int(*)(int,void*)) fputc }; + + do { + ZZJSON *tmp2; + + tmp = zzjson_create_array(&config, + zzjson_create_number_d(&config, 3.14), + zzjson_create_number_i(&config, 1234LL), + zzjson_create_number_i(&config, -4321LL), + zzjson_create_true(&config), + zzjson_create_false(&config), + zzjson_create_null(&config), + zzjson_create_string(&config, "hello, world"), + zzjson_create_object(&config, + "picard", zzjson_create_string(&config, "jean-luc"), + "riker", zzjson_create_string(&config, "william t."), + NULL), + zzjson_create_object(&config, NULL), + zzjson_create_array(&config, NULL), + NULL ); + + if (!tmp) { + fprintf(stderr, "error during creation of json tree\n"); + break; + } + + tmp2 = zzjson_array_prepend(&config, tmp, + zzjson_create_string(&config, "prepended string")); + + if (!tmp2) { + fprintf(stderr, "error during prepend\n"); + break; + } + tmp = tmp2; + + tmp2 = zzjson_array_append(&config, tmp, + zzjson_create_string(&config, "appended string (slow)")); + + if (!tmp2) { + fprintf(stderr, "error during append\n"); + break; + } + tmp = tmp2; + + zzjson_print(&config, tmp); + } while(0); + if (tmp) zzjson_free(&config, tmp); + + { + tmp = zzjson_create_array(&config, NULL); /* empty array */ + tmp = zzjson_array_prepend(&config, tmp, zzjson_create_true(&config)); + zzjson_print(&config, tmp); + zzjson_free(&config, tmp); + } + + { + tmp = zzjson_create_object(&config, NULL); /* empty object */ + tmp = zzjson_object_prepend(&config, tmp, "hello", + zzjson_create_string(&config, "world")); + tmp = zzjson_object_append(&config, tmp, "goodbye", + zzjson_create_string(&config, "cruel world")); + zzjson_print(&config, tmp); + zzjson_free(&config, tmp); + } + + return 0; +} + |