2008-08-13 02:45:55 +03:00
|
|
|
/*
|
|
|
|
* (C) Copyright 2008 Openmoko, Inc.
|
|
|
|
* Author: Andy Green <andy@openmoko.org>
|
|
|
|
*
|
|
|
|
* Parse the U-Boot header and Boot Linux
|
|
|
|
* based on various code from U-Boot
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2008-11-28 12:16:35 +02:00
|
|
|
#include <qi.h>
|
2008-08-13 02:42:35 +03:00
|
|
|
#include <neo_gta02.h>
|
|
|
|
#include "blink_led.h"
|
|
|
|
#include <string.h>
|
|
|
|
#define __ARM__
|
|
|
|
#include <image.h>
|
|
|
|
#include <setup.h>
|
2008-11-28 12:16:37 +02:00
|
|
|
#include <ext2.h>
|
|
|
|
|
|
|
|
unsigned long partition_offset_blocks = 0;
|
|
|
|
unsigned long partition_length_blocks = 0;
|
|
|
|
|
|
|
|
struct kernel_source const * this_kernel = 0;
|
2008-08-13 02:42:35 +03:00
|
|
|
|
2008-11-28 12:16:41 +02:00
|
|
|
const int INITRD_OFFSET = (8 * 1024 * 1024);
|
|
|
|
|
2008-11-28 12:16:40 +02:00
|
|
|
int raise(int n)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-11-28 12:16:41 +02:00
|
|
|
int read_file(const char * filepath, u8 * destination, int size)
|
|
|
|
{
|
|
|
|
unsigned int len = size;
|
|
|
|
|
|
|
|
switch (this_kernel->filesystem) {
|
|
|
|
case FS_EXT2:
|
|
|
|
if (!ext2fs_mount()) {
|
|
|
|
puts("Unable to mount ext2 filesystem\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
puts(" EXT2 open: ");
|
|
|
|
puts(filepath);
|
|
|
|
puts("\n");
|
|
|
|
len = ext2fs_open(filepath);
|
|
|
|
if (len < 0) {
|
|
|
|
puts("Open failed\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ext2fs_read((char *)destination, size);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FS_FAT:
|
|
|
|
/* FIXME */
|
|
|
|
case FS_RAW:
|
|
|
|
puts(" RAW open: +");
|
|
|
|
printdec(partition_offset_blocks);
|
|
|
|
puts(" 512-byte blocks\n");
|
|
|
|
if (this_kernel->block_read(destination,
|
|
|
|
partition_offset_blocks, size >> 9) < 0) {
|
|
|
|
puts ("Bad kernel header\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
2008-08-13 02:42:35 +03:00
|
|
|
|
|
|
|
void bootloader_second_phase(void)
|
|
|
|
{
|
2008-08-13 02:45:55 +03:00
|
|
|
void (*the_kernel)(int zero, int arch, uint params);
|
2008-11-28 12:16:36 +02:00
|
|
|
int kernel = 0;
|
2008-11-28 12:16:40 +02:00
|
|
|
const struct board_variant * board_variant =
|
|
|
|
(this_board->get_board_variant)();
|
2008-11-28 12:16:41 +02:00
|
|
|
unsigned int initramfs_len = 0;
|
2008-11-28 12:16:40 +02:00
|
|
|
|
2008-11-28 12:16:36 +02:00
|
|
|
/* we try the possible kernels for this board in order */
|
|
|
|
|
2008-11-28 12:16:37 +02:00
|
|
|
this_kernel = &this_board->kernel_source[kernel++];
|
|
|
|
|
|
|
|
while (this_kernel->name) {
|
|
|
|
const char *p;
|
2008-11-28 12:16:36 +02:00
|
|
|
struct tag *params = (struct tag *)this_board->linux_tag_placement;
|
2008-11-28 12:16:40 +02:00
|
|
|
void * kernel_dram = (void *)this_board->linux_mem_start + 0x8000;
|
2008-11-28 12:16:36 +02:00
|
|
|
unsigned long crc;
|
|
|
|
image_header_t *hdr;
|
2008-11-28 12:16:37 +02:00
|
|
|
u32 kernel_size;
|
|
|
|
|
|
|
|
partition_offset_blocks = 0;
|
|
|
|
partition_length_blocks = 0;
|
2008-11-28 12:16:36 +02:00
|
|
|
|
|
|
|
/* eat leading white space */
|
2008-11-28 12:16:37 +02:00
|
|
|
for (p = this_kernel->commandline; *p == ' '; p++);
|
2008-11-28 12:16:36 +02:00
|
|
|
|
2008-10-22 03:41:59 +03:00
|
|
|
puts("\nTrying kernel: ");
|
2008-11-28 12:16:37 +02:00
|
|
|
puts(this_kernel->name);
|
2008-11-28 12:16:36 +02:00
|
|
|
puts("\n");
|
|
|
|
|
|
|
|
/* if this device needs initializing, try to init it */
|
2008-11-28 12:16:37 +02:00
|
|
|
if (this_kernel->block_init)
|
|
|
|
if ((this_kernel->block_init)()) {
|
2008-11-28 12:16:36 +02:00
|
|
|
puts("block device init failed\n");
|
2008-11-28 12:16:37 +02:00
|
|
|
this_kernel = &this_board->
|
|
|
|
kernel_source[kernel++];
|
2008-11-28 12:16:36 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if there's a partition table implied, parse it, otherwise
|
|
|
|
* just use a fixed offset
|
|
|
|
*/
|
2008-11-28 12:16:37 +02:00
|
|
|
if (this_kernel->partition_index) {
|
2008-11-28 12:16:37 +02:00
|
|
|
unsigned char *p = kernel_dram;
|
2008-11-28 12:16:36 +02:00
|
|
|
|
2008-11-28 12:16:38 +02:00
|
|
|
if ((int)this_kernel->block_read(kernel_dram, 0, 4)
|
|
|
|
< 0) {
|
2008-11-28 12:16:37 +02:00
|
|
|
puts("Bad partition read\n");
|
2008-11-28 12:16:37 +02:00
|
|
|
this_kernel = &this_board->
|
|
|
|
kernel_source[kernel++];
|
2008-11-28 12:16:36 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-11-28 12:16:37 +02:00
|
|
|
if ((p[0x1fe] != 0x55) || (p[0x1ff] != 0xaa)) {
|
|
|
|
puts("partition signature missing\n");
|
2008-11-28 12:16:37 +02:00
|
|
|
this_kernel = &this_board->
|
|
|
|
kernel_source[kernel++];
|
2008-11-28 12:16:36 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-11-28 12:16:37 +02:00
|
|
|
p += 0x1be + 8 + (0x10 *
|
|
|
|
(this_kernel->partition_index - 1));
|
2008-11-28 12:16:37 +02:00
|
|
|
|
|
|
|
partition_offset_blocks = (((u32)p[3]) << 24) |
|
|
|
|
(((u32)p[2]) << 16) |
|
|
|
|
(((u32)p[1]) << 8) |
|
|
|
|
p[0];
|
|
|
|
partition_length_blocks = (((u32)p[7]) << 24) |
|
|
|
|
(((u32)p[6]) << 16) |
|
|
|
|
(((u32)p[5]) << 8) |
|
|
|
|
p[4];
|
|
|
|
|
2008-11-28 12:16:37 +02:00
|
|
|
puts(" Partition: ");
|
|
|
|
printdec(this_kernel->partition_index);
|
|
|
|
puts(" start +");
|
|
|
|
printdec(partition_offset_blocks);
|
|
|
|
puts(" 512-byte blocks, size ");
|
|
|
|
printdec(partition_length_blocks / 2048);
|
|
|
|
puts(" MiB\n");
|
2008-11-28 12:16:37 +02:00
|
|
|
|
2008-11-28 12:16:37 +02:00
|
|
|
} else
|
|
|
|
partition_offset_blocks =
|
|
|
|
this_kernel->offset_blocks512_if_no_partition;
|
|
|
|
|
2008-11-28 12:16:41 +02:00
|
|
|
/* pull the kernel image */
|
2008-11-28 12:16:40 +02:00
|
|
|
|
2008-11-28 12:16:41 +02:00
|
|
|
if (read_file(this_kernel->filepath, kernel_dram, 4096) < 0) {
|
|
|
|
this_kernel = &this_board->kernel_source[kernel++];
|
|
|
|
continue;
|
2008-11-28 12:16:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
hdr = (image_header_t *)kernel_dram;
|
|
|
|
|
2008-11-28 12:16:37 +02:00
|
|
|
if (__be32_to_cpu(hdr->ih_magic) != IH_MAGIC) {
|
2008-11-28 12:16:37 +02:00
|
|
|
puts("bad magic ");
|
|
|
|
print32(hdr->ih_magic);
|
2008-11-28 12:16:37 +02:00
|
|
|
puts("\n");
|
|
|
|
this_kernel = &this_board->kernel_source[kernel++];
|
2008-11-28 12:16:37 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-11-28 12:16:37 +02:00
|
|
|
puts(" Found: \"");
|
2008-11-28 12:16:37 +02:00
|
|
|
puts((const char *)hdr->ih_name);
|
2008-11-28 12:16:37 +02:00
|
|
|
puts("\"\n Size: ");
|
|
|
|
printdec(__be32_to_cpu(hdr->ih_size) >> 10);
|
2008-11-28 12:16:37 +02:00
|
|
|
puts(" KiB\n");
|
|
|
|
|
2008-11-28 12:16:37 +02:00
|
|
|
kernel_size = ((__be32_to_cpu(hdr->ih_size) +
|
|
|
|
sizeof(image_header_t) + 2048) & ~(2048 - 1));
|
|
|
|
|
2008-11-28 12:16:41 +02:00
|
|
|
if (read_file(this_kernel->filepath, kernel_dram,
|
|
|
|
kernel_size) < 0) {
|
|
|
|
this_kernel = &this_board->kernel_source[kernel++];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initramfs if needed */
|
|
|
|
|
|
|
|
if (this_kernel->initramfs_filepath) {
|
|
|
|
initramfs_len = read_file(this_kernel->initramfs_filepath,
|
|
|
|
(u8 *)this_board->linux_mem_start + INITRD_OFFSET, 16 * 1024 * 1024);
|
|
|
|
if (initramfs_len < 0) {
|
|
|
|
puts("initramfs load failed\n");
|
|
|
|
this_kernel = &this_board->kernel_source[kernel++];
|
2008-11-28 12:16:37 +02:00
|
|
|
continue;
|
|
|
|
}
|
2008-11-28 12:16:36 +02:00
|
|
|
}
|
|
|
|
|
2008-11-28 12:16:37 +02:00
|
|
|
puts(" Cmdline: ");
|
2008-11-28 12:16:36 +02:00
|
|
|
puts(p);
|
|
|
|
puts("\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It's good for now to know that our kernel is intact from
|
|
|
|
* the storage before we jump into it and maybe crash silently
|
|
|
|
* even though it costs us some time
|
|
|
|
*/
|
|
|
|
crc = crc32(0, kernel_dram + sizeof(image_header_t),
|
2008-11-28 12:16:37 +02:00
|
|
|
__be32_to_cpu(hdr->ih_size));
|
|
|
|
if (crc != __be32_to_cpu(hdr->ih_dcrc)) {
|
2008-11-28 12:16:36 +02:00
|
|
|
puts("\nKernel CRC ERROR: read 0x");
|
|
|
|
print32(crc);
|
|
|
|
puts(" vs hdr CRC 0x");
|
2008-11-28 12:16:37 +02:00
|
|
|
print32(__be32_to_cpu(hdr->ih_dcrc));
|
2008-11-28 12:16:36 +02:00
|
|
|
puts("\n");
|
2008-11-28 12:16:37 +02:00
|
|
|
this_kernel = &this_board->kernel_source[kernel++];
|
2008-11-28 12:16:36 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
the_kernel = (void (*)(int, int, uint))
|
|
|
|
(((char *)hdr) + sizeof(image_header_t));
|
|
|
|
|
|
|
|
/* first tag */
|
|
|
|
params->hdr.tag = ATAG_CORE;
|
|
|
|
params->hdr.size = tag_size (tag_core);
|
|
|
|
params->u.core.flags = 0;
|
|
|
|
params->u.core.pagesize = 0;
|
|
|
|
params->u.core.rootdev = 0;
|
|
|
|
params = tag_next(params);
|
|
|
|
|
|
|
|
/* revision tag */
|
|
|
|
params->hdr.tag = ATAG_REVISION;
|
|
|
|
params->hdr.size = tag_size (tag_revision);
|
|
|
|
params->u.revision.rev = board_variant->machine_revision;
|
|
|
|
params = tag_next(params);
|
|
|
|
|
|
|
|
/* memory tags */
|
|
|
|
params->hdr.tag = ATAG_MEM;
|
|
|
|
params->hdr.size = tag_size (tag_mem32);
|
|
|
|
params->u.mem.start = this_board->linux_mem_start;
|
|
|
|
params->u.mem.size = this_board->linux_mem_size;
|
|
|
|
params = tag_next(params);
|
|
|
|
|
2008-11-28 12:16:41 +02:00
|
|
|
if (this_kernel->initramfs_filepath) {
|
|
|
|
/* INITRD2 tag */
|
|
|
|
params->hdr.tag = ATAG_INITRD2;
|
|
|
|
params->hdr.size = tag_size (tag_initrd);
|
|
|
|
params->u.initrd.start = this_board->linux_mem_start +
|
|
|
|
INITRD_OFFSET;
|
|
|
|
params->u.initrd.size = initramfs_len;
|
|
|
|
params = tag_next(params);
|
|
|
|
}
|
|
|
|
|
2008-11-28 12:16:36 +02:00
|
|
|
/* kernel commandline */
|
|
|
|
|
|
|
|
if (*p) {
|
|
|
|
params->hdr.tag = ATAG_CMDLINE;
|
|
|
|
params->hdr.size = (sizeof (struct tag_header) +
|
|
|
|
strlen (p) + 1 + 4) >> 2;
|
|
|
|
strcpy (params->u.cmdline.cmdline, p);
|
|
|
|
params = tag_next (params);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* needs to always be the last tag */
|
|
|
|
params->hdr.tag = ATAG_NONE;
|
|
|
|
params->hdr.size = 0;
|
|
|
|
|
2008-11-28 12:16:39 +02:00
|
|
|
/* give board implementation a chance to shut down
|
|
|
|
* anything it may have going on, leave GPIO set for Linux
|
|
|
|
*/
|
|
|
|
if (this_board->close)
|
|
|
|
(this_board->close)();
|
|
|
|
|
2008-11-28 12:16:36 +02:00
|
|
|
puts ("Starting --->\n\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ooh that's it, we're gonna try boot this image!
|
|
|
|
* never mind the cache, Linux will take care of it
|
|
|
|
*/
|
|
|
|
the_kernel(0, this_board->linux_machine_id,
|
|
|
|
this_board->linux_tag_placement);
|
|
|
|
|
|
|
|
/* we won't come back here no matter what */
|
2008-08-13 02:42:35 +03:00
|
|
|
}
|
|
|
|
|
2008-11-28 12:16:36 +02:00
|
|
|
/* none of the kernels worked out */
|
2008-08-13 02:42:35 +03:00
|
|
|
|
2008-11-28 12:16:36 +02:00
|
|
|
puts("No usable kernel image found, we've had it :-(\n");
|
2008-08-13 02:42:35 +03:00
|
|
|
while (1)
|
2008-11-28 12:16:40 +02:00
|
|
|
;
|
2008-08-13 02:42:35 +03:00
|
|
|
}
|