[comp.os.minix] IBM_PC gn_wini.c

hall@nosc.NOSC.MIL (Robert R. Hall) (12/10/88)

As written the BIOS_WINI.C did not work with my disk drive
which is a Seagate St238 RLL disk.  In paticular is has 26
sector per track instead of 16.  Since BIOS call are now
possible it occured to me that the disk parameters should
also be obtained from the BIOS.  Here is BIOS_WINI.C modified
to get disk parameters using INT13 service call $08.
It corrected my problem and run fine on my XT clone with it's
RLL disk (the xt_wini.c would not run with this disk).
I also tried it on a Zenith 248 AT but there the w_do_rdwt
call took an excessively a long time.


-------------------    gn_wini.c    -------------------
/* This file contains a hard disk driver that uses the ROM BIOS.  It makes
 * a call and just waits for the transfer to happen.  It is not interrupt
 * driven and thus will have poor performance.  The advantage is that it should
 * work on virtually any PC, XT, 386, PS/2 or clone.  The generic boot
 * diskette uses this driver.  It is suggested that all MINIX users try the
 * other drivers, and use this one only as a last resort, if all else fails.
 *
 * The driver supports two operations: read a block and
 * write a block.  It accepts two messages, one for reading and one for
 * writing, both using message format m2 and with the same parameters:
 *
 *	m_type	  DEVICE   PROC_NR	COUNT	 POSITION  ADRRESS
 * ----------------------------------------------------------------
 * |  DISK_READ | device  | proc nr |  bytes  |	 offset | buf ptr |
 * |------------+---------+---------+---------+---------+---------|
 * | DISK_WRITE | device  | proc nr |  bytes  |	 offset | buf ptr |
 * ----------------------------------------------------------------
 *
 * The file contains one entry point:
 *
 *	 winchester_task:	main entry when system is brought up
 *
 */

#include "minix/const.h"
#include "minix/type.h"
#include "minix/callnr.h"
#include "minix/com.h"
#include "minix/error.h"
#include "const.h"
#include "type.h"
#include "glo.h"
#include "proc.h"

/* Error codes */
#define ERR		  -1	/* general error */

/* Parameters for the disk drive. */
#define MAX_DRIVES         2	/* this driver supports 2 drives (hd0 - hd9)*/
#define DEV_PER_DRIVE	   5	/* hd0 + hd1 + hd2 + hd3 + hd4 = 5 */
#define NR_DEVICES      (MAX_DRIVES * DEV_PER_DRIVE)
#define SECTOR_SIZE	 512	/* physical sector size in bytes */
#define BASE            1536	/* base address of kernel */

/* BIOS parameters */
#define BIOS_ASK        0x08	/* opcode for asking BIOS for parameters */
#define BIOS_RESET      0x00	/* opcode for resetting disk BIOS */
#define BIOS_READ       0x02	/* opcode for BIOS read */
#define BIOS_WRITE      0x03	/* opcode for BIOS write */
#define DRIVE           0x80	/* BIOS code for drive 0 */
#define PART_TABLE     0x1C6	/* IBM partition table starts here in sect 0 */

PRIVATE unsigned char buf[BLOCK_SIZE]; /* Buffer used by the startup routine */
PRIVATE message w_mess;		/* message buffer for in and out */

PRIVATE struct wini {		/* main drive struct, one entry per drive */
  int wn_opcode;		/* DISK_READ or DISK_WRITE */
  int wn_procnr;		/* which proc wanted this operation? */
  int wn_cylinder;		/* cylinder number addressed */
  int wn_sector;		/* sector addressed */
  int wn_head;			/* head number addressed */
  int wn_heads;			/* maximum number of heads */
  int wn_maxsec;		/* maximum number of sectors per track */
  long wn_low;			/* lowest cylinder of partition */
  long wn_size;			/* size of partition in sectors */
  int wn_count;			/* byte count */
  int wn_drive;			/* 0x80 or 0x81 */
  vir_bytes wn_address;		/* user virtual address */
} wini[NR_DEVICES];

PRIVATE struct param {
	int nr_cyl;		/* Number of cylinders */
	int nr_heads;		/* Number of heads */
	int nr_drives;		/* Number of drives on this controler */
	int nr_sectors;		/* Number of sectors per track */
} param0, param1;

int nr_drives;
extern int vec_table[];

/*=========================================================================*
 *			winchester_task					   * 
 *=========================================================================*/
