int idioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { struct ida_drv *drv; int part = dkpart(dev); int unit = dkunit(dev); int err; if (unit >= NID || part >= MAXPARTITIONS || !(drv = id_drive[unit]) || !(drv->flags & ID_INIT)) /* sanity check */ return(ENXIO); err = dsioctl("id", dev, cmd, addr, flag, &drv->slices, idstrategy, (ds_setgeom_t *)NULL); if (err != -1) return (err); if (dkpart(dev) != RAW_PART) return (ENOTTY); return (0); }
/* * Determine the size of the transfer, and make sure it is * within the boundaries of the partition. Adjust transfer * if needed, and signal errors or early completion. * * XXX TODO: * o Split buffers that are too big for the device. * o Check for overflow. * o Finish cleaning this up. * * This function returns 1 on success, 0 if transfer equates * to EOF (end of disk) or -1 on failure. The appropriate * 'errno' value is also set in bp->b_error and bp->b_flags * is marked with B_ERROR. */ struct bio * dscheck(cdev_t dev, struct bio *bio, struct diskslices *ssp) { struct buf *bp = bio->bio_buf; struct bio *nbio; disklabel_t lp; disklabel_ops_t ops; long nsec; u_int64_t secno; u_int64_t endsecno; u_int64_t slicerel_secno; struct diskslice *sp; u_int32_t part; u_int32_t slice; int shift; int mask; slice = dkslice(dev); part = dkpart(dev); if (bio->bio_offset < 0) { kprintf("dscheck(%s): negative bio_offset %lld\n", devtoname(dev), (long long)bio->bio_offset); goto bad; } if (slice >= ssp->dss_nslices) { kprintf("dscheck(%s): slice too large %d/%d\n", devtoname(dev), slice, ssp->dss_nslices); goto bad; } sp = &ssp->dss_slices[slice]; /* * Calculate secno and nsec */ if (ssp->dss_secmult == 1) { shift = DEV_BSHIFT; goto doshift; } else if (ssp->dss_secshift != -1) { shift = DEV_BSHIFT + ssp->dss_secshift; doshift: mask = (1 << shift) - 1; if ((int)bp->b_bcount & mask) goto bad_bcount; if ((int)bio->bio_offset & mask) goto bad_blkno; secno = bio->bio_offset >> shift; nsec = bp->b_bcount >> shift; } else {
/* ARGSUSED */ int idclose(dev_t dev, int flags, int fmt, struct proc *p) { struct ida_drv *drv; int part = dkpart(dev); int unit = dkunit(dev); if (unit >= NID || part >= MAXPARTITIONS) /* bounds check */ return(ENXIO); drv = id_drive[unit]; dsclose(dev, fmt, drv->slices); if (!dsisopen(drv->slices)) { drv->flags &= ~ID_DEV_OPEN; } return 0; }
/* * This returns the size of the passed device in number of 512 blocks (?) */ int hd_size(dev_t dev) { u_int major = major(dev); u_int unit = dkunit(dev); u_int part = dkpart(dev); struct disk *du; int val; if (unit >= MAXDISKS) return(-1); du = &hd[major].disk[unit]; if (du->dk_state == CLOSED) { val = hd_open(dev,0,0); if (val < 0) return(-1); } return((int)((u_long)du->dk_lab.d_partitions[part].p_size * du->dk_lab.d_secsize / 512)); }
/* * Initialize a drive. */ int idopen(dev_t dev, int flags, int fmt, struct proc *p) { struct ida_drv *drv; int part = dkpart(dev); int unit = dkunit(dev); struct disklabel label; int err; if (unit >= NID || part >= MAXPARTITIONS) /* bounds check */ return(ENXIO); drv = id_drive[unit]; if (!drv || !(drv->flags & ID_INIT)) /* drive not initialised */ return(ENXIO); drv->flags |= ID_DEV_OPEN; /* knock up a label for the whole disk. */ bzero(&label, sizeof label); label.d_secsize = u_unpack(drv->drv_info.secsize); label.d_nsectors = u_unpack(drv->drv_info.nsectors); label.d_ntracks = u_unpack(drv->drv_info.ntracks); label.d_ncylinders = u_unpack(drv->drv_info.ncylinders); label.d_secpercyl = u_unpack(drv->drv_info.ntracks) * u_unpack(drv->drv_info.nsectors); if (label.d_secpercyl == 0) label.d_secpercyl = 100; /* prevent accidental division by zero */ label.d_secperunit = u_unpack(drv->drv_info.secperunit); /* Initialize slice tables. */ if ((err = dsopen("id", dev, fmt, 0, &drv->slices, &label, idstrategy, (ds_setgeom_t *)NULL, &id_cdevsw)) == NULL) { return 0; } if (!dsisopen(drv->slices)) { drv->flags &= ~ID_DEV_OPEN; } return err; }
/* * Write disk label back to device after modification. */ static int l32_writedisklabel(cdev_t dev, struct diskslices *ssp, struct diskslice *sp, disklabel_t lpx) { struct disklabel32 *lp; struct disklabel32 *dlp; struct buf *bp; const char *msg; int error = 0; lp = lpx.lab32; if (lp->d_partitions[RAW_PART].p_offset != 0) return (EXDEV); /* not quite right */ bp = geteblk((int)lp->d_secsize); bp->b_bio1.bio_offset = (off_t)LABELSECTOR32 * lp->d_secsize; bp->b_bio1.bio_done = biodone_sync; bp->b_bio1.bio_flags |= BIO_SYNC; bp->b_bcount = lp->d_secsize; #if 1 /* * We read the label first to see if it's there, * in which case we will put ours at the same offset into the block.. * (I think this is stupid [Julian]) * Note that you can't write a label out over a corrupted label! * (also stupid.. how do you write the first one? by raw writes?) */ bp->b_flags &= ~B_INVAL; bp->b_cmd = BUF_CMD_READ; KKASSERT(dkpart(dev) == WHOLE_SLICE_PART); dev_dstrategy(dev, &bp->b_bio1); error = biowait(&bp->b_bio1, "labrd"); if (error) goto done; for (dlp = (struct disklabel32 *)bp->b_data; dlp <= (struct disklabel32 *) ((char *)bp->b_data + lp->d_secsize - sizeof(*dlp)); dlp = (struct disklabel32 *)((char *)dlp + sizeof(long))) { if (dlp->d_magic == DISKMAGIC32 && dlp->d_magic2 == DISKMAGIC32 && dkcksum32(dlp) == 0) { *dlp = *lp; lpx.lab32 = dlp; msg = l32_fixlabel(NULL, sp, lpx, TRUE); if (msg) { error = EINVAL; } else { bp->b_cmd = BUF_CMD_WRITE; bp->b_bio1.bio_done = biodone_sync; bp->b_bio1.bio_flags |= BIO_SYNC; KKASSERT(dkpart(dev) == WHOLE_SLICE_PART); dev_dstrategy(dev, &bp->b_bio1); error = biowait(&bp->b_bio1, "labwr"); } goto done; } } error = ESRCH; done: #else bzero(bp->b_data, lp->d_secsize); dlp = (struct disklabel32 *)bp->b_data; *dlp = *lp; bp->b_flags &= ~B_INVAL; bp->b_cmd = BUF_CMD_WRITE; bp->b_bio1.bio_done = biodone_sync; bp->b_bio1.bio_flags |= BIO_SYNC; BUF_STRATEGY(bp, 1); error = biowait(&bp->b_bio1, "labwr"); #endif bp->b_flags |= B_INVAL | B_AGE; brelse(bp); return (error); }
/* * The ioctl routine. */ int hd_ioctl(dev_t dev, int cmd, caddr_t addr, int flag) { u_int major = major(dev); u_int unit = dkunit(dev); u_int part = dkpart(dev); struct disk *du; int error = 0; #ifdef MONITOR printf("hd_ioctl: Called with dev=%d, cmd=0x%x, flag=%d by %s\n", dev,cmd,flag,myprocname(returnlink_(dev))); printf("DIOCGDINFO:%x DIOCGPART:%x DIOCSDINFO:%x \n\tDIOCWLABEL:%x DIOCWDINFO:%x\n", DIOCGDINFO,DIOCGPART,DIOCSDINFO,DIOCWLABEL,DIOCWDINFO); #endif if ((unit >= MAXDISKS) || (part >= MAXPARTITIONS)) return(ENXIO); du = &hd[major].disk[unit]; switch(cmd) { case DIOCGDINFO: *(struct disklabel *)addr = du->dk_lab; #ifdef DEBUG printf("hd_ioctl: dev_bsize = %d\n",du->dk_lab.d_secsize); #endif break; case DIOCGPART: ((struct partinfo *)addr)->disklab = &du->dk_lab; ((struct partinfo *)addr)->part = &du->dk_lab.d_partitions[part]; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; else error = setdisklabel(&du->dk_lab, (struct disklabel *)addr,0); /* XXX AMS Set this info at controller as well. */ break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; else du->dk_wlabel = *(int *)addr; break; case DIOCWDINFO: if ((flag & FWRITE) == 0) { error = EBADF; } else if ((error = setdisklabel(&du->dk_lab, (struct disklabel *)addr,0)) == 0) { int wlab; /* XXX AMS Set this info at controller as well. */ wlab = du->dk_wlabel; du->dk_wlabel = 1; error = writedisklabel(dev,hd_strategy, &du->dk_lab,part); du->dk_wlabel = wlab; } break; default: error = ENOTTY; break; } return(error); }
/* * Close a drive. */ int hd_close(dev_t dev, int flags, int fmt) { u_int major, unit, part; struct disk *du; struct hd_devices *hdev; DCB *dcb; #ifdef MONITOR printf("hd_close: Called with dev=%d, flags=%d, fmt=%d by %s\n", dev,flags,fmt,myprocname(returnlink_(dev))); #endif /* Get the unit info */ major = major(dev); unit = dkunit(dev); part = dkpart(dev); hdev = &hd[major]; dcb = hdev->dcb; if (unit >= MAXDISKS) return(ENXIO); du = &(hdev->disk[unit]); if (du->dk_state == CLOSED) return(0); if (du->dk_state == RAWOPEN) { du->dk_state = CLOSED; for (part=0; part<MAXPARTITIONS; part++) du->dk_pstatus[part] = CLOSED; goto done; } part = dkpart(dev); if (part >= du->dk_lab.d_npartitions) return(ENXIO); du->dk_pstatus[part] = CLOSED; for (part=0; (part<MAXPARTITIONS) && (du->dk_pstatus[part] == CLOSED); part++); if (part == MAXPARTITIONS) du->dk_state = CLOSED; done: if (du->dk_state == CLOSED) { if (hdev->old) { hdev->old = FALSE; /* Only unit open now closed */ #ifdef DEBUG printf("old unit dev %d was closed",dev); #endif } else { DiscOpenCloseReq openreq; openreq.DevReq.Request = FG_Close; openreq.DevReq.Action = dev_openaction; openreq.DevReq.SubDevice = unit; openreq.DevReq.Timeout = -1; openreq.DevReq.Result = MYSPECCODE; /* Special Code */ InitSemaphore(&(openreq.WaitLock),0); Operate(dcb,&openreq); Wait(&(openreq.WaitLock)); if (openreq.DevReq.Result) printf("hd_close: Warning, Fault 0x%x on major dev %d\n", (openreq.DevReq.Result==MYSPECCODE) ? (SS_Device|EC_Error|EG_WrongFn|EO_Medium): openreq.DevReq.Result, major); } } return(0); }
int hd_open(dev_t dev, int flags, int fmt) { u_int major, unit, part; struct disk *du; struct hd_devices *hdev; DCB *dcb; DiscOpenCloseReq openreq; DiscParameterReq dpreq; struct buf *bp; int error = 0; int wasold = FALSE; #ifdef MONITOR printf("hd_open: Called with dev=%d, flags=%d, fmt=%d by %s\n", dev,flags,fmt,myprocname(returnlink_(dev))); #endif /* Get the unit info */ major = major(dev); unit = dkunit(dev); part = dkpart(dev); hdev = &hd[major]; dcb = hd[major].dcb; if (unit >= MAXDISKS) return(ENXIO); du = &(hdev->disk[unit]); #ifdef DEBUG printf("hd_open: major %d unit %d part %d\n",major,unit,part); #endif /* Has the disk already been opened. */ if (du->dk_state != CLOSED) { /* Already open, so don't mess with it. */ goto getpart; } /* Undefined device OR Trap old device driver compatibility */ if ((dcb == NULL) || hdev->old) { /* Old style only supports 1 disk */ return(ENODEV); } /* Open the actual disk */ openreq.DevReq.Request = FG_Open; openreq.DevReq.Action = dev_openaction; openreq.DevReq.SubDevice = unit; openreq.DevReq.Timeout = -1; openreq.DevReq.Result = MYSPECCODE; /* Special Code */ InitSemaphore(&(openreq.WaitLock),0); Operate(dcb,&openreq); Wait(&(openreq.WaitLock)); /* Old style device driver was opened */ if ((openreq.DevReq.Result == MYSPECCODE) || ((openreq.DevReq.Result & EG_Mask) == EG_WrongFn)) { wasold = TRUE; } else if (openreq.DevReq.Result) { /* Error in opening device */ return(ENODEV); } /* Get the disk label using device driver defaults */ du->dk_lab = dflt_sizes; du->dk_state = READLABEL; bp = geteblk(DEV_BSIZE); bp->b_dev = dev & 0xFFFFFFF8; /* Force to boot partition (a) */ bp->b_blkno = LABELSECTOR; bp->b_flags = B_READ; hd_strategy(bp); /* Start the operation. */ biowait(bp); /* Wait until complete */ if (bp->b_flags & B_ERROR) { error = ENXIO; du->dk_state = CLOSED; goto done; } /* Is label there, otherwise open as raw. */ if (((struct disklabel *) (bp->b_un.b_addr + LABELOFFSET))->d_magic == DISKMAGIC) { du->dk_lab = *(struct disklabel *) (bp->b_un.b_addr + LABELOFFSET); du->dk_state = OPEN; /* Pass disk information back to device driver */ /* Standard Request */ dpreq.DevReq.Request = FG_SetInfo; dpreq.DevReq.Result = MYSPECCODE; dpreq.DevReq.Action = dev_openaction; dpreq.DevReq.SubDevice = unit; dpreq.DevReq.Timeout = -1; InitSemaphore(&dpreq.WaitLock,0); /* Setup SetInfo parameters */ dpreq.DriveType = 0; /* Fixed Disk ? */ dpreq.SectorSize = du->dk_lab.d_secsize; dpreq.SectorsPerTrack = du->dk_lab.d_nsectors; dpreq.TracksPerCyl = du->dk_lab.d_ntracks; dpreq.Cylinders = du->dk_lab.d_ncylinders; /* Make it */ Operate(dcb,&dpreq); Wait(&(dpreq.WaitLock)); if (dpreq.DevReq.Result) printf("hd_open: Warning; Fault 0x%x on major dev %d FG_SetInfo\n", (dpreq.DevReq.Result==MYSPECCODE) ? (SS_Device|EC_Error|EG_WrongFn|EO_Medium): dpreq.DevReq.Result, major); } else { printf("hd (dev %d): Bad disk label (%x)\n",bp->b_dev, ((struct disklabel *)(bp->b_un.b_addr + LABELOFFSET))->d_magic); du->dk_state = RAWOPEN; } done: /* Release buffer. */ bp->b_flags = B_INVAL | B_AGE; brelse(bp); getpart: #ifdef DEBUG printf("hd_open: device %d get part %d\n",dev,part); #endif /* Get and set the partition info */ if (part >= MAXPARTITIONS) return(ENXIO); if (du->dk_pstatus[part] != CLOSED) { /* Partition is already open, don't mess with it. */ #ifdef DEBUG printf("hd_open: device %d part %d already open\n",dev,part); #endif return(0); } if (du->dk_state == RAWOPEN) { #ifdef DEBUG printf("hd_open: device %d open as raw part %d\n",dev,part); #endif /* If no label, then only allow raw */ if (part == (RAWPARTITION - 'a')) { #ifdef DEBUG printf("hd_open: device %d opened as raw\n",dev); #endif du->dk_pstatus[part] = RAWOPEN; } else { du->dk_state = CLOSED; #ifdef DEBUG printf("hd_open: device %d refused opening partition %d as raw\n",dev,part); #endif return(ENXIO); } } else { /* Label found, so check overlap with other open partitions */ struct partition *pp; int start,end,i; if (part >= du->dk_lab.d_npartitions) return(ENXIO); pp = &du->dk_lab.d_partitions[part]; start = pp->p_offset; end = start + pp->p_size; for (pp = du->dk_lab.d_partitions, i=0; i < du->dk_lab.d_npartitions; pp++, i++) { /* Ends before this starts or starts before this ends */ if (pp->p_offset + pp->p_size <= start || pp->p_offset >= end) continue; if (du->dk_pstatus[i]) { printf("hd%d%c: overlaps open partition (%c)\n", unit,part+'a',i+'a'); } } du->dk_pstatus[part] = OPEN; } #ifdef DEBUG printf("hd_open: device %d opened error=%d\n",dev,error); #endif if (!error && wasold) hdev->old = TRUE; return(error); }
/* * Read/Write routine for a buffer. Finds the proper unit, * range checks arguments and schedules the transfer. Does not wait * for the transfer to complete. All I/O requests must be a multiple * of a sector in length. */ int hd_strategy(struct buf *bp) { u_int hdmajor = major(bp->b_dev); u_int unit = dkunit(bp->b_dev); u_int part = dkpart(bp->b_dev); struct disk *du = &(hd[hdmajor].disk[unit]); Semaphore *numbufs = &(hd[hdmajor].numbufs); struct buf *dp; struct partition *p; long maxsz, sz; #ifdef MONITOR printf("hd_strategy: Called with bp=0x%x, dev=%d, blck=%d by %s\n", bp,bp->b_dev,bp->b_blkno,myprocname(returnlink_(bp))); #endif /* Simple parameter check */ if ((unit >= MAXDISKS) || (bp->b_blkno < 0) || (part >= du->dk_lab.d_npartitions)) { printf("hd_strategy: major=%d, unit=%d, part=%d, blkno=%d, bcount=%d\n", hdmajor,unit,part,bp->b_blkno,bp->b_bcount); printf("hd: Error in hd_strategy"); bp->b_flags |= B_ERROR; goto bad; } /* Check for write protection */ if (du->dk_protected && ((bp->b_flags & B_READ) == 0)) { printf("hd_strategy: %d:%d: write protected\n",hdmajor,unit); goto bad; } if (DISKSTATE(du->dk_state) != OPEN) goto q; /* Determine size of xfer and make sure it fits. */ p = &(du->dk_lab.d_partitions[part]); maxsz = p->p_size; sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; /* XXX Check disk label writing at a later stage */ /* Check the parameters */ if ((bp->b_blkno < 0) || ((bp->b_blkno + sz) > maxsz)) { /* if exactly at end of disk, return an EOF. */ if (bp->b_blkno == maxsz) { bp->b_resid = bp->b_bcount; biodone(bp); return(0); } /* or truncate if part of it fits */ sz = maxsz - bp->b_blkno; if (sz <= 0) { printf("hd%d: invalid size %d\n",unit,sz); goto bad; } bp->b_bcount = sz << DEV_BSHIFT; } bp->b_cylin = (bp->b_blkno + p->p_offset) / du->dk_lab.d_secpercyl; q: dp = &(du->dk_queue); /* Lock buffer head, add item, free buffer head and signal */ Wait(&du->dk_guard); /* Lock */ disksort(dp, bp); /* Add */ Signal(&du->dk_guard); /* Free */ Signal(&du->dk_numq); /* Signal another buffer in this q */ Signal(numbufs); /* Signal Device Driver */ #ifdef MONITOR printf("hd_strategy: Done OK\n"); #endif return(0); bad: #ifdef MONITOR printf("hd_strategy: Done Failed\n"); #endif printf("hd_strategy: bad error\n"); bp->b_error = EINVAL; biodone(bp); return(1); }
static void hd_intr_server(int major) { struct hd_devices *hdev = &hd[major]; Semaphore *numbufs = &(hdev->numbufs); Semaphore *abort = &(hdev->abort); DCB *dcb = hdev->dcb; struct disk *this_hd = &(hdev->disk[0]); /* Start at 0 */ struct disk *last_hd = &(hdev->disk[MAXDISKS]); /* Terminate check */ int searched; DiscReq req; struct partition *pp; struct buf *dp, *bp; #ifdef DEBUG printf("hd_intr_server: Helios Major device %d running\n",major); #endif /* Loop until ordered to shut down and no more buffers to work on. */ for (;;) { /* Wait for a block to work on. */ Wait(numbufs); if (TestWait(abort)) { /* Forget anything else */ return; } /* Look for a disk to work on */ searched = 0; while (!TestWait(&this_hd->dk_numq) && searched != MAXDISKS) { this_hd++; searched++; if (this_hd >= last_hd) this_hd = &(hdev->disk[0]); } if (searched == MAXDISKS) { printf("hd_intr: PANIC %d, Failed to find buffer\n",major); continue; } /* Extract the buffer */ dp = &(this_hd->dk_queue); bp = dp->b_actf; /* Get the partition (offset) */ pp = &(this_hd->dk_lab.d_partitions[dkpart(bp->b_dev)]); /* Perform the operation */ req.DevReq.Request = ((bp->b_flags & B_READ)?FG_Read:FG_Write); req.DevReq.Action = hd_iodone; req.DevReq.SubDevice = dkunit(bp->b_dev); req.DevReq.Timeout = -1; /* No Timeout */ req.Size = bp->b_bcount; req.Buf = bp->b_un.b_addr; req.Pos = bp->b_blkno + pp->p_offset; /* Sector */ /* Check if we are writing a disk label */ if ((req.Pos <= LABELSECTOR) && (!this_hd->dk_wlabel) && (req.DevReq.Request == FG_Write)) { bp->b_error = EACCES; /* Not writeable */ goto skip; } /* Adjust to byte location */ req.Pos *= this_hd->dk_lab.d_secsize; #ifdef DEBUG printf("hd_intr_server: Major %d performing %s on dev=%d buf=0x%x (dcb=0x%x)\n", major,((bp->b_flags & B_READ)?"Read":"Write"),bp->b_dev,buf,dcb); printf("hd_intr_server: Request=0x%x, SubDevice=%d, Timeout=%d, Size=%d\n", req.DevReq.Request,req.DevReq.SubDevice,req.DevReq.Timeout,req.Size); #endif InitSemaphore(&(req.WaitLock),0); Operate(dcb,&req); Wait(&(req.WaitLock)); /* XXX Implement multiple ops * later. (i.e.TestWait for * each disk) */ hd_ops++; #ifdef DEBUG printf("hd_intr_server: Major%d Pos=%d, Buf=0x%x Actual=%d %s\n", major,req.Pos,req.Buf,req.Actual, (req.Size==req.Actual)?"O.K.":"ERROR"); #endif /* Any problems ? */ if (req.Size == req.Actual) { bp->b_error = 0; } else { /* Error occurred */ bp->b_error = EIO; bp->b_flags |= B_ERROR; } /* Operation complete, lock queue and remove bp */ skip: Wait(&this_hd->dk_guard); dp->b_actf = bp->av_forw; bp->b_resid = 0; bp->av_forw = NULL; Signal(&this_hd->dk_guard); /* Need to go into UNIX kernel to call biodone */ tokernel(); biodone(bp); fromkernel(); /* Round robin scheduling */ this_hd++; if (this_hd >= last_hd) this_hd = &(hdev->disk[0]); } }