blob: 40c9c4f2dc8d3bdcaaa98ffdd168b889df5c5692 [file] [log] [blame]
/*
* Copyright (c) 2009, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google, Inc. nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <app.h>
#include <debug.h>
#include <arch/arm.h>
#include <dev/udc.h>
#include <string.h>
#include <kernel/thread.h>
#include <arch/ops.h>
#include <dev/flash.h>
#include <lib/ptable.h>
#include <dev/keys.h>
#include "bootimg.h"
#include "fastboot.h"
#define TAGS_ADDR 0x10000100
#define KERNEL_ADDR 0x10800000
#define RAMDISK_ADDR 0x11000000
#define DEFAULT_CMDLINE "mem=50M console=null";
static struct udc_device surf_udc_device = {
.vendor_id = 0x18d1,
.product_id = 0x0001,
.version_id = 0x0100,
.manufacturer = "Google",
.product = "Android",
};
void platform_uninit_timer(void);
void boot_linux(void *kernel, unsigned *tags,
const char *cmdline, unsigned machtype,
void *ramdisk, unsigned ramdisk_size)
{
unsigned *ptr = tags;
void (*entry)(unsigned,unsigned,unsigned*) = kernel;
/* CORE */
*ptr++ = 2;
*ptr++ = 0x54410001;
if (ramdisk_size) {
*ptr++ = 4;
*ptr++ = 0x54420005;
*ptr++ = (unsigned)ramdisk;
*ptr++ = ramdisk_size;
}
if (cmdline && cmdline[0]) {
unsigned n;
/* include terminating 0 and round up to a word multiple */
n = (strlen(cmdline) + 4) & (~3);
*ptr++ = (n / 4) + 2;
*ptr++ = 0x54410009;
memcpy(ptr, cmdline, n);
ptr += (n / 4);
}
/* END */
*ptr++ = 0;
*ptr++ = 0;
dprintf(INFO, "booting linux @ %p, ramdisk @ %p (%d)\n",
kernel, ramdisk, ramdisk_size);
if (cmdline)
dprintf(INFO, "cmdline: %s\n", cmdline);
enter_critical_section();
platform_uninit_timer();
arch_disable_cache(UCACHE);
arch_disable_mmu();
entry(0, machtype, tags);
}
#define PAGE_MASK 2047
#define ROUND_TO_PAGE(x) (((x) + PAGE_MASK) & (~PAGE_MASK))
static unsigned char buf[2048];
int boot_linux_from_flash(void)
{
struct boot_img_hdr *hdr = (void*) buf;
unsigned n;
struct ptentry *ptn;
struct ptable *ptable;
unsigned offset = 0;
const char *cmdline;
ptable = flash_get_ptable();
if (ptable == NULL) {
dprintf(CRITICAL, "ERROR: Partition table not found\n");
return -1;
}
ptn = ptable_find(ptable, "boot");
if (ptn == NULL) {
dprintf(CRITICAL, "ERROR: No boot partition found\n");
return -1;
}
if (flash_read(ptn, offset, buf, 2048)) {
dprintf(CRITICAL, "ERROR: Cannot read boot image header\n");
return -1;
}
offset += 2048;
if (memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
dprintf(CRITICAL, "ERROR: Invaled boot image heador\n");
return -1;
}
n = ROUND_TO_PAGE(hdr->kernel_size);
if (flash_read(ptn, offset, (void *)hdr->kernel_addr, n)) {
dprintf(CRITICAL, "ERROR: Cannot read kernel image\n");
return -1;
}
offset += n;
n = ROUND_TO_PAGE(hdr->ramdisk_size);
if (flash_read(ptn, offset, (void *)hdr->ramdisk_addr, n)) {
dprintf(CRITICAL, "ERROR: Cannot read ramdisk image\n");
return -1;
}
offset += n;
dprintf(INFO, "\nkernel @ %x (%d bytes)\n", hdr->kernel_addr,
hdr->kernel_size);
dprintf(INFO, "ramdisk @ %x (%d bytes)\n", hdr->ramdisk_addr,
hdr->ramdisk_size);
if(hdr->cmdline[0]) {
cmdline = (char*) hdr->cmdline;
} else {
cmdline = DEFAULT_CMDLINE;
}
dprintf(INFO, "cmdline = '%s'\n", cmdline);
/* TODO: create/pass atags to kernel */
dprintf(INFO, "\nBooting Linux\n");
boot_linux((void *)hdr->kernel_addr, (void *)TAGS_ADDR,
(const char *)cmdline, 1008000,
(void *)hdr->ramdisk_addr, hdr->ramdisk_size);
return 0;
}
void cmd_boot(const char *arg, void *data, unsigned sz)
{
unsigned kernel_actual;
unsigned ramdisk_actual;
static struct boot_img_hdr hdr;
char *ptr = ((char*) data);
if (sz < sizeof(hdr)) {
fastboot_fail("invalid bootimage header");
return;
}
memcpy(&hdr, data, sizeof(hdr));
/* ensure commandline is terminated */
hdr.cmdline[BOOT_ARGS_SIZE-1] = 0;
kernel_actual = ROUND_TO_PAGE(hdr.kernel_size);
ramdisk_actual = ROUND_TO_PAGE(hdr.ramdisk_size);
if (2048 + kernel_actual + ramdisk_actual < sz) {
fastboot_fail("incomplete bootimage");
return;
}
memmove((void*) KERNEL_ADDR, ptr + 2048, hdr.kernel_size);
memmove((void*) RAMDISK_ADDR, ptr + 2048 + kernel_actual, hdr.ramdisk_size);
fastboot_okay("");
udc_stop();
boot_linux((void*) KERNEL_ADDR, (void*) TAGS_ADDR,
(const char*) hdr.cmdline, 1008000,
(void*) RAMDISK_ADDR, hdr.ramdisk_size);
}
void cmd_erase(const char *arg, void *data, unsigned sz)
{
struct ptentry *ptn;
struct ptable *ptable;
ptable = flash_get_ptable();
if (ptable == NULL) {
fastboot_fail("partition table doesn't exist");
return;
}
ptn = ptable_find(ptable, arg);
if (ptn == NULL) {
fastboot_fail("unknown partition name");
return;
}
if (flash_erase(ptn)) {
fastboot_fail("failed to erase partition");
return;
}
fastboot_okay("");
}
void cmd_flash(const char *arg, void *data, unsigned sz)
{
struct ptentry *ptn;
struct ptable *ptable;
unsigned extra = 0;
ptable = flash_get_ptable();
if (ptable == NULL) {
fastboot_fail("partition table doesn't exist");
return;
}
ptn = ptable_find(ptable, arg);
if (ptn == NULL) {
fastboot_fail("unknown partition name");
return;
}
if (!strcmp(ptn->name, "boot") || !strcmp(ptn->name, "recovery")) {
if (memcmp((void *)data, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
fastboot_fail("image is not a boot image");
return;
}
}
if (!strcmp(ptn->name, "system") || !strcmp(ptn->name, "userdata"))
extra = 64;
else
sz = ROUND_TO_PAGE(sz);
dprintf(INFO, "writing %d bytes to '%s'\n", sz, ptn->name);
if (flash_write(ptn, extra, data, sz)) {
fastboot_fail("flash write failure");
return;
}
dprintf(INFO, "partition '%s' updated\n", ptn->name);
fastboot_okay("");
}
void cmd_continue(const char *arg, void *data, unsigned sz)
{
fastboot_okay("");
udc_stop();
boot_linux_from_flash();
}
void aboot_init(const struct app_descriptor *app)
{
if (keys_get_state(KEY_BACK) != 0)
goto fastboot;
boot_linux_from_flash();
dprintf(CRITICAL, "ERROR: Could not do normal boot. Reverting "
"to fastboot mode.\n");
fastboot:
udc_init(&surf_udc_device);
fastboot_register("boot", cmd_boot);
fastboot_register("erase:", cmd_erase);
fastboot_register("flash:", cmd_flash);
fastboot_register("continue", cmd_continue);
fastboot_publish("product", "swordfish");
fastboot_publish("kernel", "lk");
fastboot_init((void*) 0x10100000, 100 * 1024 * 1024);
udc_start();
}
APP_START(aboot)
.init = aboot_init,
APP_END