PUBLIC winchester_task()
{
/* Main program of the winchester disk driver task. */

  int r, caller, proc_nr, drive;

  /* First initialize the controller */
  replace();
  init_params();

  /* Here is the main loop of the disk task.  It waits for a message, carries
   * it out, and sends a reply.
   */

  while (TRUE) {
	/* First wait for a request to read or write a disk block. */
	receive(ANY, &w_mess);	/* get a request to do some work */
	if (w_mess.m_source < 0) {
		printf("winchester task got message from %d\n",w_mess.m_source);
		continue;
	}

	caller = w_mess.m_source;
	proc_nr = w_mess.PROC_NR;
	drive = w_mess.DEVICE/5;

	/* Now carry out the work. */
	switch(w_mess.m_type) {
		case DISK_READ:
		case DISK_WRITE:	r = w_do_rdwt(&w_mess);	break;
		default:		r = EINVAL;		break;
	}

	/* Finally, prepare and send the reply message. */
	w_mess.m_type = TASK_REPLY;	
	w_mess.REP_PROC_NR = proc_nr;

	w_mess.REP_STATUS = r;	/* # of bytes transferred or error code */
	send(caller, &w_mess);	/* send reply to caller */
  }
}


/*==========================================================================*
 *				w_do_rdwt				    * 
 *==========================================================================*/
PRIVATE int w_do_rdwt(m_ptr)
message *m_ptr;			/* pointer to read or write w_message */
{
/* Carry out a read or write request from the disk. */
  register struct wini *wn;
  struct proc *rp;
  vir_bytes vir, ct;
  unsigned locyl, hicyl, c1, c2, c3;
  int r, device, errors = 0;
  long sector;
  phys_bytes user_phys;
  extern phys_bytes umap();

  /* Decode the w_message parameters. */
  device = m_ptr->DEVICE;	/* minor device #.  1-4 are partitions */
  if (device < 0 || device >= NR_DEVICES) return(EIO);
  if (m_ptr->COUNT != BLOCK_SIZE) return(EINVAL);
  wn = &wini[device];		/* 'wn' points to entry for this drive */

  /* Set opcode to BIOS_READ or BIOS_WRITE. Check for bad starting addr. */
  wn->wn_opcode = (m_ptr->m_type == DISK_WRITE ? BIOS_WRITE : BIOS_READ);
  if (m_ptr->POSITION % BLOCK_SIZE != 0) return(EINVAL);

  /* Calculate the physical parameters */
  sector = m_ptr->POSITION/SECTOR_SIZE;	/* relative sector within partition */
  if ((sector+BLOCK_SIZE/SECTOR_SIZE) > wn->wn_size) return(EOF);
  sector += wn->wn_low;		/* absolute sector number */
  wn->wn_cylinder = sector / (wn->wn_heads * wn->wn_maxsec);
  wn->wn_sector =  (sector % wn->wn_maxsec);
  wn->wn_head = (sector % (wn->wn_heads * wn->wn_maxsec) )/wn->wn_maxsec;
  wn->wn_count = m_ptr->COUNT;
  wn->wn_address = (vir_bytes) m_ptr->ADDRESS;
  wn->wn_procnr = m_ptr->PROC_NR;

  /* Do the transfer */
  rp = proc_addr(wn->wn_procnr);
  vir = (vir_bytes) wn->wn_address;
  ct = (vir_bytes) wn->wn_count;
  user_phys = umap(rp, D, vir, ct);
  Ax = (wn->wn_opcode << 8) | 2;		/* read or write 2 sectors */
  Bx = (unsigned) (user_phys & 0xF);	/* bx = low order 4 bits */
  Es = (unsigned) ((user_phys >> 4) & 0xFFFF);
  hicyl = (wn->wn_cylinder >> 8) & 03;	/* two high-order bits */
  locyl = (wn->wn_cylinder & 0xFF);	/* 8 low-order bits */
  c1 = (locyl<<8);
  c2 = (hicyl<<6);
  c3 = ((unsigned) wn->wn_sector) + 1;
  Cx = c1 | c2 | c3;
  Dx = (wn->wn_head<<8) | wn->wn_drive;
  bios13();
  r = (Ax >> 8) & 0xFF;
  return(r == 0 ? BLOCK_SIZE : EIO);
}


/*============================================================================*
 *				get_params				      *
 *============================================================================*/
get_params(dr, params)
int dr;
struct param *params;
{
   Dx = dr + DRIVE;
   Ax = (BIOS_ASK << 8);
   bios13();
   params->nr_heads = ((Dx >> 8) & 0xFF) + 1;
   params->nr_sectors = (Cx & 0x3F);
   params->nr_cyl = ((Cx & 0xC0) << 2) + ((Cx >> 8) & 0xFF);
   params->nr_drives = (Dx & 0xFF);
}

/*============================================================================*
 *				init_params				      *
 *============================================================================*/
