1 /* $NetBSD: ofdev.c,v 1.31 2024/02/10 18:43:51 andvar 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 #include "openfirm.h" 39 40 #include <sys/param.h> 41 #include <sys/disklabel.h> 42 #include <sys/bootblock.h> 43 44 #include <netinet/in.h> 45 46 #include <lib/libkern/libkern.h> 47 48 #include <lib/libsa/stand.h> 49 #include <lib/libsa/byteorder.h> 50 #include <lib/libsa/cd9660.h> 51 #include <lib/libsa/dosfs.h> 52 #include <lib/libsa/nfs.h> 53 #include <lib/libsa/ufs.h> 54 #include <lib/libsa/lfs.h> 55 #include <lib/libsa/ustarfs.h> 56 57 #include "hfs.h" 58 #include "net.h" 59 60 #ifdef DEBUG 61 # define DPRINTF printf 62 #else 63 # define DPRINTF while (0) printf 64 #endif 65 66 static int 67 strategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf, 68 size_t *rsize) 69 { 70 struct of_dev *dev = devdata; 71 u_quad_t pos; 72 int n; 73 74 if (rw != F_READ) 75 return EPERM; 76 if (dev->type != OFDEV_DISK) 77 panic("strategy"); 78 79 pos = (u_quad_t)(blk + dev->partoff) * dev->bsize; 80 81 for (;;) { 82 if (OF_seek(dev->handle, pos) < 0) 83 break; 84 n = OF_read(dev->handle, buf, size); 85 if (n == -2) 86 continue; 87 if (n < 0) 88 break; 89 *rsize = n; 90 return 0; 91 } 92 return EIO; 93 } 94 95 static int 96 devopen_dummy(struct open_file *of, ...) { 97 return -1; 98 } 99 100 static int 101 devclose(struct open_file *of) 102 { 103 uint32_t cells[2]; 104 struct of_dev *op = of->f_devdata; 105 106 cells[0] = (uintptr_t)op->dmabuf; 107 cells[1] = MAXPHYS; 108 109 if (op->type == OFDEV_NET) 110 net_close(op); 111 OF_call_method("dma-free", op->handle, 2, 0, (int *)cells); 112 OF_close(op->handle); 113 op->handle = -1; 114 return 0; 115 } 116 117 static struct devsw of_devsw[1] = { 118 { "OpenFirmware", strategy, devopen_dummy, devclose, noioctl } 119 }; 120 int ndevs = sizeof of_devsw / sizeof of_devsw[0]; 121 122 static struct fs_ops file_system_ffsv1 = FS_OPS(ffsv1); 123 static struct fs_ops file_system_ffsv2 = FS_OPS(ffsv2); 124 static struct fs_ops file_system_lfsv1 = FS_OPS(lfsv1); 125 static struct fs_ops file_system_lfsv2 = FS_OPS(lfsv2); 126 static struct fs_ops file_system_hfs = FS_OPS(hfs); 127 static struct fs_ops file_system_ustarfs = FS_OPS(ustarfs); 128 static struct fs_ops file_system_cd9660 = FS_OPS(cd9660); 129 static struct fs_ops file_system_nfs = FS_OPS(nfs); 130 static struct fs_ops file_system_dosfs = FS_OPS(dosfs); 131 132 struct fs_ops file_system[9]; 133 int nfsys; 134 135 static struct of_dev ofdev = { 136 -1, 137 }; 138 139 char opened_name[MAXBOOTPATHLEN]; 140 141 /* 142 * Check if this APM partition is a suitable root partition and return 143 * its file system type or zero. 144 */ 145 static u_int8_t 146 check_apm_root(struct part_map_entry *part, int *clust) 147 { 148 struct blockzeroblock *bzb; 149 char typestr[32], *s; 150 u_int8_t fstype; 151 152 *clust = 0; /* may become 1 for A/UX partitions */ 153 fstype = 0; 154 bzb = (struct blockzeroblock *)(&part->pmBootArgs); 155 156 /* convert partition type name to upper case */ 157 strncpy(typestr, (char *)part->pmPartType, sizeof(typestr)); 158 typestr[sizeof(typestr) - 1] = '\0'; 159 for (s = typestr; *s; s++) 160 if ((*s >= 'a') && (*s <= 'z')) 161 *s = (*s - 'a' + 'A'); 162 163 if (strcmp(PART_TYPE_NBSD_PPCBOOT, typestr) == 0) { 164 if ((bzb->bzbMagic == BZB_MAGIC) && 165 (bzb->bzbType < FSMAXTYPES)) 166 fstype = bzb->bzbType; 167 else 168 fstype = FS_BSDFFS; 169 } else if (strcmp(PART_TYPE_UNIX, typestr) == 0 && 170 bzb->bzbMagic == BZB_MAGIC && (bzb->bzbFlags & BZB_ROOTFS)) { 171 *clust = bzb->bzbCluster; 172 fstype = FS_BSDFFS; 173 } 174 175 return fstype; 176 } 177 178 /* 179 * Build a disklabel from APM partitions. 180 * We will just look for a suitable root partition and insert it into 181 * the 'a' slot. Should be sufficient to boot a kernel from it. 182 */ 183 static int 184 search_mac_label(struct of_dev *devp, char *buf, struct disklabel *lp) 185 { 186 struct part_map_entry *pme; 187 struct partition *a_part; 188 size_t nread; 189 int blkno, clust, lastblk, lastclust; 190 u_int8_t fstype; 191 192 pme = (struct part_map_entry *)buf; 193 a_part = &lp->d_partitions[0]; /* disklabel 'a' partition */ 194 lastclust = -1; 195 196 for (blkno = lastblk = 1; blkno <= lastblk; blkno++) { 197 if (strategy(devp, F_READ, blkno, DEV_BSIZE, pme, &nread) 198 || nread != DEV_BSIZE) 199 return ERDLAB; 200 if (pme->pmSig != PART_ENTRY_MAGIC || 201 pme->pmPartType[0] == '\0') 202 break; 203 lastblk = pme->pmMapBlkCnt; 204 205 fstype = check_apm_root(pme, &clust); 206 207 if (fstype && (lastclust == -1 || clust < lastclust)) { 208 a_part->p_size = pme->pmPartBlkCnt; 209 a_part->p_offset = pme->pmPyPartStart; 210 a_part->p_fstype = fstype; 211 if ((lastclust = clust) == 0) 212 break; /* we won't find a better match */ 213 } 214 } 215 if (lastclust < 0) 216 return ERDLAB; /* no root partition found */ 217 218 /* pretend we only have partitions 'a', 'b' and 'c' */ 219 lp->d_npartitions = RAW_PART + 1; 220 return 0; 221 } 222 223 static u_long 224 get_long(const void *p) 225 { 226 const unsigned char *cp = p; 227 228 return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24); 229 } 230 231 /* 232 * Find a valid disklabel from MBR partitions. 233 */ 234 static int 235 search_dos_label(struct of_dev *devp, u_long off, char *buf, 236 struct disklabel *lp, u_long off0) 237 { 238 size_t nread; 239 struct mbr_partition *p; 240 int i; 241 u_long poff; 242 static int recursion; 243 244 if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &nread) 245 || nread != DEV_BSIZE) 246 return ERDLAB; 247 248 if (*(u_int16_t *)&buf[MBR_MAGIC_OFFSET] != sa_htole16(MBR_MAGIC)) 249 return ERDLAB; 250 251 if (recursion++ <= 1) 252 off0 += off; 253 for (p = (struct mbr_partition *)(buf + MBR_PART_OFFSET), i = 4; 254 --i >= 0; p++) { 255 if (p->mbrp_type == MBR_PTYPE_NETBSD 256 #ifdef COMPAT_386BSD_MBRPART 257 || (p->mbrp_type == MBR_PTYPE_386BSD && 258 (printf("WARNING: old BSD partition ID!\n"), 1) 259 /* XXX XXX - libsa printf() is void */ ) 260 #endif 261 ) { 262 poff = get_long(&p->mbrp_start) + off0; 263 if (strategy(devp, F_READ, poff + 1, 264 DEV_BSIZE, buf, &nread) == 0 265 && nread == DEV_BSIZE) { 266 if (!getdisklabel(buf, lp)) { 267 recursion--; 268 return 0; 269 } 270 } 271 if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &nread) 272 || nread != DEV_BSIZE) { 273 recursion--; 274 return ERDLAB; 275 } 276 } else if (p->mbrp_type == MBR_PTYPE_EXT) { 277 poff = get_long(&p->mbrp_start); 278 if (!search_dos_label(devp, poff, buf, lp, off0)) { 279 recursion--; 280 return 0; 281 } 282 if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &nread) 283 || nread != DEV_BSIZE) { 284 recursion--; 285 return ERDLAB; 286 } 287 } 288 } 289 recursion--; 290 return ERDLAB; 291 } 292 293 bool 294 parsefilepath(const char *path, char *devname, char *fname, char *ppart) 295 { 296 char *cp, *lp; 297 char savec; 298 int dhandle; 299 char str[MAXBOOTPATHLEN]; 300 char devtype[16]; 301 302 DPRINTF("%s: path = %s\n", __func__, path); 303 304 devtype[0] = '\0'; 305 306 if (devname != NULL) 307 devname[0] = '\0'; 308 if (fname != NULL) 309 fname[0] = '\0'; 310 if (ppart != NULL) 311 *ppart = 0; 312 313 strlcpy(str, path, sizeof(str)); 314 lp = str; 315 for (cp = str; *cp != '\0'; lp = cp) { 316 /* For each component of the path name... */ 317 while (*++cp != '\0' && *cp != '/') 318 continue; 319 savec = *cp; 320 *cp = '\0'; 321 /* ...look whether there is a device with this name */ 322 dhandle = OF_finddevice(str); 323 DPRINTF("%s: Checking %s: dhandle = %d\n", 324 __func__, str, dhandle); 325 *cp = savec; 326 if (dhandle != -1) { 327 /* 328 * If it's a valid device, lp is a delimiter 329 * in the OF device path. 330 */ 331 if (OF_getprop(dhandle, "device_type", devtype, 332 sizeof devtype) < 0) 333 devtype[0] = '\0'; 334 continue; 335 } 336 337 /* 338 * If not, lp is the delimiter between OF device path 339 * and the specified filename. 340 */ 341 342 /* Check if the last component was a block device... */ 343 if (strcmp(devtype, "block") == 0) { 344 /* search for arguments */ 345 for (cp = lp; 346 --cp >= str && *cp != '/' && *cp != ':';) 347 continue; 348 if (cp >= str && *cp == ':') { 349 /* found arguments */ 350 for (cp = lp; 351 *--cp != ':' && *cp != ',';) 352 continue; 353 if (*++cp >= 'a' && 354 *cp <= 'a' + MAXPARTITIONS && 355 ppart != NULL) 356 *ppart = *cp; 357 } 358 } 359 if (*lp != '\0') { 360 /* set filename */ 361 DPRINTF("%s: filename = %s\n", __func__, lp); 362 if (fname != NULL) 363 strcpy(fname, lp); 364 if (str != lp) { 365 /* set device path */ 366 *lp = '\0'; 367 DPRINTF("%s: device path = %s\n", 368 __func__, str); 369 if (devname != NULL) 370 strcpy(devname, str); 371 } 372 } 373 return true; 374 } 375 376 DPRINTF("%s: invalid path?\n", __func__); 377 return false; 378 } 379 380 int 381 devopen(struct open_file *of, const char *name, char **file) 382 { 383 char *cp; 384 char partition; 385 char devname[MAXBOOTPATHLEN]; 386 char filename[MAXBOOTPATHLEN]; 387 char buf[DEV_BSIZE]; 388 struct disklabel label; 389 int handle, part; 390 size_t nread; 391 int error = 0; 392 uint32_t cells[2]; 393 394 if (ofdev.handle != -1) 395 panic("devopen"); 396 if (of->f_flags != F_READ) 397 return EPERM; 398 399 if (!parsefilepath(name, devname, filename, &partition)) 400 return ENOENT; 401 402 if (filename[0] == '\0') 403 /* no filename */ 404 return ENOENT; 405 406 if (devname[0] == '\0') 407 /* no device, use default bootdev */ 408 strlcpy(devname, bootdev, sizeof(devname)); 409 410 DPRINTF("%s: devname = %s, filename = %s\n", 411 __func__, devname, filename); 412 413 strlcpy(opened_name, devname, sizeof(opened_name)); 414 if (partition) { 415 cp = opened_name + strlen(opened_name); 416 *cp++ = ':'; 417 *cp++ = partition; 418 *cp = 0; 419 } 420 if (filename[0] != '/') 421 strlcat(opened_name, "/", sizeof(opened_name)); 422 strlcat(opened_name, filename, sizeof(opened_name)); 423 424 DPRINTF("%s: opened_name = %s\n", __func__, opened_name); 425 426 *file = opened_name + strlen(devname) + 1; 427 if ((handle = OF_finddevice(devname)) == -1) 428 return ENOENT; 429 if (OF_getprop(handle, "device_type", buf, sizeof buf) < 0) 430 return ENXIO; 431 if (!strcmp(buf, "block") && strrchr(devname, ':') == NULL) 432 /* indicate raw partition, when missing */ 433 if (ofw_version >= 3) 434 strlcat(devname, ":0", sizeof(devname)); 435 if ((handle = OF_open(devname)) == -1) 436 return ENXIO; 437 memset(&ofdev, 0, sizeof ofdev); 438 ofdev.handle = handle; 439 ofdev.dmabuf = NULL; 440 cells[0] = MAXPHYS; 441 OF_call_method("dma-alloc", handle, 1, 1, (int *)cells); 442 ofdev.dmabuf = (void*)(uintptr_t)cells[1]; 443 if (!strcmp(buf, "block")) { 444 ofdev.type = OFDEV_DISK; 445 ofdev.bsize = DEV_BSIZE; 446 /* First try to find a disklabel without partitions */ 447 if (!floppyboot && 448 (strategy(&ofdev, F_READ, 449 LABELSECTOR, DEV_BSIZE, buf, &nread) != 0 450 || nread != DEV_BSIZE 451 || getdisklabel(buf, &label))) { 452 /* Else try APM or MBR partitions */ 453 struct drvr_map *map = (struct drvr_map *)buf; 454 455 if (map->sbSig == DRIVER_MAP_MAGIC) 456 error = search_mac_label(&ofdev, buf, &label); 457 else 458 error = search_dos_label(&ofdev, 0, buf, 459 &label, 0); 460 if (error && error != ERDLAB) 461 goto bad; 462 } 463 464 if (error == ERDLAB) { 465 if (partition) 466 /* 467 * User specified a partition, 468 * but there is none 469 */ 470 goto bad; 471 /* No label, just use complete disk */ 472 ofdev.partoff = 0; 473 } else { 474 part = partition ? partition - 'a' : 0; 475 ofdev.partoff = label.d_partitions[part].p_offset; 476 } 477 478 of->f_dev = of_devsw; 479 of->f_devdata = &ofdev; 480 file_system[0] = file_system_ffsv1; 481 file_system[1] = file_system_ffsv2; 482 file_system[2] = file_system_lfsv1; 483 file_system[3] = file_system_lfsv2; 484 file_system[4] = file_system_ustarfs; 485 file_system[5] = file_system_cd9660; 486 file_system[6] = file_system_hfs; 487 file_system[7] = file_system_dosfs; 488 nfsys = 8; 489 return 0; 490 } 491 if (!strcmp(buf, "network")) { 492 ofdev.type = OFDEV_NET; 493 of->f_dev = of_devsw; 494 of->f_devdata = &ofdev; 495 file_system[0] = file_system_nfs; 496 nfsys = 1; 497 if ((error = net_open(&ofdev))) 498 goto bad; 499 return 0; 500 } 501 error = EFTYPE; 502 bad: 503 OF_close(handle); 504 ofdev.handle = -1; 505 return error; 506 } 507