Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!seismo!mcvax!oce!sch From: sch@oce.nl (Jakob Schripsema) Newsgroups: comp.os.minix Subject: MINIX on Olivetti M24 (2 of 2) Message-ID: <196@oce-rd4.oce.nl> Date: Mon, 27-Apr-87 07:29:26 EDT Article-I.D.: oce-rd4.196 Posted: Mon Apr 27 07:29:26 1987 Date-Received: Wed, 29-Apr-87 05:17:57 EDT Reply-To: sch@oce-rd4.UUCP (Jakob Schripsema) Organization: Oce-Nederland bv, Venlo, the Netherlands Lines: 1672 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= 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> 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 X X#ifdef DOS X#include 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