/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/reboot.h>
#include <sys/stat.h>
#define error(s, a...) \
{ \
printf("error: " s "\n", ##a); \
exit(-1); \
}
#define error_errno(s, a...) error(s ": %s", ##a, strerror(errno))
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
enum omap_type_enum {
OMAP4460_EMU = 0,
OMAP4460_HS,
OMAP4460_HS_PROD,
OMAP4430_HS,
};
struct omap_type {
const char *family;
const char *type;
unsigned long msv_val;
const char *msv_type;
off_t offset;
} omap_type_list[] = {
[OMAP4460_EMU] = {"OMAP4460", "EMU", 0x00000000, "eng", 0x1000},
[OMAP4460_HS] = {"OMAP4460", "HS", 0x00000000, "eng", 0x21000},
[OMAP4460_HS_PROD] = {"OMAP4460", "HS", 0xf0000f00, "prod", 0x41000},
[OMAP4430_HS] = {"OMAP4430", "HS", 0x00000000, "eng", 0x61000},
};
#define IMG_PIT_OFFSET 0UL
#define IMG_SBL_OFFSET 0x81000UL
#define MMC_PIT_OFFSET 0x4400UL
#define MMC_XLOADER_OFFSET 0x20000UL
#define MMC_SBL_OFFSET 0x80000UL
#define PIT_SIZE 0x1000UL
#define XLOADER_SIZE 0x20000UL
static void drop_caches(void)
{
int fd;
int ret;
char buf[] = "3\n";
fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
if (fd < 0)
error_errno("failed to open /proc/sys/vm/drop_caches");
ret = write(fd, buf, sizeof(buf));
if (ret < 0)
error_errno("failed to write to /proc/sys/vm/drop_caches");
}
static void read_file(const char *filename, char *buf, size_t size)
{
int fd;
ssize_t ret;
fd = open(filename, O_RDONLY);
if (fd < 0)
error_errno("failed to open %s", filename);
ret = read(fd, buf, size - 1);
if (ret < 0)
error_errno("failed to read %s", filename);
buf[ret] = 0;
while (buf[ret - 1] == '\n')
buf[--ret] = 0;
close(fd);
}
static const struct omap_type *get_omap_type(void)
{
int fd;
char family[10];
char type[5];
char msv[9];
unsigned long msv_val;
ssize_t ret;
unsigned int i;
read_file("/sys/board_properties/soc/type", type, sizeof(type));
read_file("/sys/board_properties/soc/family", family, sizeof(family));
read_file("/sys/board_properties/soc/msv", msv, sizeof(msv));
msv_val = strtoul(msv, NULL, 16);
for (i = 0; i < ARRAY_SIZE(omap_type_list); i++)
if ((strcmp(omap_type_list[i].family, family) == 0) &&
(strcmp(omap_type_list[i].type, type) == 0) &&
msv_val == omap_type_list[i].msv_val)
return &omap_type_list[i];
error("unknown omap type %s %s %s (0x%08lx)", family, type, msv, msv_val);
}
static void zero_data(int to_fd, off_t to_offset, ssize_t size)
{
char buf[4096];
int ret;
unsigned int to_write;
memset(buf, 0, sizeof(buf));
ret = lseek(to_fd, to_offset, SEEK_SET);
if (ret < 0)
error_errno("failed to seek output file to %lx", to_offset);
while (size != 0) {
to_write = size;
if (to_write > sizeof(buf))
to_write = sizeof(buf);
ret = write(to_fd, buf, to_write);
if (ret < 0)
error_errno("failed to write to output file");
size -= ret;
}
}
static void verify_data(int to_fd, off_t to_offset,
int from_fd, off_t from_offset,
ssize_t size)
{
char buf_to[4096];
char buf_from[4096];
int ret;
int to_read;
int c;
char *ptr;
ret = lseek(to_fd, to_offset, SEEK_SET);
if (ret < 0)
error_errno("failed to seek output file to %lx", to_offset);
ret = lseek(from_fd, from_offset, SEEK_SET);
if (ret < 0)
error_errno("failed to seek input file to %lx", from_offset);
while (size != 0) {
to_read = sizeof(buf_to);
if (size > 0 && to_read > size)
to_read = size;
ptr = buf_to;
c = to_read;
while (c > 0) {
ret = read(to_fd, ptr, c);
if (ret < 0)
error_errno("failed to read from output file");
if (ret == 0 && size < 0)
return;
if (ret == 0)
error_errno("eof while reading output file");
ptr += ret;
c -= ret;
}
ptr = buf_from;
c = to_read;
while (c > 0) {
ret = read(from_fd, ptr, c);
if (ret < 0)
error_errno("failed to read from input file");
if (ret == 0 && size < 0)
return;
if (ret == 0)
error_errno("eof while reading input file");
ptr += ret;
c -= ret;
}
if (memcmp(buf_from, buf_to, to_read) != 0)
error("mismatch while verifying written data");
size -= to_read;
}
}
static void copy_data(int to_fd, off_t to_offset,
int from_fd, off_t from_offset,
ssize_t size)
{
char buf[4096];
int ret;
int to_write;
const char *ptr;
ret = lseek(to_fd, to_offset, SEEK_SET);
if (ret < 0)
error_errno("failed to seek output file to %lx", to_offset);
ret = lseek(from_fd, from_offset, SEEK_SET);
if (ret < 0)
error_errno("failed to seek input file to %lx", from_offset);
while (size != 0) {
ret = read(from_fd, buf, sizeof(buf));
if (ret < 0)
error_errno("failed to read from input file");
if (ret == 0 && size > 0)
error_errno("eof while reading input file");
if (ret == 0)
return;
to_write = ret;
ptr = buf;
if (size > 0)
size -= to_write;
while (to_write > 0) {
ret = write(to_fd, ptr, to_write);
if (ret < 0)
error_errno("failed to write to output file");
to_write -= ret;
ptr += ret;
}
}
}
static void init(void)
{
int ret;
umask(0);
ret = mkdir("/dev", 0755);
if (ret && errno != EEXIST)
error_errno("failed to create /dev");
ret = mkdir("/proc", 0755);
if (ret && errno != EEXIST)
error_errno("failed to create /proc");
ret = mkdir("/sys", 0755);
if (ret && errno != EEXIST)
error_errno("failed to create /sys");
ret = mount("proc", "/proc", "proc", 0, NULL);
if (ret)
error_errno("failed to mount proc");
ret = mount("sysfs", "/sys", "sysfs", 0, NULL);
if (ret)
error_errno("failed to mount sys");
ret = mkdir("/dev/block", 0755);
if (ret && errno != EEXIST)
error_errno("failed to create /dev/block");
ret = mknod("/dev/block/mmcblk0", S_IFBLK | 0755, makedev(179, 0));
if (ret)
error_errno("failed to create mmcblk0");
}
int main(int argc, char **argv)
{
int in_fd;
int out_fd;
const struct omap_type *type;
if (getpid() == 1)
init();
in_fd = open("bootloader.img", O_RDONLY);
if (in_fd < 0)
error_errno("failed to open bootloader.img");
out_fd = open("/dev/block/mmcblk0", O_RDWR);
if (out_fd < 0)
error_errno("failed to open mmcblk0");
type = get_omap_type();
printf("Found %s %s %s\n", type->family, type->type, type->msv_type);
printf("Zeroing to end of sbl\n");
zero_data(out_fd, 0, MMC_SBL_OFFSET);
/* Don't write the partition table, let the bootloader do it on next boot */
#if 0
printf("Writing partition-table from %lx to %lx\n",
IMG_PIT_OFFSET, MMC_PIT_OFFSET);
copy_data(out_fd, MMC_PIT_OFFSET, in_fd, IMG_PIT_OFFSET, PIT_SIZE);
#endif
printf("Writing xloader from %lx to %lx\n",
type->offset, MMC_XLOADER_OFFSET);
copy_data(out_fd, MMC_XLOADER_OFFSET, in_fd, type->offset, XLOADER_SIZE);
printf("Writing sbl from %lx to %lx\n",
IMG_SBL_OFFSET, MMC_SBL_OFFSET);
copy_data(out_fd, MMC_SBL_OFFSET, in_fd, IMG_SBL_OFFSET, -1);
#if 0
printf("Verifying partition table\n");
verify_data(out_fd, MMC_PIT_OFFSET, in_fd, IMG_PIT_OFFSET, PIT_SIZE);
#endif
printf("Verifying xloader\n");
verify_data(out_fd, MMC_XLOADER_OFFSET, in_fd, type->offset, XLOADER_SIZE);
printf("Verifying sbl\n");
verify_data(out_fd, MMC_SBL_OFFSET, in_fd, IMG_SBL_OFFSET, -1);
printf("Syncing\n");
sync();
printf("Dropping caches\n");
drop_caches();
printf("Verifying xloader.img\n");
verify_data(out_fd, MMC_XLOADER_OFFSET, in_fd, type->offset, XLOADER_SIZE);
printf("Verifying sbl.img\n");
verify_data(out_fd, MMC_SBL_OFFSET, in_fd, IMG_SBL_OFFSET, -1);
printf("Done\n");
if (getpid() == 1) {
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, "bootloader");
while (1) { sleep(1); }
}
return 0;
}