2N/A/* ofdisk.c - Open Firmware disk access. */
2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2004,2006,2007,2008,2009,2011 Free Software Foundation, Inc.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <grub/misc.h>
2N/A#include <grub/disk.h>
2N/A#include <grub/mm.h>
2N/A#include <grub/arc/arc.h>
2N/A
2N/Astatic grub_arc_fileno_t last_handle = 0;
2N/Astatic char *last_path = NULL;
2N/A
2N/Astatic int lnum = 0;
2N/A
2N/Astruct arcdisk_hash_ent
2N/A{
2N/A char *devpath;
2N/A int num;
2N/A struct arcdisk_hash_ent *next;
2N/A};
2N/A
2N/A#define ARCDISK_HASH_SZ 8
2N/Astatic struct arcdisk_hash_ent *arcdisk_hash[ARCDISK_HASH_SZ];
2N/A
2N/Astatic int
2N/Aarcdisk_hash_fn (const char *devpath)
2N/A{
2N/A int hash = 0;
2N/A while (*devpath)
2N/A hash ^= *devpath++;
2N/A return (hash & (ARCDISK_HASH_SZ - 1));
2N/A}
2N/A
2N/Astatic struct arcdisk_hash_ent *
2N/Aarcdisk_hash_find (const char *devpath)
2N/A{
2N/A struct arcdisk_hash_ent *p = arcdisk_hash[arcdisk_hash_fn (devpath)];
2N/A
2N/A while (p)
2N/A {
2N/A if (!grub_strcmp (p->devpath, devpath))
2N/A break;
2N/A p = p->next;
2N/A }
2N/A return p;
2N/A}
2N/A
2N/Astatic struct arcdisk_hash_ent *
2N/Aarcdisk_hash_add (char *devpath)
2N/A{
2N/A struct arcdisk_hash_ent *p;
2N/A struct arcdisk_hash_ent **head = &arcdisk_hash[arcdisk_hash_fn(devpath)];
2N/A
2N/A p = grub_malloc (sizeof (*p));
2N/A if (!p)
2N/A return NULL;
2N/A
2N/A p->devpath = devpath;
2N/A p->next = *head;
2N/A p->num = lnum++;
2N/A *head = p;
2N/A return p;
2N/A}
2N/A
2N/A
2N/Astatic int
2N/Agrub_arcdisk_iterate (int (*hook_in) (const char *name),
2N/A grub_disk_pull_t pull)
2N/A{
2N/A auto int hook (const char *name, const struct grub_arc_component *comp);
2N/A int hook (const char *name, const struct grub_arc_component *comp)
2N/A {
2N/A if (!(comp->type == GRUB_ARC_COMPONENT_TYPE_DISK
2N/A || comp->type == GRUB_ARC_COMPONENT_TYPE_DISK
2N/A || comp->type == GRUB_ARC_COMPONENT_TYPE_TAPE))
2N/A return 0;
2N/A return hook_in (name);
2N/A }
2N/A if (pull != GRUB_DISK_PULL_NONE)
2N/A return 0;
2N/A
2N/A return grub_arc_iterate_devs (hook, 1);
2N/A}
2N/A
2N/A#define RAW_SUFFIX "partition(10)"
2N/A
2N/Astatic grub_err_t
2N/Areopen (const char *name)
2N/A{
2N/A grub_arc_fileno_t handle;
2N/A
2N/A if (last_path && grub_strcmp (last_path, name) == 0)
2N/A {
2N/A grub_dprintf ("arcdisk", "using already opened %s\n", name);
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A if (last_path)
2N/A {
2N/A GRUB_ARC_FIRMWARE_VECTOR->close (last_handle);
2N/A grub_free (last_path);
2N/A last_path = NULL;
2N/A last_handle = 0;
2N/A }
2N/A if (GRUB_ARC_FIRMWARE_VECTOR->open (name, 0, &handle))
2N/A {
2N/A grub_dprintf ("arcdisk", "couldn't open %s\n", name);
2N/A return grub_error (GRUB_ERR_IO, "couldn't open %s", name);
2N/A }
2N/A last_path = grub_strdup (name);
2N/A if (!last_path)
2N/A return grub_errno;
2N/A last_handle = handle;
2N/A grub_dprintf ("arcdisk", "opened %s\n", name);
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_arcdisk_open (const char *name, grub_disk_t disk)
2N/A{
2N/A char *fullname, *optr;
2N/A const char *iptr;
2N/A int state = 0;
2N/A grub_err_t err;
2N/A grub_arc_err_t r;
2N/A struct grub_arc_fileinfo info;
2N/A struct arcdisk_hash_ent *hash;
2N/A
2N/A if (grub_memcmp (name, "arc/", 4) != 0)
2N/A return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not arc device");
2N/A fullname = grub_malloc (2 * grub_strlen (name) + sizeof (RAW_SUFFIX));
2N/A if (!fullname)
2N/A return grub_errno;
2N/A optr = fullname;
2N/A for (iptr = name + 4; *iptr; iptr++)
2N/A if (state == 0)
2N/A {
2N/A if (!grub_isdigit (*iptr))
2N/A *optr++ = *iptr;
2N/A else
2N/A {
2N/A *optr++ = '(';
2N/A *optr++ = *iptr;
2N/A state = 1;
2N/A }
2N/A }
2N/A else
2N/A {
2N/A if (grub_isdigit (*iptr))
2N/A *optr++ = *iptr;
2N/A else
2N/A {
2N/A *optr++ = ')';
2N/A state = 0;
2N/A }
2N/A }
2N/A if (state)
2N/A *optr++ = ')';
2N/A grub_memcpy (optr, RAW_SUFFIX, sizeof (RAW_SUFFIX));
2N/A disk->data = fullname;
2N/A grub_dprintf ("arcdisk", "opening %s\n", fullname);
2N/A
2N/A hash = arcdisk_hash_find (fullname);
2N/A if (!hash)
2N/A hash = arcdisk_hash_add (fullname);
2N/A if (!hash)
2N/A return grub_errno;
2N/A
2N/A err = reopen (fullname);
2N/A if (err)
2N/A return err;
2N/A
2N/A r = GRUB_ARC_FIRMWARE_VECTOR->getfileinformation (last_handle, &info);
2N/A if (r)
2N/A {
2N/A grub_uint64_t res = 0;
2N/A int i;
2N/A
2N/A grub_dprintf ("arcdisk", "couldn't retrieve size: %ld\n", r);
2N/A for (i = 40; i >= 9; i--)
2N/A {
2N/A grub_uint64_t pos = res | (1ULL << i);
2N/A char buf[512];
2N/A long unsigned count = 0;
2N/A grub_dprintf ("arcdisk",
2N/A "seek to 0x%" PRIxGRUB_UINT64_T "\n", pos);
2N/A if (GRUB_ARC_FIRMWARE_VECTOR->seek (last_handle, &pos, 0))
2N/A continue;
2N/A if (GRUB_ARC_FIRMWARE_VECTOR->read (last_handle, buf,
2N/A 0x200, &count))
2N/A continue;
2N/A if (count == 0)
2N/A continue;
2N/A res |= (1ULL << i);
2N/A }
2N/A grub_dprintf ("arcdisk",
2N/A "determined disk size 0x%" PRIxGRUB_UINT64_T "\n", res);
2N/A disk->total_sectors = (res + 0x200) >> 9;
2N/A }
2N/A else
2N/A disk->total_sectors = (info.end >> 9);
2N/A
2N/A disk->id = hash->num;
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic void
2N/Agrub_arcdisk_close (grub_disk_t disk)
2N/A{
2N/A grub_free (disk->data);
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_arcdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
2N/A grub_size_t size, char *buf)
2N/A{
2N/A grub_err_t err;
2N/A grub_uint64_t pos = sector << 9;
2N/A unsigned long count;
2N/A grub_uint64_t totl = size << 9;
2N/A grub_arc_err_t r;
2N/A
2N/A err = reopen (disk->data);
2N/A if (err)
2N/A return err;
2N/A r = GRUB_ARC_FIRMWARE_VECTOR->seek (last_handle, &pos, 0);
2N/A if (r)
2N/A {
2N/A grub_dprintf ("arcdisk", "seek to 0x%" PRIxGRUB_UINT64_T " failed: %ld\n",
2N/A pos, r);
2N/A return grub_error (GRUB_ERR_IO, "couldn't seek");
2N/A }
2N/A
2N/A while (totl)
2N/A {
2N/A if (GRUB_ARC_FIRMWARE_VECTOR->read (last_handle, buf,
2N/A totl, &count))
2N/A return grub_error (GRUB_ERR_READ_ERROR, "read failed");
2N/A totl -= count;
2N/A buf += count;
2N/A }
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_arcdisk_write (grub_disk_t disk __attribute ((unused)),
2N/A grub_disk_addr_t sector __attribute ((unused)),
2N/A grub_size_t size __attribute ((unused)),
2N/A const char *buf __attribute ((unused)))
2N/A{
2N/A return GRUB_ERR_NOT_IMPLEMENTED_YET;
2N/A}
2N/A
2N/Astatic struct grub_disk_dev grub_arcdisk_dev =
2N/A {
2N/A .name = "arcdisk",
2N/A .id = GRUB_DISK_DEVICE_ARCDISK_ID,
2N/A .iterate = grub_arcdisk_iterate,
2N/A .open = grub_arcdisk_open,
2N/A .close = grub_arcdisk_close,
2N/A .read = grub_arcdisk_read,
2N/A .write = grub_arcdisk_write,
2N/A .next = 0
2N/A };
2N/A
2N/Avoid
2N/Agrub_arcdisk_init (void)
2N/A{
2N/A grub_disk_dev_register (&grub_arcdisk_dev);
2N/A}
2N/A
2N/Avoid
2N/Agrub_arcdisk_fini (void)
2N/A{
2N/A if (last_path)
2N/A {
2N/A GRUB_ARC_FIRMWARE_VECTOR->close (last_handle);
2N/A grub_free (last_path);
2N/A last_path = NULL;
2N/A last_handle = 0;
2N/A }
2N/A
2N/A grub_disk_dev_unregister (&grub_arcdisk_dev);
2N/A}