1/* $NetBSD: ofdev.c,v 1.7 2005/12/11 12:19:05 christos Exp $ */ 2 3/* 4 * Copyright (C) 1995, 1996 Wolfgang Solfrank. 5 * Copyright (C) 1995, 1996 TooLs GmbH. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by TooLs GmbH. 19 * 4. The name of TooLs GmbH may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33/* 34 * Device I/O routines using Open Firmware 35 */ 36 37#include <sys/param.h> 38#include <sys/disklabel.h> 39#include <sys/bootblock.h> 40 41#include <netinet/in.h> 42 43#include <lib/libkern/libkern.h> 44 45#include <lib/libsa/byteorder.h> 46#include <lib/libsa/ufs.h> 47#include <lib/libsa/cd9660.h> 48#include <lib/libsa/dosfs.h> 49#include <lib/libsa/nfs.h> 50 51#include "extern.h" 52#include "ofdev.h" 53#include "openfirm.h" 54 55extern char bootdev[]; 56 57#ifdef DEBUG 58# define DPRINTF printf 59#else 60# define DPRINTF while (0) printf 61#endif 62 63static char * 64filename(char *str, char *ppart) 65{ 66 char *cp, *lp; 67 char savec; 68 int dhandle; 69 char devtype[16]; 70 71 lp = str; 72 devtype[0] = 0; 73 *ppart = 0; 74 for (cp = str; *cp; lp = cp) { 75 /* For each component of the path name... */ 76 while (*++cp && *cp != '/'); 77 savec = *cp; 78 *cp = 0; 79 /* ...look whether there is a device with this name */ 80 dhandle = OF_finddevice(str); 81 *cp = savec; 82 if (dhandle == -1) { 83 /* if not, lp is the delimiter between device and path */ 84 /* if the last component was a block device... */ 85 if (!strcmp(devtype, "block")) { 86 /* search for arguments */ 87 for (cp = lp; 88 --cp >= str && *cp != '/' && *cp != ':';); 89 if (cp >= str && *cp == ':') { 90 /* found arguments, make firmware ignore them */ 91 *cp = 0; 92 for (cp = lp; *--cp && *cp != ',';); 93 if (*++cp >= 'a' && *cp <= 'a' + MAXPARTITIONS) 94 *ppart = *cp; 95 } 96 } 97 return lp; 98 } else if (OF_getprop(dhandle, "device_type", devtype, sizeof devtype) < 0) 99 devtype[0] = 0; 100 } 101 return 0; 102} 103 104static int 105strategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf, 106 size_t *rsize) 107{ 108 struct of_dev *dev = devdata; 109 u_quad_t pos; 110 int n; 111 112 if (rw != F_READ) 113 return EPERM; 114 if (dev->type != OFDEV_DISK) 115 panic("strategy"); 116 117 pos = (u_quad_t)(blk + dev->partoff) * dev->bsize; 118 119 for (;;) { 120 if (OF_seek(dev->handle, pos) < 0) 121 break; 122 n = OF_read(dev->handle, buf, size); 123 if (n == -2) 124 continue; 125 if (n < 0) 126 break; 127 *rsize = n; 128 return 0; 129 } 130 return EIO; 131} 132 133static int 134devclose(struct open_file *of) 135{ 136 struct of_dev *op = of->f_devdata; 137 138 if (op->type == OFDEV_NET) 139 net_close(op); 140 OF_close(op->handle); 141 op->handle = -1; 142 return 0; 143} 144 145static char ofw[] = "OpenFirmware"; 146 147struct devsw devsw[1] = { 148 { 149 ofw, 150 strategy, 151 (int (*)(struct open_file *, ...))nodev, 152 devclose, 153 noioctl 154 } 155}; 156int ndevs = sizeof devsw / sizeof devsw[0]; 157 158static struct fs_ops file_system_ufs = FS_OPS(ufs); 159static struct fs_ops file_system_cd9660 = FS_OPS(cd9660); 160static struct fs_ops file_system_dosfs = FS_OPS(dosfs); 161static struct fs_ops file_system_nfs = FS_OPS(nfs); 162 163struct fs_ops file_system[3]; 164int nfsys; 165 166static struct of_dev ofdev = { 167 -1, 168}; 169 170char opened_name[256]; 171int floppyboot; 172 173static u_long 174get_long(const void *p) 175{ 176 const unsigned char *cp = p; 177 178 return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24); 179} 180 181/* 182 * Find a valid disklabel. 183 */ 184static int 185search_label(struct of_dev *devp, u_long off, char *buf, struct disklabel *lp, 186 u_long off0) 187{ 188 size_t nread; 189 struct mbr_partition *p; 190 int i; 191 u_long poff; 192 static int recursion; 193 194 if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &nread) 195 || nread != DEV_BSIZE) 196 return ERDLAB; 197 198 if (*(u_int16_t *)&buf[MBR_MAGIC_OFFSET] != sa_htole16(MBR_MAGIC)) 199 return ERDLAB; 200 201 if (recursion++ <= 1) 202 off0 += off; 203 for (p = (struct mbr_partition *)(buf + MBR_PART_OFFSET), i = 4; 204 --i >= 0; p++) { 205 if (p->mbrp_type == MBR_PTYPE_NETBSD 206#ifdef COMPAT_386BSD_MBRPART 207 || (p->mbrp_type == MBR_PTYPE_386BSD && 208 (printf("WARNING: old BSD partition ID!\n"), 1) 209 /* XXX XXX - libsa printf() is void */ ) 210#endif 211 ) { 212 poff = get_long(&p->mbrp_start) + off0; 213 if (strategy(devp, F_READ, poff + LABELSECTOR, 214 DEV_BSIZE, buf, &nread) == 0 215 && nread == DEV_BSIZE) { 216 if (!getdisklabel(buf, lp)) { 217 recursion--; 218 return 0; 219 } 220 } 221 if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &nread) 222 || nread != DEV_BSIZE) { 223 recursion--; 224 return ERDLAB; 225 } 226 } else if (p->mbrp_type == MBR_PTYPE_EXT) { 227 poff = get_long(&p->mbrp_start); 228 if (!search_label(devp, poff, buf, lp, off0)) { 229 recursion--; 230 return 0; 231 } 232 if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &nread) 233 || nread != DEV_BSIZE) { 234 recursion--; 235 return ERDLAB; 236 } 237 } 238 } 239 recursion--; 240 return ERDLAB; 241} 242 243int 244devopen(struct open_file *of, const char *name, char **file) 245{ 246 char *cp; 247 char partition; 248 char fname[256]; 249 char buf[DEV_BSIZE]; 250 struct disklabel label; 251 int handle, part; 252 size_t nread; 253 int error = 0; 254 255 if (ofdev.handle != -1) 256 panic("devopen"); 257 if (of->f_flags != F_READ) 258 return EPERM; 259 (void)strcpy(fname, name); 260 cp = filename(fname, &partition); 261 if (cp) { 262 DPRINTF("filename=%s\n", cp); 263 strcpy(buf, cp); 264 *cp = 0; 265 } 266 if (!cp || !*buf) 267 strcpy(buf, DEFAULT_KERNEL); 268 if (!*fname) 269 strcpy(fname, bootdev); 270 DPRINTF("fname=%s\n", fname); 271 strcpy(opened_name, fname); 272 if (partition) { 273 cp = opened_name + strlen(opened_name); 274 *cp++ = ':'; 275 *cp++ = partition; 276 *cp = 0; 277 } 278 if (*buf != '/') 279 (void)strcat(opened_name, "/"); 280 (void)strcat(opened_name, buf); 281 *file = opened_name + strlen(fname) + 1; 282 if ((handle = OF_finddevice(fname)) == -1) { 283 DPRINTF("OF_finddevice(\"%s\") failed\n", fname); 284 return ENOENT; 285 } 286 if (OF_getprop(handle, "name", buf, sizeof buf) < 0) 287 return ENXIO; 288 floppyboot = !strcmp(buf, "floppy"); 289 if (OF_getprop(handle, "device_type", buf, sizeof buf) < 0) 290 return ENXIO; 291 if (!strcmp(buf, "block")) 292 /* For block devices, indicate raw partition (:0 in OpenFirmware) */ 293 (void)strcat(fname, ":0"); 294 if ((handle = OF_open(fname)) == -1) 295 return ENXIO; 296 (void)memset(&ofdev, 0, sizeof ofdev); 297 ofdev.handle = handle; 298 if (!strcmp(buf, "block")) { 299 ofdev.type = OFDEV_DISK; 300 ofdev.bsize = DEV_BSIZE; 301 /* First try to find a disklabel without MBR partitions */ 302 if (strategy(&ofdev, F_READ, 303 LABELSECTOR, DEV_BSIZE, buf, &nread) != 0 304 || nread != DEV_BSIZE 305 || getdisklabel(buf, &label)) { 306 /* Else try MBR partitions */ 307 error = search_label(&ofdev, 0, buf, &label, 0); 308 if (error && error != ERDLAB) 309 goto bad; 310 } 311 312 if (error == ERDLAB) { 313 if (partition) 314 /* User specified a parititon, but there is none */ 315 goto bad; 316 /* No, label, just use complete disk */ 317 ofdev.partoff = 0; 318 } else { 319 part = partition ? partition - 'a' : 0; 320 ofdev.partoff = label.d_partitions[part].p_offset; 321 } 322 323 of->f_dev = devsw; 324 of->f_devdata = &ofdev; 325 file_system[0] = file_system_ufs; 326 file_system[1] = file_system_cd9660; 327 file_system[1] = file_system_dosfs; 328 nfsys = 2; 329 return 0; 330 } 331 if (!strcmp(buf, "network")) { 332 ofdev.type = OFDEV_NET; 333 of->f_dev = devsw; 334 of->f_devdata = &ofdev; 335 file_system[0] = file_system_nfs; 336 nfsys = 1; 337 if ((error = net_open(&ofdev)) != 0) 338 goto bad; 339 return 0; 340 } 341 error = EFTYPE; 342bad: 343 OF_close(handle); 344 ofdev.handle = -1; 345 return error; 346} 347