/* obdisk.c - Open Boot disk access. */ /* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2019 Free Software Foundation, Inc. * * VAS_EBOOT is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VAS_EBOOT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VAS_EBOOT. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #define IEEE1275_DEV "ieee1275/" #define IEEE1275_DISK_ALIAS "/disk@" struct disk_dev { struct disk_dev *next; struct disk_dev **prev; char *name; char *raw_name; char *VasEBoot_devpath; char *VasEBoot_alias_devpath; VasEBoot_ieee1275_ihandle_t ihandle; VasEBoot_uint32_t block_size; VasEBoot_uint64_t num_blocks; unsigned int log_sector_size; VasEBoot_uint32_t opened; VasEBoot_uint32_t valid; VasEBoot_uint32_t boot_dev; }; struct parent_dev { struct parent_dev *next; struct parent_dev **prev; char *name; char *type; VasEBoot_ieee1275_ihandle_t ihandle; VasEBoot_uint32_t address_cells; }; static struct VasEBoot_scsi_test_unit_ready tur = { .opcode = VasEBoot_scsi_cmd_test_unit_ready, .lun = 0, .reserved1 = 0, .reserved2 = 0, .reserved3 = 0, .control = 0, }; static int disks_enumerated; static struct disk_dev *disk_devs; static struct parent_dev *parent_devs; static const char *block_blacklist[] = { /* Requires additional work in VasEBoot before being able to be used. */ "/iscsi-hba", /* This block device should never be used by VasEBoot. */ "/reboot-memory@0", 0 }; #define STRCMP(a, b) ((a) && (b) && (VasEBoot_strcmp (a, b) == 0)) static char * strip_ob_partition (char *path) { char *sptr; sptr = VasEBoot_strstr (path, ":"); if (sptr != NULL) *sptr = '\0'; return path; } static void escape_commas (const char *src, char *dest) { const char *iptr; for (iptr = src; *iptr; ) { if (*iptr == ',') *dest++ ='\\'; *dest++ = *iptr++; } *dest = '\0'; } static int count_commas (const char *src) { int count = 0; for ( ; *src; src++) if (*src == ',') count++; return count; } static char * decode_VasEBoot_devname (const char *name) { char *devpath; char *p, c; VasEBoot_size_t sz; if (VasEBoot_add (VasEBoot_strlen (name), 1, &sz)) { VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of device name")); return NULL; } devpath = VasEBoot_malloc (sz); if (devpath == NULL) return NULL; /* Un-escape commas. */ p = devpath; while ((c = *name++) != '\0') { if (c == '\\' && *name == ',') { *p++ = ','; name++; } else *p++ = c; } *p++ = '\0'; return devpath; } static char * encode_VasEBoot_devname (const char *path) { char *encoding, *optr; VasEBoot_size_t sz; if (path == NULL) return NULL; if (VasEBoot_add (sizeof (IEEE1275_DEV) + 1, count_commas (path), &sz) || VasEBoot_add (sz, VasEBoot_strlen (path), &sz)) { VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining encoding size")); VasEBoot_print_error (); return NULL; } encoding = VasEBoot_malloc (sz); if (encoding == NULL) { VasEBoot_print_error (); return NULL; } optr = VasEBoot_stpcpy (encoding, IEEE1275_DEV); escape_commas (path, optr); return encoding; } static char * get_parent_devname (const char *devname) { char *parent, *pptr; parent = VasEBoot_strdup (devname); if (parent == NULL) { VasEBoot_print_error (); return NULL; } pptr = VasEBoot_strstr (parent, IEEE1275_DISK_ALIAS); if (pptr != NULL) *pptr = '\0'; return parent; } static void free_parent_dev (struct parent_dev *parent) { if (parent != NULL) { VasEBoot_free (parent->name); VasEBoot_free (parent->type); VasEBoot_free (parent); } } static struct parent_dev * init_parent (const char *parent) { struct parent_dev *op; op = VasEBoot_zalloc (sizeof (struct parent_dev)); if (op == NULL) { VasEBoot_print_error (); return NULL; } op->name = VasEBoot_strdup (parent); op->type = VasEBoot_malloc (IEEE1275_MAX_PROP_LEN); if ((op->name == NULL) || (op->type == NULL)) { VasEBoot_print_error (); free_parent_dev (op); return NULL; } return op; } static struct parent_dev * open_new_parent (const char *parent) { struct parent_dev *op = init_parent(parent); VasEBoot_ieee1275_ihandle_t ihandle; VasEBoot_ieee1275_phandle_t phandle; VasEBoot_uint32_t address_cells = 2; if (op == NULL) return NULL; VasEBoot_ieee1275_open (parent, &ihandle); if (ihandle == 0) { VasEBoot_error (VAS_EBOOT_ERR_BAD_DEVICE, "unable to open %s", parent); VasEBoot_print_error (); free_parent_dev (op); return NULL; } if (VasEBoot_ieee1275_instance_to_package (ihandle, &phandle)) { VasEBoot_error (VAS_EBOOT_ERR_BAD_DEVICE, "unable to get parent %s", parent); VasEBoot_print_error (); free_parent_dev (op); return NULL; } /* * IEEE Std 1275-1994 page 110: A missing "address-cells" property * signifies that the number of address cells is two. So ignore on error. */ if (VasEBoot_ieee1275_get_integer_property (phandle, "#address-cells", &address_cells, sizeof (address_cells), 0) != 0) address_cells = 2; VasEBoot_ieee1275_get_property (phandle, "device_type", op->type, IEEE1275_MAX_PROP_LEN, NULL); op->ihandle = ihandle; op->address_cells = address_cells; return op; } static struct parent_dev * open_parent (const char *parent) { struct parent_dev *op; op = VasEBoot_named_list_find (VAS_EBOOT_AS_NAMED_LIST (parent_devs), parent); if (op == NULL) { op = open_new_parent (parent); if (op != NULL) VasEBoot_list_push (VAS_EBOOT_AS_LIST_P (&parent_devs), VAS_EBOOT_AS_LIST (op)); } return op; } static void display_parents (void) { struct parent_dev *parent; VasEBoot_printf ("-------------------- PARENTS --------------------\n"); FOR_LIST_ELEMENTS (parent, parent_devs) { VasEBoot_printf ("name: %s\n", parent->name); VasEBoot_printf ("type: %s\n", parent->type); VasEBoot_printf ("address_cells %x\n", parent->address_cells); } VasEBoot_printf ("-------------------------------------------------\n"); } static char * canonicalise_4cell_ua (VasEBoot_ieee1275_ihandle_t ihandle, char *unit_address) { VasEBoot_uint32_t phy_lo, phy_hi, lun_lo, lun_hi; int valid_phy = 0; VasEBoot_size_t size; char *canon = NULL; valid_phy = VasEBoot_ieee1275_decode_unit4 (ihandle, unit_address, VasEBoot_strlen (unit_address), &phy_lo, &phy_hi, &lun_lo, &lun_hi); if ((valid_phy == 0) && (phy_hi != 0xffffffff)) canon = VasEBoot_ieee1275_encode_uint4 (ihandle, phy_lo, phy_hi, lun_lo, lun_hi, &size); return canon; } static char * canonicalise_disk (const char *devname) { char *canon, *parent; struct parent_dev *op; canon = VasEBoot_ieee1275_canonicalise_devname (devname); if (canon == NULL) { /* This should not happen. */ VasEBoot_error (VAS_EBOOT_ERR_BAD_DEVICE, "canonicalise devname failed"); VasEBoot_print_error (); return NULL; } /* Don't try to open the parent of a virtual device. */ if (VasEBoot_strstr (canon, "virtual-devices")) return canon; parent = get_parent_devname (canon); if (parent == NULL) return NULL; op = open_parent (parent); /* * Devices with 4 address cells can have many different types of addressing * (phy, wwn, and target lun). Use the parents encode-unit / decode-unit * to find the true canonical name. */ if ((op) && (op->address_cells == 4)) { char *unit_address, *real_unit_address, *real_canon; VasEBoot_size_t real_unit_str_len; unit_address = VasEBoot_strstr (canon, IEEE1275_DISK_ALIAS); unit_address += VasEBoot_strlen (IEEE1275_DISK_ALIAS); if (unit_address == NULL) { /* * This should not be possible, but return the canonical name for * the non-disk block device. */ VasEBoot_free (parent); return (canon); } real_unit_address = canonicalise_4cell_ua (op->ihandle, unit_address); if (real_unit_address == NULL) { /* * This is not an error, since this function could be called with a devalias * containing a drive that isn't installed in the system. */ VasEBoot_free (parent); return NULL; } real_unit_str_len = VasEBoot_strlen (op->name) + sizeof (IEEE1275_DISK_ALIAS) + VasEBoot_strlen (real_unit_address); if (VasEBoot_add (VasEBoot_strlen (op->name), sizeof (IEEE1275_DISK_ALIAS), &real_unit_str_len) || VasEBoot_add (real_unit_str_len, VasEBoot_strlen (real_unit_address), &real_unit_str_len)) { VasEBoot_free (parent); VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of canonical name")); VasEBoot_print_error (); return NULL; } real_canon = VasEBoot_malloc (real_unit_str_len); if (real_canon == NULL) { VasEBoot_free (parent); VasEBoot_print_error (); return NULL; } VasEBoot_snprintf (real_canon, real_unit_str_len, "%s/disk@%s", op->name, real_unit_address); VasEBoot_free (canon); canon = real_canon; } VasEBoot_free (parent); return (canon); } static struct disk_dev * add_canon_disk (const char *cname) { VasEBoot_size_t sz; struct disk_dev *dev; dev = VasEBoot_zalloc (sizeof (struct disk_dev)); if (dev == NULL) goto failed; if (VasEBoot_ieee1275_test_flag (VAS_EBOOT_IEEE1275_FLAG_RAW_DEVNAMES)) { /* * Append :nolabel to the end of all SPARC disks. * nolabel is mutually exclusive with all other * arguments and allows a client program to open * the entire (raw) disk. Any disk label is ignored. */ if (VasEBoot_add (VasEBoot_strlen (cname), sizeof (":nolabel"), &sz)) { VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_RANGE, "overflow detected while appending :nolabel to end of canonical name"); goto failed; } dev->raw_name = VasEBoot_malloc (sz); if (dev->raw_name == NULL) goto failed; VasEBoot_snprintf (dev->raw_name, sz, "%s:nolabel", cname); } /* * Don't use VasEBoot_ieee1275_encode_devname here, the devpath in VasEBoot.cfg doesn't * understand device aliases, which the layer above sometimes sends us. */ dev->VasEBoot_devpath = encode_VasEBoot_devname(cname); if (dev->VasEBoot_devpath == NULL) goto failed; dev->name = VasEBoot_strdup (cname); if (dev->name == NULL) goto failed; dev->valid = 1; VasEBoot_list_push (VAS_EBOOT_AS_LIST_P (&disk_devs), VAS_EBOOT_AS_LIST (dev)); return dev; failed: VasEBoot_print_error (); if (dev != NULL) { VasEBoot_free (dev->name); VasEBoot_free (dev->VasEBoot_devpath); VasEBoot_free (dev->raw_name); } VasEBoot_free (dev); return NULL; } static VasEBoot_err_t add_disk (const char *path) { VasEBoot_err_t ret = VAS_EBOOT_ERR_NONE; struct disk_dev *dev; char *canon; canon = canonicalise_disk (path); dev = VasEBoot_named_list_find (VAS_EBOOT_AS_NAMED_LIST (disk_devs), canon); if ((canon != NULL) && (dev == NULL)) { struct disk_dev *ob_device; ob_device = add_canon_disk (canon); if (ob_device == NULL) ret = VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_MEMORY, "failure to add disk"); } else if (dev != NULL) dev->valid = 1; VasEBoot_free (canon); return (ret); } static VasEBoot_err_t VasEBoot_obdisk_read (VasEBoot_disk_t disk, VasEBoot_disk_addr_t sector, VasEBoot_size_t size, char *dest) { VasEBoot_err_t ret = VAS_EBOOT_ERR_NONE; struct disk_dev *dev; unsigned long long pos; VasEBoot_ssize_t result = 0; if (disk->data == NULL) return VasEBoot_error (VAS_EBOOT_ERR_BAD_DEVICE, "invalid disk data"); dev = (struct disk_dev *)disk->data; pos = sector << disk->log_sector_size; VasEBoot_ieee1275_seek (dev->ihandle, pos, &result); if (result < 0) { dev->opened = 0; return VasEBoot_error (VAS_EBOOT_ERR_READ_ERROR, "seek error, can't seek block %llu", (long long) sector); } VasEBoot_ieee1275_read (dev->ihandle, dest, size << disk->log_sector_size, &result); if (result != (VasEBoot_ssize_t) (size << disk->log_sector_size)) { dev->opened = 0; return VasEBoot_error (VAS_EBOOT_ERR_READ_ERROR, N_("failure reading sector 0x%llx " "from `%s'"), (unsigned long long) sector, disk->name); } return ret; } static void VasEBoot_obdisk_close (VasEBoot_disk_t disk) { VasEBoot_memset (disk, 0, sizeof (*disk)); } static void scan_usb_disk (const char *parent) { struct parent_dev *op; VasEBoot_ssize_t result; op = open_parent (parent); if (op == NULL) { VasEBoot_error (VAS_EBOOT_ERR_BAD_DEVICE, "unable to open %s", parent); VasEBoot_print_error (); return; } if ((VasEBoot_ieee1275_set_address (op->ihandle, 0, 0) == 0) && (VasEBoot_ieee1275_no_data_command (op->ihandle, &tur, &result) == 0) && (result == 0)) { char *buf; buf = VasEBoot_malloc (IEEE1275_MAX_PATH_LEN); if (buf == NULL) { VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_MEMORY, "disk scan failure"); VasEBoot_print_error (); return; } VasEBoot_snprintf (buf, IEEE1275_MAX_PATH_LEN, "%s/disk@0", parent); add_disk (buf); VasEBoot_free (buf); } } static void scan_nvme_disk (const char *path) { char *buf; buf = VasEBoot_malloc (IEEE1275_MAX_PATH_LEN); if (buf == NULL) { VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_MEMORY, "disk scan failure"); VasEBoot_print_error (); return; } VasEBoot_snprintf (buf, IEEE1275_MAX_PATH_LEN, "%s/disk@1", path); add_disk (buf); VasEBoot_free (buf); } static void scan_sparc_sas_2cell (struct parent_dev *op) { VasEBoot_ssize_t result; VasEBoot_uint8_t tgt; char *buf; buf = VasEBoot_malloc (IEEE1275_MAX_PATH_LEN); if (buf == NULL) { VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_MEMORY, "disk scan failure"); VasEBoot_print_error (); return; } for (tgt = 0; tgt < 0xf; tgt++) { if ((VasEBoot_ieee1275_set_address(op->ihandle, tgt, 0) == 0) && (VasEBoot_ieee1275_no_data_command (op->ihandle, &tur, &result) == 0) && (result == 0)) { VasEBoot_snprintf (buf, IEEE1275_MAX_PATH_LEN, "%s/disk@%" PRIxVAS_EBOOT_UINT32_T, op->name, tgt); add_disk (buf); } } } static void scan_sparc_sas_4cell (struct parent_dev *op) { VasEBoot_uint16_t exp; VasEBoot_uint8_t phy; char *buf; buf = VasEBoot_malloc (IEEE1275_MAX_PATH_LEN); if (buf == NULL) { VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_MEMORY, "disk scan failure"); VasEBoot_print_error (); return; } /* * Cycle thru the potential for dual ported SAS disks * behind a SAS expander. */ for (exp = 0; exp <= 0x100; exp+=0x100) /* The current limit is 32 disks on a phy. */ for (phy = 0; phy < 0x20; phy++) { char *canon = NULL; VasEBoot_snprintf (buf, IEEE1275_MAX_PATH_LEN, "p%" PRIxVAS_EBOOT_UINT32_T ",0", exp | phy); canon = canonicalise_4cell_ua (op->ihandle, buf); if (canon != NULL) { VasEBoot_snprintf (buf, IEEE1275_MAX_PATH_LEN, "%s/disk@%s", op->name, canon); add_disk (buf); VasEBoot_free (canon); } } VasEBoot_free (buf); } static void scan_sparc_sas_disk (const char *parent) { struct parent_dev *op; op = open_parent (parent); if ((op != NULL) && (op->address_cells == 4)) scan_sparc_sas_4cell (op); else if ((op != NULL) && (op->address_cells == 2)) scan_sparc_sas_2cell (op); } static void iterate_devtree (const struct VasEBoot_ieee1275_devalias *alias) { struct VasEBoot_ieee1275_devalias child; if ((VasEBoot_strcmp (alias->type, "scsi-2") == 0) || (VasEBoot_strcmp (alias->type, "scsi-sas") == 0)) return scan_sparc_sas_disk (alias->path); else if (VasEBoot_strcmp (alias->type, "nvme") == 0) return scan_nvme_disk (alias->path); else if (VasEBoot_strcmp (alias->type, "scsi-usb") == 0) return scan_usb_disk (alias->path); else if (VasEBoot_strcmp (alias->type, "block") == 0) { const char **bl = block_blacklist; while (*bl != NULL) { if (VasEBoot_strstr (alias->path, *bl)) return; bl++; } add_disk (alias->path); return; } FOR_IEEE1275_DEVCHILDREN (alias->path, child) iterate_devtree (&child); } static void enumerate_disks (void) { struct VasEBoot_ieee1275_devalias alias; FOR_IEEE1275_DEVCHILDREN("/", alias) iterate_devtree (&alias); } static VasEBoot_err_t add_bootpath (void) { struct disk_dev *ob_device; VasEBoot_err_t ret = VAS_EBOOT_ERR_NONE; char *dev, *alias; char *type; dev = VasEBoot_ieee1275_get_boot_dev (); if (dev == NULL) return VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_MEMORY, "failure adding boot device"); type = VasEBoot_ieee1275_get_device_type (dev); if (type == NULL) { VasEBoot_free (dev); return VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_MEMORY, "failure adding boot device"); } alias = NULL; if (VasEBoot_strcmp (type, "network") != 0) { dev = strip_ob_partition (dev); ob_device = add_canon_disk (dev); if (ob_device == NULL) ret = VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_MEMORY, "failure adding boot device"); ob_device->valid = 1; alias = VasEBoot_ieee1275_get_devname (dev); if (alias && VasEBoot_strcmp (alias, dev) != 0) ob_device->VasEBoot_alias_devpath = VasEBoot_ieee1275_encode_devname (dev); ob_device->boot_dev = 1; } VasEBoot_free (type); VasEBoot_free (dev); VasEBoot_free (alias); return ret; } static void enumerate_aliases (void) { struct VasEBoot_ieee1275_devalias alias; /* * Some block device aliases are not in canonical form * * For example: * * disk3 /pci@301/pci@1/scsi@0/disk@p3 * disk2 /pci@301/pci@1/scsi@0/disk@p2 * disk1 /pci@301/pci@1/scsi@0/disk@p1 * disk /pci@301/pci@1/scsi@0/disk@p0 * disk0 /pci@301/pci@1/scsi@0/disk@p0 * * None of these devices are in canonical form. * * Also, just because there is a devalias, doesn't mean there is a disk * at that location. And a valid boot block device doesn't have to have * a devalias at all. * * At this point, all valid disks have been found in the system * and devaliases that point to canonical names are stored in the * disk_devs list already. */ FOR_IEEE1275_DEVALIASES (alias) { struct disk_dev *dev; char *canon; if (VasEBoot_strcmp (alias.type, "block") != 0) continue; canon = canonicalise_disk (alias.name); if (canon == NULL) /* This is not an error, a devalias could point to a nonexistent disk. */ continue; dev = VasEBoot_named_list_find (VAS_EBOOT_AS_NAMED_LIST (disk_devs), canon); if (dev != NULL) { /* * If more than one alias points to the same device, * remove the previous one unless it is the boot dev, * since the upper level will use the first one. The reason * all the others are redone is in the case of hot-plugging * a disk. If the boot disk gets hot-plugged, it will come * thru here with a different name without the boot_dev flag * set. */ if ((dev->boot_dev) && (dev->VasEBoot_alias_devpath)) continue; VasEBoot_free (dev->VasEBoot_alias_devpath); dev->VasEBoot_alias_devpath = VasEBoot_ieee1275_encode_devname (alias.path); } VasEBoot_free (canon); } } static void display_disks (void) { struct disk_dev *dev; VasEBoot_printf ("--------------------- DISKS ---------------------\n"); FOR_LIST_ELEMENTS (dev, disk_devs) { VasEBoot_printf ("name: %s\n", dev->name); VasEBoot_printf ("VasEBoot_devpath: %s\n", dev->VasEBoot_devpath); VasEBoot_printf ("VasEBoot_alias_devpath: %s\n", dev->VasEBoot_alias_devpath); VasEBoot_printf ("valid: %s\n", (dev->valid) ? "yes" : "no"); VasEBoot_printf ("boot_dev: %s\n", (dev->boot_dev) ? "yes" : "no"); VasEBoot_printf ("opened: %s\n", (dev->ihandle) ? "yes" : "no"); VasEBoot_printf ("block size: %" PRIuVAS_EBOOT_UINT32_T "\n", dev->block_size); VasEBoot_printf ("num blocks: %" PRIuVAS_EBOOT_UINT64_T "\n", dev->num_blocks); VasEBoot_printf ("log sector size: %" PRIuVAS_EBOOT_UINT32_T "\n", dev->log_sector_size); VasEBoot_printf ("\n"); } VasEBoot_printf ("-------------------------------------------------\n"); } static void display_stats (void) { const char *debug = VasEBoot_env_get ("debug"); if (debug == NULL) return; if (VasEBoot_strword (debug, "all") || VasEBoot_strword (debug, "obdisk")) { display_parents (); display_disks (); } } static void invalidate_all_disks (void) { struct disk_dev *dev = NULL; if (disks_enumerated != 0) FOR_LIST_ELEMENTS (dev, disk_devs) dev->valid = 0; } static struct disk_dev * find_legacy_VasEBoot_devpath (const char *name) { struct disk_dev *dev = NULL; char *canon, *devpath = NULL; devpath = decode_VasEBoot_devname (name + sizeof ("ieee1275")); canon = canonicalise_disk (devpath); if (canon != NULL) dev = VasEBoot_named_list_find (VAS_EBOOT_AS_NAMED_LIST (disk_devs), canon); VasEBoot_free (devpath); VasEBoot_free (canon); return dev; } static void enumerate_devices (void) { invalidate_all_disks (); enumerate_disks (); enumerate_aliases (); disks_enumerated = 1; display_stats (); } static struct disk_dev * find_VasEBoot_devpath_real (const char *name) { struct disk_dev *dev = NULL; FOR_LIST_ELEMENTS (dev, disk_devs) { if ((STRCMP (dev->VasEBoot_devpath, name)) || (STRCMP (dev->VasEBoot_alias_devpath, name))) break; } return dev; } static struct disk_dev * find_VasEBoot_devpath (const char *name) { struct disk_dev *dev = NULL; int enumerated; do { enumerated = disks_enumerated; dev = find_VasEBoot_devpath_real (name); if (dev != NULL) break; dev = find_legacy_VasEBoot_devpath (name); if (dev != NULL) break; enumerate_devices (); } while (enumerated == 0); return dev; } static int VasEBoot_obdisk_iterate (VasEBoot_disk_dev_iterate_hook_t hook, void *hook_data, VasEBoot_disk_pull_t pull) { struct disk_dev *dev; const char *name; if (pull != VAS_EBOOT_DISK_PULL_NONE) return 0; enumerate_devices (); FOR_LIST_ELEMENTS (dev, disk_devs) { if (dev->valid == 1) { if (dev->VasEBoot_alias_devpath) name = dev->VasEBoot_alias_devpath; else name = dev->VasEBoot_devpath; if (hook (name, hook_data)) return 1; } } return 0; } static VasEBoot_err_t VasEBoot_obdisk_open (const char *name, VasEBoot_disk_t disk) { VasEBoot_ieee1275_ihandle_t ihandle = 0; struct disk_dev *dev = NULL; if (VasEBoot_strncmp (name, IEEE1275_DEV, sizeof (IEEE1275_DEV) - 1) != 0) return VasEBoot_error (VAS_EBOOT_ERR_UNKNOWN_DEVICE, "not IEEE1275 device"); dev = find_VasEBoot_devpath (name); if (dev == NULL) { VasEBoot_printf ("UNKNOWN DEVICE: %s\n", name); return VasEBoot_error (VAS_EBOOT_ERR_UNKNOWN_DEVICE, "%s", name); } if (dev->opened == 0) { if (dev->raw_name != NULL) VasEBoot_ieee1275_open (dev->raw_name, &ihandle); else VasEBoot_ieee1275_open (dev->name, &ihandle); if (ihandle == 0) { VasEBoot_printf ("Can't open device %s\n", name); return VasEBoot_error (VAS_EBOOT_ERR_UNKNOWN_DEVICE, "can't open device %s", name); } dev->block_size = VasEBoot_ieee1275_get_block_size (ihandle); dev->num_blocks = VasEBoot_ieee1275_num_blocks (ihandle); if (dev->num_blocks == 0) dev->num_blocks = VasEBoot_ieee1275_num_blocks64 (ihandle); if (dev->num_blocks == 0) dev->num_blocks = VAS_EBOOT_DISK_SIZE_UNKNOWN; if (dev->block_size != 0) dev->log_sector_size = VasEBoot_log2ull (dev->block_size); else dev->log_sector_size = 9; dev->ihandle = ihandle; dev->opened = 1; } disk->total_sectors = dev->num_blocks; disk->id = dev->ihandle; disk->data = dev; disk->log_sector_size = dev->log_sector_size; return VAS_EBOOT_ERR_NONE; } static struct VasEBoot_disk_dev VasEBoot_obdisk_dev = { .name = "obdisk", .id = VAS_EBOOT_DISK_DEVICE_OBDISK_ID, .disk_iterate = VasEBoot_obdisk_iterate, .disk_open = VasEBoot_obdisk_open, .disk_close = VasEBoot_obdisk_close, .disk_read = VasEBoot_obdisk_read, }; void VasEBoot_obdisk_init (void) { VasEBoot_disk_firmware_fini = VasEBoot_obdisk_fini; add_bootpath (); VasEBoot_disk_dev_register (&VasEBoot_obdisk_dev); } void VasEBoot_obdisk_fini (void) { struct disk_dev *dev; FOR_LIST_ELEMENTS (dev, disk_devs) { if (dev->opened != 0) VasEBoot_ieee1275_close (dev->ihandle); } VasEBoot_disk_dev_unregister (&VasEBoot_obdisk_dev); }