/* $OpenBSD: ofdev.c,v 1.9 2006/10/12 12:14:17 krw Exp $ */ /* $NetBSD: ofdev.c,v 1.1 1997/04/16 20:29:20 thorpej Exp $ */ /* * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * 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 TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ /* * Device I/O routines using Open Firmware */ #include #include #include #include #include #include #include #include #include extern char bootdev[]; static char * filename(char *str, char *ppart) { char *cp, *lp; char savec; int dhandle; char devtype[16]; lp = str; devtype[0] = 0; *ppart = 0; for (cp = str; *cp; lp = cp) { /* For each component of the path name... */ while (*++cp && *cp != '/') ; savec = *cp; *cp = 0; /* ...look whether there is a device with this name */ dhandle = OF_finddevice(str); *cp = savec; if (dhandle == -1) { /* * if not, lp is the delimiter between device and path */ /* if the last component was a block device... */ if (!strcmp(devtype, "block")) { /* search for arguments */ for (cp = lp; --cp >= str && *cp != '/' && *cp != ':';) ; if (cp >= str && *cp == ':') { /* found arguments */ for (cp = lp; *--cp != ':' && *cp != ',';) ; if (*++cp >= 'a' && *cp <= 'a' + MAXPARTITIONS) *ppart = *cp; } } return lp; } else if (OF_getprop(dhandle, "device_type", devtype, sizeof devtype) < 0) devtype[0] = 0; } return 0; } static int strategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf, size_t *rsize) { struct of_dev *dev = devdata; u_quad_t pos; int n; if (rw != F_READ) return EPERM; if (dev->type != OFDEV_DISK) panic("strategy"); pos = (u_quad_t)(blk + dev->partoff) * dev->bsize; for (;;) { if (OF_seek(dev->handle, pos) < 0) break; n = OF_read(dev->handle, buf, size); if (n == -2) continue; if (n < 0) break; *rsize = n; return 0; } return EIO; } static int devclose(struct open_file *of) { struct of_dev *op = of->f_devdata; if (op->type == OFDEV_NET) net_close(op); if (op->dmabuf) OF_call_method("dma-free", op->handle, 2, 0, op->dmabuf, MAXPHYS); OF_close(op->handle); op->handle = -1; } static struct devsw devsw[1] = { "OpenFirmware", strategy, (int (*)(struct open_file *, ...))nodev, devclose, noioctl }; int ndevs = sizeof devsw / sizeof devsw[0]; static struct fs_ops file_system_ufs = { ufs_open, ufs_close, ufs_read, ufs_write, ufs_seek, ufs_stat }; static struct fs_ops file_system_cd9660 = { cd9660_open, cd9660_close, cd9660_read, cd9660_write, cd9660_seek, cd9660_stat }; static struct fs_ops file_system_hfs = { hfs_open, hfs_close, hfs_read, hfs_write, hfs_seek, hfs_stat }; static struct fs_ops file_system_nfs = { nfs_open, nfs_close, nfs_read, nfs_write, nfs_seek, nfs_stat }; struct fs_ops file_system[3]; int nfsys; static struct of_dev ofdev = { -1, }; char opened_name[256]; static u_long get_long(p) const void *p; { const unsigned char *cp = p; return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24); } int read_mac_label(struct of_dev *devp, char *buf, struct disklabel *lp) { struct part_map_entry *part; struct buf *bp; int err; size_t read; int part_cnt; int i; char *s; if ((strategy(devp, F_READ, 1, DEV_BSIZE, buf, &read) != 0) || (read != DEV_BSIZE)) return ERDLAB; part = (struct part_map_entry *)buf; /* if first partition is not valid, assume not HFS/DPME partitioned */ if (part->pmSig != PART_ENTRY_MAGIC) return ERDLAB; part_cnt = part->pmMapBlkCnt; /* first search for "OpenBSD" partition type * standard bsd disklabel lives inside at offset 0 * otherwise, we should fake a bsd partition * with first HFS partition starting at 'i' * ? will this cause problems with booting bsd.rd from hfs */ for (i = 0; i < part_cnt; i++) { /* read the appropriate block */ if ((strategy(devp, F_READ, 1+i, DEV_BSIZE, buf, &read) != 0) || (read != DEV_BSIZE)) return ERDLAB; part = (struct part_map_entry *)buf; /* toupper the string, in case caps are different... */ for (s = part->pmPartType; *s; s++) if ((*s >= 'a') && (*s <= 'z')) *s = (*s - 'a' + 'A'); if (0 == strcmp(part->pmPartType, PART_TYPE_OPENBSD)) { /* FOUND OUR PARTITION!!! */ printf("found OpenBSD DPME partition\n"); if(strategy(devp, F_READ, part->pmPyPartStart, DEV_BSIZE, buf, &read) == 0 && read == DEV_BSIZE) { if (!getdisklabel(buf, lp)) return 0; /* If we have an OpenBSD region * but no valid parition table, * we cannot load a kernel from * it, punt. * should not have more than one * OpenBSD of DPME type. */ return ERDLAB; } } } return ERDLAB; } /* * Find a valid disklabel. */ static int search_label(devp, off, buf, lp, off0) struct of_dev *devp; u_long off; char *buf; struct disklabel *lp; u_long off0; { size_t read; struct dos_partition *p; int i; u_long poff; static int recursion; if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read) || read != DEV_BSIZE) return ERDLAB; if (buf[510] != 0x55 || buf[511] != 0xaa) return ERDLAB; if (recursion++ <= 1) off0 += off; for (p = (struct dos_partition *)(buf + DOSPARTOFF), i = 4; --i >= 0; p++) { if (p->dp_typ == DOSPTYP_OPENBSD) { poff = get_long(&p->dp_start) + off0; if (strategy(devp, F_READ, poff + LABELSECTOR, DEV_BSIZE, buf, &read) == 0 && read == DEV_BSIZE) { if (!getdisklabel(buf, lp)) { recursion--; return 0; } } if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read) || read != DEV_BSIZE) { recursion--; return ERDLAB; } } else if (p->dp_typ == DOSPTYP_EXTEND) { poff = get_long(&p->dp_start); if (!search_label(devp, poff, buf, lp, off0)) { recursion--; return 0; } if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read) || read != DEV_BSIZE) { recursion--; return ERDLAB; } } } recursion--; return ERDLAB; } int devopen(struct open_file *of, const char *name, char **file) { char *cp; char partition; char fname[256]; char buf[DEV_BSIZE]; struct disklabel label; int handle, part; size_t read; int error = 0; if (ofdev.handle != -1) panic("devopen"); if (of->f_flags != F_READ) return EPERM; strlcpy(fname, name, sizeof fname); cp = filename(fname, &partition); if (cp) { strlcpy(buf, cp, sizeof buf); *cp = 0; } if (!cp || !*buf) strlcpy(buf, DEFAULT_KERNEL, sizeof buf); if (!*fname) strlcpy(fname, bootdev, sizeof fname); strlcpy(opened_name, fname, sizeof opened_name); if (partition) { cp = opened_name + strlen(opened_name); *cp++ = ':'; *cp++ = partition; *cp = 0; } if (*buf != '/') strlcat(opened_name, "/", sizeof opened_name); strlcat(opened_name, buf, sizeof opened_name); *file = opened_name + strlen(fname) + 1; if ((handle = OF_finddevice(fname)) == -1) return ENOENT; if (OF_getprop(handle, "name", buf, sizeof buf) < 0) return ENXIO; if (OF_getprop(handle, "device_type", buf, sizeof buf) < 0) return ENXIO; if (!strcmp(buf, "block")) /* * For block devices, indicate raw partition * (:0 in OpenFirmware) */ strlcat(fname, ":0", sizeof fname); if ((handle = OF_open(fname)) == -1) return ENXIO; bzero(&ofdev, sizeof ofdev); ofdev.handle = handle; ofdev.dmabuf = NULL; OF_call_method("dma-alloc", handle, 1, 1, MAXPHYS, &ofdev.dmabuf); if (!strcmp(buf, "block")) { ofdev.type = OFDEV_DISK; ofdev.bsize = DEV_BSIZE; /* First try to find a disklabel without MBR partitions */ if (strategy(&ofdev, F_READ, LABELSECTOR, DEV_BSIZE, buf, &read) != 0 || read != DEV_BSIZE || getdisklabel(buf, &label)) { /* Else try MBR partitions */ error = read_mac_label(&ofdev, buf, &label); if (error == ERDLAB) error = search_label(&ofdev, 0, buf, &label, 0); if (error && error != ERDLAB) goto bad; } if (error == ERDLAB) { if (partition) /* * User specified a partition, * but there is none */ goto bad; /* No label, just use complete disk */ ofdev.partoff = 0; } else { part = partition ? partition - 'a' : 0; ofdev.partoff = label.d_partitions[part].p_offset; } of->f_dev = devsw; of->f_devdata = &ofdev; bcopy(&file_system_ufs, file_system, sizeof file_system[0]); bcopy(&file_system_cd9660, file_system + 1, sizeof file_system[0]); bcopy(&file_system_hfs, file_system + 2, sizeof file_system[0]); nfsys = 3; return 0; } if (!strcmp(buf, "network")) { ofdev.type = OFDEV_NET; of->f_dev = devsw; of->f_devdata = &ofdev; bcopy(&file_system_nfs, file_system, sizeof file_system[0]); nfsys = 1; if (error = net_open(&ofdev)) goto bad; return 0; } error = EFTYPE; bad: OF_close(handle); ofdev.handle = -1; return error; }