Changeset View
Changeset View
Standalone View
Standalone View
head/stand/i386/libi386/biosdisk.c
Show All 34 Lines | |||||
* | * | ||||
* - NetBSD libi386/biosdisk.c | * - NetBSD libi386/biosdisk.c | ||||
* - FreeBSD biosboot/disk.c | * - FreeBSD biosboot/disk.c | ||||
* | * | ||||
*/ | */ | ||||
#include <sys/disk.h> | #include <sys/disk.h> | ||||
#include <sys/limits.h> | #include <sys/limits.h> | ||||
#include <sys/queue.h> | |||||
#include <stand.h> | #include <stand.h> | ||||
#include <machine/bootinfo.h> | #include <machine/bootinfo.h> | ||||
#include <stdarg.h> | #include <stdarg.h> | ||||
#include <stdbool.h> | |||||
#include <bootstrap.h> | #include <bootstrap.h> | ||||
#include <btxv86.h> | #include <btxv86.h> | ||||
#include <edd.h> | #include <edd.h> | ||||
#include "disk.h" | #include "disk.h" | ||||
#include "libi386.h" | #include "libi386.h" | ||||
#define BIOS_NUMDRIVES 0x475 | #define BIOS_NUMDRIVES 0x475 | ||||
#define BIOSDISK_SECSIZE 512 | #define BIOSDISK_SECSIZE 512 | ||||
#define BUFSIZE (1 * BIOSDISK_SECSIZE) | #define BUFSIZE (1 * BIOSDISK_SECSIZE) | ||||
#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ | #define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ | ||||
#define WDMAJOR 0 /* major numbers for devices we frontend for */ | #define WDMAJOR 0 /* major numbers for devices we frontend for */ | ||||
#define WFDMAJOR 1 | #define WFDMAJOR 1 | ||||
#define FDMAJOR 2 | #define FDMAJOR 2 | ||||
#define DAMAJOR 4 | #define DAMAJOR 4 | ||||
#define ACDMAJOR 117 | |||||
#define CDMAJOR 15 | |||||
#ifdef DISK_DEBUG | #ifdef DISK_DEBUG | ||||
#define DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args) | #define DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args) | ||||
#else | #else | ||||
#define DEBUG(fmt, args...) | #define DEBUG(fmt, args...) | ||||
#endif | #endif | ||||
struct specification_packet { | |||||
uint8_t sp_size; | |||||
uint8_t sp_bootmedia; | |||||
uint8_t sp_drive; | |||||
uint8_t sp_controller; | |||||
uint32_t sp_lba; | |||||
uint16_t sp_devicespec; | |||||
uint16_t sp_buffersegment; | |||||
uint16_t sp_loadsegment; | |||||
uint16_t sp_sectorcount; | |||||
uint16_t sp_cylsec; | |||||
uint8_t sp_head; | |||||
}; | |||||
/* | /* | ||||
* List of BIOS devices, translation from disk unit number to | * List of BIOS devices, translation from disk unit number to | ||||
* BIOS unit number. | * BIOS unit number. | ||||
*/ | */ | ||||
static struct bdinfo | typedef struct bdinfo | ||||
{ | { | ||||
STAILQ_ENTRY(bdinfo) bd_link; /* link in device list */ | |||||
int bd_unit; /* BIOS unit number */ | int bd_unit; /* BIOS unit number */ | ||||
int bd_cyl; /* BIOS geometry */ | int bd_cyl; /* BIOS geometry */ | ||||
int bd_hds; | int bd_hds; | ||||
int bd_sec; | int bd_sec; | ||||
int bd_flags; | int bd_flags; | ||||
#define BD_MODEINT13 0x0000 | #define BD_MODEINT13 0x0000 | ||||
#define BD_MODEEDD1 0x0001 | #define BD_MODEEDD1 0x0001 | ||||
#define BD_MODEEDD3 0x0002 | #define BD_MODEEDD3 0x0002 | ||||
#define BD_MODEEDD (BD_MODEEDD1 | BD_MODEEDD3) | #define BD_MODEEDD (BD_MODEEDD1 | BD_MODEEDD3) | ||||
#define BD_MODEMASK 0x0003 | #define BD_MODEMASK 0x0003 | ||||
#define BD_FLOPPY 0x0004 | #define BD_FLOPPY 0x0004 | ||||
#define BD_NO_MEDIA 0x0008 | #define BD_CDROM 0x0008 | ||||
#define BD_NO_MEDIA 0x0010 | |||||
int bd_type; /* BIOS 'drive type' (floppy only) */ | int bd_type; /* BIOS 'drive type' (floppy only) */ | ||||
uint16_t bd_sectorsize; /* Sector size */ | uint16_t bd_sectorsize; /* Sector size */ | ||||
uint64_t bd_sectors; /* Disk size */ | uint64_t bd_sectors; /* Disk size */ | ||||
int bd_open; /* reference counter */ | int bd_open; /* reference counter */ | ||||
void *bd_bcache; /* buffer cache data */ | void *bd_bcache; /* buffer cache data */ | ||||
} bdinfo [MAXBDDEV]; | } bdinfo_t; | ||||
static int nbdinfo = 0; | |||||
#define BD(dev) (bdinfo[(dev)->dd.d_unit]) | |||||
#define BD_RD 0 | #define BD_RD 0 | ||||
#define BD_WR 1 | #define BD_WR 1 | ||||
static void bd_io_workaround(struct disk_devdesc *dev); | typedef STAILQ_HEAD(bdinfo_list, bdinfo) bdinfo_list_t; | ||||
static bdinfo_list_t fdinfo = STAILQ_HEAD_INITIALIZER(fdinfo); | |||||
static bdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo); | |||||
static bdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo); | |||||
static int bd_io(struct disk_devdesc *, daddr_t, int, caddr_t, int); | static void bd_io_workaround(bdinfo_t *); | ||||
static int bd_int13probe(struct bdinfo *bd); | static int bd_io(struct disk_devdesc *, bdinfo_t *, daddr_t, int, caddr_t, int); | ||||
static bool bd_int13probe(bdinfo_t *); | |||||
static int bd_init(void); | static int bd_init(void); | ||||
static int cd_init(void); | |||||
static int fd_init(void); | |||||
static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, | static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, | ||||
char *buf, size_t *rsize); | char *buf, size_t *rsize); | ||||
static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size, | static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size, | ||||
char *buf, size_t *rsize); | char *buf, size_t *rsize); | ||||
static int bd_open(struct open_file *f, ...); | static int bd_open(struct open_file *f, ...); | ||||
static int bd_close(struct open_file *f); | static int bd_close(struct open_file *f); | ||||
static int bd_ioctl(struct open_file *f, u_long cmd, void *data); | static int bd_ioctl(struct open_file *f, u_long cmd, void *data); | ||||
static int bd_print(int verbose); | static int bd_print(int verbose); | ||||
static int cd_print(int verbose); | |||||
static int fd_print(int verbose); | |||||
struct devsw biosdisk = { | struct devsw biosfd = { | ||||
"disk", | .dv_name = "fd", | ||||
DEVT_DISK, | .dv_type = DEVT_FD, | ||||
bd_init, | .dv_init = fd_init, | ||||
bd_strategy, | .dv_strategy = bd_strategy, | ||||
bd_open, | .dv_open = bd_open, | ||||
bd_close, | .dv_close = bd_close, | ||||
bd_ioctl, | .dv_ioctl = bd_ioctl, | ||||
bd_print, | .dv_print = fd_print, | ||||
NULL | .dv_cleanup = NULL | ||||
}; | }; | ||||
struct devsw bioscd = { | |||||
.dv_name = "cd", | |||||
.dv_type = DEVT_CD, | |||||
.dv_init = cd_init, | |||||
.dv_strategy = bd_strategy, | |||||
.dv_open = bd_open, | |||||
.dv_close = bd_close, | |||||
.dv_ioctl = bd_ioctl, | |||||
.dv_print = cd_print, | |||||
.dv_cleanup = NULL | |||||
}; | |||||
struct devsw bioshd = { | |||||
.dv_name = "disk", | |||||
.dv_type = DEVT_DISK, | |||||
.dv_init = bd_init, | |||||
.dv_strategy = bd_strategy, | |||||
.dv_open = bd_open, | |||||
.dv_close = bd_close, | |||||
.dv_ioctl = bd_ioctl, | |||||
.dv_print = bd_print, | |||||
.dv_cleanup = NULL | |||||
}; | |||||
static bdinfo_list_t * | |||||
bd_get_bdinfo_list(struct devsw *dev) | |||||
{ | |||||
if (dev->dv_type == DEVT_DISK) | |||||
return (&hdinfo); | |||||
if (dev->dv_type == DEVT_CD) | |||||
return (&cdinfo); | |||||
if (dev->dv_type == DEVT_FD) | |||||
return (&fdinfo); | |||||
return (NULL); | |||||
} | |||||
/* XXX this gets called way way too often, investigate */ | |||||
static bdinfo_t * | |||||
bd_get_bdinfo(struct devdesc *dev) | |||||
{ | |||||
bdinfo_list_t *bdi; | |||||
bdinfo_t *bd = NULL; | |||||
int unit; | |||||
bdi = bd_get_bdinfo_list(dev->d_dev); | |||||
if (bdi == NULL) | |||||
return (bd); | |||||
unit = 0; | |||||
STAILQ_FOREACH(bd, bdi, bd_link) { | |||||
if (unit == dev->d_unit) | |||||
return (bd); | |||||
unit++; | |||||
} | |||||
return (bd); | |||||
} | |||||
/* | /* | ||||
* Translate between BIOS device numbers and our private unit numbers. | * Translate between BIOS device numbers and our private unit numbers. | ||||
*/ | */ | ||||
int | int | ||||
bd_bios2unit(int biosdev) | bd_bios2unit(int biosdev) | ||||
{ | { | ||||
int i; | bdinfo_list_t *bdi[] = { &fdinfo, &cdinfo, &hdinfo, NULL }; | ||||
bdinfo_t *bd; | |||||
int i, unit; | |||||
DEBUG("looking for bios device 0x%x", biosdev); | DEBUG("looking for bios device 0x%x", biosdev); | ||||
for (i = 0; i < nbdinfo; i++) { | for (i = 0; bdi[i] != NULL; i++) { | ||||
DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit); | unit = 0; | ||||
if (bdinfo[i].bd_unit == biosdev) | STAILQ_FOREACH(bd, bdi[i], bd_link) { | ||||
return (i); | if (bd->bd_unit == biosdev) { | ||||
DEBUG("bd unit %d is BIOS device 0x%x", unit, | |||||
bd->bd_unit); | |||||
return (unit); | |||||
} | } | ||||
unit++; | |||||
} | |||||
} | |||||
return (-1); | return (-1); | ||||
} | } | ||||
int | int | ||||
bd_unit2bios(int unit) | bd_unit2bios(struct i386_devdesc *dev) | ||||
{ | { | ||||
bdinfo_list_t *bdi; | |||||
bdinfo_t *bd; | |||||
int unit; | |||||
if ((unit >= 0) && (unit < nbdinfo)) | bdi = bd_get_bdinfo_list(dev->dd.d_dev); | ||||
return (bdinfo[unit].bd_unit); | if (bdi == NULL) | ||||
return (-1); | return (-1); | ||||
unit = 0; | |||||
STAILQ_FOREACH(bd, bdi, bd_link) { | |||||
if (unit == dev->dd.d_unit) | |||||
return (bd->bd_unit); | |||||
unit++; | |||||
} | } | ||||
return (-1); | |||||
} | |||||
/* | /* | ||||
* Quiz the BIOS for disk devices, save a little info about them. | * Quiz the BIOS for disk devices, save a little info about them. | ||||
*/ | */ | ||||
static int | static int | ||||
fd_init(void) | |||||
{ | |||||
int unit; | |||||
bdinfo_t *bd; | |||||
for (unit = 0; unit < MAXBDDEV; unit++) { | |||||
if ((bd = calloc(1, sizeof(*bd))) == NULL) | |||||
break; | |||||
bd->bd_flags = BD_FLOPPY; | |||||
bd->bd_unit = unit; | |||||
if (!bd_int13probe(bd)) { | |||||
free(bd); | |||||
break; | |||||
} | |||||
if (bd->bd_sectors == 0) | |||||
bd->bd_flags |= BD_NO_MEDIA; | |||||
printf("BIOS drive %c: is %s%d\n", ('A' + unit), | |||||
biosfd.dv_name, unit); | |||||
STAILQ_INSERT_TAIL(&fdinfo, bd, bd_link); | |||||
} | |||||
bcache_add_dev(unit); | |||||
return (0); | |||||
} | |||||
static int | |||||
bd_init(void) | bd_init(void) | ||||
{ | { | ||||
int base, unit, nfd = 0; | int base, unit; | ||||
bdinfo_t *bd; | |||||
/* sequence 0, 0x80 */ | base = 0x80; | ||||
for (base = 0; base <= 0x80; base += 0x80) { | for (unit = 0; unit < *(unsigned char *)PTOV(BIOS_NUMDRIVES); unit++) { | ||||
for (unit = base; (nbdinfo < MAXBDDEV); unit++) { | |||||
#ifndef VIRTUALBOX | |||||
/* | /* | ||||
* Check the BIOS equipment list for number | * Check the BIOS equipment list for number of fixed disks. | ||||
* of fixed disks. | |||||
*/ | */ | ||||
if (base == 0x80 && | if ((bd = calloc(1, sizeof(*bd))) == NULL) | ||||
(nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES))) | |||||
break; | break; | ||||
#endif | bd->bd_unit = base + unit; | ||||
bdinfo[nbdinfo].bd_open = 0; | if (!bd_int13probe(bd)) { | ||||
bdinfo[nbdinfo].bd_bcache = NULL; | free(bd); | ||||
bdinfo[nbdinfo].bd_unit = unit; | |||||
bdinfo[nbdinfo].bd_flags = unit < 0x80 ? BD_FLOPPY: 0; | |||||
if (!bd_int13probe(&bdinfo[nbdinfo])) | |||||
break; | break; | ||||
} | |||||
/* XXX we need "disk aliases" to make this simpler */ | printf("BIOS drive %c: is %s%d\n", ('C' + unit), | ||||
printf("BIOS drive %c: is disk%d\n", (unit < 0x80) ? | bioshd.dv_name, unit); | ||||
('A' + unit): ('C' + unit - 0x80), nbdinfo); | |||||
nbdinfo++; | STAILQ_INSERT_TAIL(&hdinfo, bd, bd_link); | ||||
if (base == 0x80) | |||||
nfd++; | |||||
} | } | ||||
bcache_add_dev(unit); | |||||
return (0); | |||||
} | } | ||||
bcache_add_dev(nbdinfo); | |||||
/* | |||||
* We can't quiz, we have to be told what device to use, so this function | |||||
* doesn't do anything. Instead, the loader calls bc_add() with the BIOS | |||||
* device number to add. | |||||
*/ | |||||
static int | |||||
cd_init(void) | |||||
{ | |||||
return (0); | return (0); | ||||
} | } | ||||
int | |||||
bc_add(int biosdev) | |||||
{ | |||||
bdinfo_t *bd; | |||||
struct specification_packet bc_sp; | |||||
int nbcinfo = 0; | |||||
if (!STAILQ_EMPTY(&cdinfo)) | |||||
return (-1); | |||||
v86.ctl = V86_FLAGS; | |||||
v86.addr = 0x13; | |||||
v86.eax = 0x4b01; | |||||
v86.edx = biosdev; | |||||
v86.ds = VTOPSEG(&bc_sp); | |||||
v86.esi = VTOPOFF(&bc_sp); | |||||
v86int(); | |||||
if ((v86.eax & 0xff00) != 0) | |||||
return (-1); | |||||
if ((bd = calloc(1, sizeof(*bd))) == NULL) | |||||
return (-1); | |||||
bd->bd_flags = BD_CDROM; | |||||
bd->bd_unit = biosdev; | |||||
/* | /* | ||||
* Ignore result from bd_int13probe(), we will use local | |||||
* workaround below. | |||||
*/ | |||||
(void)bd_int13probe(bd); | |||||
if (bd->bd_cyl == 0) { | |||||
bd->bd_cyl = ((bc_sp.sp_cylsec & 0xc0) << 2) + | |||||
((bc_sp.sp_cylsec & 0xff00) >> 8) + 1; | |||||
} | |||||
if (bd->bd_hds == 0) | |||||
bd->bd_hds = bc_sp.sp_head + 1; | |||||
if (bd->bd_sec == 0) | |||||
bd->bd_sec = bc_sp.sp_cylsec & 0x3f; | |||||
if (bd->bd_sectors == 0) | |||||
bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec; | |||||
/* Still no size? use 7.961GB */ | |||||
if (bd->bd_sectors == 0) | |||||
bd->bd_sectors = 4173824; | |||||
STAILQ_INSERT_TAIL(&cdinfo, bd, bd_link); | |||||
printf("BIOS CD is cd%d\n", nbcinfo); | |||||
nbcinfo++; | |||||
bcache_add_dev(nbcinfo); /* register cd device in bcache */ | |||||
return(0); | |||||
} | |||||
/* | |||||
* Return EDD version or 0 if EDD is not supported on this drive. | * Return EDD version or 0 if EDD is not supported on this drive. | ||||
*/ | */ | ||||
static int | static int | ||||
bd_check_extensions(int unit) | bd_check_extensions(int unit) | ||||
{ | { | ||||
/* Determine if we can use EDD with this device. */ | /* Determine if we can use EDD with this device. */ | ||||
v86.ctl = V86_FLAGS; | v86.ctl = V86_FLAGS; | ||||
v86.addr = 0x13; | v86.addr = 0x13; | ||||
▲ Show 20 Lines • Show All 101 Lines • ▼ Show 20 Lines | bd_get_diskinfo_ext(struct bdinfo *bd) | ||||
bd->bd_sectors = total; | bd->bd_sectors = total; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Try to detect a device supported by the legacy int13 BIOS | * Try to detect a device supported by the legacy int13 BIOS | ||||
*/ | */ | ||||
static int | static bool | ||||
bd_int13probe(struct bdinfo *bd) | bd_int13probe(bdinfo_t *bd) | ||||
{ | { | ||||
int edd; | int edd, ret; | ||||
int ret; | |||||
bd->bd_flags &= ~BD_NO_MEDIA; | bd->bd_flags &= ~BD_NO_MEDIA; | ||||
edd = bd_check_extensions(bd->bd_unit); | edd = bd_check_extensions(bd->bd_unit); | ||||
if (edd == 0) | if (edd == 0) | ||||
bd->bd_flags |= BD_MODEINT13; | bd->bd_flags |= BD_MODEINT13; | ||||
else if (edd < 0x30) | else if (edd < 0x30) | ||||
bd->bd_flags |= BD_MODEEDD1; | bd->bd_flags |= BD_MODEEDD1; | ||||
Show All 13 Lines | if (bd->bd_unit < 0x80) { | ||||
/* Get disk type */ | /* Get disk type */ | ||||
v86.ctl = V86_FLAGS; | v86.ctl = V86_FLAGS; | ||||
v86.addr = 0x13; | v86.addr = 0x13; | ||||
v86.eax = 0x1500; | v86.eax = 0x1500; | ||||
v86.edx = bd->bd_unit; | v86.edx = bd->bd_unit; | ||||
v86int(); | v86int(); | ||||
if (V86_CY(v86.efl) || (v86.eax & 0x300) == 0) | if (V86_CY(v86.efl) || (v86.eax & 0x300) == 0) | ||||
return (0); | return (false); | ||||
} | } | ||||
ret = 1; | ret = 1; | ||||
if (edd != 0) | if (edd != 0) | ||||
ret = bd_get_diskinfo_ext(bd); | ret = bd_get_diskinfo_ext(bd); | ||||
if (ret != 0 || bd->bd_sectors == 0) | if (ret != 0 || bd->bd_sectors == 0) | ||||
ret = bd_get_diskinfo_std(bd); | ret = bd_get_diskinfo_std(bd); | ||||
if (ret != 0 && bd->bd_unit < 0x80) { | if (ret != 0 && bd->bd_unit < 0x80) { | ||||
/* Set defaults for 1.44 floppy */ | /* Set defaults for 1.44 floppy */ | ||||
bd->bd_cyl = 80; | bd->bd_cyl = 80; | ||||
bd->bd_hds = 2; | bd->bd_hds = 2; | ||||
bd->bd_sec = 18; | bd->bd_sec = 18; | ||||
bd->bd_type = 4; | |||||
bd->bd_sectors = 2880; | bd->bd_sectors = 2880; | ||||
/* Since we are there, there most likely is no media */ | /* Since we are there, there most likely is no media */ | ||||
bd->bd_flags |= BD_NO_MEDIA; | bd->bd_flags |= BD_NO_MEDIA; | ||||
ret = 0; | ret = 0; | ||||
} | } | ||||
if (ret != 0) { | if (ret != 0) { | ||||
/* CD is special case, bc_add() has its own fallback. */ | |||||
if ((bd->bd_flags & BD_CDROM) != 0) | |||||
return (true); | |||||
if (bd->bd_sectors != 0 && edd != 0) { | if (bd->bd_sectors != 0 && edd != 0) { | ||||
bd->bd_sec = 63; | bd->bd_sec = 63; | ||||
bd->bd_hds = 255; | bd->bd_hds = 255; | ||||
bd->bd_cyl = | bd->bd_cyl = | ||||
(bd->bd_sectors + bd->bd_sec * bd->bd_hds - 1) / | (bd->bd_sectors + bd->bd_sec * bd->bd_hds - 1) / | ||||
bd->bd_sec * bd->bd_hds; | bd->bd_sec * bd->bd_hds; | ||||
} else { | } else { | ||||
const char *dv_name; | |||||
if ((bd->bd_flags & BD_FLOPPY) != 0) | |||||
dv_name = biosfd.dv_name; | |||||
else if ((bd->bd_flags & BD_CDROM) != 0) | |||||
dv_name = bioscd.dv_name; | |||||
else | |||||
dv_name = bioshd.dv_name; | |||||
printf("Can not get information about %s unit %#x\n", | printf("Can not get information about %s unit %#x\n", | ||||
biosdisk.dv_name, bd->bd_unit); | dv_name, bd->bd_unit); | ||||
return (0); | return (false); | ||||
} | } | ||||
} | } | ||||
if (bd->bd_sec == 0) | if (bd->bd_sec == 0) | ||||
bd->bd_sec = 63; | bd->bd_sec = 63; | ||||
if (bd->bd_hds == 0) | if (bd->bd_hds == 0) | ||||
bd->bd_hds = 255; | bd->bd_hds = 255; | ||||
if (bd->bd_sectors == 0) | if (bd->bd_sectors == 0) | ||||
bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec; | bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec; | ||||
DEBUG("unit 0x%x geometry %d/%d/%d", bd->bd_unit, bd->bd_cyl, | DEBUG("unit 0x%x geometry %d/%d/%d\n", bd->bd_unit, bd->bd_cyl, | ||||
bd->bd_hds, bd->bd_sec); | bd->bd_hds, bd->bd_sec); | ||||
return (1); | return (true); | ||||
} | } | ||||
static int | |||||
bd_count(bdinfo_list_t *bdi) | |||||
{ | |||||
bdinfo_t *bd; | |||||
int i; | |||||
i = 0; | |||||
STAILQ_FOREACH(bd, bdi, bd_link) | |||||
i++; | |||||
return (i); | |||||
} | |||||
/* | /* | ||||
* Print information about disks | * Print information about disks | ||||
*/ | */ | ||||
static int | static int | ||||
bd_print(int verbose) | bd_print_common(struct devsw *dev, bdinfo_list_t *bdi, int verbose) | ||||
{ | { | ||||
static char line[80]; | char line[80]; | ||||
struct disk_devdesc dev; | struct disk_devdesc devd; | ||||
bdinfo_t *bd; | |||||
int i, ret = 0; | int i, ret = 0; | ||||
char drive; | |||||
if (nbdinfo == 0) | if (STAILQ_EMPTY(bdi)) | ||||
return (0); | return (0); | ||||
printf("%s devices:", biosdisk.dv_name); | printf("%s devices:", dev->dv_name); | ||||
if ((ret = pager_output("\n")) != 0) | if ((ret = pager_output("\n")) != 0) | ||||
return (ret); | return (ret); | ||||
for (i = 0; i < nbdinfo; i++) { | i = -1; | ||||
STAILQ_FOREACH(bd, bdi, bd_link) { | |||||
i++; | |||||
switch (dev->dv_type) { | |||||
case DEVT_FD: | |||||
drive = 'A'; | |||||
break; | |||||
case DEVT_CD: | |||||
drive = 'C' + bd_count(&hdinfo); | |||||
break; | |||||
default: | |||||
drive = 'C'; | |||||
break; | |||||
} | |||||
snprintf(line, sizeof(line), | snprintf(line, sizeof(line), | ||||
" disk%d: BIOS drive %c (%s%ju X %u):\n", i, | " %s%d: BIOS drive %c (%s%ju X %u):\n", | ||||
(bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit): | dev->dv_name, i, drive + i, | ||||
('C' + bdinfo[i].bd_unit - 0x80), | (bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA ? | ||||
(bdinfo[i].bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA ? | |||||
"no media, " : "", | "no media, " : "", | ||||
(uintmax_t)bdinfo[i].bd_sectors, | (uintmax_t)bd->bd_sectors, | ||||
bdinfo[i].bd_sectorsize); | bd->bd_sectorsize); | ||||
if ((ret = pager_output(line)) != 0) | if ((ret = pager_output(line)) != 0) | ||||
break; | break; | ||||
if ((bdinfo[i].bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) | if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) | ||||
continue; | continue; | ||||
dev.dd.d_dev = &biosdisk; | if (dev->dv_type != DEVT_DISK) | ||||
dev.dd.d_unit = i; | continue; | ||||
dev.d_slice = -1; | |||||
dev.d_partition = -1; | devd.dd.d_dev = dev; | ||||
if (disk_open(&dev, | devd.dd.d_unit = i; | ||||
bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors, | devd.d_slice = -1; | ||||
bdinfo[i].bd_sectorsize) == 0) { | devd.d_partition = -1; | ||||
snprintf(line, sizeof(line), " disk%d", i); | if (disk_open(&devd, | ||||
ret = disk_print(&dev, line, verbose); | bd->bd_sectorsize * bd->bd_sectors, | ||||
disk_close(&dev); | bd->bd_sectorsize) == 0) { | ||||
snprintf(line, sizeof(line), " %s%d", | |||||
dev->dv_name, i); | |||||
ret = disk_print(&devd, line, verbose); | |||||
disk_close(&devd); | |||||
if (ret != 0) | if (ret != 0) | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
return (ret); | return (ret); | ||||
} | } | ||||
static int | |||||
fd_print(int verbose) | |||||
{ | |||||
return (bd_print_common(&biosfd, &fdinfo, verbose)); | |||||
} | |||||
static int | |||||
bd_print(int verbose) | |||||
{ | |||||
return (bd_print_common(&bioshd, &hdinfo, verbose)); | |||||
} | |||||
static int | |||||
cd_print(int verbose) | |||||
{ | |||||
return (bd_print_common(&bioscd, &cdinfo, verbose)); | |||||
} | |||||
/* | /* | ||||
* Read disk size from partition. | * Read disk size from partition. | ||||
* This is needed to work around buggy BIOS systems returning | * This is needed to work around buggy BIOS systems returning | ||||
* wrong (truncated) disk media size. | * wrong (truncated) disk media size. | ||||
* During bd_probe() we tested if the multiplication of bd_sectors | * During bd_probe() we tested if the multiplication of bd_sectors | ||||
* would overflow so it should be safe to perform here. | * would overflow so it should be safe to perform here. | ||||
*/ | */ | ||||
static uint64_t | static uint64_t | ||||
bd_disk_get_sectors(struct disk_devdesc *dev) | bd_disk_get_sectors(struct disk_devdesc *dev) | ||||
{ | { | ||||
bdinfo_t *bd; | |||||
struct disk_devdesc disk; | struct disk_devdesc disk; | ||||
uint64_t size; | uint64_t size; | ||||
bd = bd_get_bdinfo(&dev->dd); | |||||
if (bd == NULL) | |||||
return (0); | |||||
disk.dd.d_dev = dev->dd.d_dev; | disk.dd.d_dev = dev->dd.d_dev; | ||||
disk.dd.d_unit = dev->dd.d_unit; | disk.dd.d_unit = dev->dd.d_unit; | ||||
disk.d_slice = -1; | disk.d_slice = -1; | ||||
disk.d_partition = -1; | disk.d_partition = -1; | ||||
disk.d_offset = 0; | disk.d_offset = 0; | ||||
size = BD(dev).bd_sectors * BD(dev).bd_sectorsize; | size = bd->bd_sectors * bd->bd_sectorsize; | ||||
if (disk_open(&disk, size, BD(dev).bd_sectorsize) == 0) { | if (disk_open(&disk, size, bd->bd_sectorsize) == 0) { | ||||
(void) disk_ioctl(&disk, DIOCGMEDIASIZE, &size); | (void) disk_ioctl(&disk, DIOCGMEDIASIZE, &size); | ||||
disk_close(&disk); | disk_close(&disk); | ||||
} | } | ||||
return (size / BD(dev).bd_sectorsize); | return (size / bd->bd_sectorsize); | ||||
} | } | ||||
/* | /* | ||||
* Attempt to open the disk described by (dev) for use by (f). | * Attempt to open the disk described by (dev) for use by (f). | ||||
* | * | ||||
* Note that the philosophy here is "give them exactly what | * Note that the philosophy here is "give them exactly what | ||||
* they ask for". This is necessary because being too "smart" | * they ask for". This is necessary because being too "smart" | ||||
* about what the user might want leads to complications. | * about what the user might want leads to complications. | ||||
* (eg. given no slice or partition value, with a disk that is | * (eg. given no slice or partition value, with a disk that is | ||||
* sliced - are they after the first BSD slice, or the DOS | * sliced - are they after the first BSD slice, or the DOS | ||||
* slice before it?) | * slice before it?) | ||||
*/ | */ | ||||
static int | static int | ||||
bd_open(struct open_file *f, ...) | bd_open(struct open_file *f, ...) | ||||
{ | { | ||||
bdinfo_t *bd; | |||||
struct disk_devdesc *dev; | struct disk_devdesc *dev; | ||||
va_list ap; | va_list ap; | ||||
int rc; | int rc; | ||||
va_start(ap, f); | va_start(ap, f); | ||||
dev = va_arg(ap, struct disk_devdesc *); | dev = va_arg(ap, struct disk_devdesc *); | ||||
va_end(ap); | va_end(ap); | ||||
if (dev->dd.d_unit < 0 || dev->dd.d_unit >= nbdinfo) | bd = bd_get_bdinfo(&dev->dd); | ||||
if (bd == NULL) | |||||
return (EIO); | return (EIO); | ||||
if ((BD(dev).bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) { | if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) { | ||||
if (!bd_int13probe(&BD(dev))) | if (!bd_int13probe(bd)) | ||||
return (EIO); | return (EIO); | ||||
if ((BD(dev).bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) | if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) | ||||
return (EIO); | return (EIO); | ||||
} | } | ||||
if (BD(dev).bd_bcache == NULL) | if (bd->bd_bcache == NULL) | ||||
BD(dev).bd_bcache = bcache_allocate(); | bd->bd_bcache = bcache_allocate(); | ||||
if (BD(dev).bd_open == 0) | if (bd->bd_open == 0) | ||||
BD(dev).bd_sectors = bd_disk_get_sectors(dev); | bd->bd_sectors = bd_disk_get_sectors(dev); | ||||
BD(dev).bd_open++; | bd->bd_open++; | ||||
rc = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, | rc = 0; | ||||
BD(dev).bd_sectorsize); | if (dev->dd.d_dev->dv_type == DEVT_DISK) { | ||||
rc = disk_open(dev, bd->bd_sectors * bd->bd_sectorsize, | |||||
bd->bd_sectorsize); | |||||
if (rc != 0) { | if (rc != 0) { | ||||
BD(dev).bd_open--; | bd->bd_open--; | ||||
if (BD(dev).bd_open == 0) { | if (bd->bd_open == 0) { | ||||
bcache_free(BD(dev).bd_bcache); | bcache_free(bd->bd_bcache); | ||||
BD(dev).bd_bcache = NULL; | bd->bd_bcache = NULL; | ||||
} | } | ||||
} | } | ||||
} | |||||
return (rc); | return (rc); | ||||
} | } | ||||
static int | static int | ||||
bd_close(struct open_file *f) | bd_close(struct open_file *f) | ||||
{ | { | ||||
struct disk_devdesc *dev; | struct disk_devdesc *dev; | ||||
bdinfo_t *bd; | |||||
int rc = 0; | |||||
dev = (struct disk_devdesc *)f->f_devdata; | dev = (struct disk_devdesc *)f->f_devdata; | ||||
BD(dev).bd_open--; | bd = bd_get_bdinfo(&dev->dd); | ||||
if (BD(dev).bd_open == 0) { | if (bd == NULL) | ||||
bcache_free(BD(dev).bd_bcache); | return (EIO); | ||||
BD(dev).bd_bcache = NULL; | |||||
bd->bd_open--; | |||||
if (bd->bd_open == 0) { | |||||
bcache_free(bd->bd_bcache); | |||||
bd->bd_bcache = NULL; | |||||
} | } | ||||
return (disk_close(dev)); | if (dev->dd.d_dev->dv_type == DEVT_DISK) | ||||
rc = disk_close(dev); | |||||
return (rc); | |||||
} | } | ||||
static int | static int | ||||
bd_ioctl(struct open_file *f, u_long cmd, void *data) | bd_ioctl(struct open_file *f, u_long cmd, void *data) | ||||
{ | { | ||||
bdinfo_t *bd; | |||||
struct disk_devdesc *dev; | struct disk_devdesc *dev; | ||||
int rc; | int rc; | ||||
dev = (struct disk_devdesc *)f->f_devdata; | dev = (struct disk_devdesc *)f->f_devdata; | ||||
bd = bd_get_bdinfo(&dev->dd); | |||||
if (bd == NULL) | |||||
return (EIO); | |||||
if (dev->dd.d_dev->dv_type == DEVT_DISK) { | |||||
rc = disk_ioctl(dev, cmd, data); | rc = disk_ioctl(dev, cmd, data); | ||||
if (rc != ENOTTY) | if (rc != ENOTTY) | ||||
return (rc); | return (rc); | ||||
} | |||||
switch (cmd) { | switch (cmd) { | ||||
case DIOCGSECTORSIZE: | case DIOCGSECTORSIZE: | ||||
*(uint32_t *)data = BD(dev).bd_sectorsize; | *(uint32_t *)data = bd->bd_sectorsize; | ||||
break; | break; | ||||
case DIOCGMEDIASIZE: | case DIOCGMEDIASIZE: | ||||
*(uint64_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize; | *(uint64_t *)data = bd->bd_sectors * bd->bd_sectorsize; | ||||
break; | break; | ||||
default: | default: | ||||
return (ENOTTY); | return (ENOTTY); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, | bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, | ||||
char *buf, size_t *rsize) | char *buf, size_t *rsize) | ||||
{ | { | ||||
bdinfo_t *bd; | |||||
struct bcache_devdata bcd; | struct bcache_devdata bcd; | ||||
struct disk_devdesc *dev; | struct disk_devdesc *dev; | ||||
daddr_t offset; | |||||
dev = (struct disk_devdesc *)devdata; | dev = (struct disk_devdesc *)devdata; | ||||
bd = bd_get_bdinfo(&dev->dd); | |||||
if (bd == NULL) | |||||
return (EINVAL); | |||||
bcd.dv_strategy = bd_realstrategy; | bcd.dv_strategy = bd_realstrategy; | ||||
bcd.dv_devdata = devdata; | bcd.dv_devdata = devdata; | ||||
bcd.dv_cache = BD(dev).bd_bcache; | bcd.dv_cache = bd->bd_bcache; | ||||
return (bcache_strategy(&bcd, rw, dblk + dev->d_offset, size, | |||||
offset = 0; | |||||
if (dev->dd.d_dev->dv_type == DEVT_DISK) { | |||||
offset = dev->d_offset * bd->bd_sectorsize; | |||||
offset /= BIOSDISK_SECSIZE; | |||||
} | |||||
return (bcache_strategy(&bcd, rw, dblk + offset, size, | |||||
buf, rsize)); | buf, rsize)); | ||||
} | } | ||||
static int | static int | ||||
bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, | bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, | ||||
char *buf, size_t *rsize) | char *buf, size_t *rsize) | ||||
{ | { | ||||
struct disk_devdesc *dev = (struct disk_devdesc *)devdata; | struct disk_devdesc *dev = (struct disk_devdesc *)devdata; | ||||
uint64_t disk_blocks, offset; | bdinfo_t *bd; | ||||
uint64_t disk_blocks, offset, d_offset; | |||||
size_t blks, blkoff, bsize, rest; | size_t blks, blkoff, bsize, rest; | ||||
caddr_t bbuf; | caddr_t bbuf; | ||||
int rc; | int rc; | ||||
if ((BD(dev).bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) | bd = bd_get_bdinfo(&dev->dd); | ||||
if (bd == NULL || (bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) | |||||
return (EIO); | return (EIO); | ||||
/* | /* | ||||
* First make sure the IO size is a multiple of 512 bytes. While we do | * First make sure the IO size is a multiple of 512 bytes. While we do | ||||
* process partial reads below, the strategy mechanism is built | * process partial reads below, the strategy mechanism is built | ||||
* assuming IO is a multiple of 512B blocks. If the request is not | * assuming IO is a multiple of 512B blocks. If the request is not | ||||
* a multiple of 512B blocks, it has to be some sort of bug. | * a multiple of 512B blocks, it has to be some sort of bug. | ||||
*/ | */ | ||||
if (size == 0 || (size % BIOSDISK_SECSIZE) != 0) { | if (size == 0 || (size % BIOSDISK_SECSIZE) != 0) { | ||||
printf("bd_strategy: %d bytes I/O not multiple of %d\n", | printf("bd_strategy: %d bytes I/O not multiple of %d\n", | ||||
size, BIOSDISK_SECSIZE); | size, BIOSDISK_SECSIZE); | ||||
return (EIO); | return (EIO); | ||||
} | } | ||||
DEBUG("open_disk %p", dev); | DEBUG("open_disk %p", dev); | ||||
offset = dblk * BIOSDISK_SECSIZE; | offset = dblk * BIOSDISK_SECSIZE; | ||||
dblk = offset / BD(dev).bd_sectorsize; | dblk = offset / bd->bd_sectorsize; | ||||
blkoff = offset % BD(dev).bd_sectorsize; | blkoff = offset % bd->bd_sectorsize; | ||||
/* | /* | ||||
* Check the value of the size argument. We do have quite small | * Check the value of the size argument. We do have quite small | ||||
* heap (64MB), but we do not know good upper limit, so we check against | * heap (64MB), but we do not know good upper limit, so we check against | ||||
* INT_MAX here. This will also protect us against possible overflows | * INT_MAX here. This will also protect us against possible overflows | ||||
* while translating block count to bytes. | * while translating block count to bytes. | ||||
*/ | */ | ||||
if (size > INT_MAX) { | if (size > INT_MAX) { | ||||
DEBUG("too large I/O: %zu bytes", size); | DEBUG("too large I/O: %zu bytes", size); | ||||
return (EIO); | return (EIO); | ||||
} | } | ||||
blks = size / BD(dev).bd_sectorsize; | blks = size / bd->bd_sectorsize; | ||||
if (blks == 0 || (size % BD(dev).bd_sectorsize) != 0) | if (blks == 0 || (size % bd->bd_sectorsize) != 0) | ||||
blks++; | blks++; | ||||
if (dblk > dblk + blks) | if (dblk > dblk + blks) | ||||
return (EIO); | return (EIO); | ||||
if (rsize) | if (rsize) | ||||
*rsize = 0; | *rsize = 0; | ||||
/* | /* | ||||
* Get disk blocks, this value is either for whole disk or for | * Get disk blocks, this value is either for whole disk or for | ||||
* partition. | * partition. | ||||
*/ | */ | ||||
d_offset = 0; | |||||
disk_blocks = 0; | |||||
if (dev->dd.d_dev->dv_type == DEVT_DISK) { | |||||
if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) { | if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) { | ||||
/* DIOCGMEDIASIZE does return bytes. */ | /* DIOCGMEDIASIZE does return bytes. */ | ||||
disk_blocks /= BD(dev).bd_sectorsize; | disk_blocks /= bd->bd_sectorsize; | ||||
} else { | |||||
/* We should not get here. Just try to survive. */ | |||||
disk_blocks = BD(dev).bd_sectors - dev->d_offset; | |||||
} | } | ||||
d_offset = dev->d_offset; | |||||
} | |||||
if (disk_blocks == 0) | |||||
disk_blocks = bd->bd_sectors - d_offset; | |||||
/* Validate source block address. */ | /* Validate source block address. */ | ||||
if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks) | if (dblk < d_offset || dblk >= d_offset + disk_blocks) | ||||
return (EIO); | return (EIO); | ||||
/* | /* | ||||
* Truncate if we are crossing disk or partition end. | * Truncate if we are crossing disk or partition end. | ||||
*/ | */ | ||||
if (dblk + blks >= dev->d_offset + disk_blocks) { | if (dblk + blks >= d_offset + disk_blocks) { | ||||
blks = dev->d_offset + disk_blocks - dblk; | blks = d_offset + disk_blocks - dblk; | ||||
size = blks * BD(dev).bd_sectorsize; | size = blks * bd->bd_sectorsize; | ||||
DEBUG("short I/O %d", blks); | DEBUG("short I/O %d", blks); | ||||
} | } | ||||
if (V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize == 0) | if (V86_IO_BUFFER_SIZE / bd->bd_sectorsize == 0) | ||||
panic("BUG: Real mode buffer is too small"); | panic("BUG: Real mode buffer is too small"); | ||||
bbuf = PTOV(V86_IO_BUFFER); | bbuf = PTOV(V86_IO_BUFFER); | ||||
rest = size; | rest = size; | ||||
while (blks > 0) { | while (blks > 0) { | ||||
int x = min(blks, V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize); | int x = min(blks, V86_IO_BUFFER_SIZE / bd->bd_sectorsize); | ||||
switch (rw & F_MASK) { | switch (rw & F_MASK) { | ||||
case F_READ: | case F_READ: | ||||
DEBUG("read %d from %lld to %p", x, dblk, buf); | DEBUG("read %d from %lld to %p", x, dblk, buf); | ||||
bsize = BD(dev).bd_sectorsize * x - blkoff; | bsize = bd->bd_sectorsize * x - blkoff; | ||||
if (rest < bsize) | if (rest < bsize) | ||||
bsize = rest; | bsize = rest; | ||||
if ((rc = bd_io(dev, dblk, x, bbuf, BD_RD)) != 0) | if ((rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD)) != 0) | ||||
return (EIO); | return (EIO); | ||||
bcopy(bbuf + blkoff, buf, bsize); | bcopy(bbuf + blkoff, buf, bsize); | ||||
break; | break; | ||||
case F_WRITE : | case F_WRITE : | ||||
DEBUG("write %d from %lld to %p", x, dblk, buf); | DEBUG("write %d from %lld to %p", x, dblk, buf); | ||||
if (blkoff != 0) { | if (blkoff != 0) { | ||||
/* | /* | ||||
* We got offset to sector, read 1 sector to | * We got offset to sector, read 1 sector to | ||||
* bbuf. | * bbuf. | ||||
*/ | */ | ||||
x = 1; | x = 1; | ||||
bsize = BD(dev).bd_sectorsize - blkoff; | bsize = bd->bd_sectorsize - blkoff; | ||||
bsize = min(bsize, rest); | bsize = min(bsize, rest); | ||||
rc = bd_io(dev, dblk, x, bbuf, BD_RD); | rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD); | ||||
} else if (rest < BD(dev).bd_sectorsize) { | } else if (rest < bd->bd_sectorsize) { | ||||
/* | /* | ||||
* The remaining block is not full | * The remaining block is not full | ||||
* sector. Read 1 sector to bbuf. | * sector. Read 1 sector to bbuf. | ||||
*/ | */ | ||||
x = 1; | x = 1; | ||||
bsize = rest; | bsize = rest; | ||||
rc = bd_io(dev, dblk, x, bbuf, BD_RD); | rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD); | ||||
} else { | } else { | ||||
/* We can write full sector(s). */ | /* We can write full sector(s). */ | ||||
bsize = BD(dev).bd_sectorsize * x; | bsize = bd->bd_sectorsize * x; | ||||
} | } | ||||
/* | /* | ||||
* Put your Data In, Put your Data out, | * Put your Data In, Put your Data out, | ||||
* Put your Data In, and shake it all about | * Put your Data In, and shake it all about | ||||
*/ | */ | ||||
bcopy(buf, bbuf + blkoff, bsize); | bcopy(buf, bbuf + blkoff, bsize); | ||||
if ((rc = bd_io(dev, dblk, x, bbuf, BD_WR)) != 0) | if ((rc = bd_io(dev, bd, dblk, x, bbuf, BD_WR)) != 0) | ||||
return (EIO); | return (EIO); | ||||
break; | break; | ||||
default: | default: | ||||
/* DO NOTHING */ | /* DO NOTHING */ | ||||
return (EROFS); | return (EROFS); | ||||
} | } | ||||
blkoff = 0; | blkoff = 0; | ||||
buf += bsize; | buf += bsize; | ||||
rest -= bsize; | rest -= bsize; | ||||
blks -= x; | blks -= x; | ||||
dblk += x; | dblk += x; | ||||
} | } | ||||
if (rsize != NULL) | if (rsize != NULL) | ||||
*rsize = size; | *rsize = size; | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
bd_edd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, | bd_edd_io(bdinfo_t *bd, daddr_t dblk, int blks, caddr_t dest, | ||||
int dowrite) | int dowrite) | ||||
{ | { | ||||
static struct edd_packet packet; | static struct edd_packet packet; | ||||
packet.len = sizeof(struct edd_packet); | packet.len = sizeof(struct edd_packet); | ||||
packet.count = blks; | packet.count = blks; | ||||
packet.off = VTOPOFF(dest); | packet.off = VTOPOFF(dest); | ||||
packet.seg = VTOPSEG(dest); | packet.seg = VTOPSEG(dest); | ||||
packet.lba = dblk; | packet.lba = dblk; | ||||
v86.ctl = V86_FLAGS; | v86.ctl = V86_FLAGS; | ||||
v86.addr = 0x13; | v86.addr = 0x13; | ||||
/* Should we Write with verify ?? 0x4302 ? */ | /* Should we Write with verify ?? 0x4302 ? */ | ||||
if (dowrite == BD_WR) | if (dowrite == BD_WR) | ||||
v86.eax = 0x4300; | v86.eax = 0x4300; | ||||
else | else | ||||
v86.eax = 0x4200; | v86.eax = 0x4200; | ||||
v86.edx = BD(dev).bd_unit; | v86.edx = bd->bd_unit; | ||||
v86.ds = VTOPSEG(&packet); | v86.ds = VTOPSEG(&packet); | ||||
v86.esi = VTOPOFF(&packet); | v86.esi = VTOPOFF(&packet); | ||||
v86int(); | v86int(); | ||||
if (V86_CY(v86.efl)) | if (V86_CY(v86.efl)) | ||||
return (v86.eax >> 8); | return (v86.eax >> 8); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
bd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, | bd_chs_io(bdinfo_t *bd, daddr_t dblk, int blks, caddr_t dest, | ||||
int dowrite) | int dowrite) | ||||
{ | { | ||||
uint32_t x, bpc, cyl, hd, sec; | uint32_t x, bpc, cyl, hd, sec; | ||||
bpc = BD(dev).bd_sec * BD(dev).bd_hds; /* blocks per cylinder */ | bpc = bd->bd_sec * bd->bd_hds; /* blocks per cylinder */ | ||||
x = dblk; | x = dblk; | ||||
cyl = x / bpc; /* block # / blocks per cylinder */ | cyl = x / bpc; /* block # / blocks per cylinder */ | ||||
x %= bpc; /* block offset into cylinder */ | x %= bpc; /* block offset into cylinder */ | ||||
hd = x / BD(dev).bd_sec; /* offset / blocks per track */ | hd = x / bd->bd_sec; /* offset / blocks per track */ | ||||
sec = x % BD(dev).bd_sec; /* offset into track */ | sec = x % bd->bd_sec; /* offset into track */ | ||||
/* correct sector number for 1-based BIOS numbering */ | /* correct sector number for 1-based BIOS numbering */ | ||||
sec++; | sec++; | ||||
if (cyl > 1023) { | if (cyl > 1023) { | ||||
/* CHS doesn't support cylinders > 1023. */ | /* CHS doesn't support cylinders > 1023. */ | ||||
return (1); | return (1); | ||||
} | } | ||||
v86.ctl = V86_FLAGS; | v86.ctl = V86_FLAGS; | ||||
v86.addr = 0x13; | v86.addr = 0x13; | ||||
if (dowrite == BD_WR) | if (dowrite == BD_WR) | ||||
v86.eax = 0x300 | blks; | v86.eax = 0x300 | blks; | ||||
else | else | ||||
v86.eax = 0x200 | blks; | v86.eax = 0x200 | blks; | ||||
v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; | v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; | ||||
v86.edx = (hd << 8) | BD(dev).bd_unit; | v86.edx = (hd << 8) | bd->bd_unit; | ||||
v86.es = VTOPSEG(dest); | v86.es = VTOPSEG(dest); | ||||
v86.ebx = VTOPOFF(dest); | v86.ebx = VTOPOFF(dest); | ||||
v86int(); | v86int(); | ||||
if (V86_CY(v86.efl)) | if (V86_CY(v86.efl)) | ||||
return (v86.eax >> 8); | return (v86.eax >> 8); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
bd_io_workaround(struct disk_devdesc *dev) | bd_io_workaround(bdinfo_t *bd) | ||||
{ | { | ||||
uint8_t buf[8 * 1024]; | uint8_t buf[8 * 1024]; | ||||
bd_edd_io(dev, 0xffffffff, 1, (caddr_t)buf, BD_RD); | bd_edd_io(bd, 0xffffffff, 1, (caddr_t)buf, BD_RD); | ||||
} | } | ||||
static int | static int | ||||
bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, | bd_io(struct disk_devdesc *dev, bdinfo_t *bd, daddr_t dblk, int blks, | ||||
int dowrite) | caddr_t dest, int dowrite) | ||||
{ | { | ||||
int result, retry; | int result, retry; | ||||
/* Just in case some idiot actually tries to read/write -1 blocks... */ | /* Just in case some idiot actually tries to read/write -1 blocks... */ | ||||
if (blks < 0) | if (blks < 0) | ||||
return (-1); | return (-1); | ||||
/* | /* | ||||
* Workaround for a problem with some HP ProLiant BIOS failing to work | * Workaround for a problem with some HP ProLiant BIOS failing to work | ||||
* out the boot disk after installation. hrs and kuriyama discovered | * out the boot disk after installation. hrs and kuriyama discovered | ||||
* this problem with an HP ProLiant DL320e Gen 8 with a 3TB HDD, and | * this problem with an HP ProLiant DL320e Gen 8 with a 3TB HDD, and | ||||
* discovered that an int13h call seems to cause a buffer overrun in | * discovered that an int13h call seems to cause a buffer overrun in | ||||
* the bios. The problem is alleviated by doing an extra read before | * the bios. The problem is alleviated by doing an extra read before | ||||
* the buggy read. It is not immediately known whether other models | * the buggy read. It is not immediately known whether other models | ||||
* are similarly affected. | * are similarly affected. | ||||
* Loop retrying the operation a couple of times. The BIOS | * Loop retrying the operation a couple of times. The BIOS | ||||
* may also retry. | * may also retry. | ||||
*/ | */ | ||||
if (dowrite == BD_RD && dblk >= 0x100000000) | if (dowrite == BD_RD && dblk >= 0x100000000) | ||||
bd_io_workaround(dev); | bd_io_workaround(bd); | ||||
for (retry = 0; retry < 3; retry++) { | for (retry = 0; retry < 3; retry++) { | ||||
if (BD(dev).bd_flags & BD_MODEEDD) | if (bd->bd_flags & BD_MODEEDD) | ||||
result = bd_edd_io(dev, dblk, blks, dest, dowrite); | result = bd_edd_io(bd, dblk, blks, dest, dowrite); | ||||
else | else | ||||
result = bd_chs_io(dev, dblk, blks, dest, dowrite); | result = bd_chs_io(bd, dblk, blks, dest, dowrite); | ||||
if (result == 0) { | if (result == 0) { | ||||
if (BD(dev).bd_flags & BD_NO_MEDIA) | if (bd->bd_flags & BD_NO_MEDIA) | ||||
BD(dev).bd_flags &= ~BD_NO_MEDIA; | bd->bd_flags &= ~BD_NO_MEDIA; | ||||
break; | break; | ||||
} | } | ||||
bd_reset_disk(BD(dev).bd_unit); | bd_reset_disk(bd->bd_unit); | ||||
/* | /* | ||||
* Error codes: | * Error codes: | ||||
* 20h controller failure | * 20h controller failure | ||||
* 31h no media in drive (IBM/MS INT 13 extensions) | * 31h no media in drive (IBM/MS INT 13 extensions) | ||||
* 80h no media in drive, VMWare (Fusion) | * 80h no media in drive, VMWare (Fusion) | ||||
* There is no reason to repeat the IO with errors above. | * There is no reason to repeat the IO with errors above. | ||||
*/ | */ | ||||
if (result == 0x20 || result == 0x31 || result == 0x80) { | if (result == 0x20 || result == 0x31 || result == 0x80) { | ||||
BD(dev).bd_flags |= BD_NO_MEDIA; | bd->bd_flags |= BD_NO_MEDIA; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (result != 0 && (BD(dev).bd_flags & BD_NO_MEDIA) == 0) { | if (result != 0 && (bd->bd_flags & BD_NO_MEDIA) == 0) { | ||||
if (dowrite == BD_WR) { | if (dowrite == BD_WR) { | ||||
printf("%s%d: Write %d sector(s) from %p (0x%x) " | printf("%s%d: Write %d sector(s) from %p (0x%x) " | ||||
"to %lld: 0x%x\n", dev->dd.d_dev->dv_name, | "to %lld: 0x%x\n", dev->dd.d_dev->dv_name, | ||||
dev->dd.d_unit, blks, dest, VTOP(dest), dblk, | dev->dd.d_unit, blks, dest, VTOP(dest), dblk, | ||||
result); | result); | ||||
} else { | } else { | ||||
printf("%s%d: Read %d sector(s) from %lld to %p " | printf("%s%d: Read %d sector(s) from %lld to %p " | ||||
"(0x%x): 0x%x\n", dev->dd.d_dev->dv_name, | "(0x%x): 0x%x\n", dev->dd.d_dev->dv_name, | ||||
Show All 39 Lines | |||||
* | * | ||||
* In the case where it looks like (dev) is a SCSI disk, we allow the number of | * In the case where it looks like (dev) is a SCSI disk, we allow the number of | ||||
* IDE disks to be specified in $num_ide_disks. There should be a Better Way. | * IDE disks to be specified in $num_ide_disks. There should be a Better Way. | ||||
*/ | */ | ||||
int | int | ||||
bd_getdev(struct i386_devdesc *d) | bd_getdev(struct i386_devdesc *d) | ||||
{ | { | ||||
struct disk_devdesc *dev; | struct disk_devdesc *dev; | ||||
bdinfo_t *bd; | |||||
int biosdev; | int biosdev; | ||||
int major; | int major; | ||||
int rootdev; | int rootdev; | ||||
char *nip, *cp; | char *nip, *cp; | ||||
int i, unit; | int i, unit, slice, partition; | ||||
/* XXX: Assume partition 'a'. */ | |||||
slice = 0; | |||||
partition = 0; | |||||
dev = (struct disk_devdesc *)d; | dev = (struct disk_devdesc *)d; | ||||
biosdev = bd_unit2bios(dev->dd.d_unit); | bd = bd_get_bdinfo(&dev->dd); | ||||
if (bd == NULL) | |||||
return (-1); | |||||
biosdev = bd_unit2bios(d); | |||||
DEBUG("unit %d BIOS device %d", dev->dd.d_unit, biosdev); | DEBUG("unit %d BIOS device %d", dev->dd.d_unit, biosdev); | ||||
if (biosdev == -1) /* not a BIOS device */ | if (biosdev == -1) /* not a BIOS device */ | ||||
return (-1); | return (-1); | ||||
if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, | |||||
BD(dev).bd_sectorsize) != 0) /* oops, not a viable device */ | if (dev->dd.d_dev->dv_type == DEVT_DISK) { | ||||
if (disk_open(dev, bd->bd_sectors * bd->bd_sectorsize, | |||||
bd->bd_sectorsize) != 0) /* oops, not a viable device */ | |||||
return (-1); | return (-1); | ||||
else | else | ||||
disk_close(dev); | disk_close(dev); | ||||
slice = dev->d_slice + 1; | |||||
partition = dev->d_partition; | |||||
} | |||||
if (biosdev < 0x80) { | if (biosdev < 0x80) { | ||||
/* floppy (or emulated floppy) or ATAPI device */ | /* floppy (or emulated floppy) or ATAPI device */ | ||||
if (bdinfo[dev->dd.d_unit].bd_type == DT_ATAPI) { | if (bd->bd_type == DT_ATAPI) { | ||||
/* is an ATAPI disk */ | /* is an ATAPI disk */ | ||||
major = WFDMAJOR; | major = WFDMAJOR; | ||||
} else { | } else { | ||||
/* is a floppy disk */ | /* is a floppy disk */ | ||||
major = FDMAJOR; | major = FDMAJOR; | ||||
} | } | ||||
} else { | } else { | ||||
/* assume an IDE disk */ | /* assume an IDE disk */ | ||||
major = WDMAJOR; | major = WDMAJOR; | ||||
} | } | ||||
/* default root disk unit number */ | /* default root disk unit number */ | ||||
unit = biosdev & 0x7f; | unit = biosdev & 0x7f; | ||||
if (dev->dd.d_dev->dv_type == DEVT_CD) { | |||||
/* | |||||
* XXX: Need to examine device spec here to figure out if | |||||
* SCSI or ATAPI. No idea on how to figure out device number. | |||||
* All we can really pass to the kernel is what bus and device | |||||
* on which bus we were booted from, which dev_t isn't well | |||||
* suited to since those number don't match to unit numbers | |||||
* very well. We may just need to engage in a hack where | |||||
* we pass -C to the boot args if we are the boot device. | |||||
*/ | |||||
major = ACDMAJOR; | |||||
unit = 0; /* XXX */ | |||||
} | |||||
/* XXX a better kludge to set the root disk unit number */ | /* XXX a better kludge to set the root disk unit number */ | ||||
if ((nip = getenv("root_disk_unit")) != NULL) { | if ((nip = getenv("root_disk_unit")) != NULL) { | ||||
i = strtol(nip, &cp, 0); | i = strtol(nip, &cp, 0); | ||||
/* check for parse error */ | /* check for parse error */ | ||||
if ((cp != nip) && (*cp == 0)) | if ((cp != nip) && (*cp == 0)) | ||||
unit = i; | unit = i; | ||||
} | } | ||||
rootdev = MAKEBOOTDEV(major, dev->d_slice + 1, unit, dev->d_partition); | rootdev = MAKEBOOTDEV(major, slice, unit, partition); | ||||
DEBUG("dev is 0x%x\n", rootdev); | DEBUG("dev is 0x%x\n", rootdev); | ||||
return (rootdev); | return (rootdev); | ||||
} | } |