1 /* $NetBSD: ofdev.c,v 1.13 2005/12/11 12:18:30 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 "ofdev.h" 38 39 #include <sys/param.h> 40 #include <sys/disklabel.h> 41 #include <sys/bootblock.h> 42 43 #include <netinet/in.h> 44 45 #include <lib/libkern/libkern.h> 46 47 #include <lib/libsa/byteorder.h> 48 #include <lib/libsa/ufs.h> 49 #include <lib/libsa/cd9660.h> 50 #include <lib/libsa/dosfs.h> 51 #include <lib/libsa/nfs.h> 52 53 #include "net.h" 54 #include "openfirm.h" 55 56 extern char bootdev[]; 57 58 #ifdef DEBUG 59 # define DPRINTF printf 60 #else 61 # define DPRINTF while (0) printf 62 #endif 63 64 static char * 65 filename(char *str, char *ppart) 66 { 67 char *cp, *lp; 68 char savec; 69 int dhandle; 70 char devtype[16]; 71 72 lp = str; 73 devtype[0] = 0; 74 *ppart = 0; 75 for (cp = str; *cp; lp = cp) { 76 77 /* For each component of the path name... */ 78 while (*++cp && *cp != '/') 79 ; 80 savec = *cp; 81 *cp = 0; 82 83 /* ...look whether there is a device with this name */ 84 dhandle = OF_finddevice(str); 85 *cp = savec; 86 if (dhandle == -1) { 87 88 /* 89 * if not, lp is the delimiter between device and path 90 * if the last component was a block device. 91 */ 92 93 if (!strcmp(devtype, "block")) { 94 95 /* search for arguments */ 96 for (cp = lp; 97 --cp >= str && *cp != '/' && *cp != ':';) 98 ; 99 if (cp >= str && *cp == ':') { 100 101 /* 102 * found some arguments, 103 * make OFW ignore them. 104 */ 105 106 *cp = 0; 107 for (cp = lp; *--cp && *cp != ',';) 108 ; 109 if (*++cp >= 'a' && 110 *cp < 'a' + MAXPARTITIONS) 111 *ppart = *cp; 112 } 113 } 114 return lp; 115 } 116 if (OF_getprop(dhandle, "device_type", devtype, 117 sizeof devtype) < 0) 118 devtype[0] = 0; 119 } 120 return 0; 121 } 122 123 static int 124 strategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf, 125 size_t *rsize) 126 { 127 struct of_dev *dev = devdata; 128 u_quad_t pos; 129 int n; 130 131 if (rw != F_READ) 132 return EPERM; 133 if (dev->type != OFDEV_DISK) 134 panic("strategy"); 135 136 pos = (u_quad_t)((blk + dev->partoff) * dev->bsize); 137 138 for (;;) { 139 if (OF_seek(dev->handle, pos) < 0) 140 break; 141 n = OF_read(dev->handle, buf, size); 142 if (n == -2) 143 continue; 144 if (n < 0) 145 break; 146 *rsize = n; 147 return 0; 148 } 149 return EIO; 150 } 151 152 static int 153 devopen_dummy(struct open_file *of, ...) { 154 return -1; 155 } 156 157 static int 158 devclose(struct open_file *of) 159 { 160 struct of_dev *op = of->f_devdata; 161 162 if (op->type == OFDEV_NET) 163 net_close(op); 164 OF_close(op->handle); 165 op->handle = -1; 166 return 0; 167 } 168 169 static struct devsw devsw[1] = { 170 { "OpenFirmware", strategy, devopen_dummy, devclose, noioctl } 171 }; 172 int ndevs = sizeof devsw / sizeof devsw[0]; 173 174 static struct fs_ops file_system_ufs = FS_OPS(ufs); 175 static struct fs_ops file_system_cd9660 = FS_OPS(cd9660); 176 static struct fs_ops file_system_dosfs = FS_OPS(dosfs); 177 static struct fs_ops file_system_nfs = FS_OPS(nfs); 178 179 struct fs_ops file_system[3]; 180 int nfsys; 181 182 static struct of_dev ofdev = { 183 -1, 184 }; 185 186 char opened_name[256]; 187 int floppyboot; 188 189 static u_long 190 get_long(const void *p) 191 { 192 const unsigned char *cp = p; 193 194 return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24); 195 } 196 197 /* 198 * Find a valid disklabel. 199 */ 200 static int 201 search_label(struct of_dev *devp, u_long off, char *buf, struct disklabel *lp, 202 u_long off0) 203 { 204 size_t read; 205 struct mbr_partition *p; 206 int i; 207 u_long poff; 208 static int recursion; 209 210 if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read) 211 || read != DEV_BSIZE) 212 return ERDLAB; 213 214 if (*(u_int16_t *)&buf[MBR_MAGIC_OFFSET] != sa_htole16(MBR_MAGIC)) 215 return ERDLAB; 216 217 if (recursion++ <= 1) 218 off0 += off; 219 for (p = (struct mbr_partition *)(buf + MBR_PART_OFFSET), i = 0; 220 i < MBR_PART_COUNT; i++, p++) { 221 if (p->mbrp_type == MBR_PTYPE_NETBSD 222 #ifdef COMPAT_386BSD_MBRPART 223 || (p->mbrp_type == MBR_PTYPE_386BSD && 224 (printf("WARNING: old BSD partition ID!\n"), 1) 225 /* XXX XXX - libsa printf() is void */ ) 226 #endif 227 ) { 228 poff = get_long(&p->mbrp_start) + off0; 229 if (strategy(devp, F_READ, poff + LABELSECTOR, 230 DEV_BSIZE, buf, &read) == 0 231 && read == DEV_BSIZE) { 232 if (!getdisklabel(buf, lp)) { 233 recursion--; 234 return 0; 235 } 236 } 237 if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read) 238 || read != DEV_BSIZE) { 239 recursion--; 240 return ERDLAB; 241 } 242 } else if (p->mbrp_type == MBR_PTYPE_EXT) { 243 poff = get_long(&p->mbrp_start); 244 if (!search_label(devp, poff, buf, lp, off0)) { 245 recursion--; 246 return 0; 247 } 248 if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read) 249 || read != DEV_BSIZE) { 250 recursion--; 251 return ERDLAB; 252 } 253 } 254 } 255 256 recursion--; 257 return ERDLAB; 258 } 259 260 int 261 devopen(struct open_file *of, const char *name, char **file) 262 { 263 char *cp; 264 char partition; 265 char fname[256]; 266 char buf[DEV_BSIZE]; 267 struct disklabel label; 268 int handle, part; 269 size_t read; 270 int error = 0; 271 272 if (ofdev.handle != -1) 273 panic("devopen"); 274 if (of->f_flags != F_READ) 275 return EPERM; 276 strcpy(fname, name); 277 cp = filename(fname, &partition); 278 if (cp) { 279 DPRINTF("filename=%s\n", cp); 280 strcpy(buf, cp); 281 *cp = 0; 282 } 283 if (!cp || !*buf) 284 strcpy(buf, DEFAULT_KERNEL); 285 if (!*fname) 286 strcpy(fname, bootdev); 287 DPRINTF("fname=%s\n", fname); 288 strcpy(opened_name, fname); 289 if (partition) { 290 cp = opened_name + strlen(opened_name); 291 *cp++ = ':'; 292 *cp++ = partition; 293 *cp = 0; 294 } 295 if (*buf != '/') 296 strcat(opened_name, "/"); 297 strcat(opened_name, buf); 298 *file = opened_name + strlen(fname) + 1; 299 if (partition) { 300 *file += 2; 301 } 302 if ((handle = OF_finddevice(fname)) == -1) { 303 DPRINTF("OF_finddevice(\"%s\") failed\n", fname); 304 return ENOENT; 305 } 306 if (OF_getprop(handle, "name", buf, sizeof buf) < 0) 307 return ENXIO; 308 floppyboot = !strcmp(buf, "floppy"); 309 if (OF_getprop(handle, "device_type", buf, sizeof buf) < 0) 310 return ENXIO; 311 if (!strcmp(buf, "block")) { 312 313 /* 314 * For block devices, indicate raw partition 315 * (:0 in OpenFirmware) 316 */ 317 318 strcat(fname, ":0"); 319 } 320 if ((handle = OF_open(fname)) == -1) 321 return ENXIO; 322 memset(&ofdev, 0, sizeof ofdev); 323 ofdev.handle = handle; 324 if (!strcmp(buf, "block")) { 325 ofdev.type = OFDEV_DISK; 326 ofdev.bsize = DEV_BSIZE; 327 328 /* First try to find a disklabel without MBR partitions */ 329 if (strategy(&ofdev, F_READ, 330 LABELSECTOR, DEV_BSIZE, buf, &read) != 0 331 || read != DEV_BSIZE 332 || getdisklabel(buf, &label)) { 333 334 /* Else try MBR partitions */ 335 error = search_label(&ofdev, 0, buf, &label, 0); 336 if (error && error != ERDLAB) 337 goto bad; 338 } 339 340 if (error == ERDLAB) { 341 if (partition) { 342 343 /* 344 * User specified a parititon, 345 * but there is none. 346 */ 347 348 goto bad; 349 } 350 351 /* No label, just use complete disk */ 352 ofdev.partoff = 0; 353 } else { 354 part = partition ? partition - 'a' : 0; 355 ofdev.partoff = label.d_partitions[part].p_offset; 356 } 357 358 of->f_dev = devsw; 359 of->f_devdata = &ofdev; 360 file_system[0] = file_system_ufs; 361 file_system[1] = file_system_cd9660; 362 file_system[2] = file_system_dosfs; 363 nfsys = 3; 364 return 0; 365 } 366 if (!strcmp(buf, "network")) { 367 ofdev.type = OFDEV_NET; 368 of->f_dev = devsw; 369 of->f_devdata = &ofdev; 370 file_system[0] = file_system_nfs; 371 nfsys = 1; 372 if ((error = net_open(&ofdev)) != 0) 373 goto bad; 374 return 0; 375 } 376 error = EFTYPE; 377 bad: 378 OF_close(handle); 379 ofdev.handle = -1; 380 return error; 381 } 382