#include <sys/types.h>
#include <sys/param.h>
#include <sys/controlregs.h>
#include <sys/bootconf.h>
#include <sys/bootvfs.h>
#include <sys/bootregs.h>
#include <sys/bootconf.h>
#include <sys/conf.h>
#include <sys/promif.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/biosdisk.h>
#include <sys/psw.h>
#if defined(__xpv)
#include <sys/hypervisor.h>
#endif
extern int prom_debug;
#define BIOS_RES_BUFFER_ADDR 0x7000
#define BIOSDEV_NUM 8
#define STARTING_DRVNUM 0x80
#define FP_OFF(fp) (((uintptr_t)(fp)) & 0xFFFF)
#define FP_SEG(fp) ((((uintptr_t)(fp)) >> 16) & 0xFFFF)
#ifdef DEBUG
int biosdebug = 0;
#define dprintf(fmt) \
if (biosdebug) \
prom_printf fmt
#else
#define dprintf(fmt)
#endif
biosdev_data_t biosdev_info[BIOSDEV_NUM];
static int bios_check_extension_present(uchar_t);
static int get_dev_params(uchar_t);
static int read_firstblock(uchar_t drivenum);
static int drive_present(uchar_t drivenum);
static void reset_disk(uchar_t drivenum);
static int is_eltorito(uchar_t drivenum);
#if !defined(__xpv)
void
startup_bios_disk()
{
uchar_t drivenum;
int got_devparams = 0;
int got_first_block = 0;
uchar_t name[20];
dev_info_t *devi;
int extensions;
for (drivenum = 0x80; drivenum < (0x80 + BIOSDEV_NUM); drivenum++) {
if (!drive_present(drivenum))
continue;
extensions = bios_check_extension_present(drivenum);
if (extensions && is_eltorito(drivenum))
continue;
if (extensions && get_dev_params(drivenum))
got_devparams = 1;
else
got_devparams = 0;
if ((got_first_block = read_firstblock(drivenum)) == 0) {
got_first_block = read_firstblock(drivenum);
}
if (got_devparams || got_first_block) {
(void) sprintf((char *)name, "biosdev-0x%x", drivenum);
devi = ddi_root_node();
(void) e_ddi_prop_update_byte_array(DDI_DEV_T_NONE,
devi, (char *)name,
(uchar_t *)&biosdev_info[drivenum - 0x80],
sizeof (biosdev_data_t));
}
}
}
#endif
static int
bios_check_extension_present(uchar_t drivenum)
{
struct bop_regs rp = {0};
extern struct bootops *bootops;
rp.eax.word.ax = 0x4100;
rp.ebx.word.bx = 0x55AA;
rp.edx.word.dx = drivenum;
BOP_DOINT(bootops, 0x13, &rp);
if (((rp.eflags & PS_C) != 0) || (rp.ebx.word.bx != 0xAA55)) {
dprintf(("bios_check_extension_present int13 fn 41 "
"failed %d bx = %x\n", rp.eflags, rp.ebx.word.bx));
return (0);
}
if ((rp.ecx.word.cx & 0x7) == 0) {
dprintf(("bios_check_extension_present get device parameters "
"not supported cx = %x\n", rp.ecx.word.cx));
return (0);
}
return (1);
}
static int
get_dev_params(uchar_t drivenum)
{
struct bop_regs rp = {0};
fn48_t *bufp;
extern struct bootops *bootops;
int i;
int index;
uchar_t *tmp;
dprintf(("In get_dev_params\n"));
bufp = (fn48_t *)BIOS_RES_BUFFER_ADDR;
for (i = 0; i < sizeof (*bufp); i++)
((uchar_t *)bufp)[i] = 0;
bufp->buflen = sizeof (*bufp);
rp.eax.word.ax = 0x4800;
rp.edx.byte.dl = drivenum;
rp.esi.word.si = (uint16_t)FP_OFF((uint_t)(uintptr_t)bufp);
rp.ds = FP_SEG((uint_t)(uintptr_t)bufp);
BOP_DOINT(bootops, 0x13, &rp);
if ((rp.eflags & PS_C) != 0) {
dprintf(("EDD FAILED on drive eflag = %x ah= %x\n",
rp.eflags, rp.eax.byte.ah));
return (0);
}
index = drivenum - 0x80;
biosdev_info[index].edd_valid = 1;
tmp = (uchar_t *)&biosdev_info[index].fn48_dev_params;
for (i = 0; i < sizeof (*bufp); i++)
tmp[i] = ((uchar_t *)bufp)[i];
return (1);
}
static int
drive_present(uchar_t drivenum)
{
struct bop_regs rp = {0};
rp.eax.byte.ah = 0x8;
rp.edx.byte.dl = drivenum;
BOP_DOINT(bootops, 0x13, &rp);
if (((rp.eflags & PS_C) != 0) || rp.eax.byte.ah != 0) {
dprintf(("drive not present drivenum %x eflag %x ah %x\n",
drivenum, rp.eflags, rp.eax.byte.ah));
return (0);
}
dprintf(("drive-present %x\n", drivenum));
return (1);
}
static void
reset_disk(uchar_t drivenum)
{
struct bop_regs rp = {0};
int status;
rp.eax.byte.ah = 0x0;
rp.edx.byte.dl = drivenum;
BOP_DOINT(bootops, 0x13, &rp);
status = rp.eax.byte.ah;
if (((rp.eflags & PS_C) != 0) || status != 0)
dprintf(("Bad disk reset driv %x, status %x\n", drivenum,
status));
}
static int
read_firstblock(uchar_t drivenum)
{
struct bop_regs rp = {0};
caddr_t bufp;
uchar_t status;
int i, index;
reset_disk(drivenum);
bufp = (caddr_t)BIOS_RES_BUFFER_ADDR;
rp.eax.byte.ah = 0x2;
rp.eax.byte.al = 1;
rp.ecx.byte.ch = 0;
rp.ecx.byte.cl = 1;
rp.edx.byte.dh = 0;
rp.edx.byte.dl = drivenum;
rp.ebx.word.bx = (uint16_t)FP_OFF((uint_t)(uintptr_t)bufp);
rp.es = FP_SEG((uint_t)(uintptr_t)bufp);
BOP_DOINT(bootops, 0x13, &rp);
status = rp.eax.byte.ah;
if (((rp.eflags & PS_C) != 0) || status != 0) {
dprintf(("read_firstblock AH not clear %x \n", status));
return (0);
}
dprintf(("drivenum %x uid at 0x1b8 is %x\n", drivenum,
*(uint32_t *)(bufp +0x1b8)));
index = drivenum - 0x80;
biosdev_info[index].first_block_valid = 1;
for (i = 0; i < 512; i++)
biosdev_info[index].first_block[i] = *((uchar_t *)bufp + i);
return (1);
}
static int
is_eltorito(uchar_t drivenum)
{
struct bop_regs rp = {0};
fn4b_t *bufp;
extern struct bootops *bootops;
int i;
dprintf(("In is_eltorito\n"));
bufp = (fn4b_t *)BIOS_RES_BUFFER_ADDR;
for (i = 0; i < sizeof (*bufp); i++)
((uchar_t *)bufp)[i] = 0;
bufp->pkt_size = sizeof (*bufp);
rp.eax.word.ax = 0x4b01;
rp.edx.byte.dl = drivenum;
rp.esi.word.si = (uint16_t)FP_OFF((uint_t)(uintptr_t)bufp);
rp.ds = FP_SEG((uint_t)(uintptr_t)bufp);
BOP_DOINT(bootops, 0x13, &rp);
if ((rp.eflags & PS_C) != 0 || bufp->drivenum != drivenum) {
dprintf(("fn 0x4b01 FAILED on drive "
"eflags=%x ah=%x drivenum=%x\n",
rp.eflags, rp.eax.byte.ah, bufp->drivenum));
return (0);
}
if (prom_debug)
prom_printf("INT13 FN4B01 mtype => %x", bufp->boot_mtype);
return (1);
}