sch@oce.nl (Jakob Schripsema) (04/27/87)
echo x - f_make. sed 's/^X//' >f_make. <<'*-*-END-of-f_make.-*-*' XCFLAGS= -Di8088 -w -F -T. Xh=../h Xl=/usr/lib X Xobj = main.s open.s read.s write.s pipe.s device.s \ X path.s mount.s link.s super.s inode.s cache.s filedes.s \ X stadir.s protect.s time.s misc.s utility.s table.s putc.s X Xfs: makefile $l/head.s $(obj) $l/libc.a $l/end.s X# @echo "Start linking FS. /lib/cem will be removed to make space on RAM disk" X# @rm -f /lib/cem /tmp/* X# asld -o fs $l/head.s $(obj) $l/libc.a $l/end.s X# @echo "FS done. Please restore /lib/cem manually" X @echo "Start linking FS. X @asld -o fs -T. $l/head.s $(obj) $l/libc.a $l/end.s X @echo "FS done. X Xcache.s: const.h type.h $h/const.h $h/type.h Xcache.s: $h/error.h Xcache.s: buf.h Xcache.s: file.h Xcache.s: fproc.h Xcache.s: glo.h Xcache.s: inode.h Xcache.s: super.h X Xdevice.s: const.h type.h $h/const.h $h/type.h Xdevice.s: $h/com.h Xdevice.s: $h/error.h Xdevice.s: dev.h Xdevice.s: file.h Xdevice.s: fproc.h Xdevice.s: glo.h Xdevice.s: inode.h Xdevice.s: param.h X Xfiledes.s: const.h type.h $h/const.h $h/type.h Xfiledes.s: $h/error.h Xfiledes.s: file.h Xfiledes.s: fproc.h Xfiledes.s: glo.h Xfiledes.s: inode.h X Xinode.s: const.h type.h $h/const.h $h/type.h Xinode.s: $h/error.h Xinode.s: buf.h Xinode.s: file.h Xinode.s: fproc.h Xinode.s: glo.h Xinode.s: inode.h Xinode.s: super.h X Xlink.s: const.h type.h $h/const.h $h/type.h Xlink.s: $h/error.h Xlink.s: buf.h Xlink.s: file.h Xlink.s: fproc.h Xlink.s: glo.h Xlink.s: inode.h Xlink.s: param.h X Xmain.s: const.h type.h $h/const.h $h/type.h Xmain.s: $h/callnr.h Xmain.s: $h/com.h Xmain.s: $h/error.h Xmain.s: buf.h Xmain.s: file.h Xmain.s: fproc.h Xmain.s: glo.h Xmain.s: inode.h Xmain.s: param.h Xmain.s: super.h X Xmisc.s: const.h type.h $h/const.h $h/type.h Xmisc.s: $h/callnr.h Xmisc.s: $h/com.h Xmisc.s: $h/error.h Xmisc.s: buf.h Xmisc.s: file.h Xmisc.s: fproc.h Xmisc.s: glo.h Xmisc.s: inode.h Xmisc.s: param.h Xmisc.s: super.h X Xmount.s: const.h type.h $h/const.h $h/type.h Xmount.s: $h/error.h Xmount.s: buf.h Xmount.s: file.h Xmount.s: fproc.h Xmount.s: glo.h Xmount.s: inode.h Xmount.s: param.h Xmount.s: super.h X Xopen.s: const.h type.h $h/const.h $h/type.h Xopen.s: $h/callnr.h Xopen.s: $h/error.h Xopen.s: buf.h Xopen.s: file.h Xopen.s: fproc.h Xopen.s: glo.h Xopen.s: inode.h Xopen.s: param.h X Xpath.s: const.h type.h $h/const.h $h/type.h Xpath.s: $h/error.h Xpath.s: buf.h Xpath.s: file.h Xpath.s: fproc.h Xpath.s: glo.h Xpath.s: inode.h Xpath.s: super.h X Xpipe.s: const.h type.h $h/const.h $h/type.h Xpipe.s: $h/callnr.h Xpipe.s: $h/com.h Xpipe.s: $h/error.h Xpipe.s: $h/signal.h Xpipe.s: file.h Xpipe.s: fproc.h Xpipe.s: glo.h Xpipe.s: inode.h Xpipe.s: param.h X Xprotect.s: const.h type.h $h/const.h $h/type.h Xprotect.s: $h/error.h Xprotect.s: buf.h Xprotect.s: file.h Xprotect.s: fproc.h Xprotect.s: glo.h Xprotect.s: inode.h Xprotect.s: param.h Xprotect.s: super.h X Xputc.s: const.h type.h $h/const.h $h/type.h Xputc.s: $h/com.h X Xread.s: const.h type.h $h/const.h $h/type.h Xread.s: $h/com.h Xread.s: $h/error.h Xread.s: buf.h Xread.s: file.h Xread.s: fproc.h Xread.s: glo.h Xread.s: inode.h Xread.s: param.h Xread.s: super.h X Xstadir.s: const.h type.h $h/const.h $h/type.h Xstadir.s: $h/error.h Xstadir.s: $h/stat.h Xstadir.s: file.h Xstadir.s: fproc.h Xstadir.s: glo.h Xstadir.s: inode.h Xstadir.s: param.h X Xsuper.s: const.h type.h $h/const.h $h/type.h Xsuper.s: $h/error.h Xsuper.s: buf.h Xsuper.s: inode.h Xsuper.s: super.h X Xtable.s: const.h type.h $h/const.h $h/type.h Xtable.s: $h/com.h Xtable.s: $h/callnr.h Xtable.s: $h/error.h Xtable.s: $h/stat.h Xtable.s: buf.h Xtable.s: dev.h Xtable.s: file.h Xtable.s: fproc.h Xtable.s: glo.h Xtable.s: inode.h Xtable.s: super.h X Xtime.s: const.h type.h $h/const.h $h/type.h Xtime.s: $h/callnr.h Xtime.s: $h/com.h Xtime.s: $h/error.h Xtime.s: file.h Xtime.s: fproc.h Xtime.s: glo.h Xtime.s: inode.h Xtime.s: param.h X Xutility.s: const.h type.h $h/const.h $h/type.h Xutility.s: $h/com.h Xutility.s: $h/error.h Xutility.s: buf.h Xutility.s: file.h Xutility.s: fproc.h Xutility.s: glo.h Xutility.s: inode.h Xutility.s: param.h Xutility.s: super.h X Xwrite.s: const.h type.h $h/const.h $h/type.h Xwrite.s: $h/error.h Xwrite.s: buf.h Xwrite.s: file.h Xwrite.s: fproc.h Xwrite.s: glo.h Xwrite.s: inode.h Xwrite.s: super.h *-*-END-of-f_make.-*-* echo x - h_const.h sed 's/^X//' >h_const.h <<'*-*-END-of-h_const.h-*-*' X/* Copyright (C) 1987 by Prentice-Hall, Inc. Permission is hereby granted to X * private individuals and educational institutions to modify and X * redistribute the binary and source programs of this system to other X * private individuals and educational institutions for educational and X * research purposes. For corporate or commercial use, permission from X * Prentice-Hall is required. In general, such permission will be granted, X * subject to a few conditions. X */ X X#define EXTERN extern /* used in *.h files */ X#define PRIVATE static /* PRIVATE x limits the scope of x */ X#define PUBLIC /* PUBLIC is the opposite of PRIVATE */ X#define FORWARD /* some compilers require this to be 'static' */ X X#define TRUE 1 /* used for turning integers into Booleans */ X#define FALSE 0 /* used for turning integers into Booleans */ X X#define HZ 60 /* clock freq (software settable on IBM-PC) */ X#define BLOCK_SIZE 1024 /* # bytes in a disk block */ X#define SUPER_USER (uid) 0 /* uid of superuser */ X X#define MAJOR 8 /* major device = (dev>>MAJOR) & 0377 */ X#define MINOR 0 /* minor device = (dev>>MINOR) & 0377 */ X X#define NR_TASKS 8 /* number of tasks in the transfer vector */ X#define NR_PROCS 16 /* number of slots in proc table */ X#define NR_SEGS 3 /* # segments per process */ X#define T 0 /* proc[i].mem_map[T] is for text */ X#define D 1 /* proc[i].mem_map[D] is for data */ X#define S 2 /* proc[i].mem_map[S] is for stack */ X X#define MAX_P_LONG 2147483647 /* maximum positive long, i.e. 2**31 - 1 */ X X/* Memory is allocated in clicks. */ X#define CLICK_SIZE 0020 /* unit in which memory is allocated */ X#define CLICK_SHIFT 4 /* log2 of CLICK_SIZE */ X X/* Process numbers of some important processes */ X#define MM_PROC_NR 0 /* process number of memory manager */ X#define FS_PROC_NR 1 /* process number of file system */ X#define INIT_PROC_NR 2 /* init -- the process that goes multiuser */ X#define LOW_USER 2 /* first user not part of operating system */ X X/* Miscellaneous */ X#define BYTE 0377 /* mask for 8 bits */ X#define TO_USER 0 /* flag telling to copy from fs to user */ X#define FROM_USER 1 /* flag telling to copy from user to fs */ X#define READING 0 /* copy data to user */ X#define WRITING 1 /* copy data from user */ X#define ABS -999 /* this process means absolute memory */ X X#define WORD_SIZE 2 /* number of bytes per word */ X X#define NIL_PTR (char *) 0 /* generally useful expression */ X X#define NO_NUM 0x8000 /* used as numerical argument to panic() */ X#define MAX_PATH 128 /* max length of path names */ X#define SIG_PUSH_BYTES 8 /* how many bytes pushed by signal */ X#define MAX_ISTACK_BYTES 1024 /* maximum initial stack size for EXEC */ X X/* Device numbers of root (RAM) and boot (fd0) devices. */ X#define ROOT_DEV (dev_nr) 256 /* major-minor device number of root dev */ X/* bootdev fd0 : 512, hd2 : 770 */ X#define BOOT_DEV (dev_nr) 770 /* major-minor device number of boot diskette */ X X/* Flag bits for i_mode in the inode. */ X#define I_TYPE 0170000 /* this field gives inode type */ X#define I_REGULAR 0100000 /* regular file, not dir or special */ X#define I_BLOCK_SPECIAL 0060000 /* block special file */ X#define I_DIRECTORY 0040000 /* file is a directory */ X#define I_CHAR_SPECIAL 0020000 /* character special file */ X#define I_SET_UID_BIT 0004000 /* set effective uid on exec */ X#define I_SET_GID_BIT 0002000 /* set effective gid on exec */ X#define ALL_MODES 0006777 /* all bits for user, group and others */ X#define RWX_MODES 0000777 /* mode bits for RWX only */ X#define R_BIT 0000004 /* Rwx protection bit */ X#define W_BIT 0000002 /* rWx protection bit */ X#define X_BIT 0000001 /* rwX protection bit */ X#define I_NOT_ALLOC 0000000 /* this inode is free */ *-*-END-of-h_const.h-*-* echo x - k_const.h sed 's/^X//' >k_const.h <<'*-*-END-of-k_const.h-*-*' X/* General constants used by the kernel. */ X X#ifdef i8088 X/* p_reg contains: ax, bx, cx, dx, si, di, bp, es, ds, cs, ss in that order. */ X#define NR_REGS 11 /* number of general regs in each proc slot */ X#define INIT_PSW 0x0200 /* initial psw */ X#define INIT_SP (int*)0x0010 /* initial sp: 3 words pushed by kernel */ X X/* The following values are used in the assembly code. Do not change the X * values of 'ES_REG', 'DS_REG', 'CS_REG', or 'SS_REG' without making the X * corresponding changes in the assembly code. X */ X#define ES_REG 7 /* proc[i].p_reg[ESREG] is saved es */ X#define DS_REG 8 /* proc[i].p_reg[DSREG] is saved ds */ X#define CS_REG 9 /* proc[i].p_reg[CSREG] is saved cs */ X#define SS_REG 10 /* proc[i].p_reg[SSREG] is saved ss */ X X#define VECTOR_BYTES 284 /* bytes of interrupt vectors to save */ X#define MEM_BYTES 655360L /* memory size for /dev/mem */ X X/* Interrupt vectors */ X#define DIVIDE_VECTOR 0 /* divide interrupt vector */ X#define CLOCK_VECTOR 8 /* clock interrupt vector */ X#define KEYBOARD_VECTOR 9 /* keyboard interrupt vector */ X#define XT_WINI_VECTOR 13 /* xt winchester interrupt vector */ X#define FLOPPY_VECTOR 14 /* floppy disk interrupt vector */ X#define PRINTER_VECTOR 15 /* line printer interrupt vector */ X#define SYS_VECTOR 32 /* system calls are made with int SYSVEC */ X#define AT_WINI_VECTOR 118 /* at winchester interrupt vector */ X X/* The 8259A interrupt controller has to be re-enabled after each interrupt. */ X#define INT_CTL 0x20 /* I/O port for interrupt controller */ X#define INT_CTLMASK 0x21 /* setting bits in this port disables ints */ X#define INT2_CTL 0xA0 /* I/O port for second interrupt controller */ X#define INT2_MASK 0xA1 /* setting bits in this port disables ints */ X#define ENABLE 0x20 /* code used to re-enable after an interrupt */ X X/* Hardware IDs */ X#define UNKNOWN 0 /* OS could not detect hrdware type */ X#define IBMXT 1 /* IBM XT */ X#define IBMAT 2 /* IBM AT */ X#define M24 3 /* Olivetti M24, AT&T 6300 */ X#endif X X#define TASK_STACK_BYTES 256 /* how many bytes for each task stack */ X#define K_STACK_BYTES 256 /* how many bytes for the kernel stack */ X X#define RET_REG 0 /* system call return codes go in this reg */ X#define IDLE -999 /* 'cur_proc' = IDLE means nobody is running */ X X/* The following items pertain to the 3 scheduling queues. */ X#define NQ 3 /* # of scheduling queues */ X#define TASK_Q 0 /* ready tasks are scheduled via queue 0 */ X#define SERVER_Q 1 /* ready servers are scheduled via queue 1 */ X#define USER_Q 2 /* ready users are scheduled via queue 2 */ X X#define printf printk /* the kernel really uses printk, not printf */ *-*-END-of-k_const.h-*-* echo x - k_floppy.c sed 's/^X//' >k_floppy.c <<'*-*-END-of-k_floppy.c-*-*' X/* This file contains a driver for a Floppy Disk Controller (FDC) using the X * NEC PD765 chip. The driver supports two operations: read a block and X * write a block. It accepts two messages, one for reading and one for X * writing, both using message format m2 and with the same parameters: X * X * m_type DEVICE PROC_NR COUNT POSITION ADRRESS X * ---------------------------------------------------------------- X * | DISK_READ | device | proc nr | bytes | offset | buf ptr | X * |------------+---------+---------+---------+---------+---------| X * | DISK_WRITE | device | proc nr | bytes | offset | buf ptr | X * ---------------------------------------------------------------- X * X * The file contains one entry point: X * X * floppy_task: main entry when system is brought up X * X * Changes: X * 27 october 1986 by Jakob Schripsema: fdc_results fixed for 8 MHz X * 2 februari 1987 by Jakob Schripsema: added timeout() routine X * & reset counter X */ X X#include "../h/const.h" X#include "../h/type.h" X#include "../h/callnr.h" X#include "../h/com.h" X#include "../h/error.h" X#include "const.h" X#include "type.h" X#include "glo.h" X#include "proc.h" X X/* I/O Ports used by floppy disk task. */ X#define DOR 0x3F2 /* motor drive control bits */ X#define FDC_STATUS 0x3F4 /* floppy disk controller status register */ X#define FDC_DATA 0x3F5 /* floppy disk controller data register */ X#define FDC_RATE 0x3F7 /* transfer rate register */ X#define DMA_ADDR 0x004 /* port for low 16 bits of DMA address */ X#define DMA_TOP 0x081 /* port for top 4 bits of 20-bit DMA addr */ X#define DMA_COUNT 0x005 /* port for DMA count (count = bytes - 1) */ X#define DMA_M2 0x00C /* DMA status port */ X#define DMA_M1 0x00B /* DMA status port */ X#define DMA_INIT 0x00A /* DMA init port */ X X/* Status registers returned as result of operation. */ X#define ST0 0x00 /* status register 0 */ X#define ST1 0x01 /* status register 1 */ X#define ST2 0x02 /* status register 2 */ X#define ST3 0x00 /* status register 3 (return by DRIVE_SENSE) */ X#define ST_CYL 0x03 /* slot where controller reports cylinder */ X#define ST_HEAD 0x04 /* slot where controller reports head */ X#define ST_SEC 0x05 /* slot where controller reports sector */ X#define ST_PCN 0x01 /* slot where controller reports present cyl */ X X/* Fields within the I/O ports. */ X#define MASTER 0x80 /* used to see who is master */ X#define DIRECTION 0x40 /* is FDC trying to read or write? */ X#define CTL_BUSY 0x10 /* used to see when controller is busy */ X#define CTL_ACCEPTING 0x80 /* bit pattern FDC gives when idle */ X#define MOTOR_MASK 0xF0 /* these bits control the motors in DOR */ X#define ENABLE_INT 0x0C /* used for setting DOR port */ X#define ST0_BITS 0xF8 /* check top 5 bits of seek status */ X#define ST3_FAULT 0x80 /* if this bit is set, drive is sick */ X#define ST3_WR_PROTECT 0x40 /* set when diskette is write protected */ X#define ST3_READY 0x20 /* set when drive is ready */ X#define TRANS_ST0 0x00 /* top 5 bits of ST0 for READ/WRITE */ X#define SEEK_ST0 0x20 /* top 5 bits of ST0 for SEEK */ X#define BAD_SECTOR 0x05 /* if these bits are set in ST1, recalibrate */ X#define BAD_CYL 0x1F /* if any of these bits are set, recalibrate */ X#define WRITE_PROTECT 0x02 /* bit is set if diskette is write protected */ X#define CHANGE 0xC0 /* value returned by FDC after reset */ X X/* Floppy disk controller command bytes. */ X#define FDC_SEEK 0x0F /* command the drive to seek */ X#define FDC_READ 0xE6 /* command the drive to read */ X#define FDC_WRITE 0xC5 /* command the drive to write */ X#define FDC_SENSE 0x08 /* command the controller to tell its status */ X#define FDC_RECALIBRATE 0x07 /* command the drive to go to cyl 0 */ X#define FDC_SPECIFY 0x03 /* command the drive to accept params */ X X/* DMA channel commands. */ X#define DMA_READ 0x46 /* DMA read opcode */ X#define DMA_WRITE 0x4A /* DMA write opcode */ X X/* Parameters for the disk drive. */ X#define SECTOR_SIZE 512 /* physical sector size in bytes */ X#define HC_SIZE 2400 /* # sectors on a high-capacity (1.2M) disk */ X#define NR_HEADS 0x02 /* two heads (i.e., two tracks/cylinder) */ X#define DTL 0xFF /* determines data length (sector size) */ X#define SPEC1 0xDF /* first parameter to SPECIFY */ X#define SPEC2 0x02 /* second parameter to SPECIFY */ X X#define MOTOR_OFF 3*HZ /* how long to wait before stopping motor */ X#define TIMEOUT 2*HZ /* how long to wait before aborting transfer */ X X/* Error codes */ X#define ERR_SEEK -1 /* bad seek */ X#define ERR_TRANSFER -2 /* bad transfer */ X#define ERR_STATUS -3 /* something wrong when getting status */ X#define ERR_RECALIBRATE -4 /* recalibrate didn't work properly */ X#define ERR_WR_PROTECT -5 /* diskette is write protected */ X#define ERR_DRIVE -6 /* something wrong with a drive */ X#define ERR_TIMEOUT -7 /* timeout, drive not ready */ X X/* Miscellaneous. */ X#define MOTOR_RUNNING 0xFF /* message type for clock interrupt */ X#define MAX_ERRORS 20 /* how often to try rd/wt before quitting */ X#define MAX_RESETS 2 /* max number of resets for 1 job */ X#define MAX_RESULTS 8 /* max number of bytes controller returns */ X#define NR_DRIVES 2 /* maximum number of drives */ X#define DIVISOR 128 /* used for sector size encoding */ X#define MAX_FDC_RETRY 100 /* max # times to try to output to FDC */ X#define NT 4 /* number of diskette/drive combinations */ X X/* Variables. */ XPRIVATE struct floppy { /* main drive struct, one entry per drive */ X int fl_opcode; /* DISK_READ or DISK_WRITE */ X int fl_curcyl; /* current cylinder */ X int fl_procnr; /* which proc wanted this operation? */ X int fl_drive; /* drive number addressed */ X int fl_cylinder; /* cylinder number addressed */ X int fl_sector; /* sector addressed */ X int fl_head; /* head number addressed */ X int fl_count; /* byte count */ X vir_bytes fl_address; /* user virtual address */ X char fl_results[MAX_RESULTS]; /* the controller can give lots of output */ X char fl_calibration; /* CALIBRATED or UNCALIBRATED */ X char fl_density; /* 0 = 360K/360K; 1 = 360K/1.2M; 2= 1.2M/1.2M */ X} floppy[NR_DRIVES]; X X#define UNCALIBRATED 0 /* drive needs to be calibrated at next use */ X#define CALIBRATED 1 /* no calibration needed */ X XPRIVATE int motor_status; /* current motor status is in 4 high bits */ XPRIVATE int motor_goal; /* desired motor status is in 4 high bits */ XPRIVATE int prev_motor; /* which motor was started last */ XPRIVATE int need_reset; /* set to 1 when controller must be reset */ XPRIVATE int nresets; /* # of resets during 1 'job' */ XPRIVATE int initialized; /* set to 1 after first successful transfer */ XPRIVATE int d; /* diskette/drive combination */ XPRIVATE int active_timeout; /* used to detect timeout in transfer */ X XPRIVATE message mess; /* message buffer for in and out */ X XPRIVATE char len[] = {-1,0,1,-1,2,-1,-1,3,-1,-1,-1,-1,-1,-1,-1,4}; XPRIVATE char interleave[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; X X/* Four combinations of diskette/drive are supported: X * # Drive diskette Sectors Tracks Rotation Data-rate Comment X * 0 360K 360K 9 40 300 RPM 250 kbps Standard PC DSDD X * 1 720K 360K 9 40 300 RPM 250 kbps Quad density PC X * 2 1.2M 360K 9 40 360 RPM 300 kbps PC disk in AT drive X * 3 1.2M 1.2M 15 80 360 RPM 500 kbps AT disk in AT drive X */ XPRIVATE int gap[NT] = {0x2A, 0x2A, 0x23, 0x1B}; /* gap size */ XPRIVATE int rate[NT] = {0x02, 0x02, 0x01, 0x00}; /* 250,300,500 kbps*/ XPRIVATE int nr_sectors[NT] = {9, 9, 9, 15}; /* sectors/track */ XPRIVATE int nr_blocks[NT] = {720, 720, 720, 2400}; /* sectors/diskette*/ XPRIVATE int steps_per_cyl[NT] = {1, 2, 2, 1}; /* 2 = dbl step */ XPRIVATE int mtr_setup[NT] = {HZ/4,HZ/4,3*HZ/4,3*HZ/4};/* in ticks */ X X/*===========================================================================* X * floppy_task * X *===========================================================================*/ XPUBLIC floppy_task() X{ X/* Main program of the floppy disk driver task. */ X X int r, caller, proc_nr; X extern int hw_id; X X if (hw_id == M24) mtr_setup[0] = 3*HZ/4; X X /* Here is the main loop of the disk task. It waits for a message, carries X * it out, and sends a reply. X */ X while (TRUE) { X /* First wait for a request to read or write a disk block. */ X receive(ANY, &mess); /* get a request to do some work */ X if (mess.m_source < 0) X panic("disk task got message from ", mess.m_source); X caller = mess.m_source; X proc_nr = mess.PROC_NR; X X /* Now carry out the work. */ X switch(mess.m_type) { X case DISK_READ: r = do_rdwt(&mess); break; X case DISK_WRITE: r = do_rdwt(&mess); break; X default: r = EINVAL; break; X } X X /* Finally, prepare and send the reply message. */ X mess.m_type = TASK_REPLY; X mess.REP_PROC_NR = proc_nr; X mess.REP_STATUS = r; /* # of bytes transferred or error code */ X send(caller, &mess); /* send reply to caller */ X } X} X X X/*===========================================================================* X * do_rdwt * X *===========================================================================*/ XPRIVATE int do_rdwt(m_ptr) Xmessage *m_ptr; /* pointer to read or write message */ X{ X/* Carry out a read or write request from the disk. */ X register struct floppy *fp; X int r, drive, errors, stop_motor(); X long block; X X /* Decode the message parameters. */ X drive = m_ptr->DEVICE; X if (drive < 0 || drive >= NR_DRIVES) return(EIO); X fp = &floppy[drive]; /* 'fp' points to entry for this drive */ X fp->fl_drive = drive; /* save drive number explicitly */ X fp->fl_opcode = m_ptr->m_type; /* DISK_READ or DISK_WRITE */ X if (m_ptr->POSITION % BLOCK_SIZE != 0) return(EINVAL); X block = m_ptr->POSITION/SECTOR_SIZE; X if (block >= HC_SIZE) return(EOF); /* sector is beyond end of 1.2M disk */ X d = fp->fl_density; /* diskette/drive combination */ X fp->fl_cylinder = (int) (block / (NR_HEADS * nr_sectors[d])); X fp->fl_sector = (int) interleave[block % nr_sectors[d]]; X fp->fl_head = (int) (block % (NR_HEADS*nr_sectors[d]) )/nr_sectors[d]; X fp->fl_count = m_ptr->COUNT; X fp->fl_address = (vir_bytes) m_ptr->ADDRESS; X fp->fl_procnr = m_ptr->PROC_NR; X if (fp->fl_count != BLOCK_SIZE) return(EINVAL); X X errors = 0; X nresets = 0; X X /* This loop allows a failed operation to be repeated. */ X while (errors <= MAX_ERRORS) { X X /* If a lot of errors occur when 'initialized' is 0, it probably X * means that we are trying at the wrong density. Try another one. X * Increment 'errors' here since loop is aborted on error. X */ X errors++; /* increment count once per loop cycle */ X if (errors % (MAX_ERRORS/NT) == 0) { X d = (d + 1) % NT; /* try next density */ X fp->fl_density = d; X need_reset = 1; X } X if (block >= nr_blocks[d]) continue; X X /* First check to see if a reset is needed. */ X if (need_reset) reset(); X if (nresets > MAX_RESETS) return(ERR_DRIVE); X X /* Now set up the DMA chip. */ X dma_setup(fp); X X /* See if motor is running; if not, turn it on and wait */ X start_motor(fp); X X /* If we are going to a new cylinder, perform a seek. */ X r = seek(fp); X if (r != OK) continue; /* if error, try again */ X X /* Perform the transfer. */ X r = transfer(fp); X if (r == OK) break; /* if successful, exit loop */ X if (r == ERR_WR_PROTECT || r == ERR_TIMEOUT) break; /* retries won't help */ X X } X X /* Start watch_dog timer to turn motor off in a few seconds */ X motor_goal = ENABLE_INT; /* when timer goes off, kill all motors */ X clock_mess(MOTOR_OFF, stop_motor); X if (r == OK && fp->fl_cylinder > 0) initialized = 1; /* seek works */ X return(r == OK ? BLOCK_SIZE : EIO); X} X X/*===========================================================================* X * dma_setup * X *===========================================================================*/ XPRIVATE dma_setup(fp) Xstruct floppy *fp; /* pointer to the drive struct */ X{ X/* The IBM PC can perform DMA operations by using the DMA chip. To use it, X * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address X * to be read from or written to, the byte count minus 1, and a read or write X * opcode. This routine sets up the DMA chip. Note that the chip is not X * capable of doing a DMA across a 64K boundary (e.g., you can't read a X * 512-byte block starting at physical address 65520). X */ X X int mode, low_addr, high_addr, top_addr, low_ct, high_ct, top_end; X vir_bytes vir, ct; X phys_bytes user_phys; X extern phys_bytes umap(); X X mode = (fp->fl_opcode == DISK_READ ? DMA_READ : DMA_WRITE); X vir = (vir_bytes) fp->fl_address; X ct = (vir_bytes) fp->fl_count; X user_phys = umap(proc_addr(fp->fl_procnr), D, vir, ct); X low_addr = (int) (user_phys >> 0) & BYTE; X high_addr = (int) (user_phys >> 8) & BYTE; X top_addr = (int) (user_phys >> 16) & BYTE; X low_ct = (int) ( (ct - 1) >> 0) & BYTE; X high_ct = (int) ( (ct - 1) >> 8) & BYTE; X X /* Check to see if the transfer will require the DMA address counter to X * go from one 64K segment to another. If so, do not even start it, since X * the hardware does not carry from bit 15 to bit 16 of the DMA address. X * Also check for bad buffer address. These errors mean FS contains a bug. X */ X if (user_phys == 0) panic("FS gave floppy disk driver bad addr", (int) vir); X top_end = (int) (((user_phys + ct - 1) >> 16) & BYTE); X if (top_end != top_addr) panic("Trying to DMA across 64K boundary", top_addr); X X /* Now set up the DMA registers. */ X lock(); X port_out(DMA_M2, mode); /* set the DMA mode */ X port_out(DMA_M1, mode); /* set it again */ X port_out(DMA_ADDR, low_addr); /* output low-order 8 bits */ X port_out(DMA_ADDR, high_addr);/* output next 8 bits */ X port_out(DMA_TOP, top_addr); /* output highest 4 bits */ X port_out(DMA_COUNT, low_ct); /* output low 8 bits of count - 1 */ X port_out(DMA_COUNT, high_ct); /* output high 8 bits of count - 1 */ X unlock(); X port_out(DMA_INIT, 2); /* initialize DMA */ X} X X X/*===========================================================================* X * start_motor * X *===========================================================================*/ XPRIVATE start_motor(fp) Xstruct floppy *fp; /* pointer to the drive struct */ X{ X/* Control of the floppy disk motors is a big pain. If a motor is off, you X * have to turn it on first, which takes 1/2 second. You can't leave it on X * all the time, since that would wear out the diskette. However, if you turn X * the motor off after each operation, the system performance will be awful. X * The compromise used here is to leave it on for a few seconds after each X * operation. If a new operation is started in that interval, it need not be X * turned on again. If no new operation is started, a timer goes off and the X * motor is turned off. I/O port DOR has bits to control each of 4 drives. X * Interrupts must be disabled temporarily to prevent clock interrupt from X * turning off motors while we are testing the bits. X */ X X int motor_bit, running, send_mess(); X X lock(); /* no interrupts while checking out motor */ X motor_bit = 1 << (fp->fl_drive + 4); /* bit mask for this drive */ X motor_goal = motor_bit | ENABLE_INT | fp->fl_drive; X if (motor_status & prev_motor) motor_goal |= prev_motor; X running = motor_status & motor_bit; /* nonzero if this motor is running */ X port_out(DOR, motor_goal); X motor_status = motor_goal; X prev_motor = motor_bit; /* record motor started for next time */ X unlock(); X X /* If the motor was already running, we don't have to wait for it. */ X if (running) return; /* motor was already running */ X clock_mess(mtr_setup[d], send_mess); /* motor was not running */ X receive(CLOCK, &mess); /* wait for clock interrupt */ X} X X X/*===========================================================================* X * stop_motor * X *===========================================================================*/ XPRIVATE stop_motor() X{ X/* This routine is called by the clock interrupt after several seconds have X * elapsed with no floppy disk activity. It checks to see if any drives are X * supposed to be turned off, and if so, turns them off. X */ X X if ( (motor_goal & MOTOR_MASK) != (motor_status & MOTOR_MASK) ) { X port_out(DOR, motor_goal); X motor_status = motor_goal; X } X} X X X/*===========================================================================* X * seek * X *===========================================================================*/ XPRIVATE int seek(fp) Xstruct floppy *fp; /* pointer to the drive struct */ X{ X/* Issue a SEEK command on the indicated drive unless the arm is already X * positioned on the correct cylinder. X */ X X int r; X extern int timeout(); X X /* Are we already on the correct cylinder? */ X if (fp->fl_calibration == UNCALIBRATED) X if (recalibrate(fp) != OK) return(ERR_SEEK); X if (fp->fl_curcyl == fp->fl_cylinder) return(OK); X X /* No. Wrong cylinder. Issue a SEEK and wait for interrupt. */ X fdc_out(FDC_SEEK); /* start issuing the SEEK command */ X fdc_out( (fp->fl_head << 2) | fp->fl_drive); X fdc_out(fp->fl_cylinder * steps_per_cyl[d]); X if (need_reset) return(ERR_SEEK); /* if controller is sick, abort seek */ X receive(HARDWARE, &mess); X X /* Interrupt has been received. Check drive status. */ X fdc_out(FDC_SENSE); /* probe FDC to make it return status */ X r = fdc_results(fp); /* get controller status bytes */ X if ( (fp->fl_results[ST0] & ST0_BITS) != SEEK_ST0) r = ERR_SEEK; X if (fp->fl_results[ST1] != fp->fl_cylinder * steps_per_cyl[d]) r = ERR_SEEK; X if (r != OK) X if (recalibrate(fp) != OK) return(ERR_SEEK); X return(r); X} X X X/*===========================================================================* X * transfer * X *===========================================================================*/ XPRIVATE int transfer(fp) Xregister struct floppy *fp; /* pointer to the drive struct */ X{ X/* The drive is now on the proper cylinder. Read or write 1 block. */ X X int r, s, op; X extern int olivetti; X extern int timeout(); X X /* Never attempt a transfer if the drive is uncalibrated or motor is off. */ X if (fp->fl_calibration == UNCALIBRATED) return(ERR_TRANSFER); X if ( ( (motor_status>>(fp->fl_drive+4)) & 1) == 0) return(ERR_TRANSFER); X X /* The PC-AT requires the date rate to be set to 250 or 500 kbps */ X if (hw_id == IBMAT) port_out(FDC_RATE, rate[d]); X X /* The command is issued by outputing 9 bytes to the controller chip. */ X op = (fp->fl_opcode == DISK_READ ? FDC_READ : FDC_WRITE); X fdc_out(op); /* issue the read or write command */ X fdc_out( (fp->fl_head << 2) | fp->fl_drive); X fdc_out(fp->fl_cylinder); /* tell controller which cylinder */ X fdc_out(fp->fl_head); /* tell controller which head */ X fdc_out(fp->fl_sector); /* tell controller which sector */ X fdc_out( (int) len[SECTOR_SIZE/DIVISOR]); /* sector size */ X fdc_out(nr_sectors[d]); /* tell controller how big a track is */ X fdc_out(gap[d]); /* tell controller how big sector gap is */ X fdc_out(DTL); /* tell controller about data length */ X X /* Block, waiting for disk interrupt. */ X if (need_reset) return(ERR_TRANSFER); /* if controller is sick, abort op */ X clock_mess(TIMEOUT,timeout); /* start timer */ X active_timeout = TRUE; /* and set flag to remember this */ X X receive(HARDWARE, &mess); X X /* Check for timeout */ X if (active_timeout == FALSE) return(ERR_TIMEOUT); X active_timeout = FALSE; /* all done, forget about timeout */ X X /* Get controller status and check for errors. */ X r = fdc_results(fp); X if (r != OK) return(r); X if ( (fp->fl_results[ST1] & BAD_SECTOR) || (fp->fl_results[ST2] & BAD_CYL) ) X fp->fl_calibration = UNCALIBRATED; X if (fp->fl_results[ST1] & WRITE_PROTECT) { X printf("Diskette in drive %d is write protected.\n", fp->fl_drive); X return(ERR_WR_PROTECT); X } X if ((fp->fl_results[ST0] & ST0_BITS) != TRANS_ST0) return(ERR_TRANSFER); X if (fp->fl_results[ST1] | fp->fl_results[ST2]) return(ERR_TRANSFER); X X /* Compare actual numbers of sectors transferred with expected number. */ X s = (fp->fl_results[ST_CYL] - fp->fl_cylinder) * NR_HEADS * nr_sectors[d]; X s += (fp->fl_results[ST_HEAD] - fp->fl_head) * nr_sectors[d]; X s += (fp->fl_results[ST_SEC] - fp->fl_sector); X if (s * SECTOR_SIZE != fp->fl_count) return(ERR_TRANSFER); X return(OK); X} X X/*===========================================================================* X * timeout * X *===========================================================================*/ XPRIVATE int timeout() X{ X/* The NEC 765 fdc has its ready line tied to Vcc. So it's impossible X * to check a drives ready state. If a drive isn't ready and a read or X * write is started in transfer(), the fdc waits until the drive gets ready. X * This blocks both the floppy task and FS until the drive gets ready. X * Solution : X * Before transfer() does a receive() it starts a timeout. X * If the timer runs out this routine is called. When no interrupt has X * occured in the mean time (active_timeout still TRUE) the disk probably X * isn't ready. To get the floppy task out of its deadlock the fdc is reset. X * After a reset the fdc detects 'drive-ready' (ready line still tied to X * Vcc !!) and generates a 'drive-status-changed' interrupt (see 765 spec.) X * The interrupt routine sends a message to the floppy task which readies X * the floppy task. X * As usual the problem is with the IBM PC hardware. X */ X X if (active_timeout == FALSE) return; /* no work to do */ X X /* Start fdc reset */ X lock(); X port_out(DOR, 0); /* strobe reset bit low */ X port_out(DOR, ENABLE_INT); /* strobe it high again */ X unlock(); /* interrupts allowed again */ X X need_reset = TRUE; X active_timeout = FALSE; X} X X/*===========================================================================* X * fdc_results * X *===========================================================================*/ XPRIVATE int fdc_results(fp) Xregister struct floppy *fp; /* pointer to the drive struct */ X{ X/* Extract results from the controller after an operation. */ X X int i, j, status, ready; X X /* Loop, extracting bytes from FDC until it says it has no more. */ X for (i = 0; i < MAX_RESULTS; i++) { X ready = FALSE; X for (j = 0; j < MAX_FDC_RETRY; j++) { X port_in(FDC_STATUS, &status); X if (status & MASTER) { X ready = TRUE; X break; X } X } X if (ready == FALSE) return(ERR_STATUS); /* time out */ X X if ((status & CTL_BUSY) == 0) return(OK); X if ((status & DIRECTION) == 0) return(ERR_STATUS); X port_in(FDC_DATA, &status); X fp->fl_results[i] = status & BYTE; X } X X /* FDC is giving back too many results. */ X need_reset = TRUE; /* controller chip must be reset */ X return(ERR_STATUS); X} X X X/*===========================================================================* X * fdc_out * X *===========================================================================*/ XPRIVATE fdc_out(val) Xint val; /* write this byte to floppy disk controller */ X{ X/* Output a byte to the controller. This is not entirely trivial, since you X * can only write to it when it is listening, and it decides when to listen. X * If the controller refuses to listen, the FDC chip is given a hard reset. X */ X X int retries, r; X X if (need_reset) return; /* if controller is not listening, return */ X retries = MAX_FDC_RETRY; X X /* It may take several tries to get the FDC to accept a command. */ X while (retries-- > 0) { X port_in(FDC_STATUS, &r); X r &= (MASTER | DIRECTION); /* just look at bits 2 and 3 */ X if (r != CTL_ACCEPTING) continue; /* FDC is not listening */ X port_out(FDC_DATA, val); X return; X } X X /* Controller is not listening. Hit it over the head with a hammer. */ X need_reset = TRUE; X} X X X/*===========================================================================* X * recalibrate * X *===========================================================================*/ XPRIVATE int recalibrate(fp) Xregister struct floppy *fp; /* pointer tot he drive struct */ X{ X/* The floppy disk controller has no way of determining its absolute arm X * position (cylinder). Instead, it steps the arm a cylinder at a time and X * keeps track of where it thinks it is (in software). However, after a X * SEEK, the hardware reads information from the diskette telling where the X * arm actually is. If the arm is in the wrong place, a recalibration is done, X * which forces the arm to cylinder 0. This way the controller can get back X * into sync with reality. X */ X X int r; X /* Issue the RECALIBRATE command and wait for the interrupt. */ X start_motor(fp); /* can't recalibrate with motor off */ X fdc_out(FDC_RECALIBRATE); /* tell drive to recalibrate itself */ X fdc_out(fp->fl_drive); /* specify drive */ X if (need_reset) return(ERR_SEEK); /* don't wait if controller is sick */ X receive(HARDWARE, &mess); /* wait for interrupt message */ X X /* Determine if the recalibration succeeded. */ X fdc_out(FDC_SENSE); /* issue SENSE command to see where we are */ X r = fdc_results(fp); /* get results of the SENSE command */ X fp->fl_curcyl = -1; /* force a SEEK next time */ X if (r != OK || /* controller would not respond */ X (fp->fl_results[ST0]&ST0_BITS) != SEEK_ST0 || fp->fl_results[ST_PCN] !=0){ X /* Recalibration failed. FDC must be reset. */ X need_reset = TRUE; X fp->fl_calibration = UNCALIBRATED; X return(ERR_RECALIBRATE); X } else { X /* Recalibration succeeded. */ X fp->fl_calibration = CALIBRATED; X return(OK); X } X} X X/*===========================================================================* X * reset * X *===========================================================================*/ XPRIVATE reset() X{ X/* Issue a reset to the controller. This is done after any catastrophe, X * like the controller refusing to respond. X */ X X int i, r, status; X register struct floppy *fp; X X /* Disable interrupts and strobe reset bit low. */ X need_reset = FALSE; X lock(); X motor_status = 0; X motor_goal = 0; X port_out(DOR, 0); /* strobe reset bit low */ X port_out(DOR, ENABLE_INT); /* strobe it high again */ X unlock(); /* interrupts allowed again */ X receive(HARDWARE, &mess); /* collect the RESET interrupt */ X X /* Interrupt from the reset has been received. Continue resetting. */ X fp = &floppy[0]; /* use floppy[0] for scratch */ X fp->fl_results[0] = 0; /* this byte will be checked shortly */ X fdc_out(FDC_SENSE); /* did it work? */ X r = fdc_results(fp); /* get results */ X status = fp->fl_results[0] & BYTE; X X /* Tell FDC drive parameters. */ X fdc_out(FDC_SPECIFY); /* specify some timing parameters */ X fdc_out(SPEC1); /* step-rate and head-unload-time */ X fdc_out(SPEC2); /* head-load-time and non-dma */ X X for (i = 0; i < NR_DRIVES; i++) floppy[i].fl_calibration = UNCALIBRATED; X X nresets++; /* count this reset */ X} X X X/*===========================================================================* X * clock_mess * X *===========================================================================*/ XPRIVATE clock_mess(ticks, func) Xint ticks; /* how many clock ticks to wait */ Xint (*func)(); /* function to call upon time out */ X{ X/* Send the clock task a message. */ X X mess.m_type = SET_ALARM; X mess.CLOCK_PROC_NR = FLOPPY; X mess.DELTA_TICKS = ticks; X mess.FUNC_TO_CALL = func; X sendrec(CLOCK, &mess); X} X X X/*===========================================================================* X * send_mess * X *===========================================================================*/ XPRIVATE send_mess() X{ X/* This routine is called when the clock task has timed out on motor startup.*/ X X mess.m_type = MOTOR_RUNNING; X send(FLOPPY, &mess); X} *-*-END-of-k_floppy.c-*-* echo x - k_glo.h sed 's/^X//' >k_glo.h <<'*-*-END-of-k_glo.h-*-*' X/* Global variables used in the kernel. */ X X/* Clocks and timers */ XEXTERN real_time realtime; /* real time clock */ XEXTERN int lost_ticks; /* incremented when clock int can't send mess*/ X X/* Processes, signals, and messages. */ XEXTERN int cur_proc; /* current process */ XEXTERN int prev_proc; /* previous process */ XEXTERN int sig_procs; /* number of procs with p_pending != 0 */ XEXTERN message int_mess; /* interrupt routines build message here */ X X/* CPU type. */ XEXTERN int olivetti; /* TRUE for Olivetti-style keyboard */ XEXTERN int hw_id; /* identification of hardware */ X X/* The kernel and task stacks. */ XEXTERN struct t_stack { X int stk[TASK_STACK_BYTES/sizeof(int)]; X} t_stack[NR_TASKS - 1]; /* task stacks; task = -1 never really runs */ X XEXTERN char k_stack[K_STACK_BYTES]; /* The kernel stack. */ *-*-END-of-k_glo.h-*-* echo x - k_main.c sed 's/^X//' >k_main.c <<'*-*-END-of-k_main.c-*-*' X/* This file contains the main program of MINIX. The routine main() X * initializes the system and starts the ball rolling by setting up the proc X * table, interrupt vectors, and scheduling each task to run to initialize X * itself. X * X * The entries into this file are: X * main: MINIX main program X * unexpected_int: called when an interrupt to an unused vector < 16 occurs X * trap: called when an unexpected trap to a vector >= 16 occurs X * panic: abort MINIX due to a fatal error X */ X X#include "../h/const.h" X#include "../h/type.h" X#include "../h/callnr.h" X#include "../h/com.h" X#include "../h/error.h" X#include "const.h" X#include "type.h" X#include "glo.h" X#include "proc.h" X X#define SAFETY 8 /* margin of safety for stack overflow (ints)*/ X#define VERY_BIG 39328 /* must be bigger than kernel size (clicks) */ X#define BASE 1536 /* address where MINIX starts in memory */ X#define SIZES 8 /* sizes array has 8 entries */ X#define CPU_TY1 0xFFFF /* BIOS segment that tells CPU type */ X#define CPU_TY2 0x000E /* BIOS offset that tells CPU type */ X#define PC_AT 0xFC /* IBM code for PC-AT (in BIOS at 0xFFFFE) */ X X/*===========================================================================* X * main * X *===========================================================================*/ XPUBLIC main() X{ X/* Start the ball rolling. */ X X register struct proc *rp; X register int t; X vir_clicks size; X phys_clicks base_click, mm_base, previous_base; X phys_bytes phys_b; X extern unsigned sizes[8]; /* table filled in by build */ X extern int color, vec_table[], get_chrome(), (*task[])(); X extern int s_call(), disk_int(), tty_int(), clock_int(), disk_int(); X extern int wini_int(), lpr_int(), surprise(), trp(), divide(); X extern phys_bytes umap(); X X /* Set up proc table entry for user processes. Be very careful about X * sp, since the 3 words prior to it will be clobbered when the kernel pushes X * pc, cs, and psw onto the USER's stack when starting the user the first X * time. This means that with initial sp = 0x10, user programs must leave X * the words at 0x000A, 0x000C, and 0x000E free. X */ X X lock(); /* we can't handle interrupts yet */ X base_click = BASE >> CLICK_SHIFT; X size = sizes[0] + sizes[1]; /* kernel text + data size in clicks */ X mm_base = base_click + size; /* place where MM starts (in clicks) */ X X for (rp = &proc[0]; rp <= &proc[NR_TASKS+LOW_USER]; rp++) { X for (t=0; t< NR_REGS; t++) rp->p_reg[t] = 0100*t; /* debugging */ X t = rp - proc - NR_TASKS; /* task number */ X rp->p_sp = (rp < &proc[NR_TASKS] ? t_stack[NR_TASKS+t+1].stk : INIT_SP); X rp->p_splimit = rp->p_sp; X if (rp->p_splimit != INIT_SP) X rp->p_splimit -= (TASK_STACK_BYTES - SAFETY)/sizeof(int); X rp->p_pcpsw.pc = task[t + NR_TASKS]; X if (rp->p_pcpsw.pc != 0 || t >= 0) ready(rp); X rp->p_pcpsw.psw = INIT_PSW; X rp->p_flags = 0; X X /* Set up memory map for tasks and MM, FS, INIT. */ X if (t < 0) { X /* I/O tasks. */ X rp->p_map[T].mem_len = VERY_BIG; X rp->p_map[T].mem_phys = base_click; X rp->p_map[D].mem_len = VERY_BIG; X rp->p_map[D].mem_phys = base_click + sizes[0]; X rp->p_map[S].mem_len = VERY_BIG; X rp->p_map[S].mem_phys = base_click + sizes[0] + sizes[1]; X rp->p_map[S].mem_vir = sizes[0] + sizes[1]; X } else { X /* MM, FS, and INIT. */ X previous_base = proc[NR_TASKS + t - 1].p_map[S].mem_phys; X rp->p_map[T].mem_len = sizes[2*t + 2]; X rp->p_map[T].mem_phys = (t == 0 ? mm_base : previous_base); X rp->p_map[D].mem_len = sizes[2*t + 3]; X rp->p_map[D].mem_phys = rp->p_map[T].mem_phys + sizes[2*t + 2]; X rp->p_map[S].mem_vir = sizes[2*t + 3]; X rp->p_map[S].mem_phys = rp->p_map[D].mem_phys + sizes[2*t + 3]; X } X X#ifdef i8088 X rp->p_reg[CS_REG] = rp->p_map[T].mem_phys; X rp->p_reg[DS_REG] = rp->p_map[D].mem_phys; X rp->p_reg[SS_REG] = rp->p_map[D].mem_phys; X rp->p_reg[ES_REG] = rp->p_map[D].mem_phys; X#endif X } X X proc[NR_TASKS+(HARDWARE)].p_sp = (int *) k_stack; X proc[NR_TASKS+(HARDWARE)].p_sp += K_STACK_BYTES/2; X proc[NR_TASKS+(HARDWARE)].p_splimit = (int *) k_stack; X proc[NR_TASKS+(HARDWARE)].p_splimit += SAFETY/2; X X for (rp = proc_addr(LOW_USER+1); rp < proc_addr(NR_PROCS); rp++) X rp->p_flags = P_SLOT_FREE; X X /* Determine if display is color or monochrome and CPU type (from BIOS). */ X color = get_chrome(); /* 0 = mono, 1 = color */ X hw_id = UNKNOWN; X t = get_byte(CPU_TY1, CPU_TY2); /* is this PC, XT, AT ... ? */ X if (t == PC_AT) hw_id = IBMAT; X if (hw_id == UNKNOWN && is_m24() == OK) hw_id = M24; X X /* Save the old interrupt vectors. */ X phys_b = umap(proc_addr(HARDWARE), D, (vir_bytes) vec_table, VECTOR_BYTES); X phys_copy(0L, phys_b, (long) VECTOR_BYTES); /* save all the vectors */ X X /* Set up the new interrupt vectors. */ X for (t = 0; t < 16; t++) set_vec(t, surprise, base_click); X for (t = 16; t < 256; t++) set_vec(t, trp, base_click); X set_vec(DIVIDE_VECTOR, divide, base_click); X set_vec(SYS_VECTOR, s_call, base_click); X set_vec(CLOCK_VECTOR, clock_int, base_click); X set_vec(KEYBOARD_VECTOR, tty_int, base_click); X set_vec(FLOPPY_VECTOR, disk_int, base_click); X set_vec(PRINTER_VECTOR, lpr_int, base_click); X if (hw_id == IBMAT) X set_vec(AT_WINI_VECTOR, wini_int, base_click); X else X set_vec(XT_WINI_VECTOR, wini_int, base_click); X X /* Put a ptr to proc table in a known place so it can be found in /dev/mem */ X set_vec( (BASE - 4)/4, proc, (phys_clicks) 0); X X bill_ptr = proc_addr(HARDWARE); /* it has to point somewhere */ X pick_proc(); X X /* Now go to the assembly code to start running the current process. */ X port_out(INT_CTLMASK, 0); /* do not mask out any interrupts in 8259A */ X port_out(INT2_MASK, 0); /* same for second interrupt controller */ X restart(); X} X X X/*===========================================================================* X * unexpected_int * X *===========================================================================*/ XPUBLIC unexpected_int() X{ X/* A trap or interrupt has occurred that was not expected. */ X X printf("Unexpected trap: vector < 16\n"); X printf("pc = 0x%x text+data+bss = 0x%x\n",proc_ptr->p_pcpsw.pc, X proc_ptr->p_map[D].mem_len<<4); X} X X X/*===========================================================================* X * trap * X *===========================================================================*/ XPUBLIC trap() X{ X/* A trap (vector >= 16) has occurred. It was not expected. */ X X printf("\nUnexpected trap: vector >= 16 "); X printf("This may be due to accidentally including\n"); X printf("a non-MINIX library routine that is trying to make a system call.\n"); X printf("pc = 0x%x text+data+bss = 0x%x\n",proc_ptr->p_pcpsw.pc, X proc_ptr->p_map[D].mem_len<<4); X} X X X/*===========================================================================* X * div_trap * X *===========================================================================*/ XPUBLIC div_trap() X{ X/* The divide intruction traps to vector 0 upon overflow. */ X X printf("Trap to vector 0: divide overflow. "); X printf("pc = 0x%x text+data+bss = 0x%x\n",proc_ptr->p_pcpsw.pc, X proc_ptr->p_map[D].mem_len<<4); X} X X X/*===========================================================================* X * panic * X *===========================================================================*/ XPUBLIC panic(s,n) Xchar *s; Xint n; X{ X/* The system has run aground of a fatal error. Terminate execution. X * If the panic originated in MM or FS, the string will be empty and the X * file system already syncked. If the panic originates in the kernel, we are X * kind of stuck. X */ X X if (*s != 0) { X printf("\nKernel panic: %s",s); X if (n != NO_NUM) printf(" %d", n); X printf("\n"); X } X printf("\nType space to reboot\n"); X wreboot(); X X} X X#ifdef i8088 X/*===========================================================================* X * set_vec * X *===========================================================================*/ XPRIVATE set_vec(vec_nr, addr, base_click) Xint vec_nr; /* which vector */ Xint (*addr)(); /* where to start */ Xphys_clicks base_click; /* click where kernel sits in memory */ X{ X/* Set up an interrupt vector. */ X X unsigned vec[2]; X unsigned u; X phys_bytes phys_b; X extern unsigned sizes[8]; X X /* Build the vector in the array 'vec'. */ X vec[0] = (unsigned) addr; X vec[1] = (unsigned) base_click; X u = (unsigned) vec; X X /* Copy the vector into place. */ X phys_b = ( (phys_bytes) base_click + (phys_bytes) sizes[0]) << CLICK_SHIFT; X phys_b += u; X phys_copy(phys_b, (phys_bytes) 4*vec_nr, (phys_bytes) 4); X} X#endif X X/*===========================================================================* X * is_m24 * X *===========================================================================*/ XPRIVATE is_m24() X{ X/* Reads the BIOS to to check is the hardware is a Olivetti M24 X * This routine works for BIOS versions 1.1, 1.21 and 1.36 X */ X X char buffer[9]; X phys_bytes buffaddr; X X /* read copyright string at FC00:50 */ X buffaddr = umap(proc_addr(HARDWARE), D, (vir_bytes) buffer, 8); X phys_copy(0xFC050L, buffaddr, 8L); X X /* test string */ X buffer[8] = '\0'; X if(strcmp(buffer,"OLIVETTI") == 0) X return(TRUE); X return(FALSE); X} *-*-END-of-k_main.c-*-* echo x - k_make. sed 's/^X//' >k_make. <<'*-*-END-of-k_make.-*-*' X# The kernel directory contains files xt_wini.c and at_wini.c. Before running X# make you must copy one of these to wini.c, depending on whether you have a X# PC or an AT. You must do this even if you do not have a hard disk.. XCFLAGS= -Di8088 -w -F -T. Xh=../h Xl=/usr/lib X Xobj = mpx88.s main.s proc.s system.s tty.s clock.s memory.s floppy.s wini.s \ X printer.s table.s klib88.s dmp.s X Xkernel: makefile $(obj) $l/libc.a X# @echo "Start linking Kernel. /lib/* will be removed to make space on RAM disk" X# @rm -f /lib/cem /lib/cpp /tmp/* X# @asld -o kernel $(obj) $l/libc.a $l/end.s X# @echo "Kernel done. Please restore /lib/cem and /lib/cpp manually" X @echo "Start linking Kernel." X @asld -o kernel -T. $(obj) $l/libc.a $l/end.s X @echo "Kernel done." X Xclock.s: const.h type.h $h/const.h $h/type.h Xclock.s: $h/callnr.h Xclock.s: $h/com.h Xclock.s: $h/error.h Xclock.s: $h/signal.h Xclock.s: glo.h Xclock.s: proc.h X Xfloppy.s: const.h type.h $h/const.h $h/type.h Xfloppy.s: $h/callnr.h Xfloppy.s: $h/com.h Xfloppy.s: $h/error.h Xfloppy.s: glo.h Xfloppy.s: proc.h X X Xdmp.s: const.h type.h $h/const.h $h/type.h Xdmp.s: $h/callnr.h Xdmp.s: $h/com.h Xdmp.s: $h/error.h Xdmp.s: glo.h Xdmp.s: proc.h X Xmain.s: const.h type.h $h/const.h $h/type.h Xmain.s: $h/callnr.h Xmain.s: $h/com.h Xmain.s: $h/error.h Xmain.s: glo.h Xmain.s: proc.h X Xmemory.s: const.h type.h $h/const.h $h/type.h Xmemory.s: $h/callnr.h Xmemory.s: $h/com.h Xmemory.s: $h/error.h Xmemory.s: proc.h X Xprinter.s: const.h type.h $h/const.h $h/type.h Xprinter.s: $h/callnr.h Xprinter.s: $h/com.h Xprinter.s: $h/error.h X Xproc.s: const.h type.h $h/const.h $h/type.h Xproc.s: $h/callnr.h Xproc.s: $h/com.h Xproc.s: $h/error.h Xproc.s: glo.h Xproc.s: proc.h X Xsystem.s: const.h type.h $h/const.h $h/type.h Xsystem.s: $h/callnr.h Xsystem.s: $h/com.h Xsystem.s: $h/error.h Xsystem.s: $h/signal.h Xsystem.s: glo.h Xsystem.s: proc.h X Xtable.s: const.h type.h $h/const.h $h/type.h Xtable.s: glo.h Xtable.s: proc.h X Xtty.s: const.h type.h $h/const.h $h/type.h Xtty.s: $h/callnr.h Xtty.s: $h/com.h Xtty.s: $h/error.h Xtty.s: $h/sgtty.h Xtty.s: $h/signal.h Xtty.s: glo.h Xtty.s: proc.h X Xwini.s: const.h type.h $h/const.h $h/type.h Xwini.s: $h/callnr.h Xwini.s: $h/com.h Xwini.s: $h/error.h Xwini.s: proc.h *-*-END-of-k_makmmmb d; /*
sch@oce.nl (Jakob Schripsema) (04/27/87)
echo x - k_proc.c sed 's/^X//' >k_proc.c <<'*-*-END-of-k_proc.c-*-*' X/* This file contains essentially all of the process and message handling. X * It has two main entry points from the outside: X * X * sys_call: called when a process or task does SEND, RECEIVE or SENDREC X * interrupt: called by interrupt routines to send a message to task X * X * It also has five minor entry points: X * X * ready: put a process on one of the ready queues so it can be run X * unready: remove a process from the ready queues X * sched: a process has run too long; schedule another one X * mini_send: send a message (used by interrupt signals, etc.) X * pick_proc: pick a process to run (used by system initialization) X */ X X#include "../h/const.h" X#include "../h/type.h" X#include "../h/callnr.h" X#include "../h/com.h" X#include "../h/error.h" X#include "const.h" X#include "type.h" X#include "glo.h" X#include "proc.h" X X/*===========================================================================* X * interrupt * X *===========================================================================*/ XPUBLIC interrupt(task, m_ptr) Xint task; /* number of task to be started */ Xmessage *m_ptr; /* interrupt message to send to the task */ X{ X/* An interrupt has occurred. Schedule the task that handles it. */ X X int i, n, old_map, this_bit; X X#ifdef i8088 X /* Re-enable the 8259A interrupt controller. */ X port_out(INT_CTL, ENABLE); /* this re-enables the 8259A controller chip */ X if (hw_id == IBMAT) port_out(INT2_CTL, ENABLE); /* re-enable second 8259A */ X#endif X X /* Try to send the interrupt message to the indicated task. */ X this_bit = 1 << (-task); X if (mini_send(HARDWARE, task, m_ptr) != OK) { X /* The message could not be sent to the task; it was not waiting. */ X old_map = busy_map; /* save original map of busy tasks */ X if (task == CLOCK) { X lost_ticks++; X } else { X busy_map |= this_bit; /* mark task as busy */ X task_mess[-task] = m_ptr; /* record message pointer */ X } X } else { X /* Hardware interrupt was successfully sent as a message. */ X busy_map &= ~this_bit; /* turn off the bit in case it was on */ X old_map = busy_map; X } X X /* See if any tasks that were previously busy are now listening for msgs. */ X if (old_map != 0) { X for (i = 2; i <= NR_TASKS; i++) { X /* Check each task looking for one with a pending interrupt. */ X if ( (old_map>>i) & 1) { X /* Task 'i' has a pending interrupt. */ X n = mini_send(HARDWARE, -i, task_mess[i]); X if (n == OK) busy_map &= ~(1 << i); X } X } X } X X /* If a task has just been readied and a user is running, run the task. */ X if (rdy_head[TASK_Q] != NIL_PROC && (cur_proc >= 0 || cur_proc == IDLE)) X pick_proc(); X} X X X/*===========================================================================* X * sys_call * X *===========================================================================*/ XPUBLIC sys_call(function, caller, src_dest, m_ptr) Xint function; /* SEND, RECEIVE, or BOTH */ Xint caller; /* who is making this call */ Xint src_dest; /* source to receive from or dest to send to */ Xmessage *m_ptr; /* pointer to message */ X{ X/* The only system calls that exist in MINIX are sending and receiving X * messages. These are done by trapping to the kernel with an INT instruction. X * The trap is caught and sys_call() is called to send or receive a message (or X * both). X */ X X register struct proc *rp; X int n; X X /* Check for bad system call parameters. */ X rp = proc_addr(caller); X if (src_dest < -NR_TASKS || (src_dest >= NR_PROCS && src_dest != ANY) ) { X rp->p_reg[RET_REG] = E_BAD_SRC; X return; X } X if (function != BOTH && caller >= LOW_USER) { X rp->p_reg[RET_REG] = E_NO_PERM; /* users only do BOTH */ X return; X } X X /* The parameters are ok. Do the call. */ X if (function & SEND) { X n = mini_send(caller, src_dest, m_ptr); /* func = SEND or BOTH */ X if (function == SEND || n != OK) rp->p_reg[RET_REG] = n; X if (n != OK) return; /* SEND failed */ X } X X if (function & RECEIVE) { X n = mini_rec(caller, src_dest, m_ptr); /* func = RECEIVE or BOTH */ X rp->p_reg[RET_REG] = n; X } X} X X/*===========================================================================* X * mini_send * X *===========================================================================*/ XPUBLIC int mini_send(caller, dest, m_ptr) Xint caller; /* who is trying to send a message? */ Xint dest; /* to whom is message being sent? */ Xmessage *m_ptr; /* pointer to message buffer */ X{ X/* Send a message from 'caller' to 'dest'. If 'dest' is blocked waiting for X * this message, copy the message to it and unblock 'dest'. If 'dest' is not X * waiting at all, or is waiting for another source, queue 'caller'. X */ X X register struct proc *caller_ptr, *dest_ptr, *next_ptr; X vir_bytes vb; /* message buffer pointer as vir_bytes */ X vir_clicks vlo, vhi; /* virtual clicks containing message to send */ X vir_clicks len; /* length of data segment in clicks */ X X /* User processes are only allowed to send to FS and MM. Check for this. */ X if (caller >= LOW_USER && (dest != FS_PROC_NR && dest != MM_PROC_NR)) X return(E_BAD_DEST); X caller_ptr = proc_addr(caller); /* pointer to source's proc entry */ X dest_ptr = proc_addr(dest); /* pointer to destination's proc entry */ X if (dest_ptr->p_flags & P_SLOT_FREE) return(E_BAD_DEST); /* dead dest */ X X /* Check for messages wrapping around top of memory or outside data seg. */ X len = caller_ptr->p_map[D].mem_len; X vb = (vir_bytes) m_ptr; X vlo = vb >> CLICK_SHIFT; /* vir click for bottom of message */ X vhi = (vb + MESS_SIZE - 1) >> CLICK_SHIFT; /* vir click for top of message */ X if (vhi < vlo || vhi - caller_ptr->p_map[D].mem_vir >= len)return(E_BAD_ADDR); X X /* Check to see if 'dest' is blocked waiting for this message. */ X if ( (dest_ptr->p_flags & RECEIVING) && X (dest_ptr->p_getfrom == ANY || dest_ptr->p_getfrom == caller) ) { X /* Destination is indeed waiting for this message. */ X cp_mess(caller, caller_ptr->p_map[D].mem_phys, m_ptr, X dest_ptr->p_map[D].mem_phys, dest_ptr->p_messbuf); X dest_ptr->p_flags &= ~RECEIVING; /* deblock destination */ X if (dest_ptr->p_flags == 0) ready(dest_ptr); X } else { X /* Destination is not waiting. Block and queue caller. */ X if (caller == HARDWARE) return(E_OVERRUN); X caller_ptr->p_messbuf = m_ptr; X caller_ptr->p_flags |= SENDING; X unready(caller_ptr); X X /* Process is now blocked. Put in on the destination's queue. */ X if ( (next_ptr = dest_ptr->p_callerq) == NIL_PROC) { X dest_ptr->p_callerq = caller_ptr; X } else { X while (next_ptr->p_sendlink != NIL_PROC) X next_ptr = next_ptr->p_sendlink; X next_ptr->p_sendlink = caller_ptr; X } X caller_ptr->p_sendlink = NIL_PROC; X } X return(OK); X} X X X/*===========================================================================* X * mini_rec * X *===========================================================================*/ XPRIVATE int mini_rec(caller, src, m_ptr) Xint caller; /* process trying to get message */ Xint src; /* which message source is wanted (or ANY) */ Xmessage *m_ptr; /* pointer to message buffer */ X{ X/* A process or task wants to get a message. If one is already queued, X * acquire it and deblock the sender. If no message from the desired source X * is available, block the caller. No need to check parameters for validity. X * Users calls are always sendrec(), and mini_send() has checked already. X * Calls from the tasks, MM, and FS are trusted. X */ X X register struct proc *caller_ptr, *sender_ptr, *prev_ptr; X int sender; X X caller_ptr = proc_addr(caller); /* pointer to caller's proc structure */ X X /* Check to see if a message from desired source is already available. */ X sender_ptr = caller_ptr->p_callerq; X while (sender_ptr != NIL_PROC) { X sender = sender_ptr - proc - NR_TASKS; X if (src == ANY || src == sender) { X /* An acceptable message has been found. */ X cp_mess(sender, sender_ptr->p_map[D].mem_phys, sender_ptr->p_messbuf, X caller_ptr->p_map[D].mem_phys, m_ptr); X sender_ptr->p_flags &= ~SENDING; /* deblock sender */ X if (sender_ptr->p_flags == 0) ready(sender_ptr); X if (sender_ptr == caller_ptr->p_callerq) X caller_ptr->p_callerq = sender_ptr->p_sendlink; X else X prev_ptr->p_sendlink = sender_ptr->p_sendlink; X return(OK); X } X prev_ptr = sender_ptr; X sender_ptr = sender_ptr->p_sendlink; X } X X /* No suitable message is available. Block the process trying to receive. */ X caller_ptr->p_getfrom = src; X caller_ptr->p_messbuf = m_ptr; X caller_ptr->p_flags |= RECEIVING; X unready(caller_ptr); X X /* If MM has just blocked and there are kernel signals pending, now is the X * time to tell MM about them, since it will be able to accept the message. X */ X if (sig_procs > 0 && caller == MM_PROC_NR && src == ANY) inform(MM_PROC_NR); X return(OK); X} X X X/*===========================================================================* X * pick_proc * X *===========================================================================*/ XPUBLIC pick_proc() X{ X/* Decide who to run now. */ X X register int q; /* which queue to use */ X X if (rdy_head[TASK_Q] != NIL_PROC) q = TASK_Q; X else if (rdy_head[SERVER_Q] != NIL_PROC) q = SERVER_Q; X else q = USER_Q; X X /* Set 'cur_proc' and 'proc_ptr'. If system is idle, set 'cur_proc' to a X * special value (IDLE), and set 'proc_ptr' to point to an unused proc table X * slot, namely, that of task -1 (HARDWARE), so save() will have somewhere to X * deposit the registers when a interrupt occurs on an idle machine. X * Record previous process so that when clock tick happens, the clock task X * can find out who was running just before it began to run. (While the X * clock task is running, 'cur_proc' = CLOCKTASK. In addition, set 'bill_ptr' X * to always point to the process to be billed for CPU time. X */ X prev_proc = cur_proc; X if (rdy_head[q] != NIL_PROC) { X /* Someone is runnable. */ X cur_proc = rdy_head[q] - proc - NR_TASKS; X proc_ptr = rdy_head[q]; X if (cur_proc >= LOW_USER) bill_ptr = proc_ptr; X } else { X /* No one is runnable. */ X cur_proc = IDLE; X proc_ptr = proc_addr(HARDWARE); X bill_ptr = proc_ptr; X } X} X X/*===========================================================================* X * ready * X *===========================================================================*/ XPUBLIC ready(rp) Xregister struct proc *rp; /* this process is now runnable */ X{ X/* Add 'rp' to the end of one of the queues of runnable processes. Three X * queues are maintained: X * TASK_Q - (highest priority) for runnable tasks X * SERVER_Q - (middle priority) for MM and FS only X * USER_Q - (lowest priority) for user processes X */ X X register int q; /* TASK_Q, SERVER_Q, or USER_Q */ X int r; X X lock(); /* disable interrupts */ X r = (rp - proc) - NR_TASKS; /* task or proc number */ X q = (r < 0 ? TASK_Q : r < LOW_USER ? SERVER_Q : USER_Q); X X /* See if the relevant queue is empty. */ X if (rdy_head[q] == NIL_PROC) X rdy_head[q] = rp; /* add to empty queue */ X else X rdy_tail[q]->p_nextready = rp; /* add to tail of nonempty queue */ X rdy_tail[q] = rp; /* new entry has no successor */ X rp->p_nextready = NIL_PROC; X restore(); /* restore interrupts to previous state */ X} X X X/*===========================================================================* X * unready * X *===========================================================================*/ XPUBLIC unready(rp) Xregister struct proc *rp; /* this process is no longer runnable */ X{ X/* A process has blocked. */ X X register struct proc *xp; X int r, q; X X lock(); /* disable interrupts */ X r = rp - proc - NR_TASKS; X q = (r < 0 ? TASK_Q : r < LOW_USER ? SERVER_Q : USER_Q); X if ( (xp = rdy_head[q]) == NIL_PROC) return; X if (xp == rp) { X /* Remove head of queue */ X rdy_head[q] = xp->p_nextready; X pick_proc(); X } else { X /* Search body of queue. A process can be made unready even if it is X * not running by being sent a signal that kills it. X */ X while (xp->p_nextready != rp) X if ( (xp = xp->p_nextready) == NIL_PROC) return; X xp->p_nextready = xp->p_nextready->p_nextready; X while (xp->p_nextready != NIL_PROC) xp = xp->p_nextready; X rdy_tail[q] = xp; X } X restore(); /* restore interrupts to previous state */ X} X X X/*===========================================================================* X * sched * X *===========================================================================*/ XPUBLIC sched() X{ X/* The current process has run too long. If another low priority (user) X * process is runnable, put the current process on the end of the user queue, X * possibly promoting another user to head of the queue. X */ X X lock(); /* disable interrupts */ X if (rdy_head[USER_Q] == NIL_PROC) { X restore(); /* restore interrupts to previous state */ X return; X } X X /* One or more user processes queued. */ X rdy_tail[USER_Q]->p_nextready = rdy_head[USER_Q]; X rdy_tail[USER_Q] = rdy_head[USER_Q]; X rdy_head[USER_Q] = rdy_head[USER_Q]->p_nextready; X rdy_tail[USER_Q]->p_nextready = NIL_PROC; X pick_proc(); X restore(); /* restore interrupts to previous state */ X} *-*-END-of-k_proc.c-*-* echo x - k_tty.c sed 's/^X//' >k_tty.c <<'*-*-END-of-k_tty.c-*-*' X/* X * Kernel debugging routine X */ X X/*===========================================================================* X * wfc * X *===========================================================================*/ X Xwfc() X{ X/* routine to wait for a character from the console. used for kernel debugging */ X X tty_driver_buf[0] = 0; X while(tty_driver_buf[0] == 0); X tty_driver_buf[0] = 0; X} *-*-END-of-k_tty.c-*-* echo x - k_wini.c sed 's/^X//' >k_wini.c <<'*-*-END-of-k_wini.c-*-*' X/* This file contains a driver for the IBM or DTC winchester controller. X * It was written by Adri Koppes. X * X * The driver supports two operations: read a block and X * write a block. It accepts two messages, one for reading and one for X * writing, both using message format m2 and with the same parameters: X * X * m_type DEVICE PROC_NR COUNT POSITION ADRRESS X * ---------------------------------------------------------------- X * | DISK_READ | device | proc nr | bytes | offset | buf ptr | X * |------------+---------+---------+---------+---------+---------| X * | DISK_WRITE | device | proc nr | bytes | offset | buf ptr | X * ---------------------------------------------------------------- X * X * The file contains one entry point: X * X * winchester_task: main entry when system is brought up X * X * TODO : X * change struct param into struct disk X * struct disk should contain controller address of X * controller associated with each disk X * rewrite init routines from scratch X */ X X#include "../h/const.h" X#include "../h/type.h" X#include "../h/callnr.h" X#include "../h/com.h" X#include "../h/error.h" X#include "const.h" X#include "type.h" X#include "proc.h" X X/* I/O Ports used by winchester disk task. */ X#define WIN_DATA 0x320 /* winchester disk controller data register */ X#define WIN_STATUS 0x321 /* winchester disk controller status register */ X#define WIN_SELECT 0x322 /* winchester disk controller select port */ X#define WIN_DMA 0x323 /* winchester disk controller dma register */ X#define DMA_ADDR 0x006 /* port for low 16 bits of DMA address */ X#define DMA_TOP 0x082 /* port for top 4 bits of 20-bit DMA addr */ X#define DMA_COUNT 0x007 /* port for DMA count (count = bytes - 1) */ X#define DMA_M2 0x00C /* DMA status port */ X#define DMA_M1 0x00B /* DMA status port */ X#define DMA_INIT 0x00A /* DMA init port */ X X/* Winchester disk controller command bytes. */ X#define WIN_RECALIBRATE 0x01 /* command for the drive to recalibrate */ X#define WIN_SENSE 0x03 /* command for the controller to get its status */ X#define WIN_READ 0x08 /* command for the drive to read */ X#define WIN_WRITE 0x0a /* command for the drive to write */ X#define WIN_SPECIFY 0x0C /* command for the controller to accept params */ X#define WIN_ECC_READ 0x0D /* command for the controller to read ecc length */ X X#define DMA_INT 3 /* Command with dma and interrupt */ X#define INT 2 /* Command with interrupt, no dma */ X#define NO_DMA_INT 0 /* Command without dma and interrupt */ X#define CTRL_BYTE 5 /* Control byte for controller */ X X/* DMA channel commands. */ X#define DMA_READ 0x47 /* DMA read opcode */ X#define DMA_WRITE 0x4B /* DMA write opcode */ X X/* Parameters for the disk drive. */ X#define SECTOR_SIZE 512 /* physical sector size in bytes */ X#define NR_SECTORS 0x11 /* number of sectors per track */ X X/* Error codes */ X#define ERR -1 /* general error */ X X/* Miscellaneous. */ X#define MAX_ERRORS 4 /* how often to try rd/wt before quitting */ X#define MAX_RESULTS 4 /* max number of bytes controller returns */ X#define NR_DEVICES 10 /* maximum number of drives */ X#define MAX_WIN_RETRY 10000 /* max # times to try to output to WIN */ X#define PART_TABLE 0x1C6 /* IBM partition table starts here in sect 0 */ X#define DEV_PER_DRIVE 5 /* hd0 + hd1 + hd2 + hd3 + hd4 = 5 */ X X/* Variables. */ XPRIVATE struct wini { /* main drive struct, one entry per drive */ X int wn_opcode; /* DISK_READ or DISK_WRITE */ X int wn_procnr; /* which proc wanted this operation? */ X int wn_drive; /* drive number addressed */ X int wn_cylinder; /* cylinder number addressed */ X int wn_sector; /* sector addressed */ X int wn_head; /* head number addressed */ X int wn_heads; /* maximum number of heads */ X long wn_low; /* lowest cylinder of partition */ X long wn_size; /* size of partition in blocks */ X int wn_count; /* byte count */ X vir_bytes wn_address; /* user virtual address */ X char wn_results[MAX_RESULTS]; /* the controller can give lots of output */ X} wini[NR_DEVICES]; X XPRIVATE int w_need_reset = FALSE; /* set to 1 when controller must be reset */ XPRIVATE int nr_drives; /* Number of drives */ X XPRIVATE message w_mess; /* message buffer for in and out */ X XPRIVATE int command[6]; /* Common command block */ X XPRIVATE unsigned char buf[BLOCK_SIZE]; /* Buffer used by the startup routine */ X XPRIVATE struct param { X int nr_cyl; /* Number of cylinders */ X int nr_heads; /* Number of heads */ X int reduced_wr; /* First cylinder with reduced write current */ X int wr_precomp; /* First cylinder with write precompensation */ X int max_ecc; /* Maximum ECC burst length */ X} param0, param1; X X/*===========================================================================* X * winchester_task * X *===========================================================================*/ XPUBLIC winchester_task() X{ X/* Main program of the winchester disk driver task. */ X X int r, caller, proc_nr; X X /* First initialize the controller */ X init_param(); X X /* Here is the main loop of the disk task. It waits for a message, carries X * it out, and sends a reply. X */ X X while (TRUE) { X /* First wait for a request to read or write a disk block. */ X receive(ANY, &w_mess); /* get a request to do some work */ X if (w_mess.m_source < 0) { X printf("winchester task got message from %d ", w_mess.m_source); X continue; X } X caller = w_mess.m_source; X proc_nr = w_mess.PROC_NR; X X /* Now carry out the work. */ X switch(w_mess.m_type) { X case DISK_READ: X case DISK_WRITE: r = w_do_rdwt(&w_mess); break; X default: r = EINVAL; break; X } X X /* Finally, prepare and send the reply message. */ X w_mess.m_type = TASK_REPLY; X w_mess.REP_PROC_NR = proc_nr; X X w_mess.REP_STATUS = r; /* # of bytes transferred or error code */ X send(caller, &w_mess); /* send reply to caller */ X } X} X X X/*===========================================================================* X * w_do_rdwt * X *===========================================================================*/ XPRIVATE int w_do_rdwt(m_ptr) Xmessage *m_ptr; /* pointer to read or write w_message */ X{ X/* Carry out a read or write request from the disk. */ X register struct wini *wn; X int r, device, errors = 0; X long sector; X X /* Decode the w_message parameters. */ X device = m_ptr->DEVICE; X if (device < 0 || device >= NR_DEVICES) X return(EIO); X if (m_ptr->COUNT != BLOCK_SIZE) X return(EINVAL); X wn = &wini[device]; /* 'wn' points to entry for this drive */ X wn->wn_drive = device/DEV_PER_DRIVE; /* save drive number */ X if (wn->wn_drive >= nr_drives) X return(EIO); X wn->wn_opcode = m_ptr->m_type; /* DISK_READ or DISK_WRITE */ X if (m_ptr->POSITION % BLOCK_SIZE != 0) X return(EINVAL); X sector = m_ptr->POSITION/SECTOR_SIZE; X if ((sector+BLOCK_SIZE/SECTOR_SIZE) > wn->wn_size) X return(EOF); X sector += wn->wn_low; X wn->wn_cylinder = sector / (wn->wn_heads * NR_SECTORS); X wn->wn_sector = (sector % NR_SECTORS); X wn->wn_head = (sector % (wn->wn_heads * NR_SECTORS) )/NR_SECTORS; X wn->wn_count = m_ptr->COUNT; X wn->wn_address = (vir_bytes) m_ptr->ADDRESS; X wn->wn_procnr = m_ptr->PROC_NR; X X /* This loop allows a failed operation to be repeated. */ X while (errors <= MAX_ERRORS) { X errors++; /* increment count once per loop cycle */ X if (errors >= MAX_ERRORS) X return(EIO); X X /* First check to see if a reset is needed. */ X if (w_need_reset) w_reset(); X X /* Now set up the DMA chip. */ X w_dma_setup(wn); X X /* Perform the transfer. */ X r = w_transfer(wn); X if (r == OK) break; /* if successful, exit loop */ X X } X X return(r == OK ? BLOCK_SIZE : EIO); X} X X X/*===========================================================================* X * w_dma_setup * X *===========================================================================*/ XPRIVATE w_dma_setup(wn) Xstruct wini *wn; /* pointer to the drive struct */ X{ X/* The IBM PC can perform DMA operations by using the DMA chip. To use it, X * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address X * to by read from or written to, the byte count minus 1, and a read or write X * opcode. This routine sets up the DMA chip. Note that the chip is not X * capable of doing a DMA across a 64K boundary (e.g., you can't read a X * 512-byte block starting at physical address 65520). X */ X X int mode, low_addr, high_addr, top_addr, low_ct, high_ct, top_end; X vir_bytes vir, ct; X phys_bytes user_phys; X extern phys_bytes umap(); X X mode = (wn->wn_opcode == DISK_READ ? DMA_READ : DMA_WRITE); X vir = (vir_bytes) wn->wn_address; X ct = (vir_bytes) wn->wn_count; X user_phys = umap(proc_addr(wn->wn_procnr), D, vir, ct); X low_addr = (int) user_phys & BYTE; X high_addr = (int) (user_phys >> 8) & BYTE; X top_addr = (int) (user_phys >> 16) & BYTE; X low_ct = (int) (ct - 1) & BYTE; X high_ct = (int) ( (ct - 1) >> 8) & BYTE; X X /* Check to see if the transfer will require the DMA address counter to X * go from one 64K segment to another. If so, do not even start it, since X * the hardware does not carry from bit 15 to bit 16 of the DMA address. X * Also check for bad buffer address. These errors mean FS contains a bug. X */ X if (user_phys == 0) panic("FS gave winchester disk driver bad addr", (int) vir); X top_end = (int) (((user_phys + ct - 1) >> 16) & BYTE); X if (top_end != top_addr) panic("Trying to DMA across 64K boundary", top_addr); X X /* Now set up the DMA registers. */ X lock(); X port_out(DMA_M2, mode); /* set the DMA mode */ X port_out(DMA_M1, mode); /* set it again */ X port_out(DMA_ADDR, low_addr); /* output low-order 8 bits */ X port_out(DMA_ADDR, high_addr);/* output next 8 bits */ X port_out(DMA_TOP, top_addr); /* output highest 4 bits */ X port_out(DMA_COUNT, low_ct); /* output low 8 bits of count - 1 */ X port_out(DMA_COUNT, high_ct); /* output high 8 bits of count - 1 */ X unlock(); X} X X/*===========================================================================* X * w_transfer * X *===========================================================================*/ XPRIVATE int w_transfer(wn) Xregister struct wini *wn; /* pointer to the drive struct */ X{ X/* The drive is now on the proper cylinder. Read or write 1 block. */ X X /* The command is issued by outputing 6 bytes to the controller chip. */ X command[0] = (wn->wn_opcode == DISK_READ ? WIN_READ : WIN_WRITE); X command[1] = (wn->wn_head | (wn->wn_drive << 5)); X command[2] = (((wn->wn_cylinder & 0x0300) >> 2) | wn->wn_sector); X command[3] = (wn->wn_cylinder & 0xFF); X command[4] = BLOCK_SIZE/SECTOR_SIZE; X command[5] = CTRL_BYTE; X if (com_out(DMA_INT) != OK) X return(ERR); X X port_out(DMA_INIT, 3); /* initialize DMA */ X /* Block, waiting for disk interrupt. */ X receive(HARDWARE, &w_mess); X X /* Get controller status and check for errors. */ X if (win_results(wn) == OK) X return(OK); X if ((wn->wn_results[0] & 63) == 24) X read_ecc(); X else X w_need_reset = TRUE; X return(ERR); X} X X X/*===========================================================================* X * win_results * X *===========================================================================*/ XPRIVATE int win_results(wn) Xregister struct wini *wn; /* pointer to the drive struct */ X{ X/* Extract results from the controller after an operation. */ X X register int i; X int status; X X port_in(WIN_DATA, &status); X port_out(WIN_DMA, 0); X if (!(status & 2)) X return(OK); X command[0] = WIN_SENSE; X command[1] = (wn->wn_drive << 5); X if (com_out(NO_DMA_INT) != OK) X return(ERR); X X /* Loop, extracting bytes from WIN */ X for (i = 0; i < MAX_RESULTS; i++) { X if (hd_wait(1) != OK) X return(ERR); X port_in(WIN_DATA, &status); X wn->wn_results[i] = status & BYTE; X } X if (wn->wn_results[0] & 63) X return(ERR); X else X return(OK); X} X X X/*===========================================================================* X * win_out * X *===========================================================================*/ XPRIVATE win_out(val) Xint val; /* write this byte to winchester disk controller */ X{ X/* Output a byte to the controller. This is not entirely trivial, since you X * can only write to it when it is listening, and it decides when to listen. X * If the controller refuses to listen, the WIN chip is given a hard reset. X */ X X if (w_need_reset) return; /* if controller is not listening, return */ X if (hd_wait(1) == OK) X port_out(WIN_DATA, val); X} X X/*===========================================================================* X * w_reset * X *===========================================================================*/ XPRIVATE w_reset() X{ X/* Issue a reset to the controller. This is done after any catastrophe, X * like the controller refusing to respond. X */ X X int r = 1, i; X X /* Strobe reset bit low. */ X port_out(WIN_STATUS, r); X for (i = 0; i < 10000; i++) { X port_in(WIN_STATUS, &r); X if ( (r&01) == 0)break; X } X if (r & 2) { X printf("Hard disk won't reset\n"); X return(ERR); X } X X /* Reset succeeded. Tell WIN drive parameters. */ X w_need_reset = FALSE; X X return(win_init()); X} X X/*===========================================================================* X * win_init * X *===========================================================================*/ XPRIVATE win_init() X{ X/* Routine to initialize the drive parameters after boot or reset */ X X register int i; X X command[0] = WIN_SPECIFY; /* Specify some parameters */ X command[1] = 0; /* Drive 0 */ X if (com_out(NO_DMA_INT) != OK) /* Output command block */ X return(ERR); X X lock(); X X /* No. of cylinders (high byte) */ X win_out(param0.nr_cyl >> 8); X X /* No. of cylinders (low byte) */ X win_out(param0.nr_cyl & 0xFF); X X /* No. of heads */ X win_out(param0.nr_heads); X X /* Start reduced write (high byte) */ X win_out(param0.reduced_wr >> 8); X X /* Start reduced write (low byte) */ X win_out(param0.reduced_wr & 0xFF); X X /* Start write precompensation (high byte) */ X win_out(param0.wr_precomp >> 8); X X /* Start write precompensation (low byte) */ X win_out(param0.wr_precomp & 0xFF); X X /* Ecc burst length */ X win_out(param0.max_ecc); X unlock(); X X if (check_init() != OK) { /* See if controller accepted parameters */ X w_need_reset = TRUE; X return(ERR); X } X X if (nr_drives > 1) { X command[1] = (1 << 5); /* Drive 1 */ X if (com_out(NO_DMA_INT) != OK) /* Output command block */ X return(ERR); X lock(); X X /* No. of cylinders (high byte) */ X win_out(param1.nr_cyl >> 8); X X /* No. of cylinders (low byte) */ X win_out(param1.nr_cyl & 0xFF); X X /* No. of heads */ X win_out(param1.nr_heads); X X /* Start reduced write (high byte) */ X win_out(param1.reduced_wr >> 8); X X /* Start reduced write (low byte) */ X win_out(param1.reduced_wr & 0xFF); X X /* Start write precompensation (high byte) */ X win_out(param1.wr_precomp >> 8); X X /* Start write precompensation (low byte) */ X win_out(param1.wr_precomp & 0xFF); X X /* Ecc burst length */ X win_out(param1.max_ecc); X unlock(); X if (check_init() != OK) { /* See if controller accepted parameters */ X w_need_reset = TRUE; X return(ERR); X } X } X for (i=0; i<nr_drives; i++) { X command[0] = WIN_RECALIBRATE; X command[1] = i << 5; X command[5] = CTRL_BYTE; X if (com_out(INT) != OK) X return(ERR); X receive(HARDWARE, &w_mess); X if (win_results() != OK) { X w_need_reset = TRUE; X return(ERR); X } X } X return(OK); X} X X/*============================================================================* X * check_init * X *============================================================================*/ XPRIVATE check_init() X{ X/* Routine to check if controller accepted the parameters */ X int r; X X if (hd_wait(2) == OK) { X port_in(WIN_DATA, &r); X if (r & 2) X return(ERR); X else X return(OK); X } X} X X/*============================================================================* X * read_ecc * X *============================================================================*/ XPRIVATE read_ecc() X{ X/* Read the ecc burst-length and let the controller correct the data */ X X int r; X X command[0] = WIN_ECC_READ; X if (com_out(NO_DMA_INT) == OK && hd_wait(1) == OK) { X port_in(WIN_DATA, &r); X if (hd_wait(1) == OK) { X port_in(WIN_DATA, &r); X if (r & 1) X w_need_reset = TRUE; X } X } X return(ERR); X} X X/*============================================================================* X * hd_wait * X *============================================================================*/ XPRIVATE hd_wait(bit) Xregister int bit; X{ X/* Wait until the controller is ready to receive a command or send status */ X X register int i = 0; X int r; X X do { X port_in(WIN_STATUS, &r); X r &= bit; X } while ((i++ < MAX_WIN_RETRY) && !r); X X if (i >= MAX_WIN_RETRY) { X w_need_reset = TRUE; X return(ERR); X } else X return(OK); X} X X/*============================================================================* X * com_out * X *============================================================================*/ XPRIVATE com_out(mode) Xint mode; X{ X/* Output the command block to the winchester controller and return status */ X X register int i = 0; X int r; X X port_out(WIN_SELECT, mode); X port_out(WIN_DMA, mode); X for (i=0; i<MAX_WIN_RETRY; i++) { X port_in(WIN_STATUS, &r); X if ((r & 0x0F) == 0x0D) X break; X } X if (i == MAX_WIN_RETRY) { X w_need_reset = TRUE; X return(ERR); X } X lock(); X for (i=0; i<6; i++) { X port_out(WIN_DATA, command[i]); X r = hd_wait(1); X if (r != OK) break; X port_in(WIN_STATUS,&r); X if ( (r&7) != 5) break; X } X unlock(); X return(OK); X} X X/*============================================================================* X * init_params * X *============================================================================*/ XPRIVATE init_params() X{ X/* This routine is called at startup to initialize the partition table, X * the number of drives and the controller X*/ X unsigned int i, segment, offset; X int type_0, type_1; X phys_bytes address; X extern phys_bytes umap(); X extern int vec_table[]; X X /* Read the switches from the controller */ X port_in(WIN_SELECT, &i); X X /* Calculate the drive types */ X type_0 = ((i >> 2) & 3) + ((i>>4) & 0xc); X type_1 = (i & 3) + ((i>>2) & 0xc); X X /* Copy the parameter vector from the saved vector table */ X offset = vec_table[2 * 0x41]; X segment = vec_table[2 * 0x41 + 1]; X X /* Calculate the address off the parameters and copy them to buf */ X address = ((long)segment << 4) + offset; X phys_copy(address, umap(proc_addr(WINCHESTER), D, buf, 64), 64L); X X /* Copy the parameters to the structures */ X copy_param((&buf[type_0 * 16]), ¶m0); X copy_param((&buf[type_1 * 16]), ¶m1); X X /* Get the nummer of drives from the bios */ X phys_copy(0x475L, umap(proc_addr(WINCHESTER), D, buf, 1), 1L); X nr_drives = (int) *buf; X X /* Set the parameters in the drive structure */ X for (i=0; i<5; i++) X wini[i].wn_heads = param0.nr_heads; X wini[0].wn_low = wini[5].wn_low = 0L; X wini[0].wn_size = (long)((long)param0.nr_cyl * (long)param0.nr_heads * (long)NR_SECTORS); X for (i=5; i<10; i++) X wini[i].wn_heads = param1.nr_heads; X wini[5].wn_size = (long)((long)param1.nr_cyl * (long)param1.nr_heads * (long)NR_SECTORS); X X X /* Initialize the controller */ X if ((nr_drives > 0) && (win_init() != OK)) X nr_drives = 0; X Xif (nr_drives == 0) { X printf("No winchester drive ?????\n"); X wfc(); X} X X /* Read the partition table for each drive and save them */ X for (i = 0; i < nr_drives; i++) { X w_mess.DEVICE = i * 5; X w_mess.POSITION = 0L; X w_mess.COUNT = BLOCK_SIZE; X w_mess.ADDRESS = (char *) buf; X w_mess.PROC_NR = WINCHESTER; X w_mess.m_type = DISK_READ; X if (w_do_rdwt(&w_mess) != BLOCK_SIZE) X panic("Can't read partition table of winchester ", i); X copy_prt(i * 5); X } X} X X/*============================================================================* X * copy_params * X *============================================================================*/ XPRIVATE copy_params(src, dest) Xregister unsigned char *src; Xregister struct param *dest; X{ X/* This routine copies the parameters from src to dest X * and sets the parameters for partition 0 and 5 X*/ X X dest->nr_cyl = *(int *)src; X dest->nr_heads = (int)src[2]; X dest->reduced_wr = *(int *)&src[3]; X dest->wr_precomp = *(int *)&src[5]; X dest->max_ecc = (int)src[7]; X} X X/*============================================================================* X * copy_prt * X *============================================================================*/ XPRIVATE copy_prt(drive) Xint drive; X{ X/* This routine copies the partition table for the selected drive to X * the variables wn_low and wn_size X */ X X register int i, offset; X struct wini *wn; X long adjust; X X for (i=0; i<4; i++) { X adjust = 0; X wn = &wini[i + drive + 1]; X offset = PART_TABLE + i * 0x10; X wn->wn_low = *(long *)&buf[offset]; X if ((wn->wn_low % (BLOCK_SIZE/SECTOR_SIZE)) != 0) { X adjust = wn->wn_low; X wn->wn_low = (wn->wn_low/(BLOCK_SIZE/SECTOR_SIZE)+1)*(BLOCK_SIZE/SECTOR_SIZE); X adjust = wn->wn_low - adjust; X } X wn->wn_size = *(long *)&buf[offset + sizeof(long)] - adjust; X } X sort(&wini[drive + 1]); X} X Xsort(wn) Xregister struct wini *wn; X{ X register int i,j; X X for (i=0; i<4; i++) X for (j=0; j<3; j++) X if ((wn[j].wn_low == 0) && (wn[j+1].wn_low != 0)) X swap(&wn[j], &wn[j+1]); X else if (wn[j].wn_low > wn[j+1].wn_low && wn[j+1].wn_low != 0) X swap(&wn[j], &wn[j+1]); X} X Xswap(first, second) Xregister struct wini *first, *second; X{ X register struct wini tmp; X X tmp = *first; X *first = *second; X *second = tmp; X} *-*-END-of-k_wini.c-*-* echo x - m_make. sed 's/^X//' >m_make. <<'*-*-END-of-m_make.-*-*' XCFLAGS = -Di8088 -w -F -T. Xh=../h Xl=/usr/lib X Xobj = main.s forkexit.s break.s exec.s signal.s getset.s \ X alloc.s utility.s table.s putc.s X Xmm: makefile $l/head.s $(obj) $l/libc.a $l/end.s X# @echo "Start linking MM. /lib/cem will be removed to make space on RAM disk" X# @rm -f /lib/cem /tmp/* X# @asld -o mm $l/head.s $(obj) $l/libc.a $l/end.s X# @echo "MM done. Please restore /lib/cem manually" X @echo "Start linking MM. X @asld -o mm -T. $l/head.s $(obj) $l/libc.a $l/end.s X @echo "MM done. X X Xalloc.s: const.h $h/const.h $h/type.h X Xbreak.s: const.h $h/const.h $h/type.h Xbreak.s: $h/error.h Xbreak.s: $h/signal.h Xbreak.s: glo.h Xbreak.s: mproc.h Xbreak.s: param.h X Xexec.s: const.h $h/const.h $h/type.h Xexec.s: $h/callnr.h Xexec.s: $h/error.h Xexec.s: $h/stat.h Xexec.s: glo.h Xexec.s: mproc.h Xexec.s: param.h X Xforkexit.s: const.h $h/const.h $h/type.h Xforkexit.s: $h/callnr.h Xforkexit.s: $h/error.h Xforkexit.s: glo.h Xforkexit.s: mproc.h Xforkexit.s: param.h X Xgetset.s: const.h $h/const.h $h/type.h Xgetset.s: $h/callnr.h Xgetset.s: $h/error.h Xgetset.s: glo.h Xgetset.s: mproc.h Xgetset.s: param.h X Xmain.s: const.h $h/const.h $h/type.h Xmain.s: $h/callnr.h Xmain.s: $h/com.h Xmain.s: $h/error.h Xmain.s: glo.h Xmain.s: mproc.h Xmain.s: param.h X Xputc.s: $h/const.h $h/type.h Xputc.s: $h/com.h X Xsignal.s: const.h $h/const.h $h/type.h Xsignal.s: $h/callnr.h Xsignal.s: $h/com.h Xsignal.s: $h/error.h Xsignal.s: $h/signal.h Xsignal.s: $h/stat.h Xsignal.s: glo.h Xsignal.s: mproc.h Xsignal.s: param.h X Xtable.s: const.h $h/const.h $h/type.h Xtable.s: $h/callnr.h Xtable.s: glo.h Xtable.s: mproc.h Xtable.s: param.h X Xutility.s: const.h $h/const.h $h/type.h Xutility.s: $h/callnr.h Xutility.s: $h/com.h Xutility.s: $h/error.h Xutility.s: $h/stat.h Xutility.s: glo.h Xutility.s: mproc.h *-*-END-of-m_make.-*-* echo x - readme.txt sed 's/^X//' >readme.txt <<'*-*-END-of-readme.txt-*-*' X XThis directorry should contain the following files : X X file to be placed in decription X ---- --------------- ---------- X Xk_floppy.c kernel/floppy.c changed startup time for M24 X added timeout X changes startup time if M24 X Xk_wini.c kernel/wini.c changed com_out for M24 X Xdiskpart.c tools/diskpart.c utility to partition harddisk X Xh_const.h h/const.h BOOT_DEV set to /dev/hd2 X Xk_const.h kernel/const.h added hardware types X Xk_glo.h kernel/glo.h changed pc_at into hw_id X Xk_main.c kernel/main.c added m24 detection X Xk_proc.c kernel/proc.c changed test on pc_at X Xk_tty.c kernel/tty.c added wfc() X *-*-END-of-readme.txt-*-* echo x - t_diskp.c sed 's/^X//' >t_diskp.c <<'*-*-END-of-t_diskp.c-*-*' X/* X * Diskpart. Display and modify partition table X * Written by Jakob Schripsema X * X * First run the DOS utilities FDISK and FORMAT. FDISK X * puts the boot code in sector 0. X * Then run diskpart X * X * diskpart /dev/hdx (MINIX) X * diskpart x: (DOS) X * X * Compiling X * X * cc -o diskpart -DUNIX diskpart.c (MINIX) X * cl -DDOS diskpart.c (DOS with MS C compiler) X */ X X#include <stdio.h> X X#ifdef DOS X#include <dos.h> X#endif X X/* X * Constants X */ X X#define NHEAD 4 /* # heads */ X#define NSEC 17 /* sectors / track */ X#define SECSIZE 512 /* sector size */ X#define OK 0 X#define ERR 1 X#define TABLEOFFSET 0x1be /* offset in boot sector*/ X/* X * Description of entry in partition table X */ X Xstruct part_entry { X char bootind; /* boot indicator 0/0x80 */ X char start_head; /* head value for first sector */ X char start_sec; /* sector value for first sector*/ X char start_cyl; /* track value for first sector */ X char sysind; /* system indicator 00=?? 01=DOS*/ X char last_head; /* head value for last sector */ X char last_sec; /* sector value for last sector */ X char last_cyl; /* track value for last sector */ X long lowsec; /* logical first sector */ X long size; /* size of partion in sectors */ X}; X X/* X * Globals X */ X Xchar secbuf[SECSIZE]; Xchar *devname; Xchar *dosstr = " DOS "; Xchar *ndosstr = "Non-DOS"; X X#ifdef DOS Xunion REGS regs; Xstruct SREGS sregs; Xint drivenum; X#endif X X#ifdef UNIX Xint devfd; X#endif X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X char ch; X X /* init */ X X if (argc != 2) { X printf("Usage: diskpart drive\n"); X exit(1); X } X X devname = argv[1]; X getboot(secbuf); /* get boot sector */ X X do { X dpl_partitions(); X printf("\nchange write hexdump quit (c/w/x/q) ? "); X ch = get_a_char(); X putchar('\n'); X switch (ch) { X case 'c' : X change_table(); X break; X case 'w' : X if (chk_table() == OK) { X putboot(secbuf); X exit(0); X } X break; X case 'x' : X dump_table(); X break; X case 'q' : X exit(0); X default : X printf(" %c ????\n",ch); X } X } X while (1); X} X X/* X * Read boot sector X */ X X#ifdef DOS X Xgetboot(buffer) Xchar *buffer; X{ X segread(&sregs); /* get ds */ X X if (devname[1] != ':') { X printf("Invalid drive %s\n",devname); X exit(1); X } X X if (*devname >= 'a') X *devname += 'A' - 'a'; X drivenum = (*devname - 'C') & 0xff; X if (drivenum < 0 || drivenum > 7) { X printf("Funny drive number %d\n",drivenum); X exit(1); X } Xprintf("Drive number %d\n",drivenum); X regs.x.ax = 0x201; /* read 1 sectors */ X regs.h.ch = 0; /* track */ X regs.h.cl = 1; /* first sector = 1 */ X regs.h.dh = 0; /* head = 0 */ X regs.h.dl = 0x80+drivenum;/* drive = 0 */ X sregs.es = sregs.ds; /* buffer address */ X regs.x.bx = (int)secbuf; X X int86x(0x13,®s,®s,&sregs); X if (regs.x.cflag) X { X printf("Cannot read boot sector\n"); X exit(1); X } X} X#endif X X#ifdef UNIX X Xgetboot(buffer) Xchar *buffer; X{ X devfd = open(devname,2); X if (devfd < 0) { X printf("Cannot open device %s\n",devname); X exit(1); X } X if (read(devfd,buffer,SECSIZE) != SECSIZE) { X printf("Cannot read boot sector\n"); X exit(2); X } X} X#endif X X/* X * Write boot sector X */ X X#ifdef DOS X Xputboot(buffer) Xchar *buffer; X{ X regs.x.ax = 0x301; /* read 1 sectors */ X regs.h.ch = 0; /* track */ X regs.h.cl = 1; /* first sector = 1 */ X regs.h.dh = 0; /* head = 0 */ X regs.h.dl = 0x80+drivenum;/* drive = 0 */ X sregs.es = sregs.ds; /* buffer address */ X regs.x.bx = (int)secbuf; X X int86x(0x13,®s,®s,&sregs); X if (regs.x.cflag) X { X printf("Cannot write boot sector\n"); X exit(1); X } X} X#endif X X#ifdef UNIX X Xputboot(buffer) Xchar *buffer; X{ X int r; X X if (lseek(devfd,0L,0) < 0) { X printf("Seek error during write\n"); X exit(1); X } X if (write(devfd,buffer,SECSIZE) != SECSIZE) { X printf("Write error\n"); X exit(1); X } X sync(); X} X#endif X X/* X * Dump partition table X */ X Xdump_table() X{ X struct part_entry *pe; X int i; X X pe = (struct part_entry *)&secbuf[TABLEOFFSET]; X printf("\n --first--- ---last---\n"); X printf("Prt ac hd sec cyl sys hd sec cyl low size\n"); X for (i = 1 ; i < 5 ; i++) { X printf(" %x %2x %x %2x %2x %2x %x %2x %2x %6X%9X\n", X i, X pe->bootind & 0xff, X pe->start_head & 0xff, X pe->start_sec & 0xff, X pe->start_cyl & 0xff, X pe->sysind & 0xff, X pe->last_head & 0xff, X pe->last_sec & 0xff, X pe->last_cyl & 0xff, X pe->lowsec, X pe->size); X pe++; X } X} X/* X * Display partition table X */ X Xdpl_partitions() X{ X struct part_entry *pe; X int i; X X printf("\nPartition Type Begin End Active\n"); X pe = (struct part_entry *)&secbuf[TABLEOFFSET]; X for (i = 1 ; i <= 4 ; i++) { X dpl_entry(i,pe); X pe++; X } X} X X/* X * Display an entry X */ X Xdpl_entry(number,entry) Xint number; Xstruct part_entry *entry; X{ X int low_cyl,high_cyl,temp; X char *typestring; X char active; X X if (entry->sysind == 0x01) X typestring = dosstr; X else X typestring = ndosstr; X printf("%5d %s ",number,typestring); X temp = entry->start_sec & 0xc0; X low_cyl = (entry->start_cyl & 0xff) + (temp << 2); X temp = entry->last_sec & 0xc0; X high_cyl = (entry->last_cyl & 0xff) + (temp << 2); X printf("%4d %4d",low_cyl,high_cyl); X if ((entry->bootind & 0xff) == 0x80) X active = 'A'; X else X active = 'N'; X printf(" %c\n",active); X} X X/* X * Check partition table X */ X Xchk_table() X{ X struct part_entry *pe; X int i; X int active; X long limit; X X pe = (struct part_entry *)&secbuf[TABLEOFFSET]; X limit = 0L; X active = 0; X for (i = 1 ; i < 5 ; i++) { X if (pe->size == 0L) X return(OK); X if (pe->lowsec <= limit) { X printf("Overlap between part. %d and %d\n",i,i-1); X return(ERR); X } X limit = pe->lowsec + pe->size - 1L; X if (pe->bootind == 0x80) X active++; X pe++; X } X if (active > 1) { X printf("%d active partitions\n",active); X return(ERR); X } X return(OK); X} X/* X * Check entry X * head/sector/track info must match logical sector number X * Used to check 'foreign' partition table during debugging X */ X Xchk_entry(entry) Xstruct part_entry *entry; X{ X char head; X char sector; X char track; X X sec_to_hst(entry->lowsec,&head,§or,&track); X if ( (head != entry->start_head) || X (sector != entry->start_sec) || X (track != entry->start_cyl)) X return(ERR); X sec_to_hst(entry->lowsec + entry->size - 1L,&head,§or,&track); X if ( (head != entry->last_head) || X (sector != entry->last_sec) || X (track != entry->last_cyl)) X return(ERR); X return(OK); X} X X/* X * Convert a logical sector number to X * head / sector / track X */ X Xsec_to_hst(logsec,hd,sec,cyl) Xlong logsec; Xchar *hd,*sec,*cyl; X{ X int bigcyl; X X bigcyl = logsec / (NHEAD * NSEC); X *sec = (logsec % NSEC) + 1 + ((bigcyl >> 2) & 0xc0); X *cyl = bigcyl & 0xff; X *hd = (logsec % (NHEAD * NSEC)) / NSEC; X} X X/* X * change partition table X */ X Xchange_table() X{ X struct part_entry *pe; X int i,temp,low_cyl,high_cyl; X char ch; X X pe = (struct part_entry *)&secbuf[TABLEOFFSET]; X for (i = 1 ; i <= 4 ; i++) { X temp = pe->start_sec & 0xc0; X low_cyl = (pe->start_cyl & 0xff) + (temp << 2); X temp = pe->last_sec & 0xc0; X high_cyl = (pe->last_cyl & 0xff) + (temp << 2); X printf("Partition %d from %d to %d. Change ? ", X i,low_cyl,high_cyl); X ch = get_a_char(); X if (ch == 'y' || ch == 'Y') X get_partition(pe); X pe++; X } X putchar('\n'); X} X X/* X * Get partition info : first & last cylinder X */ X Xget_partition(entry) Xstruct part_entry *entry; X{ X char buf[80]; X int first,last; X long low,high; X char ch; X X printf(" First cylinder ? "); X gets(buf); X sscanf(buf,"%d",&first); X printf(" Last cylinder ? "); X gets(buf); X sscanf(buf,"%d",&last);; X if (first == 0 && last == 0) { X entry->bootind = 0; X entry->start_head = 0; X entry->start_sec = 0; X entry->start_cyl = 0; X entry->sysind = 0; X entry->last_head = 0; X entry->last_sec = 0; X entry->last_cyl = 0; X entry->lowsec = 0L; X entry->size = 0L ; X return; X } X low = first & 0xffff; X low = low * NSEC * NHEAD; X if (low == 0) X low = 1; /* sec0 is master boot record */ X high = last & 0xffff; X high = (high + 1)*NSEC*NHEAD - 1; X entry->lowsec = low; X entry->size = high - low + 1; X sec_to_hst(low, X &entry->start_head, X &entry->start_sec, X &entry->start_cyl); X sec_to_hst(high, X &entry->last_head, X &entry->last_sec, X &entry->last_cyl); X printf(" DOS partition ? "); X ch = get_a_char(); X if (ch == 'y' || ch == 'Y') X entry->sysind = 1; X else X entry->sysind = 0; X printf(" Active partition ? "); X ch = get_a_char(); X if (ch == 'y' || ch == 'Y') X entry->bootind = 0x80; X else X entry->bootind = 0; X} X X/* X * Read 1 character and discard rest of line X */ X Xget_a_char() X{ X char ch; X X ch = getchar(); X if (ch != '\n') X while (getchar() != '\n'); X return(ch); X} X *-*-END-of-t_diskp.c-*-* echo x - t_mkboot. sed 's/^X//' >t_mkboot. <<'*-*-END-of-t_mkboot.-*-*' Xbuild bootblok ../kernel/kernel ../mm/mm ../fs/fs init fsck /dev/fd0 *-*-END-of-t_mkboot.-*-* exit