From 040743f43f8b960be6df211e4786d4f548fd3a8e Mon Sep 17 00:00:00 2001 From: Vojtech Bocek Date: Sat, 8 Feb 2014 18:57:08 +0100 Subject: [PATCH] Refactor kexec loading, add support for standalone dtb.img --- Android.mk | 3 +- kexec.c | 114 ++++++++++++++++++++++++++++++++++++++++ kexec.h | 33 ++++++++++++ multirom.c | 152 +++++++++++++++++++++++++---------------------------- multirom.h | 5 +- 5 files changed, 224 insertions(+), 83 deletions(-) create mode 100644 kexec.c create mode 100644 kexec.h diff --git a/Android.mk b/Android.mk index a909f4e..afc128e 100644 --- a/Android.mk +++ b/Android.mk @@ -23,7 +23,8 @@ LOCAL_SRC_FILES:= \ fstab.c \ workers.c \ containers.c \ - rom_quirks.c + rom_quirks.c \ + kexec.c ifeq ($(ARCH_ARM_HAVE_NEON),true) LOCAL_SRC_FILES += col32cb16blend_neon.S diff --git a/kexec.c b/kexec.c new file mode 100644 index 0000000..8102a72 --- /dev/null +++ b/kexec.c @@ -0,0 +1,114 @@ +/* + * This file is part of MultiROM. + * + * MultiROM 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 3 of the License, or + * (at your option) any later version. + * + * MultiROM 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 MultiROM. If not, see . + */ + +#include +#include +#include + +#include "kexec.h" +#include "containers.h" +#include "log.h" +#include "util.h" + +// kexec --load-hardboot ./zImage --command-line="$(cat /proc/cmdline)" --mem-min=0xA0000000 --initrd=./rd.img +// --mem-min should be somewhere in System RAM (see /proc/iomem). Location just above kernel seems to work fine. +// It must not conflict with vmalloc ram. Vmalloc area seems to be allocated from top of System RAM. + +void kexec_init(struct kexec *k, const char *path) +{ + k->args = NULL; + kexec_add_arg(k, path); +} + +void kexec_destroy(struct kexec *k) +{ + list_clear(&k->args, &free); +} + +int kexec_load_exec(struct kexec *k) +{ + int i, len; + + INFO("Loading kexec:\n"); + for(i = 0; k->args && k->args[i]; ++i) + { + len = strlen(k->args[i]); + + if(len < 480) + INFO(" %s\n", k->args[i]); + else + { + char buff[481]; + char *itr; + const char *end = k->args[i]+len; + int chunk = 0; + + for(itr = k->args[i]; itr < end; itr += chunk) + { + chunk = imin(480, end - itr); + + memcpy(buff, itr, chunk); + buff[chunk] = 0; + + INFO(" %s\n", buff); + } + } + } + + if(run_cmd(k->args) == 0) + return 0; + else + { + ERROR("kexec call failed, re-running it to get info:\n"); + char *r = run_get_stdout(k->args); + if(!r) + ERROR("run_get_stdout returned NULL!\n"); + + char *p = strtok(r, "\n\r"); + while(p) + { + ERROR(" %s\n", p); + p = strtok(NULL, "\n\r"); + } + free(r); + + return -1; + } +} + +void kexec_add_arg(struct kexec *k, const char *arg) +{ + list_add(strdup(arg), &k->args); +} + +void kexec_add_arg_prefix(struct kexec *k, const char *prefix, const char *value) +{ + int len = strlen(prefix) + strlen(value) + 1; + char *arg = malloc(len); + snprintf(arg, len, "%s%s", prefix, value); + + list_add(arg, &k->args); +} + +void kexec_add_kernel(struct kexec *k, const char *path, int hardboot) +{ + if(hardboot) + kexec_add_arg(k, "--load-hardboot"); + else + kexec_add_arg(k, "-l"); + kexec_add_arg(k, path); +} diff --git a/kexec.h b/kexec.h new file mode 100644 index 0000000..f1f8877 --- /dev/null +++ b/kexec.h @@ -0,0 +1,33 @@ +/* + * This file is part of MultiROM. + * + * MultiROM 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 3 of the License, or + * (at your option) any later version. + * + * MultiROM 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 MultiROM. If not, see . + */ + +#ifndef KEXEC_H +#define KEXEC_H + +struct kexec +{ + char **args; +}; + +void kexec_init(struct kexec *k, const char *path); +void kexec_destroy(struct kexec *k); +int kexec_load_exec(struct kexec *k); +void kexec_add_arg(struct kexec *k, const char *arg); +void kexec_add_arg_prefix(struct kexec *k, const char *prefix, const char *value); +void kexec_add_kernel(struct kexec *k, const char *path, int hardboot); + +#endif diff --git a/multirom.c b/multirom.c index 731608f..dc0d6ae 100644 --- a/multirom.c +++ b/multirom.c @@ -47,6 +47,7 @@ #include "hooks.h" #include "containers.h" #include "rom_quirks.h" +#include "kexec.h" #define REALDATA "/realdata" #define BUSYBOX_BIN "busybox" @@ -1485,6 +1486,10 @@ int multirom_find_file(char *res, const char *name_part, const char *path) int multirom_load_kexec(struct multirom_status *s, struct multirom_rom *rom) { + int res = -1; + struct kexec kexec; + int loop_mounted = 0; + // to find /data partition if(!rom->partition && multirom_update_partitions(s) < 0) { @@ -1492,35 +1497,20 @@ int multirom_load_kexec(struct multirom_status *s, struct multirom_rom *rom) return -1; } - int res = -1; - // kexec --load-hardboot ./zImage --command-line="$(cat /proc/cmdline)" --mem-min=0xA0000000 --initrd=./rd.img - // --mem-min should be somewhere in System RAM (see /proc/iomem). Location just above kernel seems to work fine. - // It must not conflict with vmalloc ram. Vmalloc area seems to be allocated from top of System RAM. - char *cmd[] = { - kexec_path, // 0 - "--load-hardboot", // 1 - malloc(1024), // 2 - path to zImage - "--mem-min="MR_KEXEC_MEM_MIN, // 3 - malloc(1024), // 4 - --initrd= - malloc(2048), // 5 - --command-line= -#ifdef MR_KEXEC_DTB - "--dtb", // 6 -#endif - NULL - }; + kexec_init(&kexec, kexec_path); + kexec_add_arg(&kexec, "--mem-min="MR_KEXEC_MEM_MIN); - int loop_mounted = 0; switch(rom->type) { case ROM_ANDROID_INTERNAL: case ROM_ANDROID_USB_DIR: case ROM_ANDROID_USB_IMG: - if(multirom_fill_kexec_android(s, rom, cmd) != 0) + if(multirom_fill_kexec_android(s, rom, &kexec) != 0) goto exit; break; case ROM_LINUX_INTERNAL: case ROM_LINUX_USB: - loop_mounted = multirom_fill_kexec_linux(s, rom, cmd); + loop_mounted = multirom_fill_kexec_linux(s, rom, &kexec); if(loop_mounted < 0) goto exit; break; @@ -1529,36 +1519,7 @@ int multirom_load_kexec(struct multirom_status *s, struct multirom_rom *rom) goto exit; } - ERROR("Loading kexec: %s %s %s %s %s\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]); - ERROR("With cmdline: "); - char *itr = cmd[5]; - int len; - for(len = strlen(itr); len > 0; len = strlen(itr)) - { - if(len > 450) - len = 450; - char *b = strndup(itr, len); - ERROR(" %s\n", b); - free(b); - itr += len; - } - - if(run_cmd(cmd) == 0) - res = 0; - else - { - ERROR("kexec call failed, re-running it to get info:\n"); - char *r = run_get_stdout(cmd); - if(!r) - ERROR("run_get_stdout returned NULL!\n"); - char *p = strtok(r, "\n\r"); - while(p) - { - ERROR(" %s\n", p); - p = strtok(NULL, "\n\r"); - } - free(r); - } + res = kexec_load_exec(&kexec); char *cmd_cp[] = { busybox_path, "cp", kexec_path, "/kexec", NULL }; run_cmd(cmd_cp); @@ -1570,13 +1531,11 @@ int multirom_load_kexec(struct multirom_status *s, struct multirom_rom *rom) multirom_copy_log(NULL); exit: - free(cmd[2]); - free(cmd[4]); - free(cmd[5]); + kexec_destroy(&kexec); return res; } -int multirom_fill_kexec_android(struct multirom_status *s, struct multirom_rom *rom, char **cmd) +int multirom_fill_kexec_android(struct multirom_status *s, struct multirom_rom *rom, struct kexec *kexec) { int res = -1; char img_path[256]; @@ -1591,9 +1550,20 @@ int multirom_fill_kexec_android(struct multirom_status *s, struct multirom_rom * if(libbootimg_dump_kernel(&img, "/zImage") < 0) goto exit; + if(libbootimg_dump_ramdisk(&img, "/initrd.img") < 0) goto exit; + kexec_add_kernel(kexec, "/zImage", 1); + kexec_add_arg(kexec, "--initrd=/initrd.img"); + +#ifdef MR_KEXEC_DTB + if(libbootimg_dump_dtb(&img, "/dtb.img") >= 0) + kexec_add_arg(kexec, "--dtb=/dtb.img"); + else + kexec_add_arg(kexec, "--dtb"); +#endif + // Trampolines in ROM boot images may get out of sync, so we need to check it and // update if needed. I can't do that during ZIP installation because of USB drives. // That header.name is added by recovery. @@ -1624,35 +1594,32 @@ int multirom_fill_kexec_android(struct multirom_status *s, struct multirom_rom * } } - char cmdline[1024]; - if(multirom_get_bootloader_cmdline(s, cmdline, sizeof(cmdline)) == -1) - { - ERROR("Failed to get cmdline\n"); - goto exit; - } + char cmdline[1536]; + strcpy(cmdline, "--command-line="); - strcpy(cmd[2], "/zImage"); - strcpy(cmd[4], "--initrd=/initrd.img"); - - strcpy(cmd[5], "--command-line="); if(img.hdr.cmdline[0] != 0) { img.hdr.cmdline[BOOT_ARGS_SIZE-1] = 0; // see multirom_get_bootloader_cmdline #ifdef FLO_CMDLINE_HACK - strcat(cmd[5], (char*)img.hdr.cmdline+26); + strcat(cmdline, (char*)img.hdr.cmdline+26); #else - strcat(cmd[5], (char*)img.hdr.cmdline); + strcat(cmdline, (char*)img.hdr.cmdline); #endif - strcat(cmd[5], " "); + strcat(cmdline, " "); } - if(cmdline[0] != 0) + + if(multirom_get_bootloader_cmdline(s, cmdline+strlen(cmdline), sizeof(cmdline)-strlen(cmdline)-1) == -1) { - strcat(cmd[5], cmdline); - strcat(cmd[5], " "); + ERROR("Failed to get cmdline\n"); + goto exit; } - strcat(cmd[5], "mrom_kexecd=1"); + + if(sizeof(cmdline)-strlen(cmdline)-1 >= sizeof("mrom_kexecd=1")) + strcat(cmdline, "mrom_kexecd=1"); + + kexec_add_arg(kexec, cmdline); res = 0; exit: @@ -1693,7 +1660,7 @@ static char *find_boot_file(char *path, char *root_path, char *base_path) return strdup(res); } -int multirom_fill_kexec_linux(struct multirom_status *s, struct multirom_rom *rom, char **cmd) +int multirom_fill_kexec_linux(struct multirom_status *s, struct multirom_rom *rom, struct kexec *kexec) { struct rom_info *info = multirom_parse_rom_info(s, rom); if(!info) @@ -1754,24 +1721,49 @@ int multirom_fill_kexec_linux(struct multirom_status *s, struct multirom_rom *ro } char *str = find_boot_file(map_get_val(info->str_vals, "kernel_path"), root_path, rom->base_path); - if(!str) + if(str) + { + kexec_add_kernel(kexec, str, 1); + free(str); + } + else + { + // kernel is required goto exit; - - cmd[2] = str; + } str = find_boot_file(map_get_val(info->str_vals, "initrd_path"), root_path, rom->base_path); if(str) { - sprintf(cmd[4], "--initrd=%s", str); + kexec_add_arg_prefix(kexec, "--initrd=", str); free(str); } - sprintf(cmd[5], "--command-line=%s ", (char*)map_get_val(info->str_vals, "base_cmdline")); + char cmdline[1536]; + snprintf(cmdline, sizeof(cmdline), "--command-line=%s ", (char*)map_get_val(info->str_vals, "base_cmdline")); - if(root_type == 0 && (str = map_get_val(info->str_vals, "dir_cmdline"))) - strcat(cmd[5], str); - else if(root_type == 1 && (str = map_get_val(info->str_vals, "img_cmdline"))) - strcat(cmd[5], str); + str = NULL; + if(root_type == 0) + str = map_get_val(info->str_vals, "dir_cmdline"); + else if(root_type == 1) + str = map_get_val(info->str_vals, "img_cmdline"); + + if(str) + { + if(strlen(str)+strlen(cmdline)+1 <= sizeof(cmdline)) + strcat(cmdline, str); + else + { + ERROR("failed to fill kexec info, cmdline is too long!\n"); + goto exit; + } + } + + kexec_add_arg(kexec, cmdline); + +#ifdef MR_KEXEC_DTB + kexec_add_arg(kexec, "--dtb"); +#endif res = loop_mounted; exit: diff --git a/multirom.h b/multirom.h index a5253cb..37f8a24 100644 --- a/multirom.h +++ b/multirom.h @@ -23,6 +23,7 @@ #include "fstab.h" #include "containers.h" +#include "kexec.h" enum { @@ -139,8 +140,8 @@ int multirom_has_kexec(void); int multirom_load_kexec(struct multirom_status *s, struct multirom_rom *rom); int multirom_get_bootloader_cmdline(struct multirom_status *s, char *str, size_t size); int multirom_find_file(char *res, const char *name_part, const char *path); -int multirom_fill_kexec_linux(struct multirom_status *s, struct multirom_rom *rom, char **cmd); -int multirom_fill_kexec_android(struct multirom_status *s, struct multirom_rom *rom, char **cmd); +int multirom_fill_kexec_linux(struct multirom_status *s, struct multirom_rom *rom, struct kexec *kexec); +int multirom_fill_kexec_android(struct multirom_status *s, struct multirom_rom *rom, struct kexec *kexec); int multirom_extract_bytes(const char *dst, FILE *src, size_t size); int multirom_update_partitions(struct multirom_status *s); void multirom_destroy_partition(void *part);