// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #ifdef CONFIG_CMD_RSVMEM #if defined(P_AO_SEC_GP_CFG3) #define REG_RSVMEM_SIZE P_AO_SEC_GP_CFG3 #define REG_RSVMEM_BL32_START P_AO_SEC_GP_CFG4 #define REG_RSVMEM_BL31_START P_AO_SEC_GP_CFG5 #elif defined(SYSCTRL_SEC_STATUS_REG15) #define REG_RSVMEM_SIZE SYSCTRL_SEC_STATUS_REG15 #define REG_RSVMEM_BL32_START SYSCTRL_SEC_STATUS_REG16 #define REG_RSVMEM_BL31_START SYSCTRL_SEC_STATUS_REG17 #endif //#define RSVMEM_DEBUG_ENABLE #ifdef RSVMEM_DEBUG_ENABLE #define rsvmem_dbg(fmt...) printf("[rsvmem] "fmt) #else #define rsvmem_dbg(fmt...) #endif #define rsvmem_info(fmt...) printf("[rsvmem] "fmt) #define rsvmem_err(fmt...) printf("[rsvmem] "fmt) #ifndef DTB_BIND_KERNEL #define RSVMEM_NONE -1 #define RSVMEM_RESERVED 0 #define RSVMEM_CMA 1 #define BL31_SHARE_MEM_SIZE 0x100000 #ifndef BL32_SHARE_MEM_SIZE #define BL32_SHARE_MEM_SIZE 0x400000 #endif static int do_rsvmem_kernel_dts_check(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { int ret = 0; char *fdt_str = NULL; void *fdt = NULL; int node, subnode; int addr_cells, size_cells; u64 r_addr, r_size, alloc_size; const fdt32_t *prop; int len, i, conflict_count = 0; u64 addr, size; const char *status; char cmdbuf[128]; const char *name; int count, conflict_flag = 0; ulong fdt_addr; if (argc < 3) return CMD_RET_USAGE; addr = simple_strtoull(argv[1], NULL, 16); size = simple_strtoull(argv[2], NULL, 16); printf("\n[rsvmem] Check reserved-memory conflict for [0x%llx ~ 0x%llx]\n", addr, addr + size); fdt_str = env_get("fdtaddr"); if (!fdt_str) { printf("[rsvmem] Error! get fdtaddr NULL!\n"); return -1; } fdt_addr = simple_strtoul(fdt_str, NULL, 16); fdt = (void *)fdt_addr; sprintf(cmdbuf, "fdt addr %s;", fdt_str); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0) { printf("[rsvmem] Error! fdt addr error.\n"); return -2; } node = fdt_path_offset(fdt, "/reserved-memory"); if (node < 0) { printf("[rsvmem] No /reserved-memory node found.\n"); return CMD_RET_FAILURE; } addr_cells = fdt_address_cells(fdt, node); if (addr_cells < 0) addr_cells = fdt_address_cells(fdt, 0); size_cells = fdt_size_cells(fdt, node); if (size_cells < 0) size_cells = fdt_size_cells(fdt, 0); printf("-----------------------------------------------"); printf("----------------------------------------------\n"); printf("| %-28s | %-8s | %-25s | %-8s | %-8s |\n", "Node", "Type", "Mem Range", "Size(MB)", "Conflict"); printf("-----------------------------------------------"); printf("----------------------------------------------\n"); fdt_for_each_subnode(subnode, fdt, node) { name = fdt_get_name(fdt, subnode, NULL); conflict_flag = 0; // disabled status = fdt_getprop(fdt, subnode, "status", NULL); if (status && !strcmp(status, "disabled")) continue; // reg prop = fdt_getprop(fdt, subnode, "reg", &len); if (prop && len >= (addr_cells + size_cells) * sizeof(fdt32_t)) { count = len / ((addr_cells + size_cells) * sizeof(fdt32_t)); for (i = 0; i < count; i++) { r_addr = fdt_read_number(prop, addr_cells); prop += addr_cells; r_size = fdt_read_number(prop, size_cells); prop += size_cells; if (!r_size) break; if ((addr + size > r_addr) && (addr < r_addr + r_size)) { conflict_count++; conflict_flag = 1; } printf("| %-28s | %-8s | [0x%08llx ~ 0x%08llx] | %-8lld | %-8s |\n", name, "reg", r_addr, r_addr + r_size, r_size / 1024 / 1024, conflict_flag ? "Y" : "N"); } continue; } // alloc-ranges prop = fdt_getprop(fdt, subnode, "alloc-ranges", &len); if (prop && len >= (addr_cells + size_cells) * sizeof(fdt32_t)) { count = len / ((addr_cells + size_cells) * sizeof(fdt32_t)); for (i = 0; i < count; i++) { r_addr = fdt_read_number(prop, addr_cells); prop += addr_cells; r_size = fdt_read_number(prop, size_cells); prop += size_cells; if (!r_size) break; prop = fdt_getprop(fdt, subnode, "size", &len); if (!prop) break; alloc_size = fdt_read_number(prop, size_cells); if (!alloc_size) break; if ((addr + size > (r_addr + r_size - alloc_size)) && (addr < r_addr + r_size)) { conflict_count++; conflict_flag = 1; } printf("| %-28s | %-8s | [0x%08llx ~ 0x%08llx] | %-8lld | %-8s |\n", name, "alloc", r_addr, r_addr + r_size, alloc_size / 1024 / 1024, conflict_flag ? "Y" : "N"); } continue; } // cma-size prop = fdt_getprop(fdt, subnode, "size", &len); if (prop && len >= size_cells * sizeof(fdt32_t)) { count = len / (size_cells * sizeof(fdt32_t)); for (i = 0; i < count; i++) { r_size = fdt_read_number(prop, size_cells); if (!r_size) break; prop += size_cells; printf("| %-28s | %-8s | [ %-8s - 0x%08llx] | %-8lld | %-8s |\n", name, "CMApool", "ddr_end", r_size, r_size / 1024 / 1024, "-"); } continue; } } printf("-----------------------------------------------"); printf("----------------------------------------------\n"); if (conflict_count > 0) { printf("[rsvmem] Error! Found %d conflict(s) dts node.\n\n", conflict_count); return CMD_RET_FAILURE; } return CMD_RET_SUCCESS; } static int do_rsvmem_check(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { unsigned int data = 0; unsigned int bl31_rsvmem_size = 0; unsigned int bl32_rsvmem_size = 0; unsigned int bl31_rsvmem_start = 0; unsigned int bl32_rsvmem_start = 0; unsigned int alignment = 0; #ifndef CONFIG_RSVMEM_ALIGNMENT_1M unsigned int alignment_temp = 0; #endif unsigned int secure_monitor_size = 0; unsigned int secure_monitor_size_final = 0; unsigned int ramoops_start = 0; unsigned int ramoops_tmp = 0, ramoops_size = 0; int reg_flag = 0; char cmdbuf[128]; char *fdtaddr = NULL; int ret = 0; char *temp_env = NULL; int rsvmemtype = RSVMEM_NONE; unsigned int aarch32 = 0; rsvmem_dbg("reserved memory check!\n"); data = readl(REG_RSVMEM_SIZE); /* workaround for bl3x size */ if ((data >> 16) & 0xff) { bl31_rsvmem_size = ((data & 0xffff0000) >> 16) << 16; bl32_rsvmem_size = (data & 0x0000ffff) << 16; } else { bl31_rsvmem_size = ((data & 0xffff0000) >> 16) << 10; bl32_rsvmem_size = (data & 0x0000ffff) << 10; } bl31_rsvmem_start = readl(REG_RSVMEM_BL31_START); bl32_rsvmem_start = readl(REG_RSVMEM_BL32_START); fdtaddr = env_get("fdtaddr"); if (fdtaddr == NULL) { rsvmem_err("get fdtaddr NULL!\n"); return -1; } memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt addr %s;", fdtaddr); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0 ) { rsvmem_err("fdt addr error.\n"); return -2; } memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt get value temp_env / \\#address-cells;"); ret = run_command(cmdbuf, 0); if (ret != 0) { rsvmem_err("fdt get size #address-cells failed.\n"); return -2; } temp_env = env_get("temp_env"); //if (temp_env && !strcmp(temp_env, "0x01000000")) if (temp_env && !strcmp(temp_env, "0x00000001")) aarch32 = 1; #ifdef CONFIG_RSVMEM_ALIGNMENT_1M alignment = 0x100000; memset(cmdbuf, 0, sizeof(cmdbuf)); if (aarch32) sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon alignment <0x%x>;", alignment); else sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon alignment <0x0 0x%x>;", alignment); run_command(cmdbuf, 0); memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt get value temp_reusable /reserved-memory/linux,secmon reusable;"); ret = run_command(cmdbuf, 0); if (!ret) { memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt rm /reserved-memory/linux,secmon reusable"); run_command(cmdbuf, 0); } #else /* Get alignment size * If arm64, alignment has 2 parameters * The second parameter need convert big-endian to little-endian */ alignment = 0x400000; memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt get value temp_alignment /reserved-memory/linux,secmon alignment;"); ret = run_command(cmdbuf, 0); if (!ret) { temp_env = env_get("temp_alignment"); alignment_temp = simple_strtoul(temp_env, NULL, 16); if (aarch32) { alignment = alignment_temp; } else { alignment = (alignment_temp & 0xff) << 24; alignment |= (alignment_temp & 0xff00) << 8; alignment |= (alignment_temp & 0xff0000) >> 8; alignment |= (alignment_temp & 0xff000000) >> 24; } } #endif memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt get value env_compatible /reserved-memory/linux,secmon compatible;"); ret = run_command(cmdbuf, 0); if (ret != 0) { rsvmem_err("fdt get prop fail.\n"); return -2; } temp_env = env_get("env_compatible"); if (strcmp(temp_env, "shared-dma-pool") == 0) rsvmemtype = RSVMEM_CMA; else if (strcmp(temp_env, "amlogic, aml_secmon_memory") == 0) rsvmemtype = RSVMEM_RESERVED; else rsvmemtype = RSVMEM_NONE; if (rsvmemtype == RSVMEM_NONE) { rsvmem_err("env set fail.\n"); return -2; } run_command("setenv env_compatible;", 0); secure_monitor_size = ((bl31_rsvmem_size + alignment - 1) / alignment) * alignment; secure_monitor_size_final = bl31_rsvmem_size + bl32_rsvmem_size + alignment - 1; secure_monitor_size_final = (secure_monitor_size_final / alignment) * alignment; ramoops_start = bl31_rsvmem_start + bl31_rsvmem_size + bl32_rsvmem_size + alignment - 1; ramoops_start = (ramoops_start / alignment) * alignment; if ((bl31_rsvmem_size > 0) && (bl31_rsvmem_start > 0)) { if (rsvmemtype == RSVMEM_RESERVED) { memset(cmdbuf, 0, sizeof(cmdbuf)); if (aarch32) sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon reg <0x%x 0x%x>;", bl31_rsvmem_start, bl31_rsvmem_size); else sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon reg <0x0 0x%x 0x0 0x%x>;", bl31_rsvmem_start, bl31_rsvmem_size); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0 ) { rsvmem_err("bl31 reserved memory set addr error.\n"); return -3; } } if (rsvmemtype == RSVMEM_CMA) { /* Check parameter reg, add for linux 5.15 and before */ reg_flag = 0; memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt get value temp_rsv_reg /reserved-memory/linux,secmon reg;"); ret = run_command(cmdbuf, 0); if (!ret) { reg_flag = 1; if (aarch32) sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon reg <0x%x 0x%x>;", bl31_rsvmem_start, secure_monitor_size); else sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon reg <0x0 0x%x 0x0 0x%x>;", bl31_rsvmem_start, secure_monitor_size); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret) { rsvmem_err("bl31 reserved memory set reg error.\n"); return -3; } } memset(cmdbuf, 0, sizeof(cmdbuf)); if (aarch32) sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon size <0x%x>;", secure_monitor_size); else sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon size <0x0 0x%x>;", secure_monitor_size); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0 ) { rsvmem_err("bl31 reserved memory set size error.\n"); /* * If reg exist, to modify bl32, * need not return if modify size failed */ if (!reg_flag) return -3; } memset(cmdbuf, 0, sizeof(cmdbuf)); if (aarch32) sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon alloc-ranges <0x%x 0x%x>;", bl31_rsvmem_start, secure_monitor_size); else sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon alloc-ranges <0x0 0x%x 0x0 0x%x>;", bl31_rsvmem_start, secure_monitor_size); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0 ) { rsvmem_err("bl31 reserved memory set alloc-ranges error.\n"); /* * If reg exist, to modify bl32, * need not return if modify size failed */ if (!reg_flag) return -3; } memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt set /secmon reserve_mem_size <0x%x>;", bl31_rsvmem_size); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0 ) { rsvmem_err("bl31 reserved memory set reserve_mem_size error.\n"); return -3; } } } if ((bl32_rsvmem_size > 0) && (bl32_rsvmem_start > 0)) { if ((rsvmemtype == RSVMEM_RESERVED) || ((bl31_rsvmem_start + bl31_rsvmem_size != bl32_rsvmem_start) && (rsvmemtype == RSVMEM_CMA))) { memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt set /reserved-memory/linux,secos status okay;"); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0 ) { rsvmem_err("bl32 reserved memory set status error.\n"); return -3; } memset(cmdbuf, 0, sizeof(cmdbuf)); if (aarch32) sprintf(cmdbuf, "fdt set /reserved-memory/linux,secos reg <0x%x 0x%x>;", bl32_rsvmem_start, bl32_rsvmem_size); else sprintf(cmdbuf, "fdt set /reserved-memory/linux,secos reg <0x0 0x%x 0x0 0x%x>;", bl32_rsvmem_start, bl32_rsvmem_size); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0 ) { rsvmem_err("bl32 reserved memory set addr error.\n"); return -3; } } if ((bl31_rsvmem_start + bl31_rsvmem_size == bl32_rsvmem_start) && (rsvmemtype == RSVMEM_CMA)) { /* Modify parameter reg, add for linux 5.15 and before */ if (reg_flag) { if (aarch32) sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon reg <0x%x 0x%x>;", bl31_rsvmem_start, secure_monitor_size_final); else sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon reg <0x0 0x%x 0x0 0x%x>;", bl31_rsvmem_start, secure_monitor_size_final); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret) { rsvmem_err("bl32 reserved memory set reg error.\n"); return -3; } } memset(cmdbuf, 0, sizeof(cmdbuf)); if (aarch32) sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon size <0x%x>;", secure_monitor_size_final); else sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon size <0x0 0x%x>;", secure_monitor_size_final); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0 ) { rsvmem_err("bl32 reserved memory set size error.\n"); /* * If reg exist, to modify reserve_mem_size, * need not return if modify size failed */ if (!reg_flag) return -3; } memset(cmdbuf, 0, sizeof(cmdbuf)); if (aarch32) sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon alloc-ranges <0x%x 0x%x>;", bl31_rsvmem_start, secure_monitor_size_final); else sprintf(cmdbuf, "fdt set /reserved-memory/linux,secmon alloc-ranges <0x0 0x%x 0x0 0x%x>;", bl31_rsvmem_start, secure_monitor_size_final); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0 ) { rsvmem_err("bl32 reserved memory set alloc-ranges error.\n"); /* * If reg exist, to modify reserve_mem_size, * need not return if modify size failed */ if (!reg_flag) return -3; } memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt set /secmon reserve_mem_size <0x%x>;", bl31_rsvmem_size + bl32_rsvmem_size); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0 ) { rsvmem_err("bl32 reserved memory set reserve_mem_size error.\n"); return -3; } memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt get value ramoops_reg /reserved-memory/ramoops reg;"); if (run_command(cmdbuf, 0) == 0) { memset(cmdbuf, 0, sizeof(cmdbuf)); temp_env = env_get("ramoops_reg"); ramoops_tmp = simple_strtoul(temp_env + strlen(temp_env) / 2, NULL, 16); ramoops_size = (ramoops_tmp & 0xff) << 24; ramoops_size |= (ramoops_tmp & 0xff00) << 8; ramoops_size |= (ramoops_tmp & 0xff0000) >> 8; ramoops_size |= (ramoops_tmp & 0xff000000) >> 24; if (aarch32) sprintf(cmdbuf, "fdt set /reserved-memory/ramoops reg <0x%x 0x%x>;", ramoops_start, ramoops_size); else sprintf(cmdbuf, "fdt set /reserved-memory/ramoops reg <0x0 0x%x 0x0 0x%x>;", ramoops_start, ramoops_size); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0 ) { rsvmem_err("fdt set /reserved-memory/ramoops reg error.\n"); return -3; } } memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt get value secmon_clear_range /secmon clear_range;"); if (run_command(cmdbuf, 0) == 0) { memset(cmdbuf, 0, sizeof(cmdbuf)); sprintf(cmdbuf, "fdt set /secmon clear_range <0x%x 0x%x>;", bl31_rsvmem_start + BL31_SHARE_MEM_SIZE , bl31_rsvmem_size + bl32_rsvmem_size - BL31_SHARE_MEM_SIZE - BL32_SHARE_MEM_SIZE); rsvmem_dbg("CMD: %s\n", cmdbuf); ret = run_command(cmdbuf, 0); if (ret != 0 ) { rsvmem_err("bl32 reserved memory set clear_range error.\n"); return -3; } } } } return ret; } static int do_rsvmem_dump(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { unsigned int data = 0; unsigned int bl31_rsvmem_size = 0; unsigned int bl32_rsvmem_size = 0; unsigned int bl31_rsvmem_start = 0; unsigned int bl32_rsvmem_start = 0; rsvmem_info("reserved memory:\n"); data = readl(REG_RSVMEM_SIZE); /* workaround for bl3x size */ if ((data >> 16) & 0xff) { bl31_rsvmem_size = ((data & 0xffff0000) >> 16) << 16; bl32_rsvmem_size = (data & 0x0000ffff) << 16; } else { bl31_rsvmem_size = ((data & 0xffff0000) >> 16) << 10; bl32_rsvmem_size = (data & 0x0000ffff) << 10; } bl31_rsvmem_start = readl(REG_RSVMEM_BL31_START); bl32_rsvmem_start = readl(REG_RSVMEM_BL32_START); rsvmem_info("bl31 reserved memory start: 0x%08x\n", bl31_rsvmem_start); rsvmem_info("bl31 reserved memory size: 0x%08x\n", bl31_rsvmem_size); rsvmem_info("bl32 reserved memory start: 0x%08x\n", bl32_rsvmem_start); rsvmem_info("bl32 reserved memory size: 0x%08x\n", bl32_rsvmem_size); return 0; } static cmd_tbl_t cmd_rsvmem_sub[] = { U_BOOT_CMD_MKENT(check, 2, 0, do_rsvmem_check, "", ""), U_BOOT_CMD_MKENT(dump, 2, 0, do_rsvmem_dump, "", ""), U_BOOT_CMD_MKENT(kernel, 4, 0, do_rsvmem_kernel_dts_check, "", ""), }; #endif static int do_rsvmem(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { #ifdef DTB_BIND_KERNEL rsvmem_err("no check for rsvmem, should check int kernel\n"); return 0; #else cmd_tbl_t *c; /* Strip off leading 'rsvmem' command argument */ argc--; argv++; c = find_cmd_tbl(argv[0], &cmd_rsvmem_sub[0], ARRAY_SIZE(cmd_rsvmem_sub)); if (c) { int ret = c->cmd(cmdtp, flag, argc, argv); if (ret < 0) { /*only negative -1 is identifid as fail */ ret = -1; } return ret; } else { return CMD_RET_USAGE; } #endif } U_BOOT_CMD( rsvmem, 4, 0, do_rsvmem, "reserve memory", "rsvmem check - check reserved memory\n" "rsvmem dump - dump reserved memory\n" "rsvmem kernel - check if region conflicts (kernel dts)\n" ); #endif