PRIVATE init_params()
{
/* This routine is called at startup to initialize the partition table,
 * the number of drives and the controller
*/
  unsigned int i, segment, offset;
  phys_bytes address;
  extern phys_bytes umap();
  extern int vec_table[];

  /* Copy the parameters to the structures */
  get_params(0, &param0);
  get_params(1, &param1);

  /* Get the number of drives */
  nr_drives = param0.nr_drives;

  /* Set the parameters in the drive structure */
  for (i = 0; i < DEV_PER_DRIVE; i++)
  {
	wini[i].wn_heads = param0.nr_heads;
	wini[i].wn_maxsec = param0.nr_sectors;
	wini[i].wn_drive = DRIVE;
  }
  wini[0].wn_low = wini[DEV_PER_DRIVE].wn_low = 0L;
  wini[0].wn_size = (long)((long)param0.nr_cyl
      * (long)param0.nr_heads * (long)param0.nr_sectors);
  for (i = DEV_PER_DRIVE; i < (2*DEV_PER_DRIVE); i++)
  {
	wini[i].wn_heads = param1.nr_heads;
	wini[i].wn_maxsec = param1.nr_sectors;
	wini[i].wn_drive = DRIVE + 1;
  }
  wini[DEV_PER_DRIVE].wn_size = (long)((long)param1.nr_cyl
       * (long)param1.nr_heads * (long)param1.nr_sectors);


  /* Read the partition table for each drive and save them */
  for (i = 0; i < nr_drives; i++) {
	w_mess.DEVICE = i * 5;
	w_mess.POSITION = 0L;
	w_mess.COUNT = BLOCK_SIZE;
	w_mess.ADDRESS = (char *) buf;
	w_mess.PROC_NR = WINCHESTER;
	w_mess.m_type = DISK_READ;
	if (w_do_rdwt(&w_mess) != BLOCK_SIZE)
		panic("Can't read partition table of winchester ", i);
	copy_prt(i * DEV_PER_DRIVE);
  }
}

/*===========================================================================*
 *				copy_prt				     *
 *===========================================================================*/
PRIVATE copy_prt(drdev)
int drdev;			/* 5 * drive */
{
/* This routine copies the partition table for the selected drive/dev to
 * the variables wn_low and wn_size
 */

  register int i, offset;
  struct wini *wn;
  long adjust;

  for (i=0; i<4; i++) {
	adjust = 0;
	wn = &wini[i + drdev + 1];
	offset = PART_TABLE + i * 0x10;
	wn->wn_low = *(long *)&buf[offset];
	if ((wn->wn_low % (BLOCK_SIZE/SECTOR_SIZE)) != 0) {
		adjust = wn->wn_low;
		wn->wn_low = (wn->wn_low/(BLOCK_SIZE/SECTOR_SIZE)+1)*(BLOCK_SIZE/SECTOR_SIZE);
		adjust = wn->wn_low - adjust;
	}
	wn->wn_size = *(long *)&buf[offset + sizeof(long)] - adjust;
  }
  sort(&wini[drdev + 1]);
}

/*===========================================================================*
 *				sort					     *
 *===========================================================================*/
PRIVATE sort(wn)
register struct wini wn[];
{
  register int i,j;
  struct wini tmp;

  for (i=0; i<4; i++)
	for (j=0; j<3; j++)
		if ((wn[j].wn_low == 0 && wn[j+1].wn_low != 0) ||
		    (wn[j].wn_low > wn[j+1].wn_low && wn[j+1].wn_low != 0)) {
			tmp = wn[j];
			wn[j] = wn[j+1];
			wn[j+1] = tmp;
		}
}

/*===========================================================================*
 *				replace					     *
 *===========================================================================*/
PRIVATE replace()
{
/* Restore the interrupt vectors > 16 to their original state, except 32. */

  phys_bytes phys_b;
  extern phys_bytes umap();
  phys_clicks base_click;
  extern int s_call();
  extern int tty_int();

  base_click = BASE >> CLICK_SHIFT;
  phys_b = umap(proc_addr(HARDWARE), D, (vir_bytes) vec_table, VECTOR_BYTES);
  phys_copy(phys_b+64L, 64L, (long) VECTOR_BYTES-64L);	/* restore vectors */
  phys_copy(phys_b+4L*XT_WINI_VECTOR, 4L*XT_WINI_VECTOR, 4L);
  set_vec(SYS_VECTOR, s_call, base_click);
  if (ps) set_vec(PS_KEYB_VECTOR, tty_int, base_click);
}
----------------------------   end to gn_wini.c

ast@cs.vu.nl (Andy Tanenbaum) (12/11/88)

In article <840@nosc.NOSC.MIL> hall@nosc.NOSC.MIL (Robert R. Hall) writes:
>
>As written the BIOS_WINI.C did not work with my disk drive
>which is a Seagate St238 RLL disk.  In paticular is has 26
>sector per track instead of 16.  Since BIOS call are now
>possible it occured to me that the disk parameters should
>also be obtained from the BIOS.  Here is BIOS_WINI.C modified
>to get disk parameters using INT13 service call $08.
>It corrected my problem and run fine on my XT clone with it's
>RLL disk (the xt_wini.c would not run with this disk).
>I also tried it on a Zenith 248 AT but there the w_do_rdwt
>call took an excessively a long time.

Thanks for posting this.  Will other people please give it a try on lots
of machines and post results.  If we could find an xx_wini.c that worked on
all machines, I could use it in the boot diskette to get rid of the PC and
AT versions and have one generic version that booted everywhere.

What is the story on the Z-248?

Andy Tanenbaum (ast@cs.vu.nl)