/* * 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; }