diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/i386/stand/as.c | 269 | ||||
-rw-r--r-- | sys/arch/i386/stand/asbootblk.c | 236 |
2 files changed, 505 insertions, 0 deletions
diff --git a/sys/arch/i386/stand/as.c b/sys/arch/i386/stand/as.c new file mode 100644 index 00000000000..796b2650378 --- /dev/null +++ b/sys/arch/i386/stand/as.c @@ -0,0 +1,269 @@ +/* $NetBSD: as.c,v 1.4 1994/10/27 04:21:45 cgd Exp $ */ + +/* + * sys/i386/stand/as.c + * + * Standalone driver for Adaptech 1542 SCSI + * + * Pace Willisson pace@blitz.com April 8, 1992 + */ + +#include <sys/param.h> +#include <sys/disklabel.h> +#include <dev/isa/ahareg.h> +#include "saio.h" + +#ifdef ASDEBUG +#define ASPRINT(x) { printf x; DELAY (10000); } +#else +#define ASPRINT(x) +#endif + +#define NRETRIES 3 + +int as_port = 0x330; + +struct mailbox_entry mailbox[2]; + +int +asopen(io) +struct iob *io; +{ + struct disklabel *dd; + char cdb[6]; + char data[12]; + int val; + int oval; + int i; + struct iob aio; + + if (io->i_unit < 0 || io->i_unit > 8 + || io->i_part < 0 || io->i_part > 8 + || io->i_ctlr < 0 || io->i_ctlr > 0) + return (-1); + + /* dma setup: see page 5-31 in the Adaptech manual */ + outb (0xd6, 0xc1); + outb (0xd4, 0x01); + + ASPRINT (("resetting adaptech card... ")); + + outb (as_port + AS_CONTROL, AS_CONTROL_SRST); + + /* delay a little */ + for (i = 0; i < 100; i++) + inb (0x84); + + while (inb (as_port + AS_STATUS) != (AS_STATUS_INIT | AS_STATUS_IDLE)) + ; + + ASPRINT (("reset ok ")); + + as_put_byte (AS_CMD_MAILBOX_INIT); + as_put_byte (1); /* one mailbox out, one in */ + as_put_byte ((int)mailbox >> 16); + as_put_byte ((int)mailbox >> 8); + as_put_byte ((int)mailbox); + + while (inb (as_port + AS_STATUS) & AS_STATUS_INIT) + ; + + ASPRINT (("mailbox init ok ")); + + /* do mode select to set the logical block size */ + bzero (cdb, 6); + cdb[0] = 0x15; /* MODE SELECT */ + cdb[4] = 12; /* parameter list length */ + + bzero (data, 12); + data[3] = 8; /* block descriptor length */ + data[9] = DEV_BSIZE >> 16; + data[10] = DEV_BSIZE >> 8; + data[11] = DEV_BSIZE; + + if (ascmd (io->i_unit, 0, cdb, 6, data, 12, 1) < 0) { + printf ("as%d: error setting logical block size\n", + io->i_unit); + return (-1); + } + + aio = *io; + aio.i_bn = LABELSECTOR; + aio.i_cc = DEV_BSIZE; + /*io->i_ma = buf;*/ + aio.i_boff = 0; + +#ifdef was + if (asstrategy (&aio, F_READ) == DEV_BSIZE) { + dd = (struct disklabel *)aio.i_ma; + io->i_boff = dd->d_partitions[io->i_part].p_offset; + ASPRINT (("partition offset %d ", io->i_boff)); + } +#else +{ +extern struct disklabel disklabel; + io->i_boff = disklabel.d_partitions[io->i_part].p_offset; + ASPRINT (("partition offset %d ", io->i_boff)); +} +#endif + + ASPRINT (("asopen ok ")); + return(0); +} + +/* func is F_WRITE or F_READ + * io->i_unit, io->i_part, io->i_bn is starting block + * io->i_cc is byte count + * io->i_ma is memory address + * io->i_boff is block offset for this partition (set up in asopen) + */ +int +asstrategy(io, func) +struct iob *io; +{ + char cdb[6]; + int blkno; + int retry; + + ASPRINT (("asstrategy(target=%d, block=%d+%d, count=%d) ", + io->i_unit, io->i_bn, io->i_boff, io->i_cc)); + + if (func == F_WRITE) { + printf ("as%d: write not supported\n", io->i_unit); + return (0); + } + + if (io->i_cc == 0) + return (0); + + if (io->i_cc % DEV_BSIZE != 0) { + printf ("as%d: transfer size not multiple of %d\n", + io->i_unit, DEV_BSIZE); + return (0); + } + + /* retry in case we get a unit-attention error, which just + * means the drive has been reset since the last command + */ + for (retry = 0; retry < NRETRIES; retry++) { + blkno = io->i_bn + io->i_boff; + + cdb[0] = 8; /* scsi read opcode */ + cdb[1] = (blkno >> 16) & 0x1f; + cdb[2] = blkno >> 8; + cdb[3] = blkno; + cdb[4] = io->i_cc / DEV_BSIZE; + cdb[5] = 0; /* control byte (used in linking) */ + + if (ascmd (io->i_unit, 1, cdb, 6, io->i_ma, io->i_cc, + retry == NRETRIES - 1) >= 0) { + ASPRINT (("asstrategy ok ")); + return (io->i_cc); + } + } + + ASPRINT (("asstrategy failed ")); + return (0); +} + +int +ascmd (target, readflag, cdb, cdblen, data, datalen, printerr) +int target; +int readflag; +char *cdb; +int cdblen; +char *data; +int datalen; +int printerr; +{ + struct ccb ccb; + int physaddr; + unsigned char *sp; + int i; + + if (mailbox[0].cmd != 0) + /* this can't happen, unless the card flakes */ + _stop ("asstart: mailbox not available\n"); + + bzero (&ccb, sizeof ccb); + + ccb.ccb_opcode = 0; + ccb.ccb_addr_and_control = target << 5; + if (datalen != 0) + ccb.ccb_addr_and_control |= readflag ? 8 : 0x10; + else + ccb.ccb_addr_and_control |= 0x18; + + ccb.ccb_data_len_msb = datalen >> 16; + ccb.ccb_data_len_mid = datalen >> 8; + ccb.ccb_data_len_lsb = datalen; + + ccb.ccb_requst_sense_allocation_len = MAXSENSE; + + physaddr = (int)data; + ccb.ccb_data_ptr_msb = physaddr >> 16; + ccb.ccb_data_ptr_mid = physaddr >> 8; + ccb.ccb_data_ptr_lsb = physaddr; + + ccb.ccb_scsi_command_len = cdblen; + bcopy (cdb, ccb.ccb_cdb, cdblen); + +#ifdef ASDEBUG + printf ("ccb: "); + for (i = 0; i < 48; i++) + printf ("%x ", ((unsigned char *)&ccb)[i]); + printf ("\n"); + /*getchar ();*/ +#endif + + physaddr = (int)&ccb; + mailbox[0].msb = physaddr >> 16; + mailbox[0].mid = physaddr >> 8; + mailbox[0].lsb = physaddr; + mailbox[0].cmd = 1; + + /* tell controller to look in its mailbox */ + outb (as_port + AS_CONTROL, AS_CONTROL_IRST); + as_put_byte (AS_CMD_START_SCSI_COMMAND); + + /* wait for status */ + ASPRINT (("waiting for status...")); + while (mailbox[1].cmd == 0) + ; + mailbox[1].cmd = 0; + + + if (ccb.ccb_host_status != 0 || ccb.ccb_target_status != 0) { +#ifdef ASDEBUG + printerr = 1; +#endif + if (printerr) { + printf ("as%d error: hst=%x tst=%x sense=", + target, + ccb.ccb_host_status, + ccb.ccb_target_status); + sp = ccb_sense (&ccb); + for (i = 0; i < 8; i++) + printf ("%x ", sp[i]); + printf ("\n"); +#ifdef ASDEBUG + /*getchar ();*/ +#endif + } + return (-1); + } + + ASPRINT (("ascmd ok ")); + + return (0); +} + +int +as_put_byte (val) +int val; +{ + while (inb (as_port + AS_STATUS) & AS_STATUS_CDF) + ; + outb (as_port + AS_DATA_OUT, val); +} + diff --git a/sys/arch/i386/stand/asbootblk.c b/sys/arch/i386/stand/asbootblk.c new file mode 100644 index 00000000000..f3ea79c233c --- /dev/null +++ b/sys/arch/i386/stand/asbootblk.c @@ -0,0 +1,236 @@ +/* $NetBSD: asbootblk.c,v 1.4 1994/10/27 04:21:46 cgd Exp $ */ + +/* + * sys/i386/stand/asbootblk.c + * + * Boot block for Adaptech 1542 SCSI + * + * April 10, 1992 + * Pace Willisson + * pace@blitz.com + * + * Placed in the public domain with NO WARRANTIES, not even the + * implied warranties for MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. + * + * To compile: + * + * cc -O -c -DRELOC=0x70000 asbootblk.c + * ld -N -T 7c00 asbootblk.o + * + * This should result in a file with 512 bytes of text and no initialized + * data. Strip the 32 bit header and place in block 0. + * + * When run, this program copies at least the first 8 blocks of SCSI + * target 0 to the address specified by RELOC, then jumps to the + * address RELOC+1024 (skipping the boot block and disk label). Usually, + * disks have 512 bytes per block, but I don't think they ever have + * less, and it wont hurt if they are bigger, as long as RELOC + 8*SIZE + * is less than 0xa0000. + * + * This bootblock does not support fdisk partitions, and can only be used + * as the master boot block. + */ + +#include <sys/param.h> +#include <sys/disklabel.h> +#include <dev/isa/ahareg.h> + +/* RELOC should be defined with a -D flag to cc */ + +#define SECOND_LEVEL_BOOT_START (RELOC + 0x400) +#define READ_SIZE 8192 + +#define as_port 0x330 +#define target 0 + + +#define NBLOCKS (READ_SIZE / 512) /* how many logical blocks to read */ + + +/* These are the parameters to pass to the second level boot */ +#define dev 4 /* major device number of as driver in + i386/stand/conf.c and i386/i386/conf.c */ +#define unit 0 /* partition number of root file system */ +#define off 0 /* block offset of root file system */ + +/* inline i/o borrowed from Roell X server */ +static __inline__ void +outb(port, val) +short port; +char val; +{ + __asm__ volatile("outb %%al, %1" : :"a" (val), "d" (port)); +} + +static __inline__ unsigned int +inb(port) +short port; +{ + unsigned int ret; + __asm__ volatile("xorl %%eax, %%eax; inb %1, %%al" + : "=a" (ret) : "d" (port)); + return ret; +} + +/* this code is linked at 0x7c00 and is loaded there by the BIOS */ + +asm (" + /* we're running in 16 real mode, so normal assembly doesn't work */ +bootbase: + /* interrupts off */ + cli + + /* load gdt */ + .byte 0x2e,0x0f,0x01,0x16 /* lgdt %cs:$imm */ + .word _gdtarg + 2 + + /* turn on protected mode */ + smsw %ax + orb $1,%al + lmsw %ax + + /* flush prefetch queue and reload %cs */ + .byte 0xea /* ljmp $8, flush */ + .word flush + .word 8 + +flush: + /* now running in 32 bit mode */ + movl $0x10,%eax + movl %ax,%ds + movl %ax,%es + movl %ax,%ss + movl $0x7c00,%esp + call _main +"); /* end of asm */ + +const char gdt[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0xff, 0xff, 0, 0, 0, 0x9f, 0xcf, 0, /* code segment */ + 0xff, 0xff, 0, 0, 0, 0x93, 0xcf, 0, /* data segment */ +}; + +const struct { + short filler; + short size; + const char *gdt; +} gdtarg = { 0, sizeof gdt - 1, gdt }; + +#define CRTBASE ((char *)0xb8000) +#define CHECKPOINT(x) (CRTBASE[0] = x) + +volatile struct mailbox_entry mailbox[2]; +const char ccb[] = { + 0, /* opcode: normal read/write */ + (target << 5) | 8, /* target num and read flag */ + 10, /* scsi cmd len */ + 1, /* no automatic request for sense */ + READ_SIZE >> 16, /* data length */ + READ_SIZE >> 8, + READ_SIZE, + RELOC >> 16, /* data pointer */ + RELOC >> 8, + RELOC, + 0, 0, 0, /* link pointer */ + 0, /* link id */ + 0, /* host status */ + 0, /* target status */ + 0, 0, /* reserved */ + + /* scsi cdb */ + 0x28, /* read opcode */ + 0, /* logical unit number */ + 0, 0, 0, 0, /* logical block address */ + 0, /* reserved */ + 0, NBLOCKS, /* transfer length */ + 0, /* link control */ +}; + +int (*f)(); + +main () +{ + int i; + extern char edata[], end[]; + char volatile * volatile p, *q; + int physaddr; + + CHECKPOINT ('a'); + + /* clear bss */ + for (p = edata; p < end; p++) + *p = 0; + + f = (int (*)())SECOND_LEVEL_BOOT_START; + + /* dma setup: see page 5-31 in the Adaptech manual */ + /* this knows we are using drq 5 */ + outb (0xd6, 0xc1); + outb (0xd4, 0x01); + + outb (as_port + AS_CONTROL, AS_CONTROL_SRST); + + /* delay a little */ + inb (0x84); + + while (inb (as_port + AS_STATUS) != (AS_STATUS_INIT | AS_STATUS_IDLE)) + ; + + CHECKPOINT ('b'); + + as_put_byte (AS_CMD_MAILBOX_INIT); + as_put_byte (1); /* one mailbox out, one in */ + as_put_byte ((int)mailbox >> 16); + as_put_byte ((int)mailbox >> 8); + as_put_byte ((int)mailbox); + + while (inb (as_port + AS_STATUS) & AS_STATUS_INIT) + ; + + CHECKPOINT ('c'); + + mailbox[0].msb = (int)ccb >> 16; + mailbox[0].mid = (int)ccb >> 8; + mailbox[0].lsb = (int)ccb; + mailbox[0].cmd = 1; + + as_put_byte (AS_CMD_START_SCSI_COMMAND); + + /* wait for done */ + while (mailbox[1].cmd == 0) + ; + + CHECKPOINT ('d'); + + if (mailbox[1].cmd != 1) { + /* some error */ + CHECKPOINT ('X'); + while (1); + } + + CHECKPOINT ('e'); + + /* the optimazation that gcc uses when it knows we are jumpping + * to a constant address is broken, so we have to use a variable + * here + */ + (*f)(dev, unit, off); +} + +int +as_put_byte (val) +int val; +{ + while (inb (as_port + AS_STATUS) & AS_STATUS_CDF) + ; + outb (as_port + AS_DATA_OUT, val); +} + +asm (" +ebootblkcode: + . = 510 + .byte 0x55 + .byte 0xaa +ebootblk: /* MUST BE EXACTLY 0x200 BIG FOR SURE */ +"); |