blob: 57e8d4ae67e1c9feabf26c65682634b3971b2ceb [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <common.h>
#include <malloc.h>
#include <linux/err.h>
#include <partition_table.h>
#include <linux/libfdt.h>
#include <asm/arch/bl31_apis.h>
#include <amlogic/aml_efuse.h>
#include <part_efi.h>
#include <blk.h>
#include <compiler.h>
#include <mmc.h>
#include <emmc_partitions.h>
#ifdef CONFIG_MULTI_DTB
extern unsigned long get_multi_dt_entry(unsigned long fdt_addr);
#endif
#define GPT_SPACE 0X2000
struct partitions_data{
int nr;
struct partitions *parts;
};
struct partitions *part_table = NULL;
int parts_total_num;
int has_boot_slot = 0;
int has_system_slot = 0;
bool dynamic_partition = false;
bool vendor_boot_partition = false;
bool is_partition_checked = false;
#if CONFIG_IS_ENABLED(EFI_PARTITION)
bool gpt_partition;
#endif
int get_partitions_table(struct partitions **table)
{
int ret = 0;
if (part_table && parts_total_num) {
*table = part_table;
ret = parts_total_num;
}
return ret;
}
int get_partition_count(void)
{
return parts_total_num;
}
struct partitions *get_partitions(void)
{
return part_table;
}
void free_partitions(void)
{
if (part_table)
free(part_table);
part_table = NULL;
}
#ifndef IS_FEAT_BOOT_VERIFY
#define IS_FEAT_BOOT_VERIFY() 0
#endif// #ifndef IS_FEAT_BOOT_VERIFY
/*
return 0 if dts is valid
other value are falure.
*/
int check_valid_dts(unsigned char *buffer)
{
int ret = -__LINE__;
char *dt_addr;
/* fixme, a work around way */
unsigned char *sbuffer = (unsigned char *)env_get_hex("loadaddr", 0x1000000 + 0x100000);
/* g12a merge to trunk, use trunk code */
//unsigned char *sbuffer = (unsigned char *)0x1000000;
if (IS_FEAT_BOOT_VERIFY()) {
memcpy(sbuffer, buffer, AML_DTB_IMG_MAX_SZ);
flush_cache((unsigned long)sbuffer, AML_DTB_IMG_MAX_SZ);
ret = aml_sec_boot_check(AML_D_P_IMG_DECRYPT, (long unsigned)sbuffer, AML_DTB_IMG_MAX_SZ, 0);
if (ret) {
printf("\n %s() %d: Decrypt dtb: Sig Check %d\n", __func__, __LINE__, ret);
return -__LINE__;
}
memcpy(buffer, sbuffer, AML_DTB_IMG_MAX_SZ);
}
#ifdef CONFIG_MULTI_DTB
dt_addr = (char *)get_multi_dt_entry((unsigned long)buffer);
#else
dt_addr = (char *)buffer;
#endif
pr_debug("start dts,buffer=%p,dt_addr=%p\n", buffer, dt_addr);
ret = fdt_check_header(dt_addr);
if ( ret < 0 )
printf("%s: %s\n",__func__,fdt_strerror(ret));
/* fixme, is it 0 when ok? */
return ret;
}
#if CONFIG_IS_ENABLED(EFI_PARTITION)
int parse_gpt(struct blk_desc *dev_desc, void *buf)
{
gpt_header *gpt_h;
gpt_entry *gpt_e;
size_t efiname_len, dosname_len;
int parts_num = 0;
int i, k;
/* determine start of GPT Header in the buffer */
gpt_h = buf + (GPT_PRIMARY_PARTITION_TABLE_LBA *
dev_desc->blksz);
/* determine start of GPT Entries in the buffer */
gpt_e = buf + (le64_to_cpu(gpt_h->partition_entry_lba) *
dev_desc->blksz);
parts_num = le32_to_cpu(gpt_h->num_partition_entries);
if (parts_num > 0) {
part_table = (struct partitions *)
malloc(sizeof(struct partitions) * parts_num);
if (!part_table) {
printf("%s part_table alloc _err\n", __func__);
return -1;
}
memset(part_table, 0, sizeof(struct partitions) * parts_num);
parts_total_num = parts_num;
}
dynamic_partition = false;
env_set("partition_mode", "normal");
vendor_boot_partition = false;
env_set("vendor_boot_mode", "false");
for (i = 0; i < parts_num; i++) {
if (!is_pte_valid(&gpt_e[i])) {
free(part_table);
return -1;
}
part_table[i].offset = le64_to_cpu(gpt_e[i].starting_lba << 9ULL);
part_table[i].size = ((le64_to_cpu(gpt_e[i].ending_lba) + 1) -
le64_to_cpu(gpt_e[i].starting_lba)) << 9ULL;
/* mask flag */
part_table[i].mask_flags =
(uint32_t)le64_to_cpu(gpt_e[i].attributes.fields.type_guid_specific);
/* partition name */
efiname_len = sizeof(gpt_e[i].partition_name) / sizeof(efi_char16_t);
dosname_len = sizeof(part_table[i].name);
memset(part_table[i].name, 0, sizeof(part_table[i].name));
for (k = 0; k < min(dosname_len, efiname_len); k++)
part_table[i].name[k] = (char)gpt_e[i].partition_name[k];
if (strcmp(part_table[i].name, "boot_a") == 0) {
has_boot_slot = 1;
printf("set has_boot_slot = 1\n");
} else if (strcmp(part_table[i].name, "boot") == 0) {
has_boot_slot = 0;
printf("set has_boot_slot = 0\n");
}
if (strcmp(part_table[i].name, "system_a") == 0)
has_system_slot = 1;
else if (strcmp(part_table[i].name, "system") == 0)
has_system_slot = 0;
if (strcmp(part_table[i].name, "super") == 0) {
dynamic_partition = true;
env_set("partition_mode", "dynamic");
printf("enable dynamic_partition\n");
}
if (strncmp(part_table[i].name, "vendor_boot", 11) == 0) {
vendor_boot_partition = true;
env_set("vendor_boot_mode", "true");
printf("enable vendor_boot\n");
}
}
is_partition_checked = false;
return 0;
}
#endif
int get_partition_from_dts(unsigned char *buffer)
{
char *dt_addr;
int nodeoffset,poffset=0;
int *parts_num;
char propname[8];
const uint32_t *phandle;
const char *uname;
const char *usize;
const char *umask;
int index;
int ret = -1;
#if CONFIG_IS_ENABLED(EFI_PARTITION)
struct blk_desc *dev_desc;
if (!buffer)
goto _err;
dev_desc = (struct blk_desc *)malloc(sizeof(struct blk_desc));
if (!dev_desc)
goto _err;
dev_desc->blksz = MMC_BLOCK_SIZE;
dev_desc->lba = GPT_SPACE;
if (!is_valid_gpt_buf(dev_desc, buffer)) {
if (!parse_gpt(dev_desc, buffer)) {
gpt_partition = true;
free(dev_desc);
return 0;
}
}
free(dev_desc);
#endif
ret = check_valid_dts(buffer);
printf("%s() %d: ret %d\n",__func__, __LINE__, ret);
if ( ret < 0 )
{
printf("%s() %d: ret %d\n",__func__, __LINE__, ret);
goto _err;
}
#ifdef CONFIG_MULTI_DTB
dt_addr = (char *)get_multi_dt_entry((unsigned long)buffer);
#else
dt_addr = (char *)buffer;
#endif
nodeoffset = fdt_path_offset(dt_addr, "/partitions");
if (nodeoffset < 0)
{
printf("%s: not find /partitions node %s.\n",__func__,fdt_strerror(nodeoffset));
ret = -1;
goto _err;
}
parts_num = (int *)fdt_getprop(dt_addr, nodeoffset, "parts", NULL);
printf("parts: %d\n",be32_to_cpup((u32*)parts_num));
if (parts_num > 0)
{
part_table = (struct partitions *)malloc(sizeof(struct partitions)*(be32_to_cpup((u32*)parts_num)));
if (!part_table) {
printf("%s part_table alloc _err\n", __func__);
return -1;
}
memset(part_table, 0, sizeof(struct partitions)*(be32_to_cpup((u32*)parts_num)));
parts_total_num = be32_to_cpup((u32*)parts_num);
}
dynamic_partition = false;
env_set("partiton_mode","normal");
vendor_boot_partition = false;
env_set("vendor_boot_mode","false");
for (index = 0; index < be32_to_cpup((u32*)parts_num); index++)
{
sprintf(propname,"part-%d", index);
phandle = fdt_getprop(dt_addr, nodeoffset, propname, NULL);
if (!phandle) {
printf("don't find match part-%d\n",index);
goto _err;
}
if (phandle) {
poffset = fdt_node_offset_by_phandle(dt_addr, be32_to_cpup((u32*)phandle));
if (!poffset) {
printf("%s:%d,can't find device node\n",__func__,__LINE__);
goto _err;
}
}
uname = fdt_getprop(dt_addr, poffset, "pname", NULL);
//printf("%s:%d uname: %s\n",__func__,__LINE__, uname);
/* a string but not */
usize = fdt_getprop(dt_addr, poffset, "size", NULL);
//printf("%s:%d size: 0x%x 0x%x\n",__func__,__LINE__, be32_to_cpup((u32*)usize), be32_to_cpup((((u32*)usize)+1)));
umask = fdt_getprop(dt_addr, poffset, "mask", NULL);
//printf("%s:%d mask: 0x%x\n",__func__,__LINE__, be32_to_cpup((u32*)umask));
/* fill parition table */
if (uname != NULL)
memcpy(part_table[index].name, uname, strlen(uname));
part_table[index].size = ((unsigned long)be32_to_cpup((u32*)usize) << 32) | (unsigned long)be32_to_cpup((((u32*)usize)+1));
part_table[index].mask_flags = be32_to_cpup((u32*)umask);
printf("%02d:%10s\t%016llx %01x\n", index, uname, part_table[index].size, part_table[index].mask_flags);
if (strcmp(uname, "boot_a") == 0) {
has_boot_slot = 1;
printf("set has_boot_slot = 1\n");
}
else if (strcmp(uname, "boot") == 0) {
has_boot_slot = 0;
printf("set has_boot_slot = 0\n");
}
if (strcmp(uname, "system_a") == 0)
has_system_slot = 1;
else if (strcmp(uname, "system") == 0)
has_system_slot = 0;
if (strcmp(uname, "super") == 0) {
dynamic_partition = true;
env_set("partiton_mode","dynamic");
printf("enable dynamic_partition\n");
}
if (strncmp(uname, "vendor_boot", 11) == 0) {
vendor_boot_partition = true;
env_set("vendor_boot_mode","true");
printf("enable vendor_boot\n");
}
}
/*enable mmc_device_init when dtb is update from ddr*/
is_partition_checked = false;
return 0;
_err:
if (part_table != NULL) {
free(part_table);
part_table = NULL;
}
return ret;
}