Refactor kexec loading, add support for standalone dtb.img

This commit is contained in:
Vojtech Bocek
2014-02-08 18:57:08 +01:00
parent 99facd0d47
commit 040743f43f
5 changed files with 224 additions and 83 deletions

View File

@@ -23,7 +23,8 @@ LOCAL_SRC_FILES:= \
fstab.c \ fstab.c \
workers.c \ workers.c \
containers.c \ containers.c \
rom_quirks.c rom_quirks.c \
kexec.c
ifeq ($(ARCH_ARM_HAVE_NEON),true) ifeq ($(ARCH_ARM_HAVE_NEON),true)
LOCAL_SRC_FILES += col32cb16blend_neon.S LOCAL_SRC_FILES += col32cb16blend_neon.S

114
kexec.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#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);
}

33
kexec.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@@ -47,6 +47,7 @@
#include "hooks.h" #include "hooks.h"
#include "containers.h" #include "containers.h"
#include "rom_quirks.h" #include "rom_quirks.h"
#include "kexec.h"
#define REALDATA "/realdata" #define REALDATA "/realdata"
#define BUSYBOX_BIN "busybox" #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 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 // to find /data partition
if(!rom->partition && multirom_update_partitions(s) < 0) 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; return -1;
} }
int res = -1; kexec_init(&kexec, kexec_path);
// kexec --load-hardboot ./zImage --command-line="$(cat /proc/cmdline)" --mem-min=0xA0000000 --initrd=./rd.img kexec_add_arg(&kexec, "--mem-min="MR_KEXEC_MEM_MIN);
// --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=<path to initrd>
malloc(2048), // 5 - --command-line=<cmdline>
#ifdef MR_KEXEC_DTB
"--dtb", // 6
#endif
NULL
};
int loop_mounted = 0;
switch(rom->type) switch(rom->type)
{ {
case ROM_ANDROID_INTERNAL: case ROM_ANDROID_INTERNAL:
case ROM_ANDROID_USB_DIR: case ROM_ANDROID_USB_DIR:
case ROM_ANDROID_USB_IMG: 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; goto exit;
break; break;
case ROM_LINUX_INTERNAL: case ROM_LINUX_INTERNAL:
case ROM_LINUX_USB: 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) if(loop_mounted < 0)
goto exit; goto exit;
break; break;
@@ -1529,36 +1519,7 @@ int multirom_load_kexec(struct multirom_status *s, struct multirom_rom *rom)
goto exit; goto exit;
} }
ERROR("Loading kexec: %s %s %s %s %s\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]); res = kexec_load_exec(&kexec);
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);
}
char *cmd_cp[] = { busybox_path, "cp", kexec_path, "/kexec", NULL }; char *cmd_cp[] = { busybox_path, "cp", kexec_path, "/kexec", NULL };
run_cmd(cmd_cp); run_cmd(cmd_cp);
@@ -1570,13 +1531,11 @@ int multirom_load_kexec(struct multirom_status *s, struct multirom_rom *rom)
multirom_copy_log(NULL); multirom_copy_log(NULL);
exit: exit:
free(cmd[2]); kexec_destroy(&kexec);
free(cmd[4]);
free(cmd[5]);
return res; 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; int res = -1;
char img_path[256]; 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) if(libbootimg_dump_kernel(&img, "/zImage") < 0)
goto exit; goto exit;
if(libbootimg_dump_ramdisk(&img, "/initrd.img") < 0) if(libbootimg_dump_ramdisk(&img, "/initrd.img") < 0)
goto exit; 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 // 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. // update if needed. I can't do that during ZIP installation because of USB drives.
// That header.name is added by recovery. // 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]; char cmdline[1536];
if(multirom_get_bootloader_cmdline(s, cmdline, sizeof(cmdline)) == -1) strcpy(cmdline, "--command-line=");
{
ERROR("Failed to get cmdline\n");
goto exit;
}
strcpy(cmd[2], "/zImage");
strcpy(cmd[4], "--initrd=/initrd.img");
strcpy(cmd[5], "--command-line=");
if(img.hdr.cmdline[0] != 0) if(img.hdr.cmdline[0] != 0)
{ {
img.hdr.cmdline[BOOT_ARGS_SIZE-1] = 0; img.hdr.cmdline[BOOT_ARGS_SIZE-1] = 0;
// see multirom_get_bootloader_cmdline // see multirom_get_bootloader_cmdline
#ifdef FLO_CMDLINE_HACK #ifdef FLO_CMDLINE_HACK
strcat(cmd[5], (char*)img.hdr.cmdline+26); strcat(cmdline, (char*)img.hdr.cmdline+26);
#else #else
strcat(cmd[5], (char*)img.hdr.cmdline); strcat(cmdline, (char*)img.hdr.cmdline);
#endif #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); ERROR("Failed to get cmdline\n");
strcat(cmd[5], " "); 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; res = 0;
exit: exit:
@@ -1693,7 +1660,7 @@ static char *find_boot_file(char *path, char *root_path, char *base_path)
return strdup(res); 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); struct rom_info *info = multirom_parse_rom_info(s, rom);
if(!info) 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); 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; goto exit;
}
cmd[2] = str;
str = find_boot_file(map_get_val(info->str_vals, "initrd_path"), root_path, rom->base_path); str = find_boot_file(map_get_val(info->str_vals, "initrd_path"), root_path, rom->base_path);
if(str) if(str)
{ {
sprintf(cmd[4], "--initrd=%s", str); kexec_add_arg_prefix(kexec, "--initrd=", str);
free(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"))) str = NULL;
strcat(cmd[5], str); if(root_type == 0)
else if(root_type == 1 && (str = map_get_val(info->str_vals, "img_cmdline"))) 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");
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; res = loop_mounted;
exit: exit:

View File

@@ -23,6 +23,7 @@
#include "fstab.h" #include "fstab.h"
#include "containers.h" #include "containers.h"
#include "kexec.h"
enum enum
{ {
@@ -139,8 +140,8 @@ int multirom_has_kexec(void);
int multirom_load_kexec(struct multirom_status *s, struct multirom_rom *rom); 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_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_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_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, char **cmd); 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_extract_bytes(const char *dst, FILE *src, size_t size);
int multirom_update_partitions(struct multirom_status *s); int multirom_update_partitions(struct multirom_status *s);
void multirom_destroy_partition(void *part); void multirom_destroy_partition(void *part);