blob: e5f066b88a54e7faf8b4775b337771f0e56defcc [file] [log] [blame]
/* cmd_bootu.c */
/*
* Copyright (C) 2008
* Giuseppe Coviello <cjg@cruxppc.org>
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <common.h>
#include <command.h>
#include <part.h>
#include <../../../disk/part_amiga.h>
#include <malloc.h>
#include <ext2fs.h>
#include "sys_dep.h"
#define BOOTLOADER_MAX_BUFFER 128*1024
#define HEADER_INFO_SIZE 20
#define UNUSED_BLOCK_ADDRESS 0xffffffff
struct BootDevice {
char *interface;
int device;
int partition;
char *filename;
block_dev_desc_t *desc;
};
struct BootDeviceTable {
char *name;
struct BootDevice device;
};
static struct BootDeviceTable table[] = {{"s4siicdrom", {"s4sii", 1}}};
extern unsigned long valid_elf_image(void *);
extern unsigned long load_elf_image(void *);
static char *argarray[5];
static int start(void *buffer)
{
short (* bls)(struct sbl_callback_context *);
unsigned long entrypoint;
short result = -1;
struct sbl_callback_context *context;
if(!valid_elf_image(buffer)) {
printf("Error: no real ELF image installed as bootloader!\n");
return;
}
context = build_callback_context(argarray);
entrypoint = load_elf_image(buffer);
bls = (short (*)(struct sbl_callback_context *)) entrypoint;
result = bls(context);
return result;
}
static int do_bootu_tftp(void)
{
char *filename;
int transfer_size;
void *buffer;
buffer = malloc(BOOTLOADER_MAX_BUFFER);
if((filename = getenv("netboot_file")) == NULL)
filename = "OS4Bootloader";
puts("Starting Net booting procedure\n");
if ((transfer_size = my_NetLoop(filename, buffer)) != -1)
puts("Successfully loaded SLB from network\n");
else {
printf("Couldn't download %s from network.\n", filename);
free(buffer);
return -1;
}
start(buffer);
return 0;
}
static int do_bootu_hd(struct BootDevice *dev)
{
void *buffer;
char *filename;
int partition;
disk_partition_t info;
ulong part_length;
ulong filelen;
if((partition = dev->partition) < 0) {
printf("Warning: partition is not set. Using the first "
"partition!\n");
partition = 1;
}
if((filename = dev->filename) == NULL) {
printf("Warning: filename is not set. Using the default: "
"Parthenope!\n");
filename = "Parthenope";
}
if(get_partition_info(dev->desc, partition, &info)) {
printf("Error: Bad partition %d!\n", partition);
return -1;
}
if((part_length = ext2fs_set_blk_dev(dev->desc, partition)) == 0) {
printf("Error: Bad partition %s %d:%d!\n", dev->interface,
dev->device, partition);
ext2fs_close();
return -1;
}
if(!ext2fs_mount(part_length)) {
printf("Error: Bad ext2 partition %s %d:%d!\n", dev->interface,
dev->device, partition);
ext2fs_close();
return -1;
}
if((filelen = ext2fs_open(filename)) < 0) {
printf("Error: File not found %s!\n", filename);
ext2fs_close();
return -1;
}
buffer = malloc(filelen);
if(ext2fs_read((char *) buffer, filelen) != filelen) {
printf("Error: Unable to read %s!\n", filename);
ext2fs_close();
free(buffer);
}
ext2fs_close();
start(buffer);
return 0;
}
static int do_bootu_amiga_hd(block_dev_desc_t *dev_desc)
{
struct rigid_disk_block *rdb;
char *blockbuffer;
void *buffer;
u32 next, chunklen;
u32 *current;
struct BootstrapCodeBlock *bcb;
rdb = get_rdisk(dev_desc);
if(rdb == NULL) {
printf("No RDB found!\n");
return 1;
}
buffer = malloc(BOOTLOADER_MAX_BUFFER);
blockbuffer = malloc(dev_desc->blksz);
next = rdb->bootcode_block;
current = (u32 *) buffer;
bcb = (struct BootstrapCodeBlock *) blockbuffer;
do {
dev_desc->block_read(dev_desc->dev, next, 1, blockbuffer);
memcpy((char *)current, blockbuffer + HEADER_INFO_SIZE,
(chunklen = bcb->bcb_SummedLongs-(HEADER_INFO_SIZE>>2))<<2);
current+=chunklen;
} while((next = bcb->bcb_Next) != UNUSED_BLOCK_ADDRESS);
start(buffer);
return 0;
}
static int do_bootu_eltorito(struct BootDevice *device)
{
void *buffer;
disk_partition_t info;
get_partition_info(device->desc, 0, &info);
buffer = malloc(info.size * info.blksz);
if(device->desc->block_read(device->desc->dev, info.start, info.size,
buffer) != info.size) {
printf("Error: read error from %s:%d\n", device->interface,
device->device);
free(buffer);
return -1;
}
start(buffer);
return 0;
}
static char *pop(char *s, char sep)
{
char *p, *x;
if((p = strchr(s, sep)) == NULL)
return strdup(s);
x = malloc(p - s + 1);
memmove(x, s, p - s);
x[p - s] = 0;
return x;
}
static struct BootDevice *BootDevice_new_from_string(char *s)
{
struct BootDevice *self;
char *value;
self = malloc(sizeof(struct BootDevice));
self->interface = pop(s, ':');
self->device = 0;
self->partition = -1;
self->filename = NULL;
s += strlen(self->interface);
if(*s == 0 || *++s == 0)
goto validate;
value = pop(s, ':');
self->device = (int) simple_strtoul(value, NULL, 16);
s += strlen(value);
free(value);
if(*s == 0 || *++s == 0)
goto validate;
value = pop(s, ':');
self->partition = (int) simple_strtoul(value, NULL, 16);
s += strlen(value);
free(value);
if(*s == 0 || *++s == 0)
goto validate;
self->filename = strdup(s);
validate:
if((self->desc = get_dev(self->interface, self->device)) == NULL) {
free(self->interface);
free(self->filename);
free(self);
return NULL;
}
return self;
}
static struct BootDevice *BootDevice_find(char *s)
{
struct BootDevice *self;
int i;
self = malloc(sizeof(struct BootDevice));
for(i = 0; i < sizeof(table) / sizeof(struct BootDeviceTable);i++) {
if(strcmp(s, table[i].name))
continue;
memmove(self, &table[i].device, sizeof(struct BootDevice));
goto found;
}
return NULL;
found:
if((self->desc = get_dev(self->interface, self->device)) == NULL) {
free(self->interface);
free(self->filename);
free(self);
return NULL;
}
return self;
}
static void BootDevice_print(struct BootDevice *self)
{
printf("%s:%d", self->interface, self->device);
if(self->partition >= 0)
printf(":%d:%s", self->partition, (self->filename == NULL ? "Parthenope" : self->filename));
printf("\n");
}
static void BootDevice_boot(struct BootDevice *self)
{
if(self->desc->type == DEV_TYPE_HARDDISK
&& self->desc->part_type == PART_TYPE_AMIGA
&& self->partition == -1)
do_bootu_amiga_hd(self->desc);
if(self->desc->type == DEV_TYPE_HARDDISK)
do_bootu_hd(self);
if(self->desc->type == DEV_TYPE_CDROM)
do_bootu_eltorito(self);
}
static void copyright(void)
{
puts("bootu (u-boot first level bootloader) 1.0\n");
puts("Copyright (C) 2008 Giuseppe Coviello.\n");
puts("This is free software. You may redistribute ");
puts("copies of it under the terms of\n");
puts("the GNU General Public License ");
puts("<http://www.gnu.org/licenses/gpl.html>.\n");
puts("There is NO WARRANTY, to the extent permitted by law.\n");
}
int bootu(char *device_str)
{
struct BootDevice *device;
if(strcmp(device_str, "net") == 0)
return do_bootu_tftp();
device = BootDevice_find(device_str);
if(device == NULL)
device = BootDevice_new_from_string(device_str);
if(device != NULL) {
BootDevice_print(device);
BootDevice_boot(device);
return -1;
}
return 0;
}
int do_bootu(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
int i;
SCAN_HANDLE scanner;
ULONG sector_size;
copyright();
argarray[0] = getenv("boot1");
argarray[1] = getenv("boot2");
argarray[2] = getenv("boot3");
argarray[3] = NULL;
for(i = 1; i < 5; i++) {
if(argarray[i - 1] == NULL)
continue;
scanner = next_unit_scan(scanner, &sector_size);
printf("%s\n", argarray[i - 1]);
if(bootu(argarray[i - 1]) == 0)
break;
}
return 0;
}
U_BOOT_CMD(
bootu, 1, 0, do_bootu,
"bootu - load and start secondory level bootloader.\n",
". 'Bootu' allows to load secondary level bootloader "
"like Parthenope or AOS SLB.\n");
U_BOOT_CMD(
boota, 1, 0, do_bootu,
"boota - load and start secondory level bootloader.\n",
". 'Boota' allows to load secondary level bootloader "
"like Parthenope or AOS SLB.\n");