/* ofdisk.c - Open Firmware disk access. */ /* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2004,2006,2007,2008,2009,2011 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 static VasEBoot_arc_fileno_t last_handle = 0; static char *last_path = NULL; static int handle_writable = 0; static int lnum = 0; struct arcdisk_hash_ent { char *devpath; int num; struct arcdisk_hash_ent *next; }; #define ARCDISK_HASH_SZ 8 static struct arcdisk_hash_ent *arcdisk_hash[ARCDISK_HASH_SZ]; static int arcdisk_hash_fn (const char *devpath) { int hash = 0; while (*devpath) hash ^= *devpath++; return (hash & (ARCDISK_HASH_SZ - 1)); } static struct arcdisk_hash_ent * arcdisk_hash_find (const char *devpath) { struct arcdisk_hash_ent *p = arcdisk_hash[arcdisk_hash_fn (devpath)]; while (p) { if (!VasEBoot_strcmp (p->devpath, devpath)) break; p = p->next; } return p; } static struct arcdisk_hash_ent * arcdisk_hash_add (char *devpath) { struct arcdisk_hash_ent *p; struct arcdisk_hash_ent **head = &arcdisk_hash[arcdisk_hash_fn(devpath)]; p = VasEBoot_malloc (sizeof (*p)); if (!p) return NULL; p->devpath = devpath; p->next = *head; p->num = lnum++; *head = p; return p; } /* Context for VasEBoot_arcdisk_iterate. */ struct VasEBoot_arcdisk_iterate_ctx { VasEBoot_disk_dev_iterate_hook_t hook; void *hook_data; }; /* Helper for VasEBoot_arcdisk_iterate. */ static int VasEBoot_arcdisk_iterate_iter (const char *name, const struct VasEBoot_arc_component *comp, void *data) { struct VasEBoot_arcdisk_iterate_ctx *ctx = data; if (!(comp->type == VAS_EBOOT_ARC_COMPONENT_TYPE_DISK || comp->type == VAS_EBOOT_ARC_COMPONENT_TYPE_FLOPPY || comp->type == VAS_EBOOT_ARC_COMPONENT_TYPE_TAPE)) return 0; return ctx->hook (name, ctx->hook_data); } static int VasEBoot_arcdisk_iterate (VasEBoot_disk_dev_iterate_hook_t hook, void *hook_data, VasEBoot_disk_pull_t pull) { struct VasEBoot_arcdisk_iterate_ctx ctx = { hook, hook_data }; if (pull != VAS_EBOOT_DISK_PULL_NONE) return 0; return VasEBoot_arc_iterate_devs (VasEBoot_arcdisk_iterate_iter, &ctx, 1); } #ifdef VAS_EBOOT_CPU_MIPSEL #define RAW_SUFFIX "partition(0)" #else #define RAW_SUFFIX "partition(10)" #endif static VasEBoot_err_t reopen (const char *name, int writable) { VasEBoot_arc_fileno_t handle; if (last_path && VasEBoot_strcmp (last_path, name) == 0 && (!writable || handle_writable)) { VasEBoot_dprintf ("arcdisk", "using already opened %s\n", name); return VAS_EBOOT_ERR_NONE; } if (last_path) { VAS_EBOOT_ARC_FIRMWARE_VECTOR->close (last_handle); VasEBoot_free (last_path); last_path = NULL; last_handle = 0; handle_writable = 0; } if (VAS_EBOOT_ARC_FIRMWARE_VECTOR->open (name, writable ? VAS_EBOOT_ARC_FILE_ACCESS_OPEN_RW : VAS_EBOOT_ARC_FILE_ACCESS_OPEN_RO, &handle)) { VasEBoot_dprintf ("arcdisk", "couldn't open %s\n", name); return VasEBoot_error (VAS_EBOOT_ERR_IO, "couldn't open %s", name); } handle_writable = writable; last_path = VasEBoot_strdup (name); if (!last_path) return VasEBoot_errno; last_handle = handle; VasEBoot_dprintf ("arcdisk", "opened %s\n", name); return VAS_EBOOT_ERR_NONE; } static VasEBoot_err_t VasEBoot_arcdisk_open (const char *name, VasEBoot_disk_t disk) { char *fullname; VasEBoot_err_t err; VasEBoot_arc_err_t r; struct VasEBoot_arc_fileinfo info; struct arcdisk_hash_ent *hash; if (VasEBoot_memcmp (name, "arc/", 4) != 0) return VasEBoot_error (VAS_EBOOT_ERR_UNKNOWN_DEVICE, "not arc device"); fullname = VasEBoot_arc_alt_name_to_norm (name, RAW_SUFFIX); disk->data = fullname; VasEBoot_dprintf ("arcdisk", "opening %s\n", fullname); hash = arcdisk_hash_find (fullname); if (!hash) hash = arcdisk_hash_add (fullname); if (!hash) return VasEBoot_errno; err = reopen (fullname, 0); if (err) return err; r = VAS_EBOOT_ARC_FIRMWARE_VECTOR->getfileinformation (last_handle, &info); if (r) { VasEBoot_uint64_t res = 0; int i; VasEBoot_dprintf ("arcdisk", "couldn't retrieve size: %ld\n", r); for (i = 40; i >= 9; i--) { VasEBoot_uint64_t pos = res | (1ULL << i); char buf[512]; long unsigned count = 0; VasEBoot_dprintf ("arcdisk", "seek to 0x%" PRIxVAS_EBOOT_UINT64_T "\n", pos); if (VAS_EBOOT_ARC_FIRMWARE_VECTOR->seek (last_handle, &pos, 0)) continue; if (VAS_EBOOT_ARC_FIRMWARE_VECTOR->read (last_handle, buf, 0x200, &count)) continue; if (count == 0) continue; res |= (1ULL << i); } VasEBoot_dprintf ("arcdisk", "determined disk size 0x%" PRIxVAS_EBOOT_UINT64_T "\n", res); disk->total_sectors = (res + 0x200) >> 9; } else disk->total_sectors = (info.end >> 9); disk->id = hash->num; return VAS_EBOOT_ERR_NONE; } static void VasEBoot_arcdisk_close (VasEBoot_disk_t disk) { VasEBoot_free (disk->data); } static VasEBoot_err_t VasEBoot_arcdisk_read (VasEBoot_disk_t disk, VasEBoot_disk_addr_t sector, VasEBoot_size_t size, char *buf) { VasEBoot_err_t err; VasEBoot_uint64_t pos = sector << 9; unsigned long count; VasEBoot_uint64_t totl = size << 9; VasEBoot_arc_err_t r; err = reopen (disk->data, 0); if (err) return err; r = VAS_EBOOT_ARC_FIRMWARE_VECTOR->seek (last_handle, &pos, 0); if (r) { VasEBoot_dprintf ("arcdisk", "seek to 0x%" PRIxVAS_EBOOT_UINT64_T " failed: %ld\n", pos, r); return VasEBoot_error (VAS_EBOOT_ERR_IO, "couldn't seek"); } while (totl) { if (VAS_EBOOT_ARC_FIRMWARE_VECTOR->read (last_handle, buf, totl, &count)) return VasEBoot_error (VAS_EBOOT_ERR_READ_ERROR, N_("failure reading sector 0x%llx " "from `%s'"), (unsigned long long) sector, disk->name); totl -= count; buf += count; } return VAS_EBOOT_ERR_NONE; } static VasEBoot_err_t VasEBoot_arcdisk_write (VasEBoot_disk_t disk, VasEBoot_disk_addr_t sector, VasEBoot_size_t size, const char *buf) { VasEBoot_err_t err; VasEBoot_uint64_t pos = sector << 9; unsigned long count; VasEBoot_uint64_t totl = size << 9; VasEBoot_arc_err_t r; err = reopen (disk->data, 1); if (err) return err; r = VAS_EBOOT_ARC_FIRMWARE_VECTOR->seek (last_handle, &pos, 0); if (r) { VasEBoot_dprintf ("arcdisk", "seek to 0x%" PRIxVAS_EBOOT_UINT64_T " failed: %ld\n", pos, r); return VasEBoot_error (VAS_EBOOT_ERR_IO, "couldn't seek"); } while (totl) { if (VAS_EBOOT_ARC_FIRMWARE_VECTOR->write (last_handle, buf, totl, &count)) return VasEBoot_error (VAS_EBOOT_ERR_WRITE_ERROR, N_("failure writing sector 0x%llx " "to `%s'"), (unsigned long long) sector, disk->name); totl -= count; buf += count; } return VAS_EBOOT_ERR_NONE; } static struct VasEBoot_disk_dev VasEBoot_arcdisk_dev = { .name = "arcdisk", .id = VAS_EBOOT_DISK_DEVICE_ARCDISK_ID, .disk_iterate = VasEBoot_arcdisk_iterate, .disk_open = VasEBoot_arcdisk_open, .disk_close = VasEBoot_arcdisk_close, .disk_read = VasEBoot_arcdisk_read, .disk_write = VasEBoot_arcdisk_write, .next = 0 }; void VasEBoot_arcdisk_init (void) { VasEBoot_disk_dev_register (&VasEBoot_arcdisk_dev); } void VasEBoot_arcdisk_fini (void) { if (last_path) { VAS_EBOOT_ARC_FIRMWARE_VECTOR->close (last_handle); VasEBoot_free (last_path); last_path = NULL; last_handle = 0; } VasEBoot_disk_dev_unregister (&VasEBoot_arcdisk_dev); }