aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--com32/gplinclude/disk/geom.h309
-rw-r--r--com32/gplinclude/disk/read.h6
-rw-r--r--com32/gplinclude/disk/util.h9
-rw-r--r--com32/gplinclude/disk/write.h11
-rw-r--r--com32/gpllib/Makefile3
-rw-r--r--com32/gpllib/disk/ata.c59
-rw-r--r--com32/gpllib/disk/geom.c250
-rw-r--r--com32/gpllib/disk/read.c89
-rw-r--r--com32/gpllib/disk/util.c151
-rw-r--r--com32/gpllib/disk/write.c107
10 files changed, 993 insertions, 1 deletions
diff --git a/com32/gplinclude/disk/geom.h b/com32/gplinclude/disk/geom.h
new file mode 100644
index 00000000..4dda88bf
--- /dev/null
+++ b/com32/gplinclude/disk/geom.h
@@ -0,0 +1,309 @@
+#ifndef _GEOM_H_
+#define _GEOM_H_
+
+#include <stdint.h>
+
+#define SECTOR 512 /* bytes/sector */
+
+/*
+ * Disk parameters
+ */
+struct driveinfo {
+ int disk;
+ int ebios; /* EBIOS supported on this disk */
+ int edd_version; /* EBIOS major version */
+ int edd_functionality_subset;
+ int cbios; /* CHS geometry is valid */
+ int heads;
+ int sectors;
+ int sectors_per_track;
+ int bytes_per_sector;
+ int cylinder;
+ int type; /* Drive type (AT/PS2 floppies only) */
+ int drives; /* Number of drives */
+ char host_bus_type[5];
+ char interface_type[8];
+};
+
+struct ebios_dapa {
+ uint16_t len;
+ uint16_t count;
+ uint16_t off;
+ uint16_t seg;
+ uint64_t lba;
+};
+
+/**
+ * INT 13 Extensions
+ *
+ * Note: if the size is less than 30 on call, the final DWORD will not be
+ * returned by a v2.x implementation; similarly for the Device Path info
+ **/
+struct device_parameter {
+ uint16_t len; /* size of returned data */
+ /**
+ * Bitfields for IBM/MS INT 13 Extensions information flags:
+ * Bit(s) Description (Table 00274)
+ * 0 DMA boundary errors handled transparently
+ * 1 cylinder/head/sectors-per-track information is valid
+ * 2 removable drive
+ * 3 write with verify supported
+ * 4 drive has change-line support (required if drive >= 80h is removable)
+ * 5 drive can be locked (required if drive >= 80h is removable)
+ * 6 CHS information set to maximum supported values, not current media
+ * 15-7 reserved (0)
+ **/
+ uint16_t info; /* information flags */
+ uint32_t cylinders; /* number of physical cylinders on drive */
+ uint32_t heads; /* number of physical heads on drive */
+ uint32_t sectors_per_track; /* number of physical sectors per track */
+ uint64_t sectors; /* total number of sectors on drive */
+ uint16_t bytes_per_sector; /* bytes per sector */
+ /* --- v2.0+ --- */
+ uint32_t dpte_pointer; /* EDD configuration parameters, FFFFh:FFFFh if not available */
+ /* --- v3.0 --- */
+ uint16_t device_path_information; /* signature BEDDh to indicate presence of Device Path info */
+ uint8_t device_path_length; /* length of Device Path information, including signature and this byte (24h for v3.0) */
+ uint8_t device_path_reserved; /* reserved (0) */
+ uint16_t device_path_reserved_2; /* reserved (0) */
+ uint8_t host_bus_type[4]; /* ASCIZ name of host bus ("ISA" or "PCI") */
+ uint8_t interface_type[8]; /* ASCIZ name of interface type
+ * "ATA"
+ * "ATAPI"
+ * "SCSI"
+ * "USB"
+ * "1394" IEEE 1394 (FireWire)
+ * "FIBRE" Fibre Channel
+ */
+ /**
+ * Format of EDD v3.0 Interface Path:
+ * Offset Size Description (Table 00275)
+ * ---ISA---
+ * 00h WORD 16-bit base address
+ * 02h 6 BYTEs reserved (0)
+ * ---PCI---
+ * 00h BYTE PCI bus number
+ * 01h BYTE PCI device number
+ * 02h BYTE PCI function number
+ * 03h 5 BYTEs reserved (0)
+ **/
+ union {
+ struct {
+ uint16_t base_address;
+ uint16_t reserved1;
+ uint32_t reserved2;
+ } __attribute__ ((packed)) isa;
+ struct {
+ uint8_t bus;
+ uint8_t slot;
+ uint8_t function;
+ uint8_t channel;
+ uint32_t reserved;
+ } __attribute__ ((packed)) pci;
+ /* pcix is same as pci */
+ struct {
+ uint64_t reserved;
+ } __attribute__ ((packed)) ibnd;
+ struct {
+ uint64_t reserved;
+ } __attribute__ ((packed)) xprs;
+ struct {
+ uint64_t reserved;
+ } __attribute__ ((packed)) htpt;
+ struct {
+ uint64_t reserved;
+ } __attribute__ ((packed)) unknown;
+ } interface_path;
+ /**
+ * Format of EDD v3.0 Device Path:
+ * Offset Size Description (Table 00276)
+ * ---ATA---
+ * 00h BYTE flag: 00h = master, 01h = slave
+ * 01h 7 BYTEs reserved (0)
+ * ---ATAPI---
+ * 00h BYTE flag: 00h = master, 01h = slave
+ * 01h BYTE logical unit number
+ * 02h 6 BYTEs reserved (0)
+ * ---SCSI---
+ * 00h BYTE logical unit number
+ * 01h 7 BYTEs reserved (0)
+ * ---USB---
+ * 00h BYTE to be determined
+ * 01h 7 BYTEs reserved (0)
+ * ---IEEE1394---
+ * 00h QWORD 64-bit FireWire General Unique Identifier (GUID)
+ * ---FibreChannel---
+ * 00h QWORD Word Wide Number (WWN)
+ **/
+ union {
+ struct {
+ uint8_t device;
+ uint8_t reserved1;
+ uint16_t reserved2;
+ uint32_t reserved3;
+ uint64_t reserved4;
+ } __attribute__ ((packed)) ata;
+ struct {
+ uint8_t device;
+ uint8_t lun;
+ uint8_t reserved1;
+ uint8_t reserved2;
+ uint32_t reserved3;
+ uint64_t reserved4;
+ } __attribute__ ((packed)) atapi;
+ struct {
+ uint16_t id;
+ uint64_t lun;
+ uint16_t reserved1;
+ uint32_t reserved2;
+ } __attribute__ ((packed)) scsi;
+ struct {
+ uint64_t serial_number;
+ uint64_t reserved;
+ } __attribute__ ((packed)) usb;
+ struct {
+ uint64_t eui;
+ uint64_t reserved;
+ } __attribute__ ((packed)) i1394;
+ struct {
+ uint64_t wwid;
+ uint64_t lun;
+ } __attribute__ ((packed)) fibre;
+ struct {
+ uint64_t identity_tag;
+ uint64_t reserved;
+ } __attribute__ ((packed)) i2o;
+ struct {
+ uint32_t array_number;
+ uint32_t reserved1;
+ uint64_t reserved2;
+ } __attribute__ ((packed)) raid;
+ struct {
+ uint8_t device;
+ uint8_t reserved1;
+ uint16_t reserved2;
+ uint32_t reserved3;
+ uint64_t reserved4;
+ } __attribute__ ((packed)) sata;
+ struct {
+ uint64_t reserved1;
+ uint64_t reserved2;
+ } __attribute__ ((packed)) unknown;
+ } device_path;
+ uint8_t reserved; /* reserved (0) */
+ uint8_t cheksum; /* checksum of bytes 1Eh-40h (two's complement of sum, which makes
+ * the 8-bit sum of bytes 1Eh-41h equal 00h) */
+} __attribute__ ((packed));
+
+/**
+ * Format of Phoenix Enhanced Disk Drive Spec translated drive parameter table:
+ * Offset Size Description (Table 00277)
+ * 00h WORD number of cylinders
+ * 02h BYTE number of heads
+ * 03h BYTE A0h (signature indicating translated table)
+ * 04h BYTE number of physical sectors per track
+ * 05h WORD starting write precompensation cylinder number
+ * 07h BYTE reserved
+ * 08h BYTE control byte (see #03198 at INT 41"DISK 0")
+ * 09h WORD number of physical cylinders
+ * 0Bh BYTE number of physical heads
+ * 0Ch WORD cylinder number of landing zone
+ * 0Eh BYTE number of logical sectors per track
+ * 0Fh BYTE checksum
+ * Program: the Phoenix Enhanced Disk Drive Specification is an addition to the
+ * IBM/MS INT 13 extensions
+ *
+ * Format of Phoenix Enhanced Disk Drive Spec Fixed Disk Parameter Table:
+ * Offset Size Description (Table 00278)
+ * 00h WORD physical I/O port base address
+ * 02h WORD disk-drive control port address
+ * 04h BYTE drive flags (see #00279)
+ * 05h BYTE proprietary information
+ * bits 7-4 reserved (0)
+ * bits 3-0: Phoenix proprietary (used by BIOS)
+ * 06h BYTE IRQ for drive (bits 3-0; bits 7-4 reserved and must be 0)
+ * 07h BYTE sector count for multi-sector transfers
+ * 08h BYTE DMA control
+ * bits 7-4: DMA type (0-2) as per ATA-2 specification
+ * bits 3-0: DMA channel
+ * 09h BYTE programmed I/O control
+ * bits 7-4: reserved (0)
+ * bits 3-0: PIO type (1-4) as per ATA-2 specification
+ * 0Ah WORD drive options (see #00280)
+ * 0Ch 2 BYTEs reserved (0)
+ * 0Eh BYTE extension revision level (high nybble=major, low nybble=minor)
+ * (currently 10h for v1.0 and 11h for v1.1-3.0)
+ * 0Fh BYTE 2's complement checksum of bytes 00h-0Eh
+ * 8-bit sum of all bytes 00h-0Fh should equal 00h
+ * SeeAlso: #00277
+ *
+ * Bitfields for Phoenix Enhanced Disk Drive Spec drive flags:
+ * Bit(s) Description (Table 00279)
+ * 7 reserved (1)
+ * 6 LBA enabled
+ * 5 reserved (1)
+ * 4 drive is slave
+ * 3-0 reserved (0)
+ * SeeAlso: #00278,#00280
+ *
+ * Bitfields for Phoenix Enhanced Disk Drive Spec drive options:
+ * Bit(s) Description (Table 00280)
+ * 0 fast PIO enabled
+ * 1 fast DMA access enabled
+ * 2 block PIO (multi-sector transfers) enabled
+ * 3 CHS translation enabled
+ * 4 LBA translation enabled
+ * 5 removable media
+ * 6 ATAPI device (CD-ROM)
+ * 7 32-bit transfer mode
+ * ---v1.1+ ---
+ * 8 ATAPI device uses DRQ to signal readiness for packet command
+ * (must be 0 if bit 6 is 0)
+ * 10-9 translation type (must be 00 if bit 3 is 0)
+ * 00 Phoenix bit-shifting translation
+ * 01 LBA-assisted translation
+ * 10 reserved
+ * 11 proprietary translation
+ * ---v3.0---
+ * 11 Ultra DMA access enabled
+ * 15-12 reserved
+ **/
+
+/*
+ * Values for diskette drive type:
+ * 01h 360K
+ * 02h 1.2M
+ * 03h 720K
+ * 04h 1.44M
+ * 05h ???
+ * reportedly an obscure drive type shipped on some IBM machines,
+ * 2.88M on some machines (at least AMI 486 BIOS)
+ * 06h 2.88M
+ * 10h ATAPI Removable Media Device
+ */
+enum diskette_drive_types {
+ DISKETTE_360K = 1,
+ DISKETTE_1_2M = 2,
+ DISKETTE_720K = 3,
+ DISKETTE_1_44M = 4,
+ DISKETTE_2_88M = 6,
+ DISKETTE_ATAPI = 10,
+};
+
+/**
+ * chs_to_lba - compute lba value from cylinder, head and sector number
+ **/
+static inline int chs_to_lba(const struct driveinfo* drive_info,
+ const unsigned int cylinder, const unsigned int head,
+ const unsigned int sector)
+{
+ return (sector - 1) + (head * drive_info->sectors_per_track) +
+ (cylinder * (drive_info->heads + 1) * drive_info->sectors_per_track);
+}
+
+void lba_to_chs(const struct driveinfo* drive_info, const int lba,
+ unsigned int* cylinder, unsigned int* head,
+ unsigned int* sector);
+int get_drive_parameters(struct driveinfo *drive_info);
+
+#endif /* _GEOM_H */
diff --git a/com32/gplinclude/disk/read.h b/com32/gplinclude/disk/read.h
new file mode 100644
index 00000000..f8000a41
--- /dev/null
+++ b/com32/gplinclude/disk/read.h
@@ -0,0 +1,6 @@
+#ifndef _READ_H_
+#define _READ_H_
+void *dev_read(int drive, unsigned int lba, int sectors);
+void *read_sectors(struct driveinfo* drive_info, const unsigned int lba,
+ const int sectors);
+#endif /* _READ_H */
diff --git a/com32/gplinclude/disk/util.h b/com32/gplinclude/disk/util.h
new file mode 100644
index 00000000..5a76e0c4
--- /dev/null
+++ b/com32/gplinclude/disk/util.h
@@ -0,0 +1,9 @@
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+#include <com32.h>
+
+int int13_retry(const com32sys_t *inreg, com32sys_t *outreg);
+void get_error(const int, char**);
+
+#endif /* _UTIL_H_ */
diff --git a/com32/gplinclude/disk/write.h b/com32/gplinclude/disk/write.h
new file mode 100644
index 00000000..89ca8736
--- /dev/null
+++ b/com32/gplinclude/disk/write.h
@@ -0,0 +1,11 @@
+#ifndef _WRITE_H_
+#define _WRITE_H_
+int write_sectors(const struct driveinfo* drive_info, const unsigned int lba,
+ const void *data, const int size);
+int write_verify_sector(struct driveinfo* drive_info,
+ const unsigned int lba,
+ const void *data);
+int write_verify_sectors(struct driveinfo* drive_info,
+ const unsigned int lba,
+ const void *data, const int size);
+#endif
diff --git a/com32/gpllib/Makefile b/com32/gpllib/Makefile
index 8e47d93f..4448b3a4 100644
--- a/com32/gpllib/Makefile
+++ b/com32/gpllib/Makefile
@@ -10,7 +10,8 @@ REQFLAGS += -I../gplinclude
LIBOBJS = dmi/dmi_battery.o dmi/dmi_chassis.o dmi/dmi_memory.o \
dmi/dmi_processor.o dmi/dmi.o dmi/dmi_bios.o dmi/dmi_base_board.o \
- dmi/dmi_ipmi.o cpuid.o vpd/vpd.o
+ dmi/dmi_ipmi.o cpuid.o disk/geom.o disk/read.o disk/write.o \
+ disk/util.o vpd/vpd.o
BINDIR = /usr/bin
LIBDIR = /usr/lib
diff --git a/com32/gpllib/disk/ata.c b/com32/gpllib/disk/ata.c
new file mode 100644
index 00000000..f1716ffa
--- /dev/null
+++ b/com32/gpllib/disk/ata.c
@@ -0,0 +1,59 @@
+/**
+ * ata_id_string - Convert IDENTIFY DEVICE page into string
+ * @id: IDENTIFY DEVICE results we will examine
+ * @s: string into which data is output
+ * @ofs: offset into identify device page
+ * @len: length of string to return. must be an even number.
+ *
+ * The strings in the IDENTIFY DEVICE page are broken up into
+ * 16-bit chunks. Run through the string, and output each
+ * 8-bit chunk linearly, regardless of platform.
+ *
+ * LOCKING:
+ * caller.
+ */
+void ata_id_string(const uint16_t * id, unsigned char *s,
+ unsigned int ofs, unsigned int len)
+{
+ unsigned int c;
+
+ while (len > 0) {
+ c = id[ofs] >> 8;
+ *s = c;
+ s++;
+
+ c = id[ofs] & 0xff;
+ *s = c;
+ s++;
+
+ ofs++;
+ len -= 2;
+ }
+}
+
+/**
+ * ata_id_c_string - Convert IDENTIFY DEVICE page into C string
+ * @id: IDENTIFY DEVICE results we will examine
+ * @s: string into which data is output
+ * @ofs: offset into identify device page
+ * @len: length of string to return. must be an odd number.
+ *
+ * This function is identical to ata_id_string except that it
+ * trims trailing spaces and terminates the resulting string with
+ * null. @len must be actual maximum length (even number) + 1.
+ *
+ * LOCKING:
+ * caller.
+ */
+void ata_id_c_string(const uint16_t * id, unsigned char *s,
+ unsigned int ofs, unsigned int len)
+{
+ unsigned char *p;
+
+ ata_id_string(id, s, ofs, len - 1);
+
+ p = s + strnlen(s, len - 1);
+ while (p > s && p[-1] == ' ')
+ p--;
+ *p = '\0';
+}
diff --git a/com32/gpllib/disk/geom.c b/com32/gpllib/disk/geom.c
new file mode 100644
index 00000000..870b8d5b
--- /dev/null
+++ b/com32/gpllib/disk/geom.c
@@ -0,0 +1,250 @@
+#include <com32.h>
+#include <string.h>
+#include <stdio.h>
+#include <disk/geom.h>
+
+#include <stdio.h>
+
+/**
+ * lba_to_chs - split given lba into cylinders/heads/sectors
+ **/
+void lba_to_chs(const struct driveinfo* drive_info, const int lba,
+ unsigned int* cylinder, unsigned int* head,
+ unsigned int* sector)
+{
+ unsigned int track;
+
+ *cylinder = (lba % drive_info->sectors_per_track) + 1;
+ track = lba / drive_info->sectors_per_track;
+ *head = track % drive_info->heads;
+ *sector = track / drive_info->heads;
+}
+
+/**
+ * detect_extensions - detect if we can use extensions
+ *
+ * INT 13 - IBM/MS INT 13 Extensions - INSTALLATION CHECK
+ * AH = 41h
+ * BX = 55AAh
+ * DL = drive (80h-FFh)
+ *
+ * Return: CF set on error (extensions not supported)
+ * AH = 01h (invalid function)
+ * CF clear if successful
+ * BX = AA55h if installed
+ * AH = major version of extensions
+ * 01h = 1.x
+ * 20h = 2.0 / EDD-1.0
+ * 21h = 2.1 / EDD-1.1
+ * 30h = EDD-3.0
+ * AL = internal use
+ * CX = API subset support bitmap (see #00271)
+ * DH = extension version (v2.0+ ??? -- not present in 1.x)
+ *
+ * Note: the Phoenix Enhanced Disk Drive Specification v1.0 uses version 2.0 of
+ * the INT 13 Extensions API
+ *
+ * Bitfields for IBM/MS INT 13 Extensions API support bitmap:
+ * Bit(s) Description (Table 00271)
+ * 0 extended disk access functions (AH=42h-44h,47h,48h) supported
+ * 1 removable drive controller functions (AH=45h,46h,48h,49h,INT 15/AH=52h)
+ * supported
+ * 2 enhanced disk drive (EDD) functions (AH=48h,AH=4Eh) supported
+ * extended drive parameter table is valid (see #00273,#00278)
+ * 3-15 reserved (0)
+ **/
+static void detect_extensions(struct driveinfo* drive_info)
+{
+ com32sys_t getebios, ebios;
+
+ memset(&getebios, 0, sizeof(com32sys_t));
+ memset(&ebios, 0, sizeof(com32sys_t));
+
+ getebios.eax.w[0] = 0x4100;
+ getebios.ebx.w[0] = 0x55aa;
+ getebios.edx.b[0] = drive_info->disk;
+ getebios.eflags.b[0] = 0x3; /* CF set */
+
+ __intcall(0x13, &getebios, &ebios);
+
+ if ( !(ebios.eflags.l & EFLAGS_CF) &&
+ ebios.ebx.w[0] == 0xaa55 ) {
+ drive_info->ebios = 1;
+ drive_info->edd_version = ebios.eax.b[1];
+ drive_info->edd_functionality_subset = ebios.ecx.w[0];
+ }
+}
+
+/**
+ * get_drive_parameters_with_extensions - retrieve disk parameters via AH=48h
+ *
+ * INT 13 - IBM/MS INT 13 Extensions - GET DRIVE PARAMETERS
+ * AH = 48h
+ * DL = drive (80h-FFh)
+ * DS:SI -> buffer for drive parameters
+ * Return: CF clear if successful
+ * AH = 00h
+ * DS:SI buffer filled
+ * CF set on error
+ * AH = error code (see #00234)
+ * BUG: several different Compaq BIOSes incorrectly report high-numbered
+ * drives (such as 90h, B0h, D0h, and F0h) as present, giving them the
+ * same geometry as drive 80h; as a workaround, scan through disk
+ * numbers, stopping as soon as the number of valid drives encountered
+ * equals the value in 0040h:0075h
+ **/
+static int get_drive_parameters_with_extensions(struct driveinfo* drive_info)
+{
+ com32sys_t inreg, outreg;
+ struct device_parameter dp;
+
+ memset(&inreg, 0, sizeof(com32sys_t));
+ memset(&outreg, 0, sizeof(com32sys_t));
+ memset(&dp, 0, sizeof(struct device_parameter));
+
+ inreg.esi.w[0] = OFFS(__com32.cs_bounce);
+ inreg.ds = SEG(__com32.cs_bounce);
+ inreg.eax.w[0] = 0x4800;
+ inreg.edx.b[0] = drive_info->disk;
+
+ __intcall(0x13, &inreg, &outreg);
+
+ /* Saving bounce buffer before anything corrupts it */
+ memcpy(&dp, __com32.cs_bounce, sizeof(struct device_parameter));
+
+ /* CF set on error */
+ if ( outreg.eflags.l & EFLAGS_CF )
+ return outreg.eax.b[1];
+
+ /* Override values found without extensions */
+ drive_info->cylinder = dp.cylinders;
+ drive_info->heads = dp.heads;
+ drive_info->sectors = dp.sectors;
+ drive_info->bytes_per_sector = dp.bytes_per_sector;
+
+ /* The rest of the functions is EDD v3.0+ only */
+ if (drive_info->edd_version < 0x30)
+ return 0;
+
+ /* "ISA" or "PCI" */
+ strncpy(drive_info->host_bus_type, (char *) dp.host_bus_type,
+ sizeof drive_info->host_bus_type);
+
+ strncpy(drive_info->interface_type, (char *) dp.interface_type,
+ sizeof drive_info->interface_type);
+
+ if ( drive_info->sectors > 0 )
+ drive_info->cbios = 1; /* Valid geometry */
+
+ return 0;
+}
+
+/**
+ * get_drive_parameters_without_extensions - retrieve drive parameters via AH=08h
+ *
+ * INT 13 - DISK - GET DRIVE PARAMETERS (PC,XT286,CONV,PS,ESDI,SCSI)
+ * AH = 08h
+ * DL = drive (bit 7 set for hard disk)
+ *
+ * Return: CF set on error
+ * AH = status (07h) (see #00234)
+ * CF clear if successful
+ * AH = 00h
+ * AL = 00h on at least some BIOSes
+ * BL = drive type (AT/PS2 floppies only) (see #00242)
+ * CH = low eight bits of maximum cylinder number
+ * CL = maximum sector number (bits 5-0)
+ * high two bits of maximum cylinder number (bits 7-6)
+ * DH = maximum head number
+ * DL = number of drives
+ * ES:DI -> drive parameter table (floppies only)
+ *
+ * Notes:
+ * - may return successful even though specified drive is greater than the
+ * number of attached drives of that type (floppy/hard); check DL to
+ * ensure validity
+ * - for systems predating the IBM AT, this call is only valid for hard
+ * disks, as it is implemented by the hard disk BIOS rather than the
+ * ROM BIOS
+ * - Toshiba laptops with HardRAM return DL=02h when called with DL=80h,
+ * but fail on DL=81h. The BIOS data at 40h:75h correctly reports 01h.
+ * may indicate only two drives present even if more are attached; to
+ * ensure a correct count, one can use AH=15h to scan through possible
+ * drives
+ * - for BIOSes which reserve the last cylinder for testing purposes, the
+ * cylinder count is automatically decremented
+ * on PS/1s with IBM ROM DOS 4, nonexistent drives return CF clear,
+ * BX=CX=0000h, and ES:DI = 0000h:0000h
+ * - the PC-Tools PCFORMAT program requires that AL=00h before it will
+ * proceed with the formatting
+ *
+ * BUG: several different Compaq BIOSes incorrectly report high-numbered
+ * drives (such as 90h, B0h, D0h, and F0h) as present, giving them the
+ * same geometry as drive 80h; as a workaround, scan through disk
+ * numbers, stopping as soon as the number of valid drives encountered
+ * equals the value in 0040h:0075h
+ *
+ * SeeAlso: AH=06h"Adaptec",AH=13h"SyQuest",AH=48h,AH=15h,INT 1E
+ * SeeAlso: INT 41"HARD DISK 0"
+ **/
+static int get_drive_parameters_without_extensions(struct driveinfo* drive_info)
+{
+ com32sys_t getparm, parm;
+
+ memset(&getparm, 0, sizeof(com32sys_t));
+ memset(&parm, 0, sizeof(com32sys_t));
+
+ getparm.eax.b[1] = 0x08;
+ getparm.edx.b[0] = drive_info->disk;
+
+ __intcall(0x13, &getparm, &parm);
+
+ /* CF set on error */
+ if ( parm.eflags.l & EFLAGS_CF )
+ return parm.eax.b[1];
+
+ /* DL contains the maximum drive number but it starts at 0! */
+ drive_info->drives = parm.edx.b[0] + 1;
+
+ // XXX broken
+ /* Drive specified greater than the bumber of attached drives */
+ //if (drive_info->disk > drive_info->drives)
+ // return -1;
+
+ drive_info->type = parm.ebx.b[0];
+
+ /* DH contains the maximum head number but it starts at 0! */
+ drive_info->heads = parm.edx.b[1] + 1;
+
+ /* Maximum sector number (bits 5-0) per track */
+ drive_info->sectors_per_track = parm.ecx.b[0] & 0x3f;
+
+ /*
+ * Maximum cylinder number:
+ * CH = low eight bits of maximum cylinder number
+ * CL = high two bits of maximum cylinder number (bits 7-6)
+ */
+ drive_info->cylinder = parm.ecx.b[1] +
+ ((parm.ecx.b[0] & 0x40) * 256 +
+ (parm.ecx.b[0] & 0x80) * 512);
+
+ if ( drive_info->sectors_per_track > 0 )
+ drive_info->cbios = 1; /* Valid geometry */
+
+ return 0;
+}
+
+/**
+ * get_drive_parameters - retrieve drive parameters
+ * @drive_info: driveinfo structure to fill
+ **/
+int get_drive_parameters(struct driveinfo *drive_info)
+{
+ detect_extensions(drive_info);
+
+ if (drive_info->ebios) {
+ get_drive_parameters_without_extensions(drive_info);
+ return get_drive_parameters_with_extensions(drive_info);
+ } else
+ return get_drive_parameters_without_extensions(drive_info);
+}
diff --git a/com32/gpllib/disk/read.c b/com32/gpllib/disk/read.c
new file mode 100644
index 00000000..0e0be8ec
--- /dev/null
+++ b/com32/gpllib/disk/read.c
@@ -0,0 +1,89 @@
+#include <com32.h>
+#include <stdlib.h>
+#include <string.h>
+#include <disk/geom.h>
+#include <disk/util.h>
+#include <disk/read.h>
+
+/**
+ * dev_read - read from a drive
+ * @drive: Drive number
+ * @lba: Position to start reading from
+ * @sectors: Number of sectors to read
+ *
+ * High-level routine to read from a hard drive.
+ **/
+void *dev_read(int drive, unsigned int lba, int sectors)
+{
+ struct driveinfo drive_info;
+ drive_info.disk = drive;
+
+ return read_sectors(&drive_info, lba, sectors);
+}
+
+/**
+ * read_sectors - read several sectors from disk
+ * @drive_info: driveinfo struct describing the disk
+ * @lba: Position to read
+ * @sectors: Number of sectors to read
+ *
+ * Return a pointer to a malloc'ed buffer containing the data.
+ **/
+void *read_sectors(struct driveinfo* drive_info, const unsigned int lba,
+ const int sectors)
+{
+ com32sys_t inreg;
+ struct ebios_dapa *dapa = __com32.cs_bounce;
+ void *buf = (char *)__com32.cs_bounce + sectors * SECTOR;
+ void *data;
+
+ if (get_drive_parameters(drive_info))
+ return NULL;
+
+ memset(&inreg, 0, sizeof inreg);
+
+ if (drive_info->ebios) {
+ dapa->len = sizeof(*dapa);
+ dapa->count = sectors;
+ 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] = drive_info->disk;
+ inreg.eax.b[1] = 0x42; /* Extended read */
+ } else {
+ unsigned int c, h, s;
+
+ if (!drive_info->cbios) {
+ /* We failed to get the geometry */
+ if (lba)
+ return NULL; /* Can only read MBR */
+
+ s = 1; h = 0; c = 0;
+ } else
+ lba_to_chs(drive_info, lba, &s, &h, &c);
+
+ if ( s > 63 || h > 256 || c > 1023 )
+ return NULL;
+
+ inreg.eax.w[0] = 0x0201; /* Read one sector */
+ inreg.ecx.b[1] = c & 0xff;
+ inreg.ecx.b[0] = s + (c >> 6);
+ inreg.edx.b[1] = h;
+ inreg.edx.b[0] = drive_info->disk;
+ inreg.ebx.w[0] = OFFS(buf);
+ inreg.es = SEG(buf);
+ }
+
+ /* Perform the read */
+ if (int13_retry(&inreg, NULL))
+ return NULL; /* Give up */
+
+ data = malloc(sectors * SECTOR);
+ if (data)
+ memcpy(data, buf, sectors * SECTOR);
+
+ return data;
+}
diff --git a/com32/gpllib/disk/util.c b/com32/gpllib/disk/util.c
new file mode 100644
index 00000000..7da8351d
--- /dev/null
+++ b/com32/gpllib/disk/util.c
@@ -0,0 +1,151 @@
+#include <com32.h>
+#include <stdlib.h>
+#include <string.h>
+#include <disk/geom.h>
+
+#define MAX_NB_RETRIES 6
+
+/**
+ * int13_retry - int13h with error handling
+ * @inreg: int13h function parameters
+ * @outreg: output registers
+ *
+ * Call int 13h, but with retry on failure. Especially floppies need this.
+ **/
+int int13_retry(const com32sys_t *inreg, com32sys_t *outreg)
+{
+ int retry = MAX_NB_RETRIES; /* 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 */
+ }
+
+ /* If we get here: error */
+ return -1;
+}
+
+/**
+ * get_error - decode a disk error status
+ * @status: Error code
+ * @buffer_ptr: Pointer to set to the error message
+ *
+ * A buffer will be allocated to contain the error message.
+ * @buffer_ptr will point to it. The caller will need to free it.
+ **/
+void get_error(int status, char** buffer_ptr)
+{
+ int buffer_size = (80 * sizeof(char));
+ char* buffer = malloc(buffer_size);
+ *buffer_ptr = buffer;
+
+ switch (status) {
+ case 0x0:
+ strncpy(buffer, "successful completion", buffer_size);
+ break;
+ case 0x01:
+ strncpy(buffer, "invalid function in AH or invalid parameter", buffer_size);
+ break;
+ case 0x02:
+ strncpy(buffer, "address mark not found", buffer_size);
+ break;
+ case 0x03:
+ strncpy(buffer, "disk write-protected", buffer_size);
+ break;
+ case 0x04:
+ strncpy(buffer, "sector not found/read error", buffer_size);
+ break;
+ case 0x05:
+ strncpy(buffer, "reset failed (hard disk)", buffer_size);
+ //strncpy(buffer, "data did not verify correctly (TI Professional PC)", buffer_size);
+ break;
+ case 0x06:
+ strncpy(buffer, "disk changed (floppy)", buffer_size);
+ break;
+ case 0x07:
+ strncpy(buffer, "drive parameter activity failed (hard disk)", buffer_size);
+ break;
+ case 0x08:
+ strncpy(buffer, "DMA overrun", buffer_size);
+ break;
+ case 0x09:
+ strncpy(buffer, "data boundary error (attempted DMA across 64K boundary or >80h sectors)", buffer_size);
+ break;
+ case 0x0A:
+ strncpy(buffer, "bad sector detected (hard disk)", buffer_size);
+ break;
+ case 0x0B:
+ strncpy(buffer, "bad track detected (hard disk)", buffer_size);
+ break;
+ case 0x0C:
+ strncpy(buffer, "unsupported track or invalid media", buffer_size);
+ break;
+ case 0x0D:
+ strncpy(buffer, "invalid number of sectors on format (PS/2 hard disk)", buffer_size);
+ break;
+ case 0x0E:
+ strncpy(buffer, "control data address mark detected (hard disk)", buffer_size);
+ break;
+ case 0x0F:
+ strncpy(buffer, "DMA arbitration level out of range (hard disk)", buffer_size);
+ break;
+ case 0x10:
+ strncpy(buffer, "uncorrectable CRC or ECC error on read", buffer_size);
+ break;
+ case 0x11:
+ strncpy(buffer, "data ECC corrected (hard disk)", buffer_size);
+ break;
+ case 0x20:
+ strncpy(buffer, "controller failure", buffer_size);
+ break;
+ case 0x31:
+ strncpy(buffer, "no media in drive (IBM/MS INT 13 extensions)", buffer_size);
+ break;
+ case 0x32:
+ strncpy(buffer, "incorrect drive type stored in CMOS (Compaq)", buffer_size);
+ break;
+ case 0x40:
+ strncpy(buffer, "seek failed", buffer_size);
+ break;
+ case 0x80:
+ strncpy(buffer, "timeout (not ready)", buffer_size);
+ break;
+ case 0xAA:
+ strncpy(buffer, "drive not ready (hard disk)", buffer_size);
+ break;
+ case 0xB0:
+ strncpy(buffer, "volume not locked in drive (INT 13 extensions)", buffer_size);
+ break;
+ case 0xB1:
+ strncpy(buffer, "volume locked in drive (INT 13 extensions)", buffer_size);
+ break;
+ case 0xB2:
+ strncpy(buffer, "volume not removable (INT 13 extensions)", buffer_size);
+ break;
+ case 0xB3:
+ strncpy(buffer, "volume in use (INT 13 extensions)", buffer_size);
+ break;
+ case 0xB4:
+ strncpy(buffer, "lock count exceeded (INT 13 extensions)", buffer_size);
+ break;
+ case 0xB5:
+ strncpy(buffer, "valid eject request failed (INT 13 extensions)", buffer_size);
+ break;
+ case 0xBB:
+ strncpy(buffer, "undefined error (hard disk)", buffer_size);
+ break;
+ case 0xCC:
+ strncpy(buffer, "write fault (hard disk)", buffer_size);
+ break;
+ case 0xE0:
+ strncpy(buffer, "status register error (hard disk)", buffer_size);
+ break;
+ case 0xFF:
+ strncpy(buffer, "sense operation failed (hard disk)", buffer_size);
+ break;
+ }
+}
diff --git a/com32/gpllib/disk/write.c b/com32/gpllib/disk/write.c
new file mode 100644
index 00000000..4b1ed702
--- /dev/null
+++ b/com32/gpllib/disk/write.c
@@ -0,0 +1,107 @@
+#include <com32.h>
+#include <stdlib.h>
+#include <string.h>
+#include <disk/geom.h>
+#include <disk/read.h>
+#include <disk/write.h>
+#include <disk/util.h>
+
+/**
+ * write_sectors - write several sectors from disk
+ * @drive_info: driveinfo struct describing the disk
+ * @lba: Position to write
+ * @data: Buffer to write
+ * @size: Size of the buffer (number of sectors)
+ **/
+int write_sectors(const struct driveinfo* drive_info, const unsigned int lba,
+ const void *data, const int size)
+{
+ com32sys_t inreg;
+ struct ebios_dapa *dapa = __com32.cs_bounce;
+ void *buf = (char *)__com32.cs_bounce + size;
+
+ memcpy(buf, data, size);
+ memset(&inreg, 0, sizeof inreg);
+
+ if ( drive_info->ebios ) {
+ dapa->len = sizeof(*dapa);
+ dapa->count = size;
+ 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] = drive_info->disk;
+ inreg.eax.w[0] = 0x4300; /* Extended write */
+ } else {
+ unsigned int c, h, s;
+
+ if ( !drive_info->cbios ) {
+ /* We failed to get the geometry */
+
+ if ( lba )
+ return -1; /* Can only write MBR */
+
+ s = 1; h = 0; c = 0;
+ } else
+ lba_to_chs(drive_info, lba, &s, &h, &c);
+
+ if ( s > 63 || h > 256 || c > 1023 )
+ return -1;
+
+ inreg.eax.w[0] = 0x0301; /* Write one sector */
+ inreg.ecx.b[1] = c & 0xff;
+ inreg.ecx.b[0] = s + (c >> 6);
+ inreg.edx.b[1] = h;
+ inreg.edx.b[0] = drive_info->disk;
+ inreg.ebx.w[0] = OFFS(buf);
+ inreg.es = SEG(buf);
+ }
+
+ /* Perform the write */
+ if (int13_retry(&inreg, NULL))
+ return -1;
+ else
+ return 0;
+}
+
+/**
+ * write_verify_sectors - write several sectors from disk
+ * @drive_info: driveinfo struct describing the disk
+ * @lba: Position to write
+ * @data: Buffer to write
+ **/
+int write_verify_sector(struct driveinfo* drive_info,
+ const unsigned int lba,
+ const void *data)
+{
+ return write_verify_sectors(drive_info, lba, data, SECTOR);
+}
+
+/**
+ * write_verify_sectors - write several sectors from disk
+ * @drive_info: driveinfo struct describing the disk
+ * @lba: Position to write
+ * @data: Buffer to write
+ * @size: Size of the buffer (number of sectors)
+ **/
+int write_verify_sectors(struct driveinfo* drive_info,
+ const unsigned int lba,
+ const void *data, const int size)
+{
+ char *rb;
+ int rv;
+
+ rv = write_sectors(drive_info, lba, data, size);
+ if (rv)
+ return rv; /* Write failure */
+
+ rb = read_sectors(drive_info, lba, size);
+ if (!rb)
+ return -1; /* Readback failure */
+
+ rv = memcmp(data, rb, SECTOR);
+ free(rb);
+ return rv ? -1 : 0;
+}