295 lines
6.8 KiB
C
295 lines
6.8 KiB
C
/* ofdisk.c - Open Firmware disk access. */
|
|
/*
|
|
* GRUB -- GRand Unified Bootloader
|
|
* Copyright (C) 2004,2006,2007,2008,2009 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>
|
|
|
|
static grub_arc_fileno_t last_handle = 0;
|
|
static char *last_path = NULL;
|
|
|
|
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))
|
|
{
|
|
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);
|
|
}
|
|
return grub_arc_iterate_devs (hook, 1);
|
|
}
|
|
|
|
#define RAW_SUFFIX "partition(10)"
|
|
|
|
static grub_err_t
|
|
reopen (const char *name)
|
|
{
|
|
grub_arc_fileno_t handle;
|
|
|
|
if (last_path && grub_strcmp (last_path, name) == 0)
|
|
{
|
|
grub_dprintf ("arcdisk", "using already opened %s\n", name);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
if (GRUB_ARC_FIRMWARE_VECTOR->open (name, 0, &handle))
|
|
{
|
|
grub_dprintf ("arcdisk", "couldn't open %s\n", name);
|
|
return grub_error (GRUB_ERR_IO, "couldn't open %s", name);
|
|
}
|
|
if (last_path)
|
|
{
|
|
GRUB_ARC_FIRMWARE_VECTOR->close (last_handle);
|
|
grub_free (last_path);
|
|
last_path = NULL;
|
|
last_handle = 0;
|
|
}
|
|
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);
|
|
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);
|
|
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, "read failed");
|
|
totl -= count;
|
|
buf += count;
|
|
}
|
|
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
static grub_err_t
|
|
grub_arcdisk_write (grub_disk_t disk __attribute ((unused)),
|
|
grub_disk_addr_t sector __attribute ((unused)),
|
|
grub_size_t size __attribute ((unused)),
|
|
const char *buf __attribute ((unused)))
|
|
{
|
|
return GRUB_ERR_NOT_IMPLEMENTED_YET;
|
|
}
|
|
|
|
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);
|
|
}
|