From a2046031ec132d3bbf12ad0472d39eb26a041540 Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Tue, 28 Sep 2010 17:33:25 +0200 Subject: disklib: updates - lbacnt, bps, misc. things With this commit, disk_info structure provides additional fields, namely: lbacnt - total amount of sectors, either returned by 13h/48h or calculated from valid (or faked - 1/1/1) geometry bps - bytes per sector - returned by 13h/48h or equal to SECTOR disk_get_params() has been adjusted to call int13h/48h to get extended drive parameters - mentioned lbacnt and bps. We don't use geometry data returned by 13h/48h, as it can be quite a bit different from the data returned by 13h/08h. disk_write_verify_sector() and disk_write_sector() can deal with more than 1 sector now. Suffix has been renamed to _sectors(). disk_read_sectors() and disk_write_sectors() have been updated to check if we don't overflow bounce buffer, and don't try to read beyond lbacnt. All disk.c functions use 'bps' now, instead of hardcoded SECTOR. struct disk_ebios_dapa - uses packed attribute now. Signed-off-by: Michal Soltys --- com32/include/syslinux/disk.h | 38 ++++++++-- com32/lib/syslinux/disk.c | 169 ++++++++++++++++++++++++++---------------- com32/modules/chain.c | 2 +- 3 files changed, 135 insertions(+), 74 deletions(-) diff --git a/com32/include/syslinux/disk.h b/com32/include/syslinux/disk.h index 4e8de4fb..f96ca686 100644 --- a/com32/include/syslinux/disk.h +++ b/com32/include/syslinux/disk.h @@ -45,9 +45,11 @@ struct disk_info { int disk; int ebios; /* EBIOS supported on this disk */ int cbios; /* CHS geometry is valid */ - unsigned int head; - unsigned int sect; - unsigned int cyl; + uint32_t bps; /* bytes per sector */ + uint64_t lbacnt; /* total amount of sectors */ + uint32_t cyl; + uint32_t head; + uint32_t spt; }; struct disk_ebios_dapa { @@ -56,7 +58,27 @@ struct disk_ebios_dapa { uint16_t off; uint16_t seg; uint64_t lba; -}; +} __attribute__ ((packed)); + +struct disk_ebios_eparam { + uint16_t len; + uint16_t info; + uint32_t cyl; + uint32_t head; + uint32_t spt; + uint64_t lbacnt; + uint16_t bps; /* bytes per sector */ + uint32_t edd; + uint16_t dpi_sig; + uint8_t dpi_len; + char reserved1[3]; + char hostbus[4]; + char if_type[8]; + char if_path[8]; + char dev_path[8]; + char reserved2; + uint8_t checksum; +} __attribute__ ((packed)); /** * CHS (cylinder, head, sector) value extraction macros. @@ -144,10 +166,10 @@ extern int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg); extern int disk_get_params(int disk, struct disk_info *const diskinfo); extern void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba, uint8_t count); -extern int disk_write_sector(const struct disk_info *const diskinfo, - uint64_t lba, const void *data); -extern int disk_write_verify_sector(const struct disk_info *const diskinfo, - uint64_t lba, const void *buf); +extern int disk_write_sectors(const struct disk_info *const diskinfo, + uint64_t lba, const void *data, uint8_t count); +extern int disk_write_verify_sectors(const struct disk_info *const diskinfo, + uint64_t lba, const void *buf, uint8_t count); extern void disk_dos_part_dump(const struct disk_dos_part_entry *const part); extern void guid_to_str(char *buf, const struct guid *const id); extern int str_to_guid(const char *buf, struct guid *const id); diff --git a/com32/lib/syslinux/disk.c b/com32/lib/syslinux/disk.c index 1efd1ec3..ddc3c5d4 100644 --- a/com32/lib/syslinux/disk.c +++ b/com32/lib/syslinux/disk.c @@ -72,45 +72,81 @@ int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg) */ int disk_get_params(int disk, struct disk_info *const diskinfo) { - static com32sys_t getparm, parm, getebios, ebios; + static com32sys_t inreg, outreg; + struct disk_ebios_eparam *eparam = __com32.cs_bounce; + memset(diskinfo, 0, sizeof *diskinfo); diskinfo->disk = disk; - diskinfo->ebios = diskinfo->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 */ + 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, &getebios, &ebios); + __intcall(0x13, &inreg, &outreg); - if (!(ebios.eflags.l & EFLAGS_CF) && - ebios.ebx.w[0] == 0xaa55 && (ebios.ecx.b[0] & 1)) { + if (!(outreg.eflags.l & EFLAGS_CF) && + outreg.ebx.w[0] == 0xaa55 && (outreg.ecx.b[0] & 1)) { diskinfo->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; + /* 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) + return -1; /* this really shouldn't happen if we have ebios */ + + diskinfo->lbacnt = eparam->lbacnt; + if (eparam->bps) + diskinfo->bps = eparam->bps; + else + diskinfo->bps = SECTOR; + /* + * 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, &getparm, &parm); + __intcall(0x13, &inreg, &outreg); - if (parm.eflags.l & EFLAGS_CF) + if (outreg.eflags.l & EFLAGS_CF) return diskinfo->ebios ? 0 : -1; - diskinfo->head = parm.edx.b[1] + 1; - diskinfo->sect = parm.ecx.b[0] & 0x3f; - diskinfo->cyl = (parm.ecx.b[1] | - ((parm.ecx.b[0] & 0xc0u) << 2)) + 1; - if (diskinfo->sect == 0) { - diskinfo->sect = 1; - } else { + diskinfo->spt = 0x3f & outreg.ecx.b[0]; + diskinfo->head = 1 + outreg.edx.b[1]; + diskinfo->cyl = 1 + (outreg.ecx.b[1] | ((outreg.ecx.b[0] & 0xc0u) << 2)); + + if (diskinfo->spt) diskinfo->cbios = 1; /* Valid geometry */ + else { + diskinfo->head = 1; + diskinfo->spt = 1; + diskinfo->cyl = 1; } + if (!diskinfo->lbacnt) + diskinfo->lbacnt = diskinfo->cyl * diskinfo->head * diskinfo->spt; + return 0; } @@ -130,11 +166,12 @@ void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba, { com32sys_t inreg; struct disk_ebios_dapa *dapa = __com32.cs_bounce; - void *buf = (char *)__com32.cs_bounce + SECTOR; + void *buf = (char *)__com32.cs_bounce + diskinfo->bps; void *data; + uint32_t maxcnt; - if (!count) - /* Silly */ + maxcnt = (__com32.cs_bounce_size - diskinfo->bps) / diskinfo->bps; + if (!count || count > maxcnt || lba + count > diskinfo->lbacnt) return NULL; memset(&inreg, 0, sizeof inreg); @@ -152,22 +189,19 @@ void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba, inreg.eax.b[1] = 0x42; /* Extended read */ } else { unsigned int c, h, s, t; - + /* + * if we passed lba + count check and we get here, that means that + * lbacnt was calculated from chs geometry, thus 32bits are perfectly + * enough and lbacnt corresponds to cylinder boundary + */ if (!diskinfo->cbios) { /* We failed to get the geometry */ - - if (lba) - return NULL; /* Can only read MBR */ - s = 1; h = 0; c = 0; } else { - if (lba + count > diskinfo->cyl * diskinfo->head * diskinfo->sect) - /* beyond acceptable geometry */ - return NULL; - s = (lba % diskinfo->sect) + 1; - t = lba / diskinfo->sect; /* Track = head*cyl */ + s = (lba % diskinfo->spt) + 1; + t = lba / diskinfo->spt; h = t % diskinfo->head; c = t / diskinfo->head; } @@ -185,36 +219,42 @@ void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba, if (disk_int13_retry(&inreg, NULL)) return NULL; - data = malloc(count * SECTOR); + data = malloc(count * diskinfo->bps); if (data) - memcpy(data, buf, count * SECTOR); + memcpy(data, buf, count * diskinfo->bps); return data; } /** - * Write a disk block. + * 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 a sector to a disk drive, starting at lba. + * Write sector(s) to a disk drive, starting at lba. */ -int disk_write_sector(const struct disk_info *const diskinfo, uint64_t lba, - const void *data) +int disk_write_sectors(const struct disk_info *const diskinfo, uint64_t lba, + const void *data, uint8_t count) { com32sys_t inreg; struct disk_ebios_dapa *dapa = __com32.cs_bounce; - void *buf = (char *)__com32.cs_bounce + SECTOR; + void *buf = (char *)__com32.cs_bounce + diskinfo->bps; + uint32_t maxcnt; + + maxcnt = (__com32.cs_bounce_size - diskinfo->bps) / diskinfo->bps; + if (!count || count > maxcnt || lba + count > diskinfo->lbacnt) + return -1; - memcpy(buf, data, SECTOR); + memcpy(buf, data, count * diskinfo->bps); memset(&inreg, 0, sizeof inreg); if (diskinfo->ebios) { dapa->len = sizeof(*dapa); - dapa->count = 1; /* 1 sector */ + dapa->count = count; dapa->off = OFFS(buf); dapa->seg = SEG(buf); dapa->lba = lba; @@ -222,30 +262,28 @@ int disk_write_sector(const struct disk_info *const diskinfo, uint64_t lba, inreg.esi.w[0] = OFFS(dapa); inreg.ds = SEG(dapa); inreg.edx.b[0] = diskinfo->disk; - inreg.eax.w[0] = 0x4300; /* Extended write */ + inreg.eax.b[1] = 0x43; /* Extended write */ } else { unsigned int c, h, s, t; - + /* + * if we passed lba + count check and we get here, that means that + * lbacnt was calculated from chs geometry, thus 32bits are perfectly + * enough and lbacnt corresponds to cylinder boundary + */ if (!diskinfo->cbios) { /* We failed to get the geometry */ - - if (lba) - return -1; /* Can only write MBR */ - s = 1; h = 0; c = 0; } else { - if (lba >= diskinfo->cyl * diskinfo->head * diskinfo->sect) - /* beyond acceptable geometry */ - return -1; - s = (lba % diskinfo->sect) + 1; - t = lba / diskinfo->sect; /* Track = head*cyl */ + s = (lba % diskinfo->spt) + 1; + t = lba / diskinfo->spt; h = t % diskinfo->head; c = t / diskinfo->head; } - inreg.eax.w[0] = 0x0301; /* Write one sector */ + inreg.eax.b[0] = count; + inreg.eax.b[1] = 0x03; /* Write */ inreg.ecx.b[1] = c & 0xff; inreg.ecx.b[0] = ((c >> 2) & 0xc0u) | s; inreg.edx.b[1] = h; @@ -261,30 +299,31 @@ int disk_write_sector(const struct disk_info *const diskinfo, uint64_t lba, } /** - * Write a disk block and verify it was written. + * 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 a sector to a disk drive starting at lba, then reads it back - * to verify it was written correctly. + * Writes sectors to a disk drive starting at lba, then reads them back + * to verify they were written correctly. */ -int disk_write_verify_sector(const struct disk_info *const diskinfo, - uint64_t lba, const void *buf) +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_sector(diskinfo, lba, buf); + rv = disk_write_sectors(diskinfo, lba, buf, count); if (rv) return rv; /* Write failure */ - rb = disk_read_sectors(diskinfo, lba, 1); + rb = disk_read_sectors(diskinfo, lba, count); if (!rb) return -1; /* Readback failure */ - rv = memcmp(buf, rb, SECTOR); + rv = memcmp(buf, rb, count * diskinfo->bps); free(rb); return rv ? -1 : 0; } diff --git a/com32/modules/chain.c b/com32/modules/chain.c index 60e3abce..cb3ee20a 100644 --- a/com32/modules/chain.c +++ b/com32/modules/chain.c @@ -722,7 +722,7 @@ static int hide_unhide(struct disk_dos_mbr *mbr, int part) } if (write_back) - return disk_write_verify_sector(&diskinfo, 0, mbr); + return disk_write_verify_sectors(&diskinfo, 0, mbr, 1); return 0; /* ok */ } -- cgit v1.2.1