diff options
Diffstat (limited to 'stand/pc98/libpc98')
-rw-r--r-- | stand/pc98/libpc98/Makefile | 51 | ||||
-rw-r--r-- | stand/pc98/libpc98/bioscd.c | 420 | ||||
-rw-r--r-- | stand/pc98/libpc98/biosdisk.c | 1120 | ||||
-rw-r--r-- | stand/pc98/libpc98/biosmem.c | 64 | ||||
-rw-r--r-- | stand/pc98/libpc98/biossmap.c | 38 | ||||
-rw-r--r-- | stand/pc98/libpc98/comconsole.c | 367 | ||||
-rw-r--r-- | stand/pc98/libpc98/libpc98.h | 29 | ||||
-rw-r--r-- | stand/pc98/libpc98/pc98_sys.c | 78 | ||||
-rw-r--r-- | stand/pc98/libpc98/time.c | 98 | ||||
-rw-r--r-- | stand/pc98/libpc98/vidconsole.c | 596 |
10 files changed, 2861 insertions, 0 deletions
diff --git a/stand/pc98/libpc98/Makefile b/stand/pc98/libpc98/Makefile new file mode 100644 index 0000000..f3e27a4 --- /dev/null +++ b/stand/pc98/libpc98/Makefile @@ -0,0 +1,51 @@ +# $FreeBSD$ +# +LIB= pc98 +INTERNALLIB= + +.PATH: ${.CURDIR}/../../i386/libi386 + +SRCS= bioscd.c biosdisk.c biosmem.c biospnp.c \ + biospci.c biossmap.c bootinfo.c bootinfo32.c \ + comconsole.c devicename.c elf32_freebsd.c \ + i386_copy.c i386_module.c nullconsole.c pc98_sys.c pxe.c pxetramp.s \ + time.c vidconsole.c +.PATH: ${.CURDIR}/../../zfs +SRCS+= devicename_stubs.c + +# Enable PXE TFTP or NFS support, not both. +.if defined(LOADER_TFTP_SUPPORT) +CFLAGS+= -DLOADER_TFTP_SUPPORT +.else +CFLAGS+= -DLOADER_NFS_SUPPORT +.endif + +BOOT_COMCONSOLE_PORT?= 0x238 +CFLAGS+= -DCOMPORT=${BOOT_COMCONSOLE_PORT} + +BOOT_COMCONSOLE_SPEED?= 9600 +CFLAGS+= -DCOMSPEED=${BOOT_COMCONSOLE_SPEED} + +.ifdef(BOOT_BIOSDISK_DEBUG) +# Make the disk code more talkative +CFLAGS+= -DDISK_DEBUG +.endif + +# Include simple terminal emulation (cons25-compatible) +CFLAGS+= -DTERM_EMU + +# XXX: make alloca() useable +CFLAGS+= -Dalloca=__builtin_alloca + +CFLAGS+= -I${.CURDIR}/../../ficl -I${.CURDIR}/../../ficl/i386 \ + -I${.CURDIR}/../../common \ + -I${.CURDIR}/../btx/lib \ + -I${.CURDIR}/../../i386/libi386 \ + -I${.CURDIR}/../../.. -I. +# the location of libstand +CFLAGS+= -I${.CURDIR}/../../../../lib/libstand/ + +# Handle FreeBSD specific %b and %D printf format specifiers +CFLAGS+= ${FORMAT_EXTENSIONS} + +.include <bsd.lib.mk> diff --git a/stand/pc98/libpc98/bioscd.c b/stand/pc98/libpc98/bioscd.c new file mode 100644 index 0000000..f259701 --- /dev/null +++ b/stand/pc98/libpc98/bioscd.c @@ -0,0 +1,420 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * BIOS CD device handling for CD's that have been booted off of via no + * emulation booting as defined in the El Torito standard. + * + * Ideas and algorithms from: + * + * - FreeBSD libi386/biosdisk.c + * + */ + +#include <stand.h> + +#include <sys/param.h> +#include <machine/bootinfo.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 + +/* Major numbers for devices we frontend for. */ +#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; +}; + +/* + * List of BIOS devices, translation from disk unit number to + * BIOS unit number. + */ +static struct bcinfo { + int bc_unit; /* BIOS unit number */ + struct specification_packet bc_sp; + int bc_open; /* reference counter */ + void *bc_bcache; /* buffer cache data */ +} bcinfo [MAXBCDEV]; +static int nbcinfo = 0; + +#define BC(dev) (bcinfo[(dev)->d_unit]) + +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_realstrategy(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 int bc_print(int verbose); + +struct devsw bioscd = { + "cd", + DEVT_CD, + bc_init, + bc_strategy, + bc_open, + bc_close, + noioctl, + bc_print, + NULL +}; + +/* + * Translate between BIOS device numbers and our private unit numbers. + */ +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); +} + +/* + * We can't quiz, we have to be told what device to use, so this functoin + * doesn't do anything. Instead, the loader calls bc_add() with the BIOS + * device number to add. + */ +static int +bc_init(void) +{ + + return (0); +} + +int +bc_add(int biosdev) +{ + + if (nbcinfo >= MAXBCDEV) + return (-1); + bcinfo[nbcinfo].bc_unit = biosdev; + + /* SCSI CD-ROM only */ + if ((biosdev & 0xf0) != 0xa0) + return (-1); + if ((((uint32_t *)PTOV(0xA1460))[biosdev & 0x0f] & 0x1f) != 5) + return (-1); + + printf("BIOS CD is cd%d\n", nbcinfo); + nbcinfo++; + bcache_add_dev(nbcinfo); /* register cd device in bcache */ + return(0); +} + +/* + * Print information about disks + */ +static int +bc_print(int verbose) +{ + char line[80]; + int i, ret = 0; + + if (nbcinfo == 0) + return (0); + + printf("%s devices:", bioscd.dv_name); + if ((ret = pager_output("\n")) != 0) + return (ret); + + for (i = 0; i < nbcinfo; i++) { + sprintf(line, " cd%d: Device 0x%x\n", i, + bcinfo[i].bc_sp.sp_devicespec); + if ((ret = pager_output(line)) != 0) + break; + } + return (ret); +} + +/* + * Attempt to open the disk described by (dev) for use by (f). + */ +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_unit >= nbcinfo) { + DEBUG("attempt to open nonexistent disk"); + return(ENXIO); + } + + BC(dev).bc_open++; + if (BC(dev).bc_bcache == NULL) + BC(dev).bc_bcache = bcache_allocate(); + return(0); +} + +static int +bc_close(struct open_file *f) +{ + struct i386_devdesc *dev; + + dev = (struct i386_devdesc *)f->f_devdata; + BC(dev).bc_open--; + if (BC(dev).bc_open == 0) { + bcache_free(BC(dev).bc_bcache); + BC(dev).bc_bcache = NULL; + } + return(0); +} + +static int +bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, + char *buf, size_t *rsize) +{ + struct bcache_devdata bcd; + struct i386_devdesc *dev; + + dev = (struct i386_devdesc *)devdata; + bcd.dv_strategy = bc_realstrategy; + bcd.dv_devdata = devdata; + bcd.dv_cache = BC(dev).bc_bcache; + + return (bcache_strategy(&bcd, rw, dblk, size, buf, rsize)); +} + +static int +bc_realstrategy(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 + 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_unit; + blks = size / BIOSCD_SECSIZE; + if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) + return (EINVAL); + dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); + DEBUG("read %d from %lld 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("frag read %d from %lld+%d to %p", + fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); + if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) { + DEBUG("frag read error"); + return(EIO); + } + bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); +#endif + if (rsize) + *rsize = size; + return (0); +} + +/* Max number of sectors to bounce-buffer at a time. */ +#define CD_BOUNCEBUF 8 + +static int +bc_read(int unit, daddr_t dblk, int blks, caddr_t dest) +{ + u_int maxfer, resid, result, retry, x; + caddr_t bbuf, p, xp; + int biosdev; +#ifdef DISK_DEBUG + int error; +#endif + + /* Just in case some idiot actually tries to read -1 blocks... */ + if (blks < 0) + return (-1); + + /* If nothing to do, just return succcess. */ + if (blks == 0) + return (0); + + /* Decide whether we have to bounce */ + if (VTOP(dest) >> 20 != 0) { + /* + * The destination buffer is above first 1MB of + * physical memory so we have to arrange a suitable + * bounce buffer. + */ + x = min(CD_BOUNCEBUF, (unsigned)blks); + bbuf = alloca(x * BIOSCD_SECSIZE); + maxfer = x; + } else { + bbuf = NULL; + maxfer = 0; + } + + biosdev = bc_unit2bios(unit); + resid = blks; + p = dest; + + while (resid > 0) { + if (bbuf) + xp = bbuf; + else + xp = p; + x = resid; + if (maxfer > 0) + x = min(x, maxfer); + + /* + * Loop retrying the operation a couple of times. The BIOS + * may also retry. + */ + for (retry = 0; retry < 3; retry++) { + /* If retrying, reset the drive */ + if (retry > 0) { + v86.ctl = V86_FLAGS; + v86.addr = 0x1b; + v86.eax = 0x0300 | biosdev; + v86int(); + } + + v86.ctl = V86_FLAGS; + v86.addr = 0x1b; + v86.eax = 0x0600 | (biosdev & 0x7f); + v86.ebx = x * BIOSCD_SECSIZE; + v86.ecx = dblk & 0xffff; + v86.edx = (dblk >> 16) & 0xffff; + v86.ebp = VTOPOFF(xp); + v86.es = VTOPSEG(xp); + v86int(); + result = V86_CY(v86.efl); + if (result == 0) + break; + } + +#ifdef DISK_DEBUG + error = (v86.eax >> 8) & 0xff; +#endif + DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p, + VTOP(p), result ? "failed" : "ok"); + DEBUG("unit %d status 0x%x", unit, error); + if (bbuf != NULL) + bcopy(bbuf, p, x * BIOSCD_SECSIZE); + p += (x * BIOSCD_SECSIZE); + dblk += x; + resid -= x; + } + +/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ + return(0); +} + +/* + * Return a suitable dev_t value for (dev). + */ +int +bc_getdev(struct i386_devdesc *dev) +{ + int biosdev, unit, device; + int major; + int rootdev; + + unit = dev->d_unit; + biosdev = bc_unit2bios(unit); + DEBUG("unit %d BIOS device %d", unit, biosdev); + if (biosdev == -1) /* not a BIOS device */ + return(-1); + + device = biosdev & 0xf0; + if (device == 0x80) + major = ACDMAJOR; + else if (device == 0xa0) + major = CDMAJOR; + else + return (-1); + + unit = 0; /* XXX */ + + /* XXX: Assume partition 'a'. */ + rootdev = MAKEBOOTDEV(major, 0, unit, 0); + DEBUG("dev is 0x%x\n", rootdev); + return(rootdev); +} diff --git a/stand/pc98/libpc98/biosdisk.c b/stand/pc98/libpc98/biosdisk.c new file mode 100644 index 0000000..86a550d --- /dev/null +++ b/stand/pc98/libpc98/biosdisk.c @@ -0,0 +1,1120 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * BIOS disk device handling. + * + * Ideas and algorithms from: + * + * - NetBSD libi386/biosdisk.c + * - FreeBSD biosboot/disk.c + * + */ + +#include <stand.h> + +#include <sys/disklabel.h> +#include <sys/diskpc98.h> +#include <machine/bootinfo.h> + +#include <stdarg.h> + +#include <bootstrap.h> +#include <btxv86.h> +#include "libi386.h" + +#define BIOS_NUMDRIVES 0x475 +#define BIOSDISK_SECSIZE 512 +#define BUFSIZE (1 * BIOSDISK_SECSIZE) + +#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ +#define WDMAJOR 0 /* major numbers for devices we frontend for */ +#define WFDMAJOR 1 +#define FDMAJOR 2 +#define DAMAJOR 4 + +#ifdef DISK_DEBUG +# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) +#else +# define DEBUG(fmt, args...) +#endif + +struct open_disk { + int od_dkunit; /* disk unit number */ + int od_unit; /* BIOS unit number */ + int od_cyl; /* BIOS geometry */ + int od_hds; + int od_sec; + int od_boff; /* block offset from beginning of BIOS disk */ + int od_flags; +#define BD_MODEINT13 0x0000 +#define BD_MODEEDD1 0x0001 +#define BD_MODEEDD3 0x0002 +#define BD_MODEMASK 0x0003 +#define BD_FLOPPY 0x0004 +#define BD_LABELOK 0x0008 +#define BD_PARTTABOK 0x0010 +#define BD_OPTICAL 0x0020 + struct disklabel od_disklabel; + int od_nslices; /* slice count */ + struct pc98_partition od_slicetab[PC98_NPARTS]; +}; + +/* + * List of BIOS devices, translation from disk unit number to + * BIOS unit number. + */ +static struct bdinfo +{ + int bd_unit; /* BIOS unit number */ + int bd_flags; + int bd_type; /* BIOS 'drive type' (floppy only) */ + int bd_da_unit; /* kernel unit number for da */ + int bd_open; /* reference counter */ + void *bd_bcache; /* buffer cache data */ +} bdinfo [MAXBDDEV]; +static int nbdinfo = 0; + +#define BD(dev) (bdinfo[(dev)->d_unit]) + +static int bd_getgeom(struct open_disk *od); +static int bd_read(struct open_disk *od, daddr_t dblk, int blks, + caddr_t dest); +static int bd_write(struct open_disk *od, daddr_t dblk, int blks, + caddr_t dest); + +static int bd_int13probe(struct bdinfo *bd); + +static int bd_printslice(struct open_disk *od, struct pc98_partition *dp, + char *prefix, int verbose); +static int bd_printbsdslice(struct open_disk *od, daddr_t offset, + char *prefix, int verbose); + +static int bd_init(void); +static int bd_strategy(void *devdata, int flag, daddr_t dblk, + size_t size, char *buf, size_t *rsize); +static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, + size_t size, char *buf, size_t *rsize); +static int bd_open(struct open_file *f, ...); +static int bd_close(struct open_file *f); +static int bd_print(int verbose); + +struct devsw biosdisk = { + "disk", + DEVT_DISK, + bd_init, + bd_strategy, + bd_open, + bd_close, + noioctl, + bd_print, + NULL +}; + +static int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev); +static void bd_closedisk(struct open_disk *od); +static int bd_open_pc98(struct open_disk *od, struct i386_devdesc *dev); +static int bd_bestslice(struct open_disk *od); +static void bd_checkextended(struct open_disk *od, int slicenum); + +/* + * Translate between BIOS device numbers and our private unit numbers. + */ +int +bd_bios2unit(int biosdev) +{ + int i; + + DEBUG("looking for bios device 0x%x", biosdev); + for (i = 0; i < nbdinfo; i++) { + DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit); + if (bdinfo[i].bd_unit == biosdev) + return(i); + } + return(-1); +} + +int +bd_unit2bios(int unit) +{ + if ((unit >= 0) && (unit < nbdinfo)) + return(bdinfo[unit].bd_unit); + return(-1); +} + +/* + * Quiz the BIOS for disk devices, save a little info about them. + */ +static int +bd_init(void) +{ + int base, unit; + int da_drive=0, n=-0x10; + + /* sequence 0x90, 0x80, 0xa0 */ + for (base = 0x90; base <= 0xa0; base += n, n += 0x30) { + for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4); unit++) { + bdinfo[nbdinfo].bd_open = 0; + bdinfo[nbdinfo].bd_bcache = NULL; + bdinfo[nbdinfo].bd_unit = unit; + bdinfo[nbdinfo].bd_flags = (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0; + + if (!bd_int13probe(&bdinfo[nbdinfo])){ + if (((unit & 0xf0) == 0x90 && (unit & 0x0f) < 4) || + ((unit & 0xf0) == 0xa0 && (unit & 0x0f) < 6)) + continue; /* Target IDs are not contiguous. */ + else + break; + } + + if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY){ + /* available 1.44MB access? */ + if (*(u_char *)PTOV(0xA15AE) & (1<<(unit & 0xf))) { + /* boot media 1.2MB FD? */ + if ((*(u_char *)PTOV(0xA1584) & 0xf0) != 0x90) + bdinfo[nbdinfo].bd_unit = 0x30 + (unit & 0xf); + } + } + else { + if ((unit & 0xF0) == 0xA0) /* SCSI HD or MO */ + bdinfo[nbdinfo].bd_da_unit = da_drive++; + } + /* XXX we need "disk aliases" to make this simpler */ + printf("BIOS drive %c: is disk%d\n", + 'A' + nbdinfo, nbdinfo); + nbdinfo++; + } + } + bcache_add_dev(nbdinfo); + return(0); +} + +/* + * Try to detect a device supported by the legacy int13 BIOS + */ +static int +bd_int13probe(struct bdinfo *bd) +{ + int addr; + + if (bd->bd_flags & BD_FLOPPY) { + addr = 0xa155c; + } else { + if ((bd->bd_unit & 0xf0) == 0x80) + addr = 0xa155d; + else + addr = 0xa1482; + } + if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) { + bd->bd_flags |= BD_MODEINT13; + return(1); + } + if ((bd->bd_unit & 0xF0) == 0xA0) { + int media = ((unsigned *)PTOV(0xA1460))[bd->bd_unit & 0x0F] & 0x1F; + + if (media == 7) { /* MO */ + bd->bd_flags |= BD_MODEINT13 | BD_OPTICAL; + return(1); + } + } + return(0); +} + +/* + * Print information about disks + */ +static int +bd_print(int verbose) +{ + int i, j, ret = 0; + char line[80]; + struct i386_devdesc dev; + struct open_disk *od; + struct pc98_partition *dptr; + + if (nbdinfo == 0) + return (0); + + printf("%s devices:", biosdisk.dv_name); + if ((ret = pager_output("\n")) != 0) + return (ret); + + for (i = 0; i < nbdinfo; i++) { + snprintf(line, sizeof(line), " disk%d: BIOS drive %c:\n", + i, 'A' + i); + if ((ret = pager_output(line)) != 0) + break; + + /* try to open the whole disk */ + dev.d_unit = i; + dev.d_kind.biosdisk.slice = -1; + dev.d_kind.biosdisk.partition = -1; + + if (!bd_opendisk(&od, &dev)) { + + /* Do we have a partition table? */ + if (od->od_flags & BD_PARTTABOK) { + dptr = &od->od_slicetab[0]; + + /* Check for a "dedicated" disk */ + for (j = 0; j < od->od_nslices; j++) { + snprintf(line, sizeof(line), " disk%ds%d", i, j + 1); + if ((ret = bd_printslice(od, &dptr[j], line, verbose)) != 0) + break; + } + } + bd_closedisk(od); + if (ret != 0) + break; + } + } + return (ret); +} + +/* Given a size in 512 byte sectors, convert it to a human-readable number. */ +static char * +display_size(uint64_t size) +{ + static char buf[80]; + char unit; + + size /= 2; + unit = 'K'; + if (size >= 10485760000LL) { + size /= 1073741824; + unit = 'T'; + } else if (size >= 10240000) { + size /= 1048576; + unit = 'G'; + } else if (size >= 10000) { + size /= 1024; + unit = 'M'; + } + sprintf(buf, "%6ld%cB", (long)size, unit); + return (buf); +} + +/* + * Print information about slices on a disk. For the size calculations we + * assume a 512 byte sector. + */ +static int +bd_printslice(struct open_disk *od, struct pc98_partition *dp, char *prefix, + int verbose) +{ + int cylsecs, start, size; + char stats[80]; + char line[80]; + + cylsecs = od->od_hds * od->od_sec; + start = dp->dp_scyl * cylsecs + dp->dp_shd * od->od_sec + dp->dp_ssect; + size = (dp->dp_ecyl - dp->dp_scyl + 1) * cylsecs; + + if (verbose) + sprintf(stats, " %s (%d - %d)", display_size(size), + start, start + size); + else + stats[0] = '\0'; + + switch(dp->dp_mid & PC98_MID_MASK) { + case PC98_MID_386BSD: + return (bd_printbsdslice(od, start, prefix, verbose)); + case 0x00: /* unused partition */ + return (0); + case 0x01: + sprintf(line, "%s: FAT-12%s\n", prefix, stats); + break; + case 0x11: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + sprintf(line, "%s: FAT-16%s\n", prefix, stats); + break; + default: + sprintf(line, "%s: Unknown fs: 0x%x %s\n", prefix, dp->dp_mid, + stats); + } + return (pager_output(line)); +} + +/* + * Print out each valid partition in the disklabel of a FreeBSD slice. + * For size calculations, we assume a 512 byte sector size. + */ +static int +bd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix, + int verbose) +{ + char line[80]; + char buf[BIOSDISK_SECSIZE]; + struct disklabel *lp; + int i; + + /* read disklabel */ + if (bd_read(od, offset + LABELSECTOR, 1, buf)) + return (0); + lp =(struct disklabel *)(&buf[0]); + if (lp->d_magic != DISKMAGIC) { + sprintf(line, "%s: FFS bad disklabel\n", prefix); + return (pager_output(line)); + } + + /* Print partitions */ + for (i = 0; i < lp->d_npartitions; i++) { + /* + * For each partition, make sure we know what type of fs it is. If + * not, then skip it. However, since floppies often have bogus + * fstypes, print the 'a' partition on a floppy even if it is marked + * unused. + */ + if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) || + (lp->d_partitions[i].p_fstype == FS_SWAP) || + (lp->d_partitions[i].p_fstype == FS_VINUM) || + ((lp->d_partitions[i].p_fstype == FS_UNUSED) && + (od->od_flags & BD_FLOPPY) && (i == 0))) { + + /* Only print out statistics in verbose mode */ + if (verbose) + sprintf(line, " %s%c: %s %s (%d - %d)\n", prefix, 'a' + i, + (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap " : + (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : + "FFS ", + display_size(lp->d_partitions[i].p_size), + lp->d_partitions[i].p_offset, + lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size); + else + sprintf(line, " %s%c: %s\n", prefix, 'a' + i, + (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : + (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : + "FFS"); + if (pager_output(line)) + return (1); + } + } + return (0); +} + + +/* + * Attempt to open the disk described by (dev) for use by (f). + * + * Note that the philosophy here is "give them exactly what + * they ask for". This is necessary because being too "smart" + * about what the user might want leads to complications. + * (eg. given no slice or partition value, with a disk that is + * sliced - are they after the first BSD slice, or the DOS + * slice before it?) + */ +static int +bd_open(struct open_file *f, ...) +{ + va_list ap; + struct i386_devdesc *dev; + struct open_disk *od; + int error; + + va_start(ap, f); + dev = va_arg(ap, struct i386_devdesc *); + va_end(ap); + if ((error = bd_opendisk(&od, dev))) + return(error); + + BD(dev).bd_open++; + if (BD(dev).bd_bcache == NULL) + BD(dev).bd_bcache = bcache_allocate(); + + /* + * Save our context + */ + ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od; + DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff); + return(0); +} + +static int +bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev) +{ + struct open_disk *od; + int error; + + if (dev->d_unit >= nbdinfo) { + DEBUG("attempt to open nonexistent disk"); + return(ENXIO); + } + + od = (struct open_disk *)malloc(sizeof(struct open_disk)); + if (!od) { + DEBUG("no memory"); + return (ENOMEM); + } + + /* Look up BIOS unit number, intialise open_disk structure */ + od->od_dkunit = dev->d_unit; + od->od_unit = bdinfo[od->od_dkunit].bd_unit; + od->od_flags = bdinfo[od->od_dkunit].bd_flags; + od->od_boff = 0; + error = 0; + DEBUG("open '%s', unit 0x%x slice %d partition %d", + i386_fmtdev(dev), dev->d_unit, + dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition); + + /* Get geometry for this open (removable device may have changed) */ + if (bd_getgeom(od)) { + DEBUG("can't get geometry"); + error = ENXIO; + goto out; + } + + /* Determine disk layout. */ + error = bd_open_pc98(od, dev); + + out: + if (error) { + free(od); + } else { + *odp = od; /* return the open disk */ + } + return(error); +} + +static int +bd_open_pc98(struct open_disk *od, struct i386_devdesc *dev) +{ + struct pc98_partition *dptr; + struct disklabel *lp; + int sector, slice, i; + char buf[BUFSIZE]; + + /* + * Following calculations attempt to determine the correct value + * for d->od_boff by looking for the slice and partition specified, + * or searching for reasonable defaults. + */ + + /* + * Find the slice in the DOS slice table. + */ + od->od_nslices = 0; + if (od->od_flags & BD_FLOPPY) { + sector = 0; + goto unsliced; + } + if (bd_read(od, 0, 1, buf)) { + DEBUG("error reading MBR"); + return (EIO); + } + + /* + * Check the slice table magic. + */ + if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { + /* If a slice number was explicitly supplied, this is an error */ + if (dev->d_kind.biosdisk.slice > 0) { + DEBUG("no slice table/MBR (no magic)"); + return (ENOENT); + } + sector = 0; + goto unsliced; /* may be a floppy */ + } + if (bd_read(od, 1, 1, buf)) { + DEBUG("error reading MBR"); + return (EIO); + } + + /* + * copy the partition table, then pick up any extended partitions. + */ + bcopy(buf + PC98_PARTOFF, &od->od_slicetab, + sizeof(struct pc98_partition) * PC98_NPARTS); + od->od_nslices = PC98_NPARTS; /* extended slices start here */ + od->od_flags |= BD_PARTTABOK; + dptr = &od->od_slicetab[0]; + + /* Is this a request for the whole disk? */ + if (dev->d_kind.biosdisk.slice == -1) { + sector = 0; + goto unsliced; + } + + /* + * if a slice number was supplied but not found, this is an error. + */ + if (dev->d_kind.biosdisk.slice > 0) { + slice = dev->d_kind.biosdisk.slice - 1; + if (slice >= od->od_nslices) { + DEBUG("slice %d not found", slice); + return (ENOENT); + } + } + + /* Try to auto-detect the best slice; this should always give a slice number */ + if (dev->d_kind.biosdisk.slice == 0) { + slice = bd_bestslice(od); + if (slice == -1) { + return (ENOENT); + } + dev->d_kind.biosdisk.slice = slice; + } + + dptr = &od->od_slicetab[0]; + /* + * Accept the supplied slice number unequivocally (we may be looking + * at a DOS partition). + */ + dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */ + sector = dptr->dp_scyl * od->od_hds * od->od_sec + + dptr->dp_shd * od->od_sec + dptr->dp_ssect; + { + int end = dptr->dp_ecyl * od->od_hds * od->od_sec + + dptr->dp_ehd * od->od_sec + dptr->dp_esect; + DEBUG("slice entry %d at %d, %d sectors", + dev->d_kind.biosdisk.slice - 1, sector, end-sector); + } + + /* + * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition + */ + if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0)) + dev->d_kind.biosdisk.partition = 0; + + unsliced: + /* + * Now we have the slice offset, look for the partition in the disklabel if we have + * a partition to start with. + * + * XXX we might want to check the label checksum. + */ + if (dev->d_kind.biosdisk.partition < 0) { + od->od_boff = sector; /* no partition, must be after the slice */ + DEBUG("opening raw slice"); + } else { + + if (bd_read(od, sector + LABELSECTOR, 1, buf)) { + DEBUG("error reading disklabel"); + return (EIO); + } + DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel); + bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel)); + lp = &od->od_disklabel; + od->od_flags |= BD_LABELOK; + + if (lp->d_magic != DISKMAGIC) { + DEBUG("no disklabel"); + return (ENOENT); + } + if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) { + DEBUG("partition '%c' exceeds partitions in table (a-'%c')", + 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions); + return (EPART); + } + +#ifdef DISK_DEBUG + /* Complain if the partition is unused unless this is a floppy. */ + if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) && + !(od->od_flags & BD_FLOPPY)) + DEBUG("warning, partition marked as unused"); +#endif + + od->od_boff = + lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset - + lp->d_partitions[RAW_PART].p_offset + + sector; + } + return (0); +} + +/* + * Search for a slice with the following preferences: + * + * 1: Active FreeBSD slice + * 2: Non-active FreeBSD slice + * 3: Active Linux slice + * 4: non-active Linux slice + * 5: Active FAT/FAT32 slice + * 6: non-active FAT/FAT32 slice + */ +#define PREF_RAWDISK 0 +#define PREF_FBSD_ACT 1 +#define PREF_FBSD 2 +#define PREF_LINUX_ACT 3 +#define PREF_LINUX 4 +#define PREF_DOS_ACT 5 +#define PREF_DOS 6 +#define PREF_NONE 7 + +/* + * slicelimit is in the range 0 .. PC98_NPARTS + */ +static int +bd_bestslice(struct open_disk *od) +{ + struct pc98_partition *dp; + int pref, preflevel; + int i, prefslice; + + prefslice = 0; + preflevel = PREF_NONE; + + dp = &od->od_slicetab[0]; + for (i = 0; i < od->od_nslices; i++, dp++) { + switch(dp->dp_mid & PC98_MID_MASK) { + case PC98_MID_386BSD: /* FreeBSD */ + if ((dp->dp_mid & PC98_MID_BOOTABLE) && + (preflevel > PREF_FBSD_ACT)) { + pref = i; + preflevel = PREF_FBSD_ACT; + } else if (preflevel > PREF_FBSD) { + pref = i; + preflevel = PREF_FBSD; + } + break; + + case 0x11: /* DOS/Windows */ + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x63: + if ((dp->dp_mid & PC98_MID_BOOTABLE) && + (preflevel > PREF_DOS_ACT)) { + pref = i; + preflevel = PREF_DOS_ACT; + } else if (preflevel > PREF_DOS) { + pref = i; + preflevel = PREF_DOS; + } + break; + } + } + return (prefslice); +} + +static int +bd_close(struct open_file *f) +{ + struct i386_devdesc *dev = f->f_devdata; + struct open_disk *od = (struct open_disk *)(dev->d_kind.biosdisk.data); + + BD(dev).bd_open--; + if (BD(dev).bd_open == 0) { + bcache_free(BD(dev).bd_bcache); + BD(dev).bd_bcache = NULL; + } + + bd_closedisk(od); + return(0); +} + +static void +bd_closedisk(struct open_disk *od) +{ + DEBUG("open_disk %p", od); +#if 0 + /* XXX is this required? (especially if disk already open...) */ + if (od->od_flags & BD_FLOPPY) + delay(3000000); +#endif + free(od); +} + +static int +bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, + char *buf, size_t *rsize) +{ + struct bcache_devdata bcd; + struct i386_devdesc *dev = devdata; + struct open_disk *od = (struct open_disk *)(dev->d_kind.biosdisk.data); + + bcd.dv_strategy = bd_realstrategy; + bcd.dv_devdata = devdata; + bcd.dv_cache = BD(dev).bd_bcache; + return(bcache_strategy(&bcd, rw, dblk+od->od_boff, size, buf, rsize)); +} + +static int +bd_realstrategy(void *devdata, int rw, daddr_t dblk, + size_t size, char *buf, size_t *rsize) +{ + struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); + int blks; +#ifdef BD_SUPPORT_FRAGS + char fragbuf[BIOSDISK_SECSIZE]; + size_t fragsize; + + fragsize = size % BIOSDISK_SECSIZE; +#else + if (size % BIOSDISK_SECSIZE) + panic("bd_strategy: %d bytes I/O not multiple of block size", size); +#endif + + DEBUG("open_disk %p", od); + blks = size / BIOSDISK_SECSIZE; + if (rsize) + *rsize = 0; + + switch(rw){ + case F_READ: + DEBUG("read %d from %d to %p", blks, dblk, buf); + + if (blks && bd_read(od, dblk, blks, buf)) { + DEBUG("read error"); + return (EIO); + } +#ifdef BD_SUPPORT_FRAGS + DEBUG("bd_strategy: frag read %d from %d+%d to %p", + fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE)); + if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) { + DEBUG("frag read error"); + return(EIO); + } + bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize); +#endif + break; + case F_WRITE : + DEBUG("write %d from %d to %p", blks, dblk, buf); + + if (blks && bd_write(od, dblk, blks, buf)) { + DEBUG("write error"); + return (EIO); + } +#ifdef BD_SUPPORT_FRAGS + if(fragsize) { + DEBUG("Attempted to write a frag"); + return (EIO); + } +#endif + break; + default: + /* DO NOTHING */ + return (EROFS); + } + + if (rsize) + *rsize = size; + return (0); +} + +/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */ +#define FLOPPY_BOUNCEBUF 18 + +static int +bd_chs_io(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest, int write) +{ + u_int x, bpc, cyl, hd, sec; + + bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ + x = dblk; + cyl = x / bpc; /* block # / blocks per cylinder */ + x %= bpc; /* block offset into cylinder */ + hd = x / od->od_sec; /* offset / blocks per track */ + sec = x % od->od_sec; /* offset into track */ + + v86.ctl = V86_FLAGS; + v86.addr = 0x1b; + if (write) + v86.eax = 0x0500 | od->od_unit; + else + v86.eax = 0x0600 | od->od_unit; + if (od->od_flags & BD_FLOPPY) { + v86.eax |= 0xd000; + v86.ecx = 0x0200 | (cyl & 0xff); + v86.edx = (hd << 8) | (sec + 1); + } else if (od->od_flags & BD_OPTICAL) { + v86.eax &= 0xFF7F; + v86.ecx = dblk & 0xFFFF; + v86.edx = dblk >> 16; + } else { + v86.ecx = cyl; + v86.edx = (hd << 8) | sec; + } + v86.ebx = blks * BIOSDISK_SECSIZE; + v86.es = VTOPSEG(dest); + v86.ebp = VTOPOFF(dest); + v86int(); + return (V86_CY(v86.efl)); +} + +static int +bd_io(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest, int write) +{ + u_int x, sec, result, resid, retry, maxfer; + caddr_t p, xp, bbuf, breg; + + /* Just in case some idiot actually tries to read/write -1 blocks... */ + if (blks < 0) + return (-1); + + resid = blks; + p = dest; + + /* Decide whether we have to bounce */ + if (VTOP(dest) >> 20 != 0 || + ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { + + /* + * There is a 64k physical boundary somewhere in the + * destination buffer, or the destination buffer is above + * first 1MB of physical memory so we have to arrange a + * suitable bounce buffer. Allocate a buffer twice as large + * as we need to. Use the bottom half unless there is a break + * there, in which case we use the top half. + */ + x = min(od->od_sec, (unsigned)blks); + bbuf = alloca(x * 2 * BIOSDISK_SECSIZE); + if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == + ((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE) & 0xffff0000)) { + breg = bbuf; + } else { + breg = bbuf + x * BIOSDISK_SECSIZE; + } + maxfer = x; /* limit transfers to bounce region size */ + } else { + breg = bbuf = NULL; + maxfer = 0; + } + + while (resid > 0) { + /* + * Play it safe and don't cross track boundaries. + * (XXX this is probably unnecessary) + */ + sec = dblk % od->od_sec; /* offset into track */ + x = min(od->od_sec - sec, resid); + if (maxfer > 0) + x = min(x, maxfer); /* fit bounce buffer */ + + /* where do we transfer to? */ + xp = bbuf == NULL ? p : breg; + + /* + * Put your Data In, Put your Data out, + * Put your Data In, and shake it all about + */ + if (write && bbuf != NULL) + bcopy(p, breg, x * BIOSDISK_SECSIZE); + + /* + * Loop retrying the operation a couple of times. The BIOS + * may also retry. + */ + for (retry = 0; retry < 3; retry++) { + /* if retrying, reset the drive */ + if (retry > 0) { + v86.ctl = V86_FLAGS; + v86.addr = 0x1b; + v86.eax = 0x0300 | od->od_unit; + v86int(); + } + + result = bd_chs_io(od, dblk, x, xp, write); + if (result == 0) + break; + } + + if (write) + DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x, + p, VTOP(p), dblk, result ? "failed" : "ok"); + else + DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x, + dblk, p, VTOP(p), result ? "failed" : "ok"); + if (result) { + return(-1); + } + if (!write && bbuf != NULL) + bcopy(breg, p, x * BIOSDISK_SECSIZE); + p += (x * BIOSDISK_SECSIZE); + dblk += x; + resid -= x; + } + +/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ + return(0); +} + +static int +bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) +{ + + return (bd_io(od, dblk, blks, dest, 0)); +} + +static int +bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) +{ + + return (bd_io(od, dblk, blks, dest, 1)); +} + +static int +bd_getgeom(struct open_disk *od) +{ + + if (od->od_flags & BD_FLOPPY) { + od->od_cyl = 79; + od->od_hds = 2; + od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15; + } else if (od->od_flags & BD_OPTICAL) { + od->od_cyl = 0xFFFE; + od->od_hds = 8; + od->od_sec = 32; + } else { + v86.ctl = V86_FLAGS; + v86.addr = 0x1b; + v86.eax = 0x8400 | od->od_unit; + v86int(); + + od->od_cyl = v86.ecx; + od->od_hds = (v86.edx >> 8) & 0xff; + od->od_sec = v86.edx & 0xff; + if (V86_CY(v86.efl)) + return(1); + } + + DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec); + return(0); +} + +/* + * Return the BIOS geometry of a given "fixed drive" in a format + * suitable for the legacy bootinfo structure. Since the kernel is + * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we + * prefer to get the information directly, rather than rely on being + * able to put it together from information already maintained for + * different purposes and for a probably different number of drives. + * + * For valid drives, the geometry is expected in the format (31..0) + * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are + * indicated by returning the geometry of a "1.2M" PC-format floppy + * disk. And, incidentally, what is returned is not the geometry as + * such but the highest valid cylinder, head, and sector numbers. + */ +u_int32_t +bd_getbigeom(int bunit) +{ + int hds = 0; + int unit = 0x80; /* IDE HDD */ + u_int addr = 0xA155d; + + while (unit < 0xa7) { + if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f))) + if (hds++ == bunit) + break; + + if (unit >= 0xA0) { + int media = ((unsigned *)PTOV(0xA1460))[unit & 0x0F] & 0x1F; + + if (media == 7 && hds++ == bunit) /* SCSI MO */ + return(0xFFFE0820); /* C:65535 H:8 S:32 */ + } + if (++unit == 0x84) { + unit = 0xA0; /* SCSI HDD */ + addr = 0xA1482; + } + } + if (unit == 0xa7) + return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ + v86.ctl = V86_FLAGS; + v86.addr = 0x1b; + v86.eax = 0x8400 | unit; + v86int(); + if (V86_CY(v86.efl)) + return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ + return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff); +} + +/* + * Return a suitable dev_t value for (dev). + * + * 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. + */ +int +bd_getdev(struct i386_devdesc *dev) +{ + struct open_disk *od; + int biosdev; + int major; + int rootdev; + char *nip, *cp; + int unitofs = 0, i, unit; + + biosdev = bd_unit2bios(dev->d_unit); + DEBUG("unit %d BIOS device %d", dev->d_unit, biosdev); + if (biosdev == -1) /* not a BIOS device */ + return(-1); + if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */ + return(-1); + + if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) { + /* floppy (or emulated floppy) or ATAPI device */ + if (bdinfo[dev->d_unit].bd_type == DT_ATAPI) { + /* is an ATAPI disk */ + major = WFDMAJOR; + } else { + /* is a floppy disk */ + major = FDMAJOR; + } + } else { + /* harddisk */ + if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) { + /* label OK, disk labelled as SCSI */ + major = DAMAJOR; + /* check for unit number correction hint, now deprecated */ + if ((nip = getenv("num_ide_disks")) != NULL) { + i = strtol(nip, &cp, 0); + /* check for parse error */ + if ((cp != nip) && (*cp == 0)) + unitofs = i; + } + } else { + /* assume an IDE disk */ + major = WDMAJOR; + } + } + /* default root disk unit number */ + if ((biosdev & 0xf0) == 0xa0) + unit = bdinfo[dev->d_unit].bd_da_unit; + else + unit = biosdev & 0xf; + + /* XXX a better kludge to set the root disk unit number */ + if ((nip = getenv("root_disk_unit")) != NULL) { + i = strtol(nip, &cp, 0); + /* check for parse error */ + if ((cp != nip) && (*cp == 0)) + unit = i; + } + + rootdev = MAKEBOOTDEV(major, dev->d_kind.biosdisk.slice + 1, unit, + dev->d_kind.biosdisk.partition); + DEBUG("dev is 0x%x\n", rootdev); + return(rootdev); +} diff --git a/stand/pc98/libpc98/biosmem.c b/stand/pc98/libpc98/biosmem.c new file mode 100644 index 0000000..c5a9b30 --- /dev/null +++ b/stand/pc98/libpc98/biosmem.c @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Obtain memory configuration information from the BIOS + */ +#include <stand.h> +#include "libi386.h" +#include "btxv86.h" + +vm_offset_t memtop, memtop_copyin, high_heap_base; +uint32_t bios_basemem, bios_extmem, high_heap_size; + +/* + * The minimum amount of memory to reserve in bios_extmem for the heap. + */ +#define HEAP_MIN (64 * 1024 * 1024) + +void +bios_getmem(void) +{ + + bios_basemem = ((*(u_char *)PTOV(0xA1501) & 0x07) + 1) * 128 * 1024; + bios_extmem = *(u_char *)PTOV(0xA1401) * 128 * 1024 + + *(u_int16_t *)PTOV(0xA1594) * 1024 * 1024; + + /* Set memtop to actual top of memory */ + memtop = memtop_copyin = 0x100000 + bios_extmem; + + /* + * If we have extended memory, use the last 3MB of 'extended' memory + * as a high heap candidate. + */ + if (bios_extmem >= HEAP_MIN) { + high_heap_size = HEAP_MIN; + high_heap_base = memtop - HEAP_MIN; + } +} diff --git a/stand/pc98/libpc98/biossmap.c b/stand/pc98/libpc98/biossmap.c new file mode 100644 index 0000000..5a7a89f --- /dev/null +++ b/stand/pc98/libpc98/biossmap.c @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2006 TAKAHASHI Yoshihiro <nyan@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <sys/param.h> +#include "libi386.h" + +void +bios_addsmapdata(struct preloaded_file *kfp) +{ + +} diff --git a/stand/pc98/libpc98/comconsole.c b/stand/pc98/libpc98/comconsole.c new file mode 100644 index 0000000..1bf2d6a --- /dev/null +++ b/stand/pc98/libpc98/comconsole.c @@ -0,0 +1,367 @@ +/*- + * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <bootstrap.h> +#include <machine/cpufunc.h> +#include <dev/ic/ns16550.h> +#include <dev/pci/pcireg.h> +#include "libi386.h" + +#define COMC_FMT 0x3 /* 8N1 */ +#define COMC_TXWAIT 0x40000 /* transmit timeout */ +#define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */ +#define COMC_DIV2BPS(x) (115200 / (x)) /* DLAB divisor to speed */ + +#ifndef COMPORT +#define COMPORT 0x238 +#endif +#ifndef COMSPEED +#define COMSPEED 9600 +#endif + +static void comc_probe(struct console *cp); +static int comc_init(int arg); +static void comc_putchar(int c); +static int comc_getchar(void); +static int comc_getspeed(void); +static int comc_ischar(void); +static int comc_parseint(const char *string); +static uint32_t comc_parse_pcidev(const char *string); +static int comc_pcidev_set(struct env_var *ev, int flags, + const void *value); +static int comc_pcidev_handle(uint32_t locator); +static int comc_port_set(struct env_var *ev, int flags, + const void *value); +static void comc_setup(int speed, int port); +static int comc_speed_set(struct env_var *ev, int flags, + const void *value); + +static int comc_curspeed; +static int comc_port = COMPORT; +static uint32_t comc_locator; + +struct console comconsole = { + "comconsole", + "serial port", + 0, + comc_probe, + comc_init, + comc_putchar, + comc_getchar, + comc_ischar +}; + +static void +comc_probe(struct console *cp) +{ + char intbuf[16]; + char *cons, *env; + int speed, port; + uint32_t locator; + + if (comc_curspeed == 0) { + comc_curspeed = COMSPEED; + /* + * Assume that the speed was set by an earlier boot loader if + * comconsole is already the preferred console. + */ + cons = getenv("console"); + if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) || + getenv("boot_multicons") != NULL) { + comc_curspeed = comc_getspeed(); + } + + env = getenv("comconsole_speed"); + if (env != NULL) { + speed = comc_parseint(env); + if (speed > 0) + comc_curspeed = speed; + } + + sprintf(intbuf, "%d", comc_curspeed); + unsetenv("comconsole_speed"); + env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set, + env_nounset); + + env = getenv("comconsole_port"); + if (env != NULL) { + port = comc_parseint(env); + if (port > 0) + comc_port = port; + } + + sprintf(intbuf, "%d", comc_port); + unsetenv("comconsole_port"); + env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set, + env_nounset); + + env = getenv("comconsole_pcidev"); + if (env != NULL) { + locator = comc_parse_pcidev(env); + if (locator != 0) + comc_pcidev_handle(locator); + } + + unsetenv("comconsole_pcidev"); + env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set, + env_nounset); + } + comc_setup(comc_curspeed, comc_port); +} + +static int +comc_init(int arg) +{ + + comc_setup(comc_curspeed, comc_port); + + if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) == + (C_PRESENTIN | C_PRESENTOUT)) + return (CMD_OK); + return (CMD_ERROR); +} + +static void +comc_putchar(int c) +{ + int wait; + + for (wait = COMC_TXWAIT; wait > 0; wait--) + if (inb(comc_port + com_lsr) & LSR_TXRDY) { + outb(comc_port + com_data, (u_char)c); + break; + } +} + +static int +comc_getchar(void) +{ + return (comc_ischar() ? inb(comc_port + com_data) : -1); +} + +static int +comc_ischar(void) +{ + return (inb(comc_port + com_lsr) & LSR_RXRDY); +} + +static int +comc_speed_set(struct env_var *ev, int flags, const void *value) +{ + int speed; + + if (value == NULL || (speed = comc_parseint(value)) <= 0) { + printf("Invalid speed\n"); + return (CMD_ERROR); + } + + if (comc_curspeed != speed) + comc_setup(speed, comc_port); + + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + + return (CMD_OK); +} + +static int +comc_port_set(struct env_var *ev, int flags, const void *value) +{ + int port; + + if (value == NULL || (port = comc_parseint(value)) <= 0) { + printf("Invalid port\n"); + return (CMD_ERROR); + } + + if (comc_port != port) + comc_setup(comc_curspeed, port); + + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + + return (CMD_OK); +} + +/* + * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10. + * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0] + */ +static uint32_t +comc_parse_pcidev(const char *string) +{ + char *p, *p1; + uint8_t bus, dev, func, bar; + uint32_t locator; + int pres; + + pres = strtol(string, &p, 0); + if (p == string || *p != ':' || pres < 0 ) + return (0); + bus = pres; + p1 = ++p; + + pres = strtol(p1, &p, 0); + if (p == string || *p != ':' || pres < 0 ) + return (0); + dev = pres; + p1 = ++p; + + pres = strtol(p1, &p, 0); + if (p == string || (*p != ':' && *p != '\0') || pres < 0 ) + return (0); + func = pres; + + if (*p == ':') { + p1 = ++p; + pres = strtol(p1, &p, 0); + if (p == string || *p != '\0' || pres <= 0 ) + return (0); + bar = pres; + } else + bar = 0x10; + + locator = (bar << 16) | biospci_locator(bus, dev, func); + return (locator); +} + +static int +comc_pcidev_handle(uint32_t locator) +{ + char intbuf[64]; + uint32_t port; + + if (biospci_read_config(locator & 0xffff, + (locator & 0xff0000) >> 16, 2, &port) == -1) { + printf("Cannot read bar at 0x%x\n", locator); + return (CMD_ERROR); + } + if (!PCI_BAR_IO(port)) { + printf("Memory bar at 0x%x\n", locator); + return (CMD_ERROR); + } + port &= PCIM_BAR_IO_BASE; + + sprintf(intbuf, "%d", port); + unsetenv("comconsole_port"); + env_setenv("comconsole_port", EV_VOLATILE, intbuf, + comc_port_set, env_nounset); + + comc_setup(comc_curspeed, port); + comc_locator = locator; + + return (CMD_OK); +} + +static int +comc_pcidev_set(struct env_var *ev, int flags, const void *value) +{ + uint32_t locator; + int error; + + if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) { + printf("Invalid pcidev\n"); + return (CMD_ERROR); + } + if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && + comc_locator != locator) { + error = comc_pcidev_handle(locator); + if (error != CMD_OK) + return (error); + } + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + return (CMD_OK); +} + +static void +comc_setup(int speed, int port) +{ + static int TRY_COUNT = 1000000; + char intbuf[64]; + int tries; + + unsetenv("hw.uart.console"); + comc_curspeed = speed; + comc_port = port; + if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0) + return; + + outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT); + outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff); + outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8); + outb(comc_port + com_cfcr, COMC_FMT); + outb(comc_port + com_mcr, MCR_RTS | MCR_DTR); + + tries = 0; + do + inb(comc_port + com_data); + while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT); + + if (tries < TRY_COUNT) { + comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT); + sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed); + env_setenv("hw.uart.console", EV_VOLATILE, intbuf, NULL, NULL); + } else + comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); +} + +static int +comc_parseint(const char *speedstr) +{ + char *p; + int speed; + + speed = strtol(speedstr, &p, 0); + if (p == speedstr || *p != '\0' || speed <= 0) + return (-1); + + return (speed); +} + +static int +comc_getspeed(void) +{ + u_int divisor; + u_char dlbh; + u_char dlbl; + u_char cfcr; + + cfcr = inb(comc_port + com_cfcr); + outb(comc_port + com_cfcr, CFCR_DLAB | cfcr); + + dlbl = inb(comc_port + com_dlbl); + dlbh = inb(comc_port + com_dlbh); + + outb(comc_port + com_cfcr, cfcr); + + divisor = dlbh << 8 | dlbl; + + /* XXX there should be more sanity checking. */ + if (divisor == 0) + return (COMSPEED); + return (COMC_DIV2BPS(divisor)); +} diff --git a/stand/pc98/libpc98/libpc98.h b/stand/pc98/libpc98/libpc98.h new file mode 100644 index 0000000..78b07a1 --- /dev/null +++ b/stand/pc98/libpc98/libpc98.h @@ -0,0 +1,29 @@ +/*- + * Copyright (c) 2009 TAKAHASHI Yoshihiro <nyan@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +void set_machine_type(void); diff --git a/stand/pc98/libpc98/pc98_sys.c b/stand/pc98/libpc98/pc98_sys.c new file mode 100644 index 0000000..7f66d02 --- /dev/null +++ b/stand/pc98/libpc98/pc98_sys.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 2009 TAKAHASHI Yoshihiro <nyan@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <btxv86.h> +#include <machine/cpufunc.h> +#define _KERNEL +#include <pc98/pc98/pc98_machdep.h> + +/* + * Set machine type to PC98_SYSTEM_PARAMETER. + */ +void +set_machine_type(void) +{ + int i; + u_long ret, data; + + /* PC98_SYSTEM_PARAMETER (0x501) */ + ret = ((*(u_char *)PTOV(0xA1501)) & 0x08) >> 3; + + /* Wait V-SYNC */ + while (inb(0x60) & 0x20) {} + while (!(inb(0x60) & 0x20)) {} + + /* ANK 'A' font */ + outb(0xa1, 0x00); + outb(0xa3, 0x41); + + /* M_NORMAL, use CG window (all NEC OK) */ + for (i = data = 0; i < 4; i++) + data += *((u_long *)PTOV(0xA4000) + i); /* 0xa4000 */ + if (data == 0x6efc58fc) /* DA data */ + ret |= M_NEC_PC98; + else + ret |= M_EPSON_PC98; + ret |= (inb(0x42) & 0x20) ? M_8M : 0; + + /* PC98_SYSTEM_PARAMETER(0x400) */ + if ((*(u_char *)PTOV(0xA1400)) & 0x80) + ret |= M_NOTE; + if (ret & M_NEC_PC98) { + /* PC98_SYSTEM_PARAMETER(0x458) */ + if ((*(u_char *)PTOV(0xA1458)) & 0x80) + ret |= M_H98; + else + ret |= M_NOT_H98; + } else + ret |= M_NOT_H98; + + (*(u_long *)PTOV(0xA1620)) = ret; +} diff --git a/stand/pc98/libpc98/time.c b/stand/pc98/libpc98/time.c new file mode 100644 index 0000000..5d832bb --- /dev/null +++ b/stand/pc98/libpc98/time.c @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <btxv86.h> +#include <machine/cpufunc.h> +#include "bootstrap.h" +#include "libi386.h" + +static int bios_seconds(void); + +/* + * Return the BIOS time-of-day value. + * + * XXX uses undocumented BCD support from libstand. + */ +static int +bios_seconds(void) +{ + int hr, minute, sec; + unsigned char bios_time[6]; + + v86.ctl = 0; + v86.addr = 0x1c; /* int 0x1c, function 0 */ + v86.eax = 0x0000; + v86.es = VTOPSEG(bios_time); + v86.ebx = VTOPOFF(bios_time); + v86int(); + + hr = bcd2bin(bios_time[3]); + minute = bcd2bin(bios_time[4]); + sec = bcd2bin(bios_time[5]); + + return (hr * 3600 + minute * 60 + sec); +} + +/* + * Return the time in seconds since the beginning of the day. + */ +time_t +time(time_t *t) +{ + static time_t lasttime; + time_t now; + + now = bios_seconds(); + + if (now < lasttime) + now += 24 * 3600; + lasttime = now; + + if (t != NULL) + *t = now; + return(now); +} + +/* + * Use the BIOS Wait function to pause for (period) microseconds. + * + * Resolution of this function is variable, but typically around + * 1ms. + */ +void +delay(int period) +{ + int i; + + period = (period + 500) / 1000; + for( ; period != 0 ; period--) + for(i=800;i != 0; i--) + outb(0x5f,0); /* wait 600ns */ +} diff --git a/stand/pc98/libpc98/vidconsole.c b/stand/pc98/libpc98/vidconsole.c new file mode 100644 index 0000000..7cf81e8 --- /dev/null +++ b/stand/pc98/libpc98/vidconsole.c @@ -0,0 +1,596 @@ +/*- + * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) + * Copyright (c) 1997 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <bootstrap.h> +#include <btxv86.h> +#include <machine/cpufunc.h> +#include "libi386.h" + +#if KEYBOARD_PROBE +#include <machine/cpufunc.h> + +static int probe_keyboard(void); +#endif +static void vidc_probe(struct console *cp); +static int vidc_init(int arg); +static void vidc_putchar(int c); +static int vidc_getchar(void); +static int vidc_ischar(void); + +static int vidc_started; + +#ifdef TERM_EMU +#define MAXARGS 8 +#define DEFAULT_FGCOLOR 7 +#define DEFAULT_BGCOLOR 0 + +void end_term(void); +void bail_out(int c); +void vidc_term_emu(int c); +void get_pos(void); +void curs_move(int x, int y); +void write_char(int c, int fg, int bg); +void scroll_up(int rows, int fg, int bg); +void CD(void); +void CM(void); +void HO(void); + +static int args[MAXARGS], argc; +static int fg_c, bg_c, curx, cury; +static int esc; +#endif + +static unsigned short *crtat, *Crtat; +static int row = 25, col = 80; +#ifdef TERM_EMU +static u_int8_t ibmpc_to_pc98[256] = { + 0x01, 0x21, 0x81, 0xa1, 0x41, 0x61, 0xc1, 0xe1, + 0x09, 0x29, 0x89, 0xa9, 0x49, 0x69, 0xc9, 0xe9, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, + 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, + 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, + 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, + + 0x03, 0x23, 0x83, 0xa3, 0x43, 0x63, 0xc3, 0xe3, + 0x0b, 0x2b, 0x8b, 0xab, 0x4b, 0x6b, 0xcb, 0xeb, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, + 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, + 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, + 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, + 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, +}; +#define at2pc98(fg_at, bg_at) ibmpc_to_pc98[((bg_at) << 4) | (fg_at)] +#endif /* TERM_EMU */ + +struct console vidconsole = { + "vidconsole", + "internal video/keyboard", + 0, + vidc_probe, + vidc_init, + vidc_putchar, + vidc_getchar, + vidc_ischar +}; + +static void +vidc_probe(struct console *cp) +{ + + /* look for a keyboard */ +#if KEYBOARD_PROBE + if (probe_keyboard()) +#endif + { + + cp->c_flags |= C_PRESENTIN; + } + + /* XXX for now, always assume we can do BIOS screen output */ + cp->c_flags |= C_PRESENTOUT; +} + +static int +vidc_init(int arg) +{ + int i, hw_cursor; + + if (vidc_started && arg == 0) + return (0); + vidc_started = 1; + Crtat = (unsigned short *)PTOV(0xA0000); + while ((inb(0x60) & 0x04) == 0) + ; + outb(0x62, 0xe0); + while ((inb(0x60) & 0x01) == 0) + ; + hw_cursor = inb(0x62); + hw_cursor |= (inb(0x62) << 8); + inb(0x62); + inb(0x62); + inb(0x62); + crtat = Crtat + hw_cursor; +#ifdef TERM_EMU + /* Init terminal emulator */ + end_term(); + get_pos(); + curs_move(curx, cury); + fg_c = DEFAULT_FGCOLOR; + bg_c = DEFAULT_BGCOLOR; +#endif + for (i = 0; i < 10 && vidc_ischar(); i++) + (void)vidc_getchar(); + return (0); /* XXX reinit? */ +} + +static void +beep(void) +{ + + outb(0x37, 6); + delay(40000); + outb(0x37, 7); +} + +#if 0 +static void +vidc_biosputchar(int c) +{ + unsigned short *cp; + int i, pos; + +#ifdef TERM_EMU + *crtat = (c == 0x5c ? 0xfc : c); + *(crtat + 0x1000) = at2pc98(fg, bg); +#else + switch(c) { + case '\b': + crtat--; + break; + case '\r': + crtat -= (crtat - Crtat) % col; + break; + case '\n': + crtat += col; + break; + default: + *crtat = (c == 0x5c ? 0xfc : c); + *(crtat++ + 0x1000) = 0xe1; + break; + } + + if (crtat >= Crtat + col * row) { + cp = Crtat; + for (i = 1; i < row; i++) { + bcopy((void *)(cp + col), (void *)cp, col * 2); + cp += col; + } + for (i = 0; i < col; i++) { + *cp++ = ' '; + } + crtat -= col; + } + pos = crtat - Crtat; + while ((inb(0x60) & 0x04) == 0) {} + outb(0x62, 0x49); + outb(0x60, pos & 0xff); + outb(0x60, pos >> 8); +#endif +} +#endif + +static void +vidc_rawputchar(int c) +{ + int i; + + if (c == '\t') + /* lame tab expansion */ + for (i = 0; i < 8; i++) + vidc_rawputchar(' '); + else { + /* Emulate AH=0eh (teletype output) */ + switch(c) { + case '\a': + beep(); + return; + case '\r': + curx = 0; + curs_move(curx, cury); + return; + case '\n': + cury++; + if (cury > 24) { + scroll_up(1, fg_c, bg_c); + cury--; + } else { + curs_move(curx, cury); + } + return; + case '\b': + if (curx > 0) { + curx--; + curs_move(curx, cury); + /* write_char(' ', fg_c, bg_c); XXX destructive(!) */ + return; + } + return; + default: + write_char(c, fg_c, bg_c); + curx++; + if (curx > 79) { + curx = 0; + cury++; + } + if (cury > 24) { + curx = 0; + scroll_up(1, fg_c, bg_c); + cury--; + } + } + curs_move(curx, cury); + } +} + +#ifdef TERM_EMU + +/* Get cursor position on the screen. Result is in edx. Sets + * curx and cury appropriately. + */ +void +get_pos(void) +{ + int pos = crtat - Crtat; + + curx = pos % col; + cury = pos / col; +} + +/* Move cursor to x rows and y cols (0-based). */ +void +curs_move(int x, int y) +{ + int pos; + + pos = x + y * col; + crtat = Crtat + pos; + pos = crtat - Crtat; + while((inb(0x60) & 0x04) == 0) {} + outb(0x62, 0x49); + outb(0x60, pos & 0xff); + outb(0x60, pos >> 8); + curx = x; + cury = y; +#define isvisible(c) (((c) >= 32) && ((c) < 255)) + if (!isvisible(*crtat & 0x00ff)) { + write_char(' ', fg_c, bg_c); + } +} + +/* Scroll up the whole window by a number of rows. If rows==0, + * clear the window. fg and bg are attributes for the new lines + * inserted in the window. + */ +void +scroll_up(int rows, int fgcol, int bgcol) +{ + unsigned short *cp; + int i; + + if (rows == 0) + rows = 25; + cp = Crtat; + for (i = rows; i < row; i++) { + bcopy((void *)(cp + col), (void *)cp, col * 2); + cp += col; + } + for (i = 0; i < col; i++) { + *(cp + 0x1000) = at2pc98(fgcol, bgcol); + *cp++ = ' '; + } +} + +/* Write character and attribute at cursor position. */ +void +write_char(int c, int fgcol, int bgcol) +{ + + *crtat = (c == 0x5c ? 0xfc : (c & 0xff)); + *(crtat + 0x1000) = at2pc98(fgcol, bgcol); +} + +/**************************************************************/ +/* + * Screen manipulation functions. They use accumulated data in + * args[] and argc variables. + * + */ + +/* Clear display from current position to end of screen */ +void +CD(void) +{ + int pos; + + get_pos(); + for (pos = 0; crtat + pos <= Crtat + col * row; pos++) { + *(crtat + pos) = ' '; + *(crtat + pos + 0x1000) = at2pc98(fg_c, bg_c); + } + end_term(); +} + +/* Absolute cursor move to args[0] rows and args[1] columns + * (the coordinates are 1-based). + */ +void +CM(void) +{ + + if (args[0] > 0) + args[0]--; + if (args[1] > 0) + args[1]--; + curs_move(args[1], args[0]); + end_term(); +} + +/* Home cursor (left top corner) */ +void +HO(void) +{ + + argc = 1; + args[0] = args[1] = 1; + CM(); +} + +/* Clear internal state of the terminal emulation code */ +void +end_term(void) +{ + + esc = 0; + argc = -1; +} + +/* Gracefully exit ESC-sequence processing in case of misunderstanding */ +void +bail_out(int c) +{ + char buf[16], *ch; + int i; + + if (esc) { + vidc_rawputchar('\033'); + if (esc != '\033') + vidc_rawputchar(esc); + for (i = 0; i <= argc; ++i) { + sprintf(buf, "%d", args[i]); + ch = buf; + while (*ch) + vidc_rawputchar(*ch++); + } + } + vidc_rawputchar(c); + end_term(); +} + +static void +get_arg(int c) +{ + + if (argc < 0) + argc = 0; + args[argc] *= 10; + args[argc] += c - '0'; +} + +/* Emulate basic capabilities of cons25 terminal */ +void +vidc_term_emu(int c) +{ + static int ansi_col[] = { + 0, 4, 2, 6, 1, 5, 3, 7, + }; + int t; + int i; + + switch (esc) { + case 0: + switch (c) { + case '\033': + esc = c; + break; + default: + vidc_rawputchar(c); + break; + } + break; + + case '\033': + switch (c) { + case '[': + esc = c; + args[0] = 0; + argc = -1; + break; + default: + bail_out(c); + break; + } + break; + + case '[': + switch (c) { + case ';': + if (argc < 0) /* XXX */ + argc = 0; + else if (argc + 1 >= MAXARGS) + bail_out(c); + else + args[++argc] = 0; + break; + case 'H': + if (argc < 0) + HO(); + else if (argc == 1) + CM(); + else + bail_out(c); + break; + case 'J': + if (argc < 0) + CD(); + else + bail_out(c); + break; + case 'm': + if (argc < 0) { + fg_c = DEFAULT_FGCOLOR; + bg_c = DEFAULT_BGCOLOR; + } + for (i = 0; i <= argc; ++i) { + switch (args[i]) { + case 0: /* back to normal */ + fg_c = DEFAULT_FGCOLOR; + bg_c = DEFAULT_BGCOLOR; + break; + case 1: /* bold */ + fg_c |= 0x8; + break; + case 4: /* underline */ + case 5: /* blink */ + bg_c |= 0x8; + break; + case 7: /* reverse */ + t = fg_c; + fg_c = bg_c; + bg_c = t; + break; + case 30: case 31: case 32: case 33: + case 34: case 35: case 36: case 37: + fg_c = ansi_col[args[i] - 30]; + break; + case 39: /* normal */ + fg_c = DEFAULT_FGCOLOR; + break; + case 40: case 41: case 42: case 43: + case 44: case 45: case 46: case 47: + bg_c = ansi_col[args[i] - 40]; + break; + case 49: /* normal */ + bg_c = DEFAULT_BGCOLOR; + break; + } + } + end_term(); + break; + default: + if (isdigit(c)) + get_arg(c); + else + bail_out(c); + break; + } + break; + + default: + bail_out(c); + break; + } +} +#endif + +static void +vidc_putchar(int c) +{ +#ifdef TERM_EMU + vidc_term_emu(c); +#else + vidc_rawputchar(c); +#endif +} + +static int +vidc_getchar(void) +{ + + if (vidc_ischar()) { + v86.ctl = 0; + v86.addr = 0x18; + v86.eax = 0x0; + v86int(); + return (v86.eax & 0xff); + } else { + return (-1); + } +} + +static int +vidc_ischar(void) +{ + + v86.ctl = 0; + v86.addr = 0x18; + v86.eax = 0x100; + v86int(); + return ((v86.ebx >> 8) & 0x1); +} + +#if KEYBOARD_PROBE +static int +probe_keyboard(void) +{ + return (*(u_char *)PTOV(0xA1481) & 0x48); +} +#endif /* KEYBOARD_PROBE */ |