blob: 7a6b2b04f3d1d0a555b5dd9420cd2fa8503b31bf [file] [log] [blame]
/*
* Copyright (c) 2019 MediaTek Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <debug.h>
#include <lib/android_dtbo.h>
#include <lib/mempool.h>
#include <libfdt.h>
#include <platform.h>
#include <trace.h>
#include "blxboot_plat.h"
#include "imageinfo.h"
#define LOCAL_TRACE 0
struct bootargs_overlay {
int noffset;
int len;
const void *path;
const char *prop;
};
int extract_fdt(void *fdt, int size)
{
int ret = 0;
ret = fdt_open_into(fdt, fdt, size);
if (ret) {
dprintf(CRITICAL, "open fdt failed\n");
return ret;
}
ret = fdt_check_header(fdt);
if (ret) {
dprintf(CRITICAL, "check fdt failed\n");
return ret;
}
return ret;
}
static int dtbo_overlay(void *fdt_dtb, void *dtbo_entry)
{
struct bootargs_overlay bootargs;
int noffset;
if (fdt_check_header(dtbo_entry)) {
LTRACEF("%s failed.\n", "dtbo check header");
return -1;
}
do {
noffset = fdt_path_offset(dtbo_entry, "/__symbols__");
if (noffset < 0)
break;
bootargs.path = fdt_getprop(dtbo_entry, noffset, "chosen", NULL);
if (bootargs.path == NULL)
break;
bootargs.noffset = fdt_path_offset(dtbo_entry, bootargs.path);
if (bootargs.noffset < 0)
break;
bootargs.prop = fdt_getprop(dtbo_entry, bootargs.noffset,
"bootargs_ext", &bootargs.len);
if (bootargs.prop == NULL)
break;
plat_fixup_append((char *)bootargs.prop);
} while (0);
if (fdt_overlay_apply(fdt_dtb, dtbo_entry)) {
LTRACEF("%s failed.\n", "fdt_overlay_apply");
return -1;
}
return 0;
}
static int android_dtbo_overlay(void *fdt_dtb, void *dtbo, bool update_cmd)
{
/* assume the maxium dtbo entries in dtbo.img */
#define MAX_DTBO_ENTRIES_COUNT 100
static const char android_dtbo_param[] = "androidboot.dtbo_idx=";
uint32_t i;
uint32_t dtbo_entry_count;
uint32_t dtbo_version;
char *dtbo_idx_str;
char *dtbo_idx_str_end;
void *dtbo_entry;
struct dt_table_entry *tbl_entry;
if (android_dtbo_check_header(dtbo) != DTBO_RET_OK) {
LTRACEF("%s failed.\n", "android dtbo check header");
return -1;
}
dtbo_entry_count = android_dtbo_dt_entry_count(dtbo);
if (dtbo_entry_count >= MAX_DTBO_ENTRIES_COUNT) {
LTRACEF("Too many dtbo entries.\n");
return -1;
}
dtbo_idx_str_end = NULL;
if (update_cmd) {
dtbo_idx_str = mempool_alloc((dtbo_entry_count * 3) +
strlen(android_dtbo_param) + 1, MEMPOOL_ANY);
if (dtbo_idx_str == NULL) {
LTRACEF("mempool_alloc for dtboidx_str failed.\n");
return -1;
}
dtbo_idx_str_end = dtbo_idx_str;
sprintf(dtbo_idx_str_end, "%s", android_dtbo_param);
dtbo_idx_str_end += strlen(android_dtbo_param);
}
dtbo_version = android_dtbo_version(dtbo);
for (i = 0; i < dtbo_entry_count; i++) {
if (android_dtbo_get_dt_table_entry(dtbo, i, &tbl_entry) != DTBO_RET_OK)
break;
if (plat_compare_dtbo_hwinfo(dtbo_version, tbl_entry) != 0)
continue;
dtbo_entry = android_dtbo_get_dt_dtbo_entry(dtbo, i);
if (dtbo_entry == NULL)
break;
if (dtbo_overlay(fdt_dtb, dtbo_entry)) {
LTRACEF("fdt_overlay_apply failed: index=%u\n", i);
continue;
}
if (update_cmd) {
sprintf(dtbo_idx_str_end, "%d,", i);
dtbo_idx_str_end += i < 10 ? 2 : 3;
}
}
if (update_cmd) {
if (dtbo_idx_str_end != (dtbo_idx_str + strlen(android_dtbo_param))) {
*(dtbo_idx_str + strlen(dtbo_idx_str) - 1) = '\0';
plat_fixup_append(dtbo_idx_str);
}
mempool_free(dtbo_idx_str);
}
return 0;
}
int overlay_fdt(void *fdt_dtb, void *dtbo, void *vpd)
{
/* [TODO] clarify: can we remove ERR_ADDR, just to check it against NULL */
if (fdt_dtb == (void *)ERR_ADDR) {
LTRACEF("no valid dtb.\n");
return -1;
}
if (extract_fdt(fdt_dtb, MAX_DTB_SIZE)) {
LTRACEF("%s failed.\n", "extract_fdt");
return -1;
}
if (vpd && (android_dtbo_overlay(fdt_dtb, vpd, false) != 0) &&
(dtbo_overlay(fdt_dtb, vpd) != 0))
return -1;
if (dtbo && (android_dtbo_overlay(fdt_dtb, dtbo, true) != 0) &&
(dtbo_overlay(fdt_dtb, dtbo) != 0))
return -1;
if (fdt_pack(fdt_dtb))
return -1;
return 0;
}