/*
 * $Id:$
 *
 * 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 "disklabel.h"
#include "asreg.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) */
#define CHECKPOINT2(x) (CRTBASE[2] = x)

volatile struct mailbox_entry mailbox[2];
const char ccb[] = {
	3, /* 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)();
asm(" .globl ___main ; ___main: ret ");

main ()
{
	int i;
	extern char edata[], end[];
	extern char base();
	char volatile * volatile p, *q;

	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 Adaptec 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 */
	/*for (i=100000; i > 0 ; i--)
		(void)inb (0x84);*/
	
	/*if (inb (as_port + AS_INTR) & AS_INTR_HACC)
		outb (as_port + AS_CONTROL, AS_CONTROL_IRST); */

	while (inb (as_port + AS_STATUS) != (AS_STATUS_INIT | AS_STATUS_IDLE))
		;

	/*if (inb (as_port + AS_INTR))
		outb (as_port + AS_CONTROL, AS_CONTROL_IRST); */
	/* if (inb (as_port + AS_INTR) & AS_INTR_HACC)
		outb (as_port + AS_CONTROL, AS_CONTROL_IRST); */

	CHECKPOINT ('A');
	/*while (inb (as_port + AS_STATUS) & AS_STATUS_DF) {
		for (i=10000; i > 0 ; i--)
			(void)inb (0x84);
		(void) inb (as_port + AS_INTR);
	}*/

	/*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);

	/*if (inb (as_port + AS_INTR) & AS_INTR_HACC)
		outb (as_port + AS_CONTROL, AS_CONTROL_IRST);*/

	/* CHECKPOINT ('B'); */
	/* while ((inb (as_port + AS_INTR) & AS_INTR_HACC) == 0)
		;

	outb (as_port + AS_CONTROL, AS_CONTROL_IRST); */

	CHECKPOINT ('C');
	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;
	p = (char *)base;
	q = (char *)ccb;
	q[20] = p[3];
	q[21] = p[2];
	q[22] = p[1];
	q[23] = p[0];

	as_put_byte (AS_CMD_START_SCSI_COMMAND);

	/* wait for done */
	while (mailbox[1].cmd == 0)
		;

#ifdef NOPE
	CHECKPOINT ('d');

	if (mailbox[1].cmd != 1) {
		/* some error */
		CHECKPOINT2 ('@' + mailbox[1].cmd);
	for (i=100000; i > 0 ; i--)
		(void)inb (0x84);
	}

	CHECKPOINT ('e');
#endif

	/* 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, *(long *)p);
}

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:
	. = 506
	.globl _base
_base:	.long 0x0000
	.byte 0x55
	.byte 0xaa
ebootblk: 			/* MUST BE EXACTLY 0x200 BIG FOR SURE */
");
