326 lines
8.2 KiB
C
326 lines
8.2 KiB
C
/* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <VasEBoot/misc.h>
|
|
#include <VasEBoot/disk.h>
|
|
#include <VasEBoot/mm.h>
|
|
#include <VasEBoot/arc/arc.h>
|
|
#include <VasEBoot/i18n.h>
|
|
|
|
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);
|
|
}
|