#include <stand.h>
#include <sys/param.h>
#include <machine/bootinfo.h>
#include <machine/psl.h>
#include <stdarg.h>
#include <bootstrap.h>
#include <btxv86.h>
#include "libi386.h"
#define BIOSCD_SECSIZE 2048
#define BUFSIZE (1 * BIOSCD_SECSIZE)
#define MAXBCDEV 1
#define ACDMAJOR 117
#define CDMAJOR 15
#ifdef DISK_DEBUG
# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
#else
# define DEBUG(fmt, args...)
#endif
struct specification_packet {
u_char sp_size;
u_char sp_bootmedia;
u_char sp_drive;
u_char sp_controller;
u_int sp_lba;
u_short sp_devicespec;
u_short sp_buffersegment;
u_short sp_loadsegment;
u_short sp_sectorcount;
u_short sp_cylsec;
u_char sp_head;
};
static struct bcinfo {
int bc_unit;
struct specification_packet bc_sp;
} bcinfo [MAXBCDEV];
static int nbcinfo = 0;
static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
static int bc_init(void);
static int bc_strategy(void *devdata, int flag, daddr_t dblk,
size_t size, char *buf, size_t *rsize);
static int bc_open(struct open_file *f, ...);
static int bc_close(struct open_file *f);
static void bc_print(int verbose);
struct devsw bioscd = {
"cd",
DEVT_CD,
bc_init,
bc_strategy,
bc_open,
bc_close,
noioctl,
bc_print,
NULL
};
int
bc_bios2unit(int biosdev)
{
int i;
DEBUG("looking for bios device 0x%x", biosdev);
for (i = 0; i < nbcinfo; i++) {
DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
if (bcinfo[i].bc_unit == biosdev)
return(i);
}
return(-1);
}
int
bc_unit2bios(int unit)
{
if ((unit >= 0) && (unit < nbcinfo))
return(bcinfo[unit].bc_unit);
return(-1);
}
static int
bc_init(void)
{
return (0);
}
int
bc_add(int biosdev)
{
if (nbcinfo >= MAXBCDEV)
return (-1);
bcinfo[nbcinfo].bc_unit = biosdev;
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x4b01;
v86.edx = biosdev;
v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
v86int();
if ((v86.eax & 0xff00) != 0)
return (-1);
printf("BIOS CD is cd%d\n", nbcinfo);
nbcinfo++;
return(0);
}
static void
bc_print(int verbose)
{
int i;
char line[80];
for (i = 0; i < nbcinfo; i++) {
sprintf(line, " cd%d: Device 0x%x\n", i,
bcinfo[i].bc_sp.sp_devicespec);
pager_output(line);
}
}
static int
bc_open(struct open_file *f, ...)
{
va_list ap;
struct i386_devdesc *dev;
va_start(ap, f);
dev = va_arg(ap, struct i386_devdesc *);
va_end(ap);
if (dev->d_kind.bioscd.unit >= nbcinfo) {
DEBUG("attempt to open nonexistent disk");
return(ENXIO);
}
return(0);
}
static int
bc_close(struct open_file *f)
{
return(0);
}
static int
bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
size_t *rsize)
{
struct i386_devdesc *dev;
int unit;
int blks;
#ifdef BD_SUPPORT_FRAGS
#error "xxx broken code xxx"
char fragbuf[BIOSCD_SECSIZE];
size_t fragsize;
fragsize = size % BIOSCD_SECSIZE;
#else
if (size % BIOSCD_SECSIZE)
return (EINVAL);
#endif
if (rw != F_READ)
return(EROFS);
dev = (struct i386_devdesc *)devdata;
unit = dev->d_kind.bioscd.unit;
blks = size / BIOSCD_SECSIZE;
if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
return (EINVAL);
dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
DEBUG("read %d from %d to %p", blks, dblk, buf);
if (rsize)
*rsize = 0;
if (blks && bc_read(unit, dblk, blks, buf)) {
DEBUG("read error");
return (EIO);
}
#ifdef BD_SUPPORT_FRAGS
DEBUG("bc_strategy: frag read %d from %d+%d to %p",
fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) {
DEBUG("frag read error");
return(EIO);
}
bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
#endif
if (rsize)
*rsize = size;
return (0);
}
static int
bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
{
u_int result, retry;
static unsigned short packet[8];
int biosdev;
int n;
#ifdef DISK_DEBUG
int error;
#endif
if (blks < 0)
return (-1);
if (blks == 0)
return (0);
biosdev = bc_unit2bios(unit);
for (retry = 0; retry < 3; retry++) {
if (retry > 0) {
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0;
v86.edx = biosdev;
v86int();
}
n = BOUNCEBUF_SIZE / BIOSCD_SECSIZE;
if (n > blks)
n = blks;
packet[0] = 0x10;
packet[1] = n;
packet[2] = VTOPOFF(bounce_base);
packet[3] = VTOPSEG(bounce_base);
packet[4] = dblk & 0xffff;
packet[5] = dblk >> 16;
packet[6] = 0;
packet[7] = 0;
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x4200;
v86.edx = biosdev;
v86.ds = VTOPSEG(packet);
v86.esi = VTOPOFF(packet);
v86int();
result = (v86.efl & PSL_C);
if (result == 0) {
bcopy(bounce_base, dest, n * BIOSCD_SECSIZE);
blks -= n;
dest += n * BIOSCD_SECSIZE;
if (blks == 0)
break;
retry = 0;
}
}
#ifdef DISK_DEBUG
error = (v86.eax >> 8) & 0xff;
#endif
DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest,
VTOP(dest), result ? "failed" : "ok");
DEBUG("unit %d status 0x%x", unit, error);
return(0);
}
int
bc_getdev(struct i386_devdesc *dev)
{
int biosdev, unit;
int major;
int rootdev;
unit = dev->d_kind.bioscd.unit;
biosdev = bc_unit2bios(unit);
DEBUG("unit %d BIOS device %d", unit, biosdev);
if (biosdev == -1)
return(-1);
major = ACDMAJOR;
unit = 0;
rootdev = MAKEBOOTDEV(major, 0, unit, 0);
DEBUG("dev is 0x%x\n", rootdev);
return(rootdev);
}