/* $NetBSD: diskio.c,v 1.6 2014/03/26 18:04:34 christos Exp $ */ /* * Copyright (c) 1995 Waldi Ravens. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Waldi Ravens. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include "libtos.h" #include "aptck.h" #include "ahdilbl.h" #include struct pun_info { u_int16_t puns; u_int8_t pun[16]; u_int32_t part_start[16]; u_int32_t P_cookie; u_int32_t *P_cookptr; u_int16_t P_version; u_int16_t P_max_sector; u_int32_t reserved[16]; }; static char * strbd PROTO((char *, ...)); static int setmami PROTO((disk_t *, char *)); static int setnames PROTO((disk_t *)); static int setsizes PROTO((disk_t *)); static int ahdi_compatible PROTO((void)); disk_t * disk_open(char *name) { disk_t *dd; dd = xmalloc(sizeof *dd); memset(dd, 0, sizeof *dd); if (setmami(dd, name) || setnames(dd) || setsizes(dd)) { disk_close(dd); return(NULL); } return(dd); } void disk_close(disk_t *dd) { if (dd) { free(dd->product); free(dd->sname); free(dd->fname); free(dd->roots); free(dd->parts); free(dd); } } void * disk_read(dd, start, count) disk_t *dd; u_int start, count; { char *buffer; int bdev; long e; buffer = xmalloc(count * dd->bsize); e = XHReadWrite(dd->major, dd->minor, 0, start, count, buffer); if (!e) return(buffer); if (e == -32 || (e == -1 && XHGetVersion() == -1)) { if (!ahdi_compatible()) fatal(-1, "AHDI 3.0 compatible harddisk driver required"); bdev = BIOSDEV(dd->major, dd->minor); if (bdev && !bios_read(buffer, start, count, bdev)) return(buffer); } free(buffer); return(NULL); } int disk_write(dd, start, count, buffer) disk_t *dd; u_int start, count; void *buffer; { int bdev; long e; e = XHReadWrite(dd->major, dd->minor, 1, start, count, buffer); if (e == -32 || (e == -1 && XHGetVersion() == -1)) { if (!ahdi_compatible()) fatal(-1, "AHDI 3.0 compatible harddisk driver required"); bdev = BIOSDEV(dd->major, dd->minor); if (bdev) e = bios_write(buffer, start, count, bdev); } return((int)e); } static int ahdi_compatible(void) { static int ahdi_compat; if (!ahdi_compat) { long oldsp = Super(0L); struct pun_info *punp = *((struct pun_info **)0x0516); Super(oldsp); if (punp && punp->P_cookie == 0x41484449 && punp->P_cookptr == &punp->P_cookie && punp->P_version >= 0x0300) ahdi_compat = 1; } return(ahdi_compat); } static int setmami(disk_t *dd, char *name) { char *p = name; u_int target, lun; bus_t bus; if (*p == 'i') { bus = IDE; if (*++p < '0' || *p > '1') { if (*p) error(-1, "%s: invalid IDE target `%c'", name, *p); else error(-1, "%s: missing IDE target", name); return(-1); } target = *p++ - '0'; lun = 0; } else { char *b; if (*p == 'a') { bus = ACSI; b = "ACSI"; } else if (*p == 's') { bus = SCSI; b = "SCSI"; } else { error(-1, "%s: invalid DISK argument", name); return(-1); } if (*++p < '0' || *p > '7') { if (*p) error(-1, "%s: invalid %s target `%c'", name, b, *p); else error(-1, "%s: missing %s target", name, b); return(-1); } target = *p++ - '0'; if (*p < '0' || *p > '7') { if (*p) { error(-1, "%s: invalid %s lun `%c'", name, b, *p); return(-1); } lun = 0; } else lun = *p++ - '0'; } if (*p) { error(-1, "%s: invalid DISK argument", name); return(-1); } dd->major = MAJOR(bus, target, lun); dd->minor = MINOR(bus, target, lun); return(0); } static int setnames(disk_t *dd) { char sn[16], us[16], ls[16], *bs; int b, u, l; b = BUS(dd->major, dd->minor); u = TARGET(dd->major, dd->minor); l = LUN(dd->major, dd->minor); switch (b) { case IDE: bs = "IDE"; break; case ACSI: bs = "ACSI"; break; case SCSI: bs = "SCSI"; break; default: error(-1, "invalid bus no. %d", b); return(-1); } if (u < 0 || u > 7 || (b == IDE && u > 1)) { error(-1, "invalid %s target `%d'", bs, u); return(-1); } snprintf(us, sizeof(us), " target %d", u); if (l < 0 || l > 7 || (b == IDE && l > 0)) { error(-1, "invalid %s lun `%d'", bs, l); return(-1); } if (b == IDE) { snprintf(sn, sizeof(sn), "i%d", u); ls[0] = '\0'; } else { snprintf(sn, sizeof(sn), "%c%d%d", tolower(*bs), u, l); snprintf(ls, sizeof(ls), " lun %d", l); } dd->fname = strbd(bs, us, ls, NULL); dd->sname = strbd(sn, NULL); return(0); } static int setsizes(disk_t *dd) { if (XHGetVersion() != -1) { char *p, prod[1024]; if (XHInqTarget2(dd->major, dd->minor, &dd->bsize, NULL, prod, sizeof(prod))) { if (XHInqTarget(dd->major, dd->minor, &dd->bsize, NULL, prod)) { error(-1, "%s: device not configured", dd->sname); return(-1); } } p = strrchr(prod, '\0'); while (isspace(*--p)) *p = '\0'; dd->product = strbd(prod, NULL); if (!XHGetCapacity(dd->major, dd->minor, &dd->msize, &dd->bsize)) return(0); } else { dd->product = strbd("unknown", NULL); dd->bsize = AHDI_BSIZE; /* XXX */ } /* Trial&error search for last sector on medium */ { u_int u, l, m; void *p, (*oldvec)(); /* turn off etv_critic handler */ oldvec = Setexc(257, bios_critic); u = (u_int)-2; l = 0; while (u != l) { m = l + ((u - l + 1) / 2); p = disk_read(dd, m, 1); free(p); if (p == NULL) u = m - 1; else l = m; } /* turn on etv_critic handler */ (void)Setexc(257, oldvec); if (l) { dd->msize = l + 1; return(0); } error(-1, "%s: device not configured", dd->sname); return(-1); } } char * strbd(char *string1) { char *p, *result; size_t length = 1; va_list ap; va_start(ap, string1); for (p = string1; p; p = va_arg(ap, char *)) length += strlen(p); va_end(ap); *(result = xmalloc(length)) = '\0'; va_start(ap, string1); for (p = string1; p; p = va_arg(ap, char *)) strcat(result, p); va_end(ap); return(result); }