| /* ofdisk.c - Open Firmware disk access. */ |
| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 2004,2006,2007,2008,2009,2011 Free Software Foundation, Inc. |
| * |
| * GRUB 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. |
| * |
| * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <grub/misc.h> |
| #include <grub/disk.h> |
| #include <grub/mm.h> |
| #include <grub/arc/arc.h> |
| #include <grub/i18n.h> |
| |
| static grub_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 (!grub_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 = grub_malloc (sizeof (*p)); |
| if (!p) |
| return NULL; |
| |
| p->devpath = devpath; |
| p->next = *head; |
| p->num = lnum++; |
| *head = p; |
| return p; |
| } |
| |
| |
| static int |
| grub_arcdisk_iterate (int (*hook_in) (const char *name), |
| grub_disk_pull_t pull) |
| { |
| auto int hook (const char *name, const struct grub_arc_component *comp); |
| int hook (const char *name, const struct grub_arc_component *comp) |
| { |
| if (!(comp->type == GRUB_ARC_COMPONENT_TYPE_DISK |
| || comp->type == GRUB_ARC_COMPONENT_TYPE_DISK |
| || comp->type == GRUB_ARC_COMPONENT_TYPE_TAPE)) |
| return 0; |
| return hook_in (name); |
| } |
| if (pull != GRUB_DISK_PULL_NONE) |
| return 0; |
| |
| return grub_arc_iterate_devs (hook, 1); |
| } |
| |
| #define RAW_SUFFIX "partition(10)" |
| |
| static grub_err_t |
| reopen (const char *name, int writable) |
| { |
| grub_arc_fileno_t handle; |
| |
| if (last_path && grub_strcmp (last_path, name) == 0 |
| && (!writable || handle_writable)) |
| { |
| grub_dprintf ("arcdisk", "using already opened %s\n", name); |
| return GRUB_ERR_NONE; |
| } |
| if (last_path) |
| { |
| GRUB_ARC_FIRMWARE_VECTOR->close (last_handle); |
| grub_free (last_path); |
| last_path = NULL; |
| last_handle = 0; |
| handle_writable = 0; |
| } |
| if (GRUB_ARC_FIRMWARE_VECTOR->open (name, |
| writable ? GRUB_ARC_FILE_ACCESS_OPEN_RW |
| : GRUB_ARC_FILE_ACCESS_OPEN_RO, &handle)) |
| { |
| grub_dprintf ("arcdisk", "couldn't open %s\n", name); |
| return grub_error (GRUB_ERR_IO, "couldn't open %s", name); |
| } |
| handle_writable = writable; |
| last_path = grub_strdup (name); |
| if (!last_path) |
| return grub_errno; |
| last_handle = handle; |
| grub_dprintf ("arcdisk", "opened %s\n", name); |
| return GRUB_ERR_NONE; |
| } |
| |
| static grub_err_t |
| grub_arcdisk_open (const char *name, grub_disk_t disk) |
| { |
| char *fullname, *optr; |
| const char *iptr; |
| int state = 0; |
| grub_err_t err; |
| grub_arc_err_t r; |
| struct grub_arc_fileinfo info; |
| struct arcdisk_hash_ent *hash; |
| |
| if (grub_memcmp (name, "arc/", 4) != 0) |
| return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not arc device"); |
| fullname = grub_malloc (2 * grub_strlen (name) + sizeof (RAW_SUFFIX)); |
| if (!fullname) |
| return grub_errno; |
| optr = fullname; |
| for (iptr = name + 4; *iptr; iptr++) |
| if (state == 0) |
| { |
| if (!grub_isdigit (*iptr)) |
| *optr++ = *iptr; |
| else |
| { |
| *optr++ = '('; |
| *optr++ = *iptr; |
| state = 1; |
| } |
| } |
| else |
| { |
| if (grub_isdigit (*iptr)) |
| *optr++ = *iptr; |
| else |
| { |
| *optr++ = ')'; |
| state = 0; |
| } |
| } |
| if (state) |
| *optr++ = ')'; |
| grub_memcpy (optr, RAW_SUFFIX, sizeof (RAW_SUFFIX)); |
| disk->data = fullname; |
| grub_dprintf ("arcdisk", "opening %s\n", fullname); |
| |
| hash = arcdisk_hash_find (fullname); |
| if (!hash) |
| hash = arcdisk_hash_add (fullname); |
| if (!hash) |
| return grub_errno; |
| |
| err = reopen (fullname, 0); |
| if (err) |
| return err; |
| |
| r = GRUB_ARC_FIRMWARE_VECTOR->getfileinformation (last_handle, &info); |
| if (r) |
| { |
| grub_uint64_t res = 0; |
| int i; |
| |
| grub_dprintf ("arcdisk", "couldn't retrieve size: %ld\n", r); |
| for (i = 40; i >= 9; i--) |
| { |
| grub_uint64_t pos = res | (1ULL << i); |
| char buf[512]; |
| long unsigned count = 0; |
| grub_dprintf ("arcdisk", |
| "seek to 0x%" PRIxGRUB_UINT64_T "\n", pos); |
| if (GRUB_ARC_FIRMWARE_VECTOR->seek (last_handle, &pos, 0)) |
| continue; |
| if (GRUB_ARC_FIRMWARE_VECTOR->read (last_handle, buf, |
| 0x200, &count)) |
| continue; |
| if (count == 0) |
| continue; |
| res |= (1ULL << i); |
| } |
| grub_dprintf ("arcdisk", |
| "determined disk size 0x%" PRIxGRUB_UINT64_T "\n", res); |
| disk->total_sectors = (res + 0x200) >> 9; |
| } |
| else |
| disk->total_sectors = (info.end >> 9); |
| |
| disk->id = hash->num; |
| return GRUB_ERR_NONE; |
| } |
| |
| static void |
| grub_arcdisk_close (grub_disk_t disk) |
| { |
| grub_free (disk->data); |
| } |
| |
| static grub_err_t |
| grub_arcdisk_read (grub_disk_t disk, grub_disk_addr_t sector, |
| grub_size_t size, char *buf) |
| { |
| grub_err_t err; |
| grub_uint64_t pos = sector << 9; |
| unsigned long count; |
| grub_uint64_t totl = size << 9; |
| grub_arc_err_t r; |
| |
| err = reopen (disk->data, 0); |
| if (err) |
| return err; |
| r = GRUB_ARC_FIRMWARE_VECTOR->seek (last_handle, &pos, 0); |
| if (r) |
| { |
| grub_dprintf ("arcdisk", "seek to 0x%" PRIxGRUB_UINT64_T " failed: %ld\n", |
| pos, r); |
| return grub_error (GRUB_ERR_IO, "couldn't seek"); |
| } |
| |
| while (totl) |
| { |
| if (GRUB_ARC_FIRMWARE_VECTOR->read (last_handle, buf, |
| totl, &count)) |
| return grub_error (GRUB_ERR_READ_ERROR, |
| N_("failure reading sector 0x%llx " |
| "from `%s'"), |
| (unsigned long long) sector, |
| disk->name); |
| totl -= count; |
| buf += count; |
| } |
| |
| return GRUB_ERR_NONE; |
| } |
| |
| static grub_err_t |
| grub_arcdisk_write (grub_disk_t disk, grub_disk_addr_t sector, |
| grub_size_t size, const char *buf) |
| { |
| grub_err_t err; |
| grub_uint64_t pos = sector << 9; |
| unsigned long count; |
| grub_uint64_t totl = size << 9; |
| grub_arc_err_t r; |
| |
| err = reopen (disk->data, 1); |
| if (err) |
| return err; |
| r = GRUB_ARC_FIRMWARE_VECTOR->seek (last_handle, &pos, 0); |
| if (r) |
| { |
| grub_dprintf ("arcdisk", "seek to 0x%" PRIxGRUB_UINT64_T " failed: %ld\n", |
| pos, r); |
| return grub_error (GRUB_ERR_IO, "couldn't seek"); |
| } |
| |
| while (totl) |
| { |
| if (GRUB_ARC_FIRMWARE_VECTOR->write (last_handle, buf, |
| totl, &count)) |
| return grub_error (GRUB_ERR_WRITE_ERROR, N_("failure writing sector 0x%llx " |
| "to `%s'"), |
| (unsigned long long) sector, |
| disk->name); |
| totl -= count; |
| buf += count; |
| } |
| |
| return GRUB_ERR_NONE; |
| } |
| |
| static struct grub_disk_dev grub_arcdisk_dev = |
| { |
| .name = "arcdisk", |
| .id = GRUB_DISK_DEVICE_ARCDISK_ID, |
| .iterate = grub_arcdisk_iterate, |
| .open = grub_arcdisk_open, |
| .close = grub_arcdisk_close, |
| .read = grub_arcdisk_read, |
| .write = grub_arcdisk_write, |
| .next = 0 |
| }; |
| |
| void |
| grub_arcdisk_init (void) |
| { |
| grub_disk_dev_register (&grub_arcdisk_dev); |
| } |
| |
| void |
| grub_arcdisk_fini (void) |
| { |
| if (last_path) |
| { |
| GRUB_ARC_FIRMWARE_VECTOR->close (last_handle); |
| grub_free (last_path); |
| last_path = NULL; |
| last_handle = 0; |
| } |
| |
| grub_disk_dev_unregister (&grub_arcdisk_dev); |
| } |