Rework disk library to work on a firmware independent way. Each specific
implementation goes through subdirs like "bios/" and "efi/"
respectively.
Signed-off-by: Paulo Alcantara <pcacjr at zytor.com>
---
com32/include/syslinux/disk.h | 50 +++-
com32/lib/Makefile | 2 +
com32/lib/syslinux/disk.c | 583 ------------------------------------
com32/lib/syslinux/disk/bios/disk.c | 364 ++++++++++++++++++++++
com32/lib/syslinux/disk/common.c | 259 ++++++++++++++++
com32/lib/syslinux/disk/efi/disk.c | 351 ++++++++++++++++++++++
efi/Makefile | 1 +
efi/check-gnu-efi.sh | 2 +
mk/lib.mk | 104 +++++--
9 files changed, 1092 insertions(+), 624 deletions(-)
delete mode 100644 com32/lib/syslinux/disk.c
create mode 100644 com32/lib/syslinux/disk/bios/disk.c
create mode 100644 com32/lib/syslinux/disk/common.c
create mode 100644 com32/lib/syslinux/disk/efi/disk.c
diff --git a/com32/include/syslinux/disk.h b/com32/include/syslinux/disk.h
index b8361fe..ff95d9f 100644
--- a/com32/include/syslinux/disk.h
+++ b/com32/include/syslinux/disk.h
@@ -3,6 +3,7 @@
* Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
* Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
* Copyright (C) 2010 Shao Miller
+ * Copyright (C) 2015 Paulo Alcantara <pcacjr at zytor.com>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -33,8 +34,8 @@
* Deal with disks and partitions
*/
-#ifndef _SYSLINUX_DISK_H
-#define _SYSLINUX_DISK_H
+#ifndef _SYSLINUX_DISK_H_
+#define _SYSLINUX_DISK_H_
#include <com32.h>
#include <stdint.h>
@@ -50,15 +51,24 @@ enum disk_op_codes {
struct disk_info {
int disk;
- int ebios; /* EBIOS supported on this disk */
- int cbios; /* CHS geometry is valid */
- uint32_t bps; /* bytes per sector */
- uint64_t lbacnt; /* total amount of sectors */
+ uint32_t bps; /* bytes per sector */
+ uint64_t lbacnt; /* total amount of sectors */
uint32_t cyl;
uint32_t head;
uint32_t spt;
+ union {
+ struct { /* BIOS-specific information */
+ int ebios; /* EBIOS supported on this disk */
+ int cbios; /* CHS geometry is valid */
+ };
+ struct { /* EFI-specific information */
+ void *biop; /* EFI_BLOCK_IO_PROTOCOL pointer */
+ };
+ };
};
+#ifndef SYSLINUX_EFI
+
struct disk_ebios_dapa {
uint16_t len;
uint16_t count;
@@ -87,6 +97,8 @@ struct disk_ebios_eparam {
uint8_t checksum;
} __attribute__ ((packed));
+#endif
+
/**
* CHS (cylinder, head, sector) value extraction macros.
* Taken from WinVBlock. None expand to an lvalue.
@@ -169,19 +181,25 @@ struct disk_gpt_header {
} __attribute__ ((packed));
static const char disk_gpt_sig_magic[] = "EFI PART";
+/* common.c */
+extern void disk_dos_part_dump(const struct disk_dos_part_entry *const part);
+extern void guid_to_str(char *buf, const struct guid *const id);
+extern int str_to_guid(const char *buf, struct guid *const id);
+extern void disk_gpt_part_dump(const struct disk_gpt_part_entry *const
+ gpt_part);
+
+/* disk.c */
+#ifndef SYSLINUX_EFI
extern int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg);
+#endif
+
extern int disk_get_params(int disk, struct disk_info *const diskinfo);
extern void *disk_read_sectors(const struct disk_info *const diskinfo,
- uint64_t lba, uint8_t count);
+ uint64_t lba, uint8_t count);
extern int disk_write_sectors(const struct disk_info *const diskinfo,
- uint64_t lba, const void *data, uint8_t count);
+ uint64_t lba, const void *data, uint8_t count);
extern int disk_write_verify_sectors(const struct disk_info *const diskinfo,
- uint64_t lba, const void *buf, uint8_t count);
-extern void disk_dos_part_dump(const struct disk_dos_part_entry *const part);
-extern void guid_to_str(char *buf, const struct guid *const id);
-extern int str_to_guid(const char *buf, struct guid *const id);
-extern void disk_gpt_part_dump(const struct disk_gpt_part_entry *const
- gpt_part);
-extern void disk_gpt_header_dump(const struct disk_gpt_header *const gpt);
+ uint64_t lba, const void *buf,
+ uint8_t count);
-#endif /* _SYSLINUX_DISK_H */
+#endif /* _SYSLINUX_DISK_H_ */
diff --git a/com32/lib/Makefile b/com32/lib/Makefile
index 1624ae7..7c54fa2 100644
--- a/com32/lib/Makefile
+++ b/com32/lib/Makefile
@@ -56,6 +56,8 @@ MINLIBOBJS = \
syslinux/ipappend.o \
syslinux/dsinfo.o \
$(LIBOTHER_OBJS) \
+ $(LIBSYS_OBJS) \
+ $(LIBPCI_OBJS) \
$(LIBGCC_OBJS) \
$(LIBCONSOLE_OBJS) \
$(LIBLOAD_OBJS) \
diff --git a/com32/lib/syslinux/disk.c b/com32/lib/syslinux/disk.c
deleted file mode 100644
index afbfc84..0000000
--- a/com32/lib/syslinux/disk.c
+++ /dev/null
@@ -1,583 +0,0 @@
-/* ----------------------------------------------------------------------- *
- *
- * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
- * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
- * Copyright (C) 2010 Shao Miller
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom
- * the Software is furnished to do so, subject to the following
- * conditions:
- *
- * The above copyright notice and this permission notice shall
- * be included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * ----------------------------------------------------------------------- */
-
-/**
- * @file disk.c
- *
- * Deal with disks and partitions
- */
-
-#include <core.h>
-#include <dprintf.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslinux/disk.h>
-
-/**
- * Call int 13h, but with retry on failure. Especially floppies need this.
- *
- * @v inreg CPU register settings upon INT call
- * @v outreg CPU register settings returned by INT call
- * @ret (int) 0 upon success, -1 upon failure
- */
-int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg)
-{
- int retry = 6; /* Number of retries */
- com32sys_t tmpregs;
-
- if (!outreg)
- outreg = &tmpregs;
-
- while (retry--) {
- __intcall(0x13, inreg, outreg);
- if (!(outreg->eflags.l & EFLAGS_CF))
- return 0; /* CF=0, OK */
- }
-
- return -1; /* Error */
-}
-
-/**
- * Query disk parameters and EBIOS availability for a particular disk.
- *
- * @v disk The INT 0x13 disk drive number to process
- * @v diskinfo The structure to save the queried params to
- * @ret (int) 0 upon success, -1 upon failure
- */
-int disk_get_params(int disk, struct disk_info *const diskinfo)
-{
- static com32sys_t inreg, outreg;
- struct disk_ebios_eparam *eparam;
- int rv = 0;
-
- memset(diskinfo, 0, sizeof *diskinfo);
- diskinfo->disk = disk;
- diskinfo->bps = SECTOR;
-
- /* Get EBIOS support */
- memset(&inreg, 0, sizeof inreg);
- inreg.eax.b[1] = 0x41;
- inreg.ebx.w[0] = 0x55aa;
- inreg.edx.b[0] = disk;
- inreg.eflags.b[0] = 0x3; /* CF set */
-
- __intcall(0x13, &inreg, &outreg);
-
- if (!(outreg.eflags.l & EFLAGS_CF) &&
- outreg.ebx.w[0] == 0xaa55 && (outreg.ecx.b[0] & 1)) {
- diskinfo->ebios = 1;
- }
-
- eparam = lmalloc(sizeof *eparam);
- if (!eparam)
- return -1;
-
- /* Get extended disk parameters if ebios == 1 */
- if (diskinfo->ebios) {
- memset(&inreg, 0, sizeof inreg);
- inreg.eax.b[1] = 0x48;
- inreg.edx.b[0] = disk;
- inreg.esi.w[0] = OFFS(eparam);
- inreg.ds = SEG(eparam);
-
- memset(eparam, 0, sizeof *eparam);
- eparam->len = sizeof *eparam;
-
- __intcall(0x13, &inreg, &outreg);
-
- if (!(outreg.eflags.l & EFLAGS_CF)) {
- diskinfo->lbacnt = eparam->lbacnt;
- if (eparam->bps)
- diskinfo->bps = eparam->bps;
- /*
- * don't think about using geometry data returned by
- * 48h, as it can differ from 08h a lot ...
- */
- }
- }
- /*
- * Get disk parameters the old way - really only useful for hard
- * disks, but if we have a partitioned floppy it's actually our best
- * chance...
- */
- memset(&inreg, 0, sizeof inreg);
- inreg.eax.b[1] = 0x08;
- inreg.edx.b[0] = disk;
-
- __intcall(0x13, &inreg, &outreg);
-
- if (outreg.eflags.l & EFLAGS_CF) {
- rv = diskinfo->ebios ? 0 : -1;
- goto out;
- }
-
- diskinfo->spt = 0x3f & outreg.ecx.b[0];
- diskinfo->head = 1 + outreg.edx.b[1];
- diskinfo->cyl = 1 + (outreg.ecx.b[1] | ((outreg.ecx.b[0] & 0xc0u)
<< 2));
-
- if (diskinfo->spt)
- diskinfo->cbios = 1; /* Valid geometry */
- else {
- diskinfo->head = 1;
- diskinfo->spt = 1;
- diskinfo->cyl = 1;
- }
-
- if (!diskinfo->lbacnt)
- diskinfo->lbacnt = diskinfo->cyl * diskinfo->head * diskinfo->spt;
-
-out:
- lfree(eparam);
- return rv;
-}
-
-/**
- * Fill inreg based on EBIOS addressing properties.
- *
- * @v diskinfo The disk drive to read from
- * @v inreg Register data structure to be filled.
- * @v lba The logical block address to begin reading at
- * @v count The number of sectors to read
- * @v op_code Code to write/read operation
- * @ret lmalloc'd buf upon success, NULL upon failure
- */
-static void *ebios_setup(const struct disk_info *const diskinfo, com32sys_t
*inreg,
- uint64_t lba, uint8_t count, uint8_t op_code)
-{
- static struct disk_ebios_dapa *dapa = NULL;
- void *buf;
-
- if (!dapa) {
- dapa = lmalloc(sizeof *dapa);
- if (!dapa)
- return NULL;
- }
-
- buf = lmalloc(count * diskinfo->bps);
- if (!buf)
- return NULL;
-
- dapa->len = sizeof(*dapa);
- dapa->count = count;
- dapa->off = OFFS(buf);
- dapa->seg = SEG(buf);
- dapa->lba = lba;
-
- inreg->eax.b[1] = op_code;
- inreg->esi.w[0] = OFFS(dapa);
- inreg->ds = SEG(dapa);
- inreg->edx.b[0] = diskinfo->disk;
-
- return buf;
-}
-
-/**
- * Fill inreg based on CHS addressing properties.
- *
- * @v diskinfo The disk drive to read from
- * @v inreg Register data structure to be filled.
- * @v lba The logical block address to begin reading at
- * @v count The number of sectors to read
- * @v op_code Code to write/read operation
- * @ret lmalloc'd buf upon success, NULL upon failure
- */
-static void *chs_setup(const struct disk_info *const diskinfo, com32sys_t
*inreg,
- uint64_t lba, uint8_t count, uint8_t op_code)
-{
- unsigned int c, h, s, t;
- void *buf;
-
- buf = lmalloc(count * diskinfo->bps);
- if (!buf)
- return NULL;
-
- /*
- * if we passed lba + count check and we get here, that means that
- * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus
- * 32bits are perfectly enough and lbacnt corresponds to cylinder
- * boundary
- */
- s = lba % diskinfo->spt;
- t = lba / diskinfo->spt;
- h = t % diskinfo->head;
- c = t / diskinfo->head;
-
- memset(inreg, 0, sizeof *inreg);
- inreg->eax.b[0] = count;
- inreg->eax.b[1] = op_code;
- inreg->ecx.b[1] = c;
- inreg->ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
- inreg->edx.b[1] = h;
- inreg->edx.b[0] = diskinfo->disk;
- inreg->ebx.w[0] = OFFS(buf);
- inreg->es = SEG(buf);
-
- return buf;
-}
-
-/**
- * Get disk block(s) and return a malloc'd buffer.
- *
- * @v diskinfo The disk drive to read from
- * @v lba The logical block address to begin reading at
- * @v count The number of sectors to read
- * @ret data An allocated buffer with the read data
- *
- * Uses the disk number and information from diskinfo. Read count sectors
- * from drive, starting at lba. Return a new buffer, or NULL upon failure.
- */
-void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba,
- uint8_t count)
-{
- com32sys_t inreg;
- void *buf;
- void *data = NULL;
- uint32_t maxcnt;
- uint32_t size = 65536;
-
- maxcnt = (size - diskinfo->bps) / diskinfo->bps;
- if (!count || count > maxcnt || lba + count > diskinfo->lbacnt)
- return NULL;
-
- memset(&inreg, 0, sizeof inreg);
-
- if (diskinfo->ebios)
- buf = ebios_setup(diskinfo, &inreg, lba, count, EBIOS_READ_CODE);
- else
- buf = chs_setup(diskinfo, &inreg, lba, count, CHS_READ_CODE);
-
- if (!buf)
- return NULL;
-
- if (disk_int13_retry(&inreg, NULL))
- goto out;
-
- data = malloc(count * diskinfo->bps);
- if (data)
- memcpy(data, buf, count * diskinfo->bps);
-out:
- lfree(buf);
- return data;
-}
-
-/**
- * Write disk block(s).
- *
- * @v diskinfo The disk drive to write to
- * @v lba The logical block address to begin writing at
- * @v data The data to write
- * @v count The number of sectors to write
- * @ret (int) 0 upon success, -1 upon failure
- *
- * Uses the disk number and information from diskinfo.
- * Write sector(s) to a disk drive, starting at lba.
- */
-int disk_write_sectors(const struct disk_info *const diskinfo, uint64_t lba,
- const void *data, uint8_t count)
-{
- com32sys_t inreg;
- void *buf;
- uint32_t maxcnt;
- uint32_t size = 65536;
- int rv = -1;
-
- maxcnt = (size - diskinfo->bps) / diskinfo->bps;
- if (!count || count > maxcnt || lba + count > diskinfo->lbacnt)
- return -1;
-
- memset(&inreg, 0, sizeof inreg);
-
- if (diskinfo->ebios)
- buf = ebios_setup(diskinfo, &inreg, lba, count, EBIOS_WRITE_CODE);
- else
- buf = chs_setup(diskinfo, &inreg, lba, count, CHS_WRITE_CODE);
-
- if (!buf)
- return -1;
-
- memcpy(buf, data, count * diskinfo->bps);
-
- if (disk_int13_retry(&inreg, NULL))
- goto out;
-
- rv = 0; /* ok */
-out:
- lfree(buf);
- return rv;
-}
-
-/**
- * Write disk blocks and verify they were written.
- *
- * @v diskinfo The disk drive to write to
- * @v lba The logical block address to begin writing at
- * @v buf The data to write
- * @v count The number of sectors to write
- * @ret rv 0 upon success, -1 upon failure
- *
- * Uses the disk number and information from diskinfo.
- * Writes sectors to a disk drive starting at lba, then reads them back
- * to verify they were written correctly.
- */
-int disk_write_verify_sectors(const struct disk_info *const diskinfo,
- uint64_t lba, const void *buf, uint8_t count)
-{
- char *rb;
- int rv;
-
- rv = disk_write_sectors(diskinfo, lba, buf, count);
- if (rv)
- return rv; /* Write failure */
- rb = disk_read_sectors(diskinfo, lba, count);
- if (!rb)
- return -1; /* Readback failure */
- rv = memcmp(buf, rb, count * diskinfo->bps);
- free(rb);
- return rv ? -1 : 0;
-}
-
-/**
- * Dump info about a DOS partition entry
- *
- * @v part The 16-byte partition entry to examine
- */
-void disk_dos_part_dump(const struct disk_dos_part_entry *const part)
-{
- (void)part;
- dprintf("Partition status _____ : 0x%.2x\n"
- "Partition CHS start\n"
- " Cylinder ___________ : 0x%.4x (%u)\n"
- " Head _______________ : 0x%.2x (%u)\n"
- " Sector _____________ : 0x%.2x (%u)\n"
- "Partition type _______ : 0x%.2x\n"
- "Partition CHS end\n"
- " Cylinder ___________ : 0x%.4x (%u)\n"
- " Head _______________ : 0x%.2x (%u)\n"
- " Sector _____________ : 0x%.2x (%u)\n"
- "Partition LBA start __ : 0x%.8x (%u)\n"
- "Partition LBA count __ : 0x%.8x (%u)\n"
- "-------------------------------\n",
- part->active_flag,
- chs_cylinder(part->start),
- chs_cylinder(part->start),
- chs_head(part->start),
- chs_head(part->start),
- chs_sector(part->start),
- chs_sector(part->start),
- part->ostype,
- chs_cylinder(part->end),
- chs_cylinder(part->end),
- chs_head(part->end),
- chs_head(part->end),
- chs_sector(part->end),
- chs_sector(part->end),
- part->start_lba, part->start_lba, part->length, part->length);
-}
-
-/* Trivial error message output */
-static inline void error(const char *msg)
-{
- fputs(msg, stderr);
-}
-
-/**
- * This walk-map effectively reverses the little-endian
- * portions of a GPT disk/partition GUID for a string representation.
- * There might be a better header for this...
- */
-static const char guid_le_walk_map[] = {
- 3, -1, -1, -1, 0,
- 5, -1, 0,
- 3, -1, 0,
- 2, 1, 0,
- 1, 1, 1, 1, 1, 1
-};
-
-/**
- * Fill a buffer with a textual GUID representation.
- *
- * @v buf Points to a minimum array of 37 chars
- * @v id The GUID to represent as text
- *
- * The buffer must be >= char[37] and will be populated
- * with an ASCII NUL C string terminator.
- * Example: 11111111-2222-3333-4444-444444444444
- * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
- */
-void guid_to_str(char *buf, const struct guid *const id)
-{
- unsigned int i = 0;
- const char *walker = (const char *)id;
-
- while (i < sizeof(guid_le_walk_map)) {
- walker += guid_le_walk_map[i];
- if (!guid_le_walk_map[i])
- *buf = '-';
- else {
- *buf = ((*walker & 0xF0) >> 4) + '0';
- if (*buf > '9')
- *buf += 'A' - '9' - 1;
- buf++;
- *buf = (*walker & 0x0F) + '0';
- if (*buf > '9')
- *buf += 'A' - '9' - 1;
- }
- buf++;
- i++;
- }
- *buf = 0;
-}
-
-/**
- * Create a GUID structure from a textual GUID representation.
- *
- * @v buf Points to a GUID string to parse
- * @v id Points to a GUID to be populated
- * @ret (int) Returns 0 upon success, -1 upon failure
- *
- * The input buffer must be >= 32 hexadecimal chars and be
- * terminated with an ASCII NUL. Returns non-zero on failure.
- * Example: 11111111-2222-3333-4444-444444444444
- * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
- */
-int str_to_guid(const char *buf, struct guid *const id)
-{
- char guid_seq[sizeof(struct guid) * 2];
- unsigned int i = 0;
- char *walker = (char *)id;
-
- while (*buf && i < sizeof(guid_seq)) {
- switch (*buf) {
- /* Skip these three characters */
- case '{':
- case '}':
- case '-':
- break;
- default:
- /* Copy something useful to the temp. sequence */
- if ((*buf >= '0') && (*buf <= '9'))
- guid_seq[i] = *buf - '0';
- else if ((*buf >= 'A') && (*buf <= 'F'))
- guid_seq[i] = *buf - 'A' + 10;
- else if ((*buf >= 'a') && (*buf <= 'f'))
- guid_seq[i] = *buf - 'a' + 10;
- else {
- /* Or not */
- error("Illegal character in GUID!\n");
- return -1;
- }
- i++;
- }
- buf++;
- }
- /* Check for insufficient valid characters */
- if (i < sizeof(guid_seq)) {
- error("Too few GUID characters!\n");
- return -1;
- }
- buf = guid_seq;
- i = 0;
- while (i < sizeof(guid_le_walk_map)) {
- if (!guid_le_walk_map[i])
- i++;
- walker += guid_le_walk_map[i];
- *walker = *buf << 4;
- buf++;
- *walker |= *buf;
- buf++;
- i++;
- }
- return 0;
-}
-
-/**
- * Display GPT partition details.
- *
- * @v gpt_part The GPT partition entry to display
- */
-void disk_gpt_part_dump(const struct disk_gpt_part_entry *const gpt_part)
-{
- unsigned int i;
- char guid_text[37];
-
- dprintf("----------------------------------\n"
- "GPT part. LBA first __ : 0x%.16" PRIx64 "x\n"
- "GPT part. LBA last ___ : 0x%.16" PRIx64 "x\n"
- "GPT part. attribs ____ : 0x%.16" PRIx64 "x\n"
- "GPT part. name _______ : '",
- gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs);
- for (i = 0; i < sizeof(gpt_part->name); i++) {
- if (gpt_part->name[i])
- dprintf("%c", gpt_part->name[i]);
- }
- dprintf("'");
- guid_to_str(guid_text, &gpt_part->type);
- dprintf("GPT part. type GUID __ : {%s}\n", guid_text);
- guid_to_str(guid_text, &gpt_part->uid);
- dprintf("GPT part. unique ID __ : {%s}\n", guid_text);
-}
-
-/**
- * Display GPT header details.
- *
- * @v gpt The GPT header to display
- */
-void disk_gpt_header_dump(const struct disk_gpt_header *const gpt)
-{
- char guid_text[37];
-
- printf("GPT sig ______________ : '%8.8s'\n"
- "GPT major revision ___ : 0x%.4x\n"
- "GPT minor revision ___ : 0x%.4x\n"
- "GPT header size ______ : 0x%.8x\n"
- "GPT header checksum __ : 0x%.8x\n"
- "GPT reserved _________ : '%4.4s'\n"
- "GPT LBA current ______ : 0x%.16" PRIx64 "x\n"
- "GPT LBA alternative __ : 0x%.16" PRIx64 "x\n"
- "GPT LBA first usable _ : 0x%.16" PRIx64 "x\n"
- "GPT LBA last usable __ : 0x%.16" PRIx64 "x\n"
- "GPT LBA part. table __ : 0x%.16" PRIx64 "x\n"
- "GPT partition count __ : 0x%.8x\n"
- "GPT partition size ___ : 0x%.8x\n"
- "GPT part. table chksum : 0x%.8x\n",
- gpt->sig,
- gpt->rev.fields.major,
- gpt->rev.fields.minor,
- gpt->hdr_size,
- gpt->chksum,
- gpt->reserved1,
- gpt->lba_cur,
- gpt->lba_alt,
- gpt->lba_first_usable,
- gpt->lba_last_usable,
- gpt->lba_table, gpt->part_count, gpt->part_size,
gpt->table_chksum);
- guid_to_str(guid_text, &gpt->disk_guid);
- printf("GPT disk GUID ________ : {%s}\n", guid_text);
-}
diff --git a/com32/lib/syslinux/disk/bios/disk.c
b/com32/lib/syslinux/disk/bios/disk.c
new file mode 100644
index 0000000..76e98c6
--- /dev/null
+++ b/com32/lib/syslinux/disk/bios/disk.c
@@ -0,0 +1,364 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright (C) 2010 Shao Miller
+ * Copyright (C) 2015 Paulo Alcantara <pcacjr at zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/**
+ * @file disk.c
+ *
+ * Deal with disks and partitions
+ */
+
+#include <core.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslinux/disk.h>
+
+/**
+ * Call int 13h, but with retry on failure. Especially floppies need this.
+ *
+ * @v inreg CPU register settings upon INT call
+ * @v outreg CPU register settings returned by INT call
+ * @ret (int) 0 upon success, -1 upon failure
+ */
+int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg)
+{
+ int retry = 6; /* Number of retries */
+ com32sys_t tmpregs;
+
+ if (!outreg)
+ outreg = &tmpregs;
+
+ while (retry--) {
+ __intcall(0x13, inreg, outreg);
+ if (!(outreg->eflags.l & EFLAGS_CF))
+ return 0; /* CF=0, OK */
+ }
+
+ return -1; /* Error */
+}
+
+/**
+ * Query disk parameters and EBIOS availability for a particular disk.
+ *
+ * @v disk The INT 0x13 disk drive number to process
+ * @v diskinfo The structure to save the queried params to
+ * @ret (int) 0 upon success, -1 upon failure
+ */
+int disk_get_params(int disk, struct disk_info *const diskinfo)
+{
+ static com32sys_t inreg, outreg;
+ struct disk_ebios_eparam *eparam;
+ int rv = 0;
+
+ memset(diskinfo, 0, sizeof *diskinfo);
+ diskinfo->disk = disk;
+ diskinfo->bps = SECTOR;
+
+ /* Get EBIOS support */
+ memset(&inreg, 0, sizeof inreg);
+ inreg.eax.b[1] = 0x41;
+ inreg.ebx.w[0] = 0x55aa;
+ inreg.edx.b[0] = disk;
+ inreg.eflags.b[0] = 0x3; /* CF set */
+
+ __intcall(0x13, &inreg, &outreg);
+
+ if (!(outreg.eflags.l & EFLAGS_CF) &&
+ outreg.ebx.w[0] == 0xaa55 && (outreg.ecx.b[0] & 1)) {
+ diskinfo->ebios = 1;
+ }
+
+ eparam = lmalloc(sizeof *eparam);
+ if (!eparam)
+ return -1;
+
+ /* Get extended disk parameters if ebios == 1 */
+ if (diskinfo->ebios) {
+ memset(&inreg, 0, sizeof inreg);
+ inreg.eax.b[1] = 0x48;
+ inreg.edx.b[0] = disk;
+ inreg.esi.w[0] = OFFS(eparam);
+ inreg.ds = SEG(eparam);
+
+ memset(eparam, 0, sizeof *eparam);
+ eparam->len = sizeof *eparam;
+
+ __intcall(0x13, &inreg, &outreg);
+
+ if (!(outreg.eflags.l & EFLAGS_CF)) {
+ diskinfo->lbacnt = eparam->lbacnt;
+ if (eparam->bps)
+ diskinfo->bps = eparam->bps;
+ /*
+ * don't think about using geometry data returned by
+ * 48h, as it can differ from 08h a lot ...
+ */
+ }
+ }
+ /*
+ * Get disk parameters the old way - really only useful for hard
+ * disks, but if we have a partitioned floppy it's actually our best
+ * chance...
+ */
+ memset(&inreg, 0, sizeof inreg);
+ inreg.eax.b[1] = 0x08;
+ inreg.edx.b[0] = disk;
+
+ __intcall(0x13, &inreg, &outreg);
+
+ if (outreg.eflags.l & EFLAGS_CF) {
+ rv = diskinfo->ebios ? 0 : -1;
+ goto out;
+ }
+
+ diskinfo->spt = 0x3f & outreg.ecx.b[0];
+ diskinfo->head = 1 + outreg.edx.b[1];
+ diskinfo->cyl = 1 + (outreg.ecx.b[1] | ((outreg.ecx.b[0] & 0xc0u)
<< 2));
+
+ if (diskinfo->spt)
+ diskinfo->cbios = 1; /* Valid geometry */
+ else {
+ diskinfo->head = 1;
+ diskinfo->spt = 1;
+ diskinfo->cyl = 1;
+ }
+
+ if (!diskinfo->lbacnt)
+ diskinfo->lbacnt = diskinfo->cyl * diskinfo->head * diskinfo->spt;
+
+out:
+ lfree(eparam);
+ return rv;
+}
+
+/**
+ * Fill inreg based on EBIOS addressing properties.
+ *
+ * @v diskinfo The disk drive to read from
+ * @v inreg Register data structure to be filled.
+ * @v lba The logical block address to begin reading at
+ * @v count The number of sectors to read
+ * @v op_code Code to write/read operation
+ * @ret lmalloc'd buf upon success, NULL upon failure
+ */
+static void *ebios_setup(const struct disk_info *const diskinfo, com32sys_t
*inreg,
+ uint64_t lba, uint8_t count, uint8_t op_code)
+{
+ static struct disk_ebios_dapa *dapa = NULL;
+ void *buf;
+
+ if (!dapa) {
+ dapa = lmalloc(sizeof *dapa);
+ if (!dapa)
+ return NULL;
+ }
+
+ buf = lmalloc(count * diskinfo->bps);
+ if (!buf)
+ return NULL;
+
+ dapa->len = sizeof(*dapa);
+ dapa->count = count;
+ dapa->off = OFFS(buf);
+ dapa->seg = SEG(buf);
+ dapa->lba = lba;
+
+ inreg->eax.b[1] = op_code;
+ inreg->esi.w[0] = OFFS(dapa);
+ inreg->ds = SEG(dapa);
+ inreg->edx.b[0] = diskinfo->disk;
+
+ return buf;
+}
+
+/**
+ * Fill inreg based on CHS addressing properties.
+ *
+ * @v diskinfo The disk drive to read from
+ * @v inreg Register data structure to be filled.
+ * @v lba The logical block address to begin reading at
+ * @v count The number of sectors to read
+ * @v op_code Code to write/read operation
+ * @ret lmalloc'd buf upon success, NULL upon failure
+ */
+static void *chs_setup(const struct disk_info *const diskinfo, com32sys_t
*inreg,
+ uint64_t lba, uint8_t count, uint8_t op_code)
+{
+ unsigned int c, h, s, t;
+ void *buf;
+
+ buf = lmalloc(count * diskinfo->bps);
+ if (!buf)
+ return NULL;
+
+ /*
+ * if we passed lba + count check and we get here, that means that
+ * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus
+ * 32bits are perfectly enough and lbacnt corresponds to cylinder
+ * boundary
+ */
+ s = lba % diskinfo->spt;
+ t = lba / diskinfo->spt;
+ h = t % diskinfo->head;
+ c = t / diskinfo->head;
+
+ memset(inreg, 0, sizeof *inreg);
+ inreg->eax.b[0] = count;
+ inreg->eax.b[1] = op_code;
+ inreg->ecx.b[1] = c;
+ inreg->ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
+ inreg->edx.b[1] = h;
+ inreg->edx.b[0] = diskinfo->disk;
+ inreg->ebx.w[0] = OFFS(buf);
+ inreg->es = SEG(buf);
+
+ return buf;
+}
+
+/**
+ * Get disk block(s) and return a malloc'd buffer.
+ *
+ * @v diskinfo The disk drive to read from
+ * @v lba The logical block address to begin reading at
+ * @v count The number of sectors to read
+ * @ret data An allocated buffer with the read data
+ *
+ * Uses the disk number and information from diskinfo. Read count sectors
+ * from drive, starting at lba. Return a new buffer, or NULL upon failure.
+ */
+void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba,
+ uint8_t count)
+{
+ com32sys_t inreg;
+ void *buf;
+ void *data = NULL;
+ uint32_t maxcnt;
+ uint32_t size = 65536;
+
+ maxcnt = (size - diskinfo->bps) / diskinfo->bps;
+ if (!count || count > maxcnt || lba + count > diskinfo->lbacnt)
+ return NULL;
+
+ memset(&inreg, 0, sizeof inreg);
+
+ if (diskinfo->ebios)
+ buf = ebios_setup(diskinfo, &inreg, lba, count, EBIOS_READ_CODE);
+ else
+ buf = chs_setup(diskinfo, &inreg, lba, count, CHS_READ_CODE);
+
+ if (!buf)
+ return NULL;
+
+ if (disk_int13_retry(&inreg, NULL))
+ goto out;
+
+ data = malloc(count * diskinfo->bps);
+ if (data)
+ memcpy(data, buf, count * diskinfo->bps);
+out:
+ lfree(buf);
+ return data;
+}
+
+/**
+ * Write disk block(s).
+ *
+ * @v diskinfo The disk drive to write to
+ * @v lba The logical block address to begin writing at
+ * @v data The data to write
+ * @v count The number of sectors to write
+ * @ret (int) 0 upon success, -1 upon failure
+ *
+ * Uses the disk number and information from diskinfo.
+ * Write sector(s) to a disk drive, starting at lba.
+ */
+int disk_write_sectors(const struct disk_info *const diskinfo, uint64_t lba,
+ const void *data, uint8_t count)
+{
+ com32sys_t inreg;
+ void *buf;
+ uint32_t maxcnt;
+ uint32_t size = 65536;
+ int rv = -1;
+
+ maxcnt = (size - diskinfo->bps) / diskinfo->bps;
+ if (!count || count > maxcnt || lba + count > diskinfo->lbacnt)
+ return -1;
+
+ memset(&inreg, 0, sizeof inreg);
+
+ if (diskinfo->ebios)
+ buf = ebios_setup(diskinfo, &inreg, lba, count, EBIOS_WRITE_CODE);
+ else
+ buf = chs_setup(diskinfo, &inreg, lba, count, CHS_WRITE_CODE);
+
+ if (!buf)
+ return -1;
+
+ memcpy(buf, data, count * diskinfo->bps);
+
+ if (disk_int13_retry(&inreg, NULL))
+ goto out;
+
+ rv = 0; /* ok */
+out:
+ lfree(buf);
+ return rv;
+}
+
+/**
+ * Write disk blocks and verify they were written.
+ *
+ * @v diskinfo The disk drive to write to
+ * @v lba The logical block address to begin writing at
+ * @v buf The data to write
+ * @v count The number of sectors to write
+ * @ret rv 0 upon success, -1 upon failure
+ *
+ * Uses the disk number and information from diskinfo.
+ * Writes sectors to a disk drive starting at lba, then reads them back
+ * to verify they were written correctly.
+ */
+int disk_write_verify_sectors(const struct disk_info *const diskinfo,
+ uint64_t lba, const void *buf, uint8_t count)
+{
+ char *rb;
+ int rv;
+
+ rv = disk_write_sectors(diskinfo, lba, buf, count);
+ if (rv)
+ return rv; /* Write failure */
+ rb = disk_read_sectors(diskinfo, lba, count);
+ if (!rb)
+ return -1; /* Readback failure */
+ rv = memcmp(buf, rb, count * diskinfo->bps);
+ free(rb);
+ return rv ? -1 : 0;
+}
diff --git a/com32/lib/syslinux/disk/common.c b/com32/lib/syslinux/disk/common.c
new file mode 100644
index 0000000..727f576
--- /dev/null
+++ b/com32/lib/syslinux/disk/common.c
@@ -0,0 +1,259 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright (C) 2010 Shao Miller
+ * Copyright (C) 2015 Paulo Alcantara <pcacjr at zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <core.h>
+#include <dprintf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <syslinux/disk.h>
+
+/**
+ * Dump info about a DOS partition entry
+ *
+ * @v part The 16-byte partition entry to examine
+ */
+void disk_dos_part_dump(const struct disk_dos_part_entry *const part)
+{
+ (void)part;
+ dprintf("Partition status _____ : 0x%.2x\n"
+ "Partition CHS start\n"
+ " Cylinder ___________ : 0x%.4x (%u)\n"
+ " Head _______________ : 0x%.2x (%u)\n"
+ " Sector _____________ : 0x%.2x (%u)\n"
+ "Partition type _______ : 0x%.2x\n"
+ "Partition CHS end\n"
+ " Cylinder ___________ : 0x%.4x (%u)\n"
+ " Head _______________ : 0x%.2x (%u)\n"
+ " Sector _____________ : 0x%.2x (%u)\n"
+ "Partition LBA start __ : 0x%.8x (%u)\n"
+ "Partition LBA count __ : 0x%.8x (%u)\n"
+ "-------------------------------\n",
+ part->active_flag,
+ chs_cylinder(part->start),
+ chs_cylinder(part->start),
+ chs_head(part->start),
+ chs_head(part->start),
+ chs_sector(part->start),
+ chs_sector(part->start),
+ part->ostype,
+ chs_cylinder(part->end),
+ chs_cylinder(part->end),
+ chs_head(part->end),
+ chs_head(part->end),
+ chs_sector(part->end),
+ chs_sector(part->end),
+ part->start_lba, part->start_lba, part->length, part->length);
+}
+
+/* Trivial error message output */
+static inline void error(const char *msg)
+{
+ fprintf(stderr, msg);
+}
+
+/**
+ * This walk-map effectively reverses the little-endian
+ * portions of a GPT disk/partition GUID for a string representation.
+ * There might be a better header for this...
+ */
+static const char guid_le_walk_map[] = {
+ 3, -1, -1, -1, 0,
+ 5, -1, 0,
+ 3, -1, 0,
+ 2, 1, 0,
+ 1, 1, 1, 1, 1, 1
+};
+
+/**
+ * Fill a buffer with a textual GUID representation.
+ *
+ * @v buf Points to a minimum array of 37 chars
+ * @v id The GUID to represent as text
+ *
+ * The buffer must be >= char[37] and will be populated
+ * with an ASCII NUL C string terminator.
+ * Example: 11111111-2222-3333-4444-444444444444
+ * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
+ */
+void guid_to_str(char *buf, const struct guid *const id)
+{
+ unsigned int i = 0;
+ const char *walker = (const char *)id;
+
+ while (i < sizeof(guid_le_walk_map)) {
+ walker += guid_le_walk_map[i];
+ if (!guid_le_walk_map[i])
+ *buf = '-';
+ else {
+ *buf = ((*walker & 0xF0) >> 4) + '0';
+ if (*buf > '9')
+ *buf += 'A' - '9' - 1;
+ buf++;
+ *buf = (*walker & 0x0F) + '0';
+ if (*buf > '9')
+ *buf += 'A' - '9' - 1;
+ }
+ buf++;
+ i++;
+ }
+ *buf = 0;
+}
+
+/**
+ * Create a GUID structure from a textual GUID representation.
+ *
+ * @v buf Points to a GUID string to parse
+ * @v id Points to a GUID to be populated
+ * @ret (int) Returns 0 upon success, -1 upon failure
+ *
+ * The input buffer must be >= 32 hexadecimal chars and be
+ * terminated with an ASCII NUL. Returns non-zero on failure.
+ * Example: 11111111-2222-3333-4444-444444444444
+ * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
+ */
+int str_to_guid(const char *buf, struct guid *const id)
+{
+ char guid_seq[sizeof(struct guid) * 2];
+ unsigned int i = 0;
+ char *walker = (char *)id;
+
+ while (*buf && i < sizeof(guid_seq)) {
+ switch (*buf) {
+ /* Skip these three characters */
+ case '{':
+ case '}':
+ case '-':
+ break;
+ default:
+ /* Copy something useful to the temp. sequence */
+ if ((*buf >= '0') && (*buf <= '9'))
+ guid_seq[i] = *buf - '0';
+ else if ((*buf >= 'A') && (*buf <= 'F'))
+ guid_seq[i] = *buf - 'A' + 10;
+ else if ((*buf >= 'a') && (*buf <= 'f'))
+ guid_seq[i] = *buf - 'a' + 10;
+ else {
+ /* Or not */
+ error("Illegal character in GUID!\n");
+ return -1;
+ }
+ i++;
+ }
+ buf++;
+ }
+ /* Check for insufficient valid characters */
+ if (i < sizeof(guid_seq)) {
+ error("Too few GUID characters!\n");
+ return -1;
+ }
+ buf = guid_seq;
+ i = 0;
+ while (i < sizeof(guid_le_walk_map)) {
+ if (!guid_le_walk_map[i])
+ i++;
+ walker += guid_le_walk_map[i];
+ *walker = *buf << 4;
+ buf++;
+ *walker |= *buf;
+ buf++;
+ i++;
+ }
+ return 0;
+}
+
+/**
+ * Display GPT partition details.
+ *
+ * @v gpt_part The GPT partition entry to display
+ */
+void disk_gpt_part_dump(const struct disk_gpt_part_entry *const gpt_part)
+{
+ unsigned int i;
+ unsigned int k;
+ static char part_name[73];
+ char guid_text[37];
+
+ dprintf("----------------------------------\n"
+ "GPT part. LBA first __ : 0x%.16" PRIx64 "x\n"
+ "GPT part. LBA last ___ : 0x%.16" PRIx64 "x\n"
+ "GPT part. attribs ____ : 0x%.16" PRIx64 "x\n"
+ "GPT part. name _______ : '",
+ gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs);
+ for (i = 0, k = 0; i < sizeof(gpt_part->name); i++) {
+ if (gpt_part->name[i])
+ part_name[k++] = gpt_part->name[i];
+ }
+ part_name[k] = '\0';
+ dprintf("%s'", part_name);
+ guid_to_str(guid_text, &gpt_part->type);
+ dprintf("GPT part. type GUID __ : {%s}\n", guid_text);
+ guid_to_str(guid_text, &gpt_part->uid);
+ dprintf("GPT part. unique ID __ : {%s}\n", guid_text);
+}
+
+/**
+ * Display GPT header details.
+ *
+ * @v gpt The GPT header to display
+ */
+void disk_gpt_header_dump(const struct disk_gpt_header *const gpt)
+{
+ char guid_text[37];
+
+ printf("GPT sig ______________ : '%8.8s'\n"
+ "GPT major revision ___ : 0x%.4x\n"
+ "GPT minor revision ___ : 0x%.4x\n"
+ "GPT header size ______ : 0x%.8x\n"
+ "GPT header checksum __ : 0x%.8x\n"
+ "GPT reserved _________ : '%4.4s'\n"
+ "GPT LBA current ______ : 0x%.16" PRIx64 "x\n"
+ "GPT LBA alternative __ : 0x%.16" PRIx64 "x\n"
+ "GPT LBA first usable _ : 0x%.16" PRIx64 "x\n"
+ "GPT LBA last usable __ : 0x%.16" PRIx64 "x\n"
+ "GPT LBA part. table __ : 0x%.16" PRIx64 "x\n"
+ "GPT partition count __ : 0x%.8x\n"
+ "GPT partition size ___ : 0x%.8x\n"
+ "GPT part. table chksum : 0x%.8x\n",
+ gpt->sig,
+ gpt->rev.fields.major,
+ gpt->rev.fields.minor,
+ gpt->hdr_size,
+ gpt->chksum,
+ gpt->reserved1,
+ gpt->lba_cur,
+ gpt->lba_alt,
+ gpt->lba_first_usable,
+ gpt->lba_last_usable,
+ gpt->lba_table, gpt->part_count, gpt->part_size,
gpt->table_chksum);
+ guid_to_str(guid_text, &gpt->disk_guid);
+ printf("GPT disk GUID ________ : {%s}\n", guid_text);
+}
diff --git a/com32/lib/syslinux/disk/efi/disk.c
b/com32/lib/syslinux/disk/efi/disk.c
new file mode 100644
index 0000000..2361724
--- /dev/null
+++ b/com32/lib/syslinux/disk/efi/disk.c
@@ -0,0 +1,351 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright (C) 2010 Shao Miller
+ * Copyright (C) 2015 Paulo Alcantara <pcacjr at zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/**
+ * @file disk.c
+ *
+ * Deal with disks and partitions
+ */
+
+#include <core.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <syslinux/disk.h>
+
+#define DISKS_MAX 0x80
+
+static EFI_HANDLE disk_dev_handles[DISKS_MAX] = { NULL, };
+
+/* Find all device handles that support EFI_BLOCK_IO_PROTOCOL */
+static EFI_STATUS find_all_block_devs(EFI_HANDLE **bdevs,
+ unsigned long *bdevsno)
+{
+ EFI_STATUS status;
+ unsigned long len = 0;
+
+ *bdevsno = 0;
+
+ status = uefi_call_wrapper(BS->LocateHandle, 5, ByProtocol,
+ &BlockIoProtocol, NULL, &len, NULL);
+ if (EFI_ERROR(status) && status != EFI_BUFFER_TOO_SMALL) {
+ printf("%s: failed to locate BlockIo device handles\n",
__func__);
+ return status;
+ }
+
+ *bdevs = malloc(len);
+ if (!*bdevs) {
+ status = EFI_OUT_OF_RESOURCES;
+ return status;
+ }
+
+ status = uefi_call_wrapper(BS->LocateHandle, 5, ByProtocol,
+ &BlockIoProtocol, NULL, &len, *bdevs);
+ if (EFI_ERROR(status)) {
+ printf("%s: failed to locate BlockIo device handles\n",
__func__);
+ free(*bdevs);
+ return status;
+ }
+
+ *bdevsno = len / sizeof(EFI_HANDLE);
+
+ return status;
+}
+
+#if 0
+static void __print_dev_path(const CHAR16 *str)
+{
+ unsigned char *start = (unsigned char *)(uintptr_t)str;
+ unsigned char *end = (unsigned char *)((uintptr_t)start + StrSize(str));
+
+ while (start < end) {
+ if (*start)
+ printf("%c", *start);
+ start++;
+ }
+ printf("\n");
+}
+
+static void print_dev_path(const EFI_DEVICE_PATH *path)
+{
+ CHAR16 *s;
+
+ s = DevicePathToStr(path);
+ __print_dev_path(s);
+ FreePool(s);
+}
+#endif
+
+static EFI_DEVICE_PATH *find_hdd_pci_dev_path(EFI_HANDLE dh)
+{
+ EFI_DEVICE_PATH *path;
+ EFI_DEVICE_PATH *cur;
+
+ path = DuplicateDevicePath(DevicePathFromHandle(dh));
+ cur = path;
+ do {
+ if (DevicePathType(cur) == MEDIA_DEVICE_PATH &&
+ DevicePathSubType(cur) == MEDIA_HARDDRIVE_DP) {
+ cur->Type = END_DEVICE_PATH_TYPE;
+ cur->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
+ return path;
+ }
+ cur = NextDevicePathNode(cur);
+ } while (!IsDevicePathEnd(cur));
+
+ return NULL;
+}
+
+static int add_disk_dev_handle(EFI_DEVICE_PATH *path)
+{
+ EFI_STATUS status;
+ EFI_DEVICE_PATH *rempath = path;
+ EFI_HANDLE dh = NULL;
+ unsigned int i;
+
+ status = uefi_call_wrapper(BS->LocateDevicePath, 3,
&BlockIoProtocol,
+ &rempath, &dh);
+ if (EFI_ERROR(status)) {
+ printf("%s: failed to locate PCI device handle\n", __func__);
+ return -1;
+ }
+
+ for (i = 0; i < DISKS_MAX && disk_dev_handles[i]; i++) {
+ if (disk_dev_handles[i] == dh)
+ break;
+ }
+
+ if (__unlikely(i == DISKS_MAX))
+ return 0;
+
+ if (!disk_dev_handles[i])
+ disk_dev_handles[i] = dh;
+ return 0;
+}
+
+static int do_enumerate_disks(void)
+{
+ EFI_STATUS status;
+ EFI_HANDLE *bdevs = NULL;
+ unsigned long bdevsno = 0;
+ unsigned long i;
+ int ret = -1;
+ EFI_DEVICE_PATH *path;
+
+ status = find_all_block_devs(&bdevs, &bdevsno);
+ if (EFI_ERROR(status)) {
+ printf("%s: failed to locate BlockIo device handles\n",
__func__);
+ return -1;
+ }
+
+ for (i = 0; i < bdevsno; i++) {
+ path = find_hdd_pci_dev_path(bdevs[i]);
+ if (path) {
+ if (add_disk_dev_handle(path)) {
+ free(path);
+ goto out;
+ }
+ free(path);
+ }
+ }
+
+ ret = 0;
+
+out:
+ free(bdevs);
+ return ret;
+}
+
+/**
+ * Query disk parameters for a particular disk.
+ *
+ * @v disk The disk drive number
+ * @v diskinfo The structure to save the queried params to
+ * @ret (int) 0 upon success, -1 upon failure
+ */
+int disk_get_params(int disk, struct disk_info *const diskinfo)
+{
+ int ret;
+ EFI_STATUS status;
+ EFI_BLOCK_IO *bio;
+
+ memset(diskinfo, 0, sizeof(*diskinfo));
+ diskinfo->disk = disk;
+
+ if (disk >= DISKS_MAX)
+ return -1;
+
+ if (!disk_dev_handles[0]) {
+ ret = do_enumerate_disks();
+ if (ret)
+ return ret;
+ }
+
+ status = uefi_call_wrapper(BS->HandleProtocol, 3,
+ disk_dev_handles[disk], &BlockIoProtocol,
+ &bio);
+ if (EFI_ERROR(status)) {
+ printf("%s: failed to get BlockIo protocol pointer\n",
__func__);
+ return -1;
+ }
+
+ diskinfo->bps = bio->Media->BlockSize;
+ diskinfo->lbacnt = bio->Media->LastBlock + 1;
+ diskinfo->cyl = 1;
+ diskinfo->head = 1;
+ diskinfo->spt = 1;
+
+ diskinfo->biop = bio;
+
+ return 0;
+}
+
+/**
+ * Get disk block(s) and return a malloc'd buffer.
+ *
+ * @v diskinfo The disk drive to read from
+ * @v lba The logical block address to begin reading at
+ * @v count The number of sectors to read
+ * @ret data An allocated buffer with the read data
+ *
+ * Uses the disk number and information from diskinfo. Read count sectors
+ * from drive, starting at lba. Return a new buffer, or NULL upon failure.
+ */
+void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba,
+ uint8_t count)
+{
+ EFI_STATUS status;
+ unsigned long len;
+ void *data;
+ EFI_BLOCK_IO *bio;
+
+ if (lba + count > diskinfo->lbacnt)
+ count = diskinfo->lbacnt - lba; /* truncate read */
+
+ len = count * diskinfo->bps;
+ data = lmalloc(count * diskinfo->bps);
+ if (!data)
+ return NULL;
+
+ bio = (EFI_BLOCK_IO *)diskinfo->biop;
+
+ status = uefi_call_wrapper(bio->ReadBlocks, 5, bio,
bio->Media->MediaId,
+ lba, len, data);
+ if (EFI_ERROR(status)) {
+ printf("%s: failed to read blocks from disk %d\n", __func__,
+ diskinfo->disk);
+ free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+/**
+ * Write disk block(s).
+ *
+ * @v diskinfo The disk drive to write to
+ * @v lba The logical block address to begin writing at
+ * @v data The data to write
+ * @v count The number of sectors to write
+ * @ret (int) 0 upon success, -1 upon failure
+ *
+ * Uses the disk number and information from diskinfo.
+ * Write sector(s) to a disk drive, starting at lba.
+ */
+int disk_write_sectors(const struct disk_info *const diskinfo, uint64_t lba,
+ const void *data, uint8_t count)
+{
+ EFI_BLOCK_IO *bio;
+ EFI_STATUS status;
+ unsigned long len;
+
+ bio = (EFI_BLOCK_IO *)diskinfo->biop;
+ if (bio->Media->ReadOnly) {
+ printf("%s: media %d is marked read-only\n", __func__,
diskinfo->disk);
+ return -1;
+ }
+
+ if (lba + count > diskinfo->lbacnt)
+ count = diskinfo->lbacnt - lba; /* truncate write */
+
+ len = count * diskinfo->bps;
+
+ status = uefi_call_wrapper(bio->WriteBlocks, 5, bio,
bio->Media->MediaId,
+ lba, len, data);
+ if (EFI_ERROR(status)) {
+ printf("%s: failed to write blocks from disk %d\n", __func__,
+ diskinfo->disk);
+ return -1;
+ }
+
+ status = uefi_call_wrapper(bio->FlushBlocks, 1, bio);
+ if (EFI_ERROR(status)) {
+ printf("%s: failed to flush out blocks from disk %d\n",
__func__,
+ diskinfo->disk);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Write disk blocks and verify they were written.
+ *
+ * @v diskinfo The disk drive to write to
+ * @v lba The logical block address to begin writing at
+ * @v buf The data to write
+ * @v count The number of sectors to write
+ * @ret rv 0 upon success, -1 upon failure
+ *
+ * Uses the disk number and information from diskinfo.
+ * Writes sectors to a disk drive starting at lba, then reads them back
+ * to verify they were written correctly.
+ */
+int disk_write_verify_sectors(const struct disk_info *const diskinfo,
+ uint64_t lba, const void *buf, uint8_t count)
+{
+ int ret;
+ void *rb;
+
+ ret = disk_write_sectors(diskinfo, lba, buf, count);
+ if (ret)
+ return ret;
+ rb = disk_read_sectors(diskinfo, lba, count);
+ if (!rb)
+ return -1;
+ ret = memcmp(buf, rb, count * diskinfo->bps);
+ free(rb);
+ return ret ? -1 : 0;
+}
diff --git a/efi/Makefile b/efi/Makefile
index bbf23f2..0c18d14 100644
--- a/efi/Makefile
+++ b/efi/Makefile
@@ -42,6 +42,7 @@ CORE_OBJS += $(addprefix $(OBJ)/../core/, \
fs/pxe/ftp.o fs/pxe/ftp_readdir.o fs/pxe/http.o fs/pxe/http_readdir.o)
LIB_OBJS = $(addprefix $(objdir)/com32/lib/,$(CORELIBOBJS)) \
+ $(addprefix $(objdir)/com32/lib/,$(LIBOTHER_OBJS)) \
$(LIBEFI)
CSRC = $(wildcard $(SRC)/*.c)
diff --git a/efi/check-gnu-efi.sh b/efi/check-gnu-efi.sh
index 7d99e9a..67fe67d 100755
--- a/efi/check-gnu-efi.sh
+++ b/efi/check-gnu-efi.sh
@@ -25,6 +25,7 @@ if [ ! \( -f "$objdir/include/efi/$ARCH/efibind.h"
-a -f "$objdir/lib/libefi.a"
# Syslinux disables built-in implicit rules.
export MAKEFLAGS
+ pushd "$objdir/efi" >/dev/null
../../efi/build-gnu-efi.sh $ARCH "$objdir"
if [ $? -ne 0 ]; then
printf "Failed to build gnu-efi. "
@@ -33,6 +34,7 @@ if [ ! \( -f "$objdir/include/efi/$ARCH/efibind.h"
-a -f "$objdir/lib/libefi.a"
exit 1
fi
+ popd >/dev/null
else
printf "skip gnu-efi build/install\n"
fi
diff --git a/mk/lib.mk b/mk/lib.mk
index ceb95bd..f00dd94 100644
--- a/mk/lib.mk
+++ b/mk/lib.mk
@@ -38,6 +38,31 @@ LIBFLAGS = -DDYNAMIC_CRC_TABLE -DPNG_NO_CONSOLE_IO \
-DPNG_NO_MNG_FEATURES \
-DPNG_NO_READ_tIME -DPNG_NO_WRITE_tIME
+ifdef EFI_BUILD
+
+ifeq ($(ARCH),i386)
+ ARCHOPT = -m32 -march=i386
+ EFI_SUBARCH = ia32
+endif
+ifeq ($(ARCH),x86_64)
+ ARCHOPT = -m64 -march=x86-64
+ EFI_SUBARCH = $(ARCH)
+endif
+
+EFI_OBJDIR = $(OBJ)/../..
+EFIINC = $(EFI_OBJDIR)/include/efi
+
+FW_SUBDIR = efi
+FWFLAGS = -I$(EFIINC) -I$(EFIINC)/$(EFI_SUBARCH) -I$(EFIINC)/protocol \
+ -DEFI_FUNCTION_WRAPPER -fPIC -fshort-wchar -ffreestanding \
+ -std=gnu99 -DSYSLINUX_EFI
+else
+
+FW_SUBDIR = bios
+FWFLAGS +
+endif # ifdef EFI_BUILD
+
# We need some features in libpng which apparently aren't available in the
# fixed-point versions. It's OK, because we have to have a non-graphical
# fallback anyway, just use that on old machines...
@@ -52,13 +77,37 @@ OPTFLAGS = -Os -march=$(MARCH) -falign-functions=0
-falign-jumps=0 \
-falign-labels=0 -ffast-math -fomit-frame-pointer
WARNFLAGS = $(GCCWARN) -Wpointer-arith -Wwrite-strings -Wstrict-prototypes
-Winline
-CFLAGS = $(OPTFLAGS) $(REQFLAGS) $(WARNFLAGS) $(LIBFLAGS)
+CFLAGS = $(OPTFLAGS) $(REQFLAGS) $(WARNFLAGS) $(LIBFLAGS) $(FWFLAGS)
ifndef EFI_BUILD
CFLAGS += -mregparm=3 -DREGPARM=3
endif
VPATH = $(SRC)
+
+LIBSYS_OBJS = \
+ sys/line_input.o \
+ sys/colortable.o sys/screensize.o \
+ \
+ sys/stdcon_read.o sys/stdcon_write.o sys/rawcon_read.o \
+ sys/rawcon_write.o \
+ sys/null_read.o sys/null_write.o sys/serial_write.o \
+ \
+ sys/xserial_write.o \
+ \
+ sys/ansi.o \
+ \
+ sys/ansicon_write.o sys/ansiserial_write.o \
+ \
+ sys/x86_init_fpu.o math/pow.o math/strtod.o \
+ \
+ syslinux/setup_data.o
+
+LIBPCI_OBJS = \
+ pci/cfgtype.o pci/scan.o pci/bios.o \
+ pci/readb.o pci/readw.o pci/readl.o \
+ pci/writeb.o pci/writew.o pci/writel.o
+
LIBOTHER_OBJS = \
atoi.o atol.o atoll.o calloc.o creat.o \
fgets.o fprintf.o fputc.o \
@@ -86,29 +135,7 @@ LIBOTHER_OBJS = \
\
suffix_number.o \
\
- getcwd.o fdopendir.o \
- \
- sys/line_input.o \
- sys/colortable.o sys/screensize.o \
- \
- sys/stdcon_read.o sys/stdcon_write.o sys/rawcon_read.o \
- sys/rawcon_write.o \
- sys/null_read.o sys/null_write.o sys/serial_write.o \
- \
- sys/xserial_write.o \
- \
- sys/ansi.o \
- \
- sys/ansicon_write.o sys/ansiserial_write.o \
- \
- pci/cfgtype.o pci/scan.o pci/bios.o \
- pci/readb.o pci/readw.o pci/readl.o \
- pci/writeb.o pci/writew.o pci/writel.o \
- \
- sys/x86_init_fpu.o math/pow.o math/strtod.o \
- syslinux/disk.o \
- \
- syslinux/setup_data.o
+ getcwd.o fdopendir.o
## CORE OBJECTS, INCLUDED IN THE ROOT COM32 MODULE
LIBENTRY_OBJS = \
@@ -175,10 +202,16 @@ LIBZLIB_OBJS = \
zlib/inflate.o zlib/infback.o zlib/inftrees.o zlib/inffast.o \
sys/zfile.o sys/zfopen.o
+LIBDISK_OBJS = \
+ syslinux/disk/common.o \
+ syslinux/disk/$(FW_SUBDIR)/disk.o
+
MINLIBOBJS = \
$(addprefix $(OBJ)/,syslinux/ipappend.o \
syslinux/dsinfo.o \
$(LIBOTHER_OBJS) \
+ $(LIBSYS_OBJS) \
+ $(LIBPCI_OBJS) \
$(LIBGCC_OBJS) \
$(LIBCONSOLE_OBJS) \
$(LIBLOAD_OBJS) \
@@ -201,7 +234,28 @@ CORELIBOBJS = \
libgcc/__divdi3.o libgcc/__moddi3.o \
syslinux/debug.o \
$(LIBENTRY_OBJS) \
- $(LIBMODULE_OBJS)
+ $(LIBMODULE_OBJS) \
+ $(LIBDISK_OBJS)
+
+ifdef EFI_BUILD
+
+.PHONY: subdirs
+subdirs:
+ @mkdir -p $(EFI_OBJDIR)/efi
+
+LIBEFI = $(EFI_OBJDIR)/lib/libefi.a
+
+$(LIBEFI): subdirs
+ @echo Building gnu-efi for $(EFI_SUBARCH)
+ $(topdir)/efi/check-gnu-efi.sh $(EFI_SUBARCH) $(EFI_OBJDIR)
+
+# add library objects which support both BIOS and UEFI implementations
+MULTI_FW_SUP_LIB_OBJS = $(LIBDISK_OBJS)
+
+# make parallel build happy
+$(MULTI_FW_SUP_LIB_OBJS): $(LIBEFI)
+
+endif # ifdef EFI_BUILD
LDFLAGS = -m elf_$(ARCH) --hash-style=gnu -T $(com32)/lib/$(ARCH)/elf.ld
--
2.4.3