blob: 1bd03b6bfa74b5b6bbc6ab78534162564f083ac8 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
* Copyright (c) 2009, The Linux Foundation. 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.
*
* 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 <lib/ptable.h>
#include <malloc.h>
#include <dev/flash.h>
#include <string.h>
#include <jtag.h>
#include <kernel/thread.h>
#include <smem.h>
#include <platform.h>
#include "bootimg.h"
#define FLASH_PAGE_SIZE 2048
#define FLASH_PAGE_BITS 11
unsigned page_size = 0;
unsigned page_mask = 0;
static unsigned load_addr = 0xffffffff;
#define ROUND_TO_PAGE(x,y) (((x) + (y)) & (~(y)))
void acpu_clock_init(void);
int startswith(const char *str, const char *prefix)
{
while(*prefix){
if (*prefix++ != *str++) return 0;
}
return 1;
}
/* XXX */
void verify_flash(struct ptentry *p, void *addr, unsigned len, int extra)
{
uint32_t offset = 0;
void *buf = malloc(FLASH_PAGE_SIZE + extra);
int verify_extra = extra;
if(verify_extra > 4)
verify_extra = 16;
while(len > 0) {
flash_read_ext(p, extra, offset, buf, FLASH_PAGE_SIZE);
if(memcmp(addr, buf, FLASH_PAGE_SIZE + verify_extra)) {
dprintf(CRITICAL, "verify failed at 0x%08x\n", offset);
jtag_fail("verify failed");
return;
}
offset += FLASH_PAGE_SIZE;
addr += FLASH_PAGE_SIZE;
len -= FLASH_PAGE_SIZE;
if(extra) {
addr += extra;
len -= extra;
}
}
dprintf(INFO, "verify done %d extra bytes\n", verify_extra);
jtag_okay("verify done");
}
void handle_flash(const char *name, unsigned addr, unsigned sz)
{
struct ptentry *ptn;
struct ptable *ptable;
void *data = (void *) addr;
unsigned extra = 0;
ptable = flash_get_ptable();
if (ptable == NULL) {
jtag_fail("partition table doesn't exist");
return;
}
ptn = ptable_find(ptable, name);
if (ptn == NULL) {
jtag_fail("unknown partition name");
return;
}
if (!strcmp(ptn->name, "boot") || !strcmp(ptn->name, "recovery")) {
if (memcmp((void *)data, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
jtag_fail("image is not a boot image");
return;
}
}
if (!strcmp(ptn->name, "system") || !strcmp(ptn->name, "userdata") || !strcmp(ptn->name, "persist"))
extra = ((page_size >> 9) * 16);
else
sz = ROUND_TO_PAGE(sz, page_mask);
data = (void *)target_get_scratch_address();
dprintf(INFO, "writing %d bytes to '%s'\n", sz, ptn->name);
if (flash_write(ptn, extra, data, sz)) {
jtag_fail("flash write failure");
return;
}
dprintf(INFO, "partition '%s' updated\n", ptn->name);
jtag_okay("Done");
enter_critical_section();
platform_uninit_timer();
arch_disable_cache(UCACHE);
arch_disable_mmu();
}
static unsigned char *tmpbuf = 0;
/*XXX*/
void handle_dump(const char *name, unsigned offset)
{
struct ptentry *p;
struct ptable *ptable;
if(tmpbuf == 0) {
tmpbuf = malloc(4096);
}
dprintf(INFO, "dump '%s' partition\n", name);
ptable = flash_get_ptable();
if (ptable == NULL) {
jtag_fail("partition table doesn't exist");
return;
}
p = ptable_find(ptable, name);
if(p == 0) {
jtag_fail("partition not found");
return;
} else {
#if 0
/* XXX reimpl */
if(flash_read_page(p->start * 64, tmpbuf, tmpbuf + 2048)){
jtag_fail("flash_read() failed");
return;
}
#endif
dprintf(INFO, "page %d data:\n", p->start * 64);
hexdump(tmpbuf, 256);
dprintf(INFO, "page %d extra:\n", p->start * 64);
hexdump(tmpbuf, 16);
jtag_okay("done");
enter_critical_section();
platform_uninit_timer();
arch_disable_cache(UCACHE);
arch_disable_mmu();
}
}
void handle_query_load_address(unsigned addr)
{
unsigned *return_addr = (unsigned *)addr;
if (return_addr)
*return_addr = target_get_scratch_address();
jtag_okay("done");
}
void handle_command(const char *cmd, unsigned a0, unsigned a1, unsigned a2)
{
if(startswith(cmd,"flash:")){
handle_flash(cmd + 6, a0, a1);
return;
}
if(startswith(cmd,"dump:")){
handle_dump(cmd + 5, a0);
return;
}
if(startswith(cmd,"loadaddr:")){
handle_query_load_address(a0);
return;
}
jtag_fail("unknown command");
}
void nandwrite_init(void)
{
page_size = flash_page_size();
page_mask = page_size - 1;
jtag_cmd_loop(handle_command);
}