@@ -15,6 +15,7 @@ config SYS_CONFIG_NAME
config CMD_BCM_EXT_UTILS
bool "Enable Broadcom-specific u-boot commands"
default y
+ select CMD_BCM_LOGSETUP
config CHIMP_OPTEE
bool "Enable secure ChiMP firmware loading"
@@ -2200,4 +2200,6 @@ config CMD_UBIFS
help
UBIFS is a file system for flash devices which works on top of UBI.
+source cmd/bcm/Kconfig
+
endmenu
@@ -190,6 +190,8 @@ obj-$(CONFIG_$(SPL_)CMD_TLV_EEPROM) += tlv_eeprom.o
# core command
obj-y += nvedit.o
+obj-$(CONFIG_CMD_BCM_EXT_UTILS) += bcm/
+
obj-$(CONFIG_TI_COMMON_CMD_OPTIONS) += ti/
filechk_data_gz = (echo "static const char data_gz[] ="; cat $< | scripts/bin2c; echo ";")
new file mode 100644
@@ -0,0 +1,12 @@
+menu "Broadcom Extended Utilities"
+
+config CMD_BCM_LOGSETUP
+ bool "Command to setup logging on Broadcom boards"
+ depends on TARGET_BCMNS3
+ default n
+ help
+ Support specific log setup on Broadcom SoCs. This command
+ allows checking if logging support is present, and update
+ log sections.
+
+endmenu
new file mode 100644
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2020 Broadcom
+
+obj-$(CONFIG_CMD_BCM_LOGSETUP) += logsetup.o
new file mode 100644
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2020 Broadcom
+ */
+
+#ifndef __ELOG_H__
+#define __ELOG_H__
+
+#define GLOBAL_META_HDR_SIG 0x45524c47
+#define MAX_REC_COUNT 13
+#define MAX_REC_FORMAT 1
+#define MAX_SRC_TYPE 3
+#define MAX_NVM_TYPE 3
+/* A special type. Use defaults specified in CRMU config */
+#define NVM_DEFAULT 0xff
+
+/* Max. number of cmd parameters per elog spec */
+#define PARAM_COUNT 3
+
+#define REC_DESC_LENGTH 8
+
+enum {
+ LOG_SETUP_CMD_VALIDATE_META,
+ LOG_SETUP_CMD_WRITE_META,
+ LOG_SETUP_CMD_ERASE,
+ LOG_SETUP_CMD_READ,
+ LOG_SETUP_CMD_CHECK
+};
+
+#pragma pack(push, 1)
+
+struct meta_record {
+ u8 record_type;
+ u8 record_format;
+ u8 src_mem_type;
+ u8 alt_src_mem_type;
+ u8 nvm_type;
+ char rec_desc[REC_DESC_LENGTH];
+ u64 src_mem_addr;
+ u64 alt_src_mem_addr;
+ u64 rec_addr;
+ u32 rec_size;
+ u32 sector_size;
+ u8 padding[3];
+};
+
+struct global_header {
+ u32 signature;
+ u32 sector_size;
+ u8 revision;
+ u8 rec_count;
+ u16 padding;
+};
+
+struct log_setup {
+ u32 cmd;
+ u32 params[PARAM_COUNT];
+ u32 result;
+ u32 ret_code;
+};
+
+#pragma pack(pop)
+
+#endif
new file mode 100644
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 Broadcom
+ */
+
+/*
+ * Create a binary file ready to be flashed
+ * as a global meta for logging, from a source file.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <string.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+
+#include "elog.h"
+
+#define FILE_LINE_BUF_SIZE 1024
+#define GLOBAL_PARAM_COUNT 3
+#define REC_PARAM_COUNT 11
+
+#define GLOBAL_NAME "GLOBAL"
+#define RECORD_NAME "RECORD"
+
+#define MCU_IPC_MCU_CMD_ELOG_SETUP 0x84
+/* SPI write operations can be slow */
+#define DEFAULT_TIMEOUT_MS 10000
+
+#define MCU_IPC_CMD_DONE_MASK 0x80000000
+#define MCU_IPC_CMD_REPLY_MASK 0x3fff0000
+#define MCU_IPC_CMD_REPLY_SHIFT 16
+
+#define MIN_ARGS_COUNT 3
+#define MAX_ARGS_COUNT 5
+#define SEP_STR ","
+#define SUPPORTED_CMD "-s"
+#define CHK_HDR_CMD "-c"
+
+enum {
+ PARAM_GLOBAL,
+ PARAM_REC
+};
+
+static uintptr_t crmu_mbox0_address;
+
+/*
+ * Send a logging command to MCU patch for execution.
+ *
+ * Parameters:
+ * cmd: an IPC command code
+ * param: a pointer to parameters structure for IPC
+ * is_real_cmd: whether command produces a response in the structure.
+ * Only "support" command does not.
+ */
+static int send_crmu_log_cmd(u32 cmd, u32 param, int is_real_cmd)
+{
+ u32 timeout;
+ u32 val;
+ int ret = CMD_RET_FAILURE;
+ struct log_setup *setup;
+
+ setup = (struct log_setup *)(uintptr_t)param;
+ timeout = DEFAULT_TIMEOUT_MS;
+
+ writel(cmd, crmu_mbox0_address);
+ writel(param, crmu_mbox0_address + sizeof(u32));
+
+ do {
+ mdelay(1);
+ val = readl(is_real_cmd ? (uintptr_t)&setup->ret_code :
+ crmu_mbox0_address);
+ if (val & MCU_IPC_CMD_DONE_MASK) {
+ ret = CMD_RET_SUCCESS;
+ break;
+ }
+
+ } while (timeout--);
+
+ if (ret == CMD_RET_FAILURE) {
+ pr_err("CRMU cmd timeout!\n");
+ return ret;
+ }
+
+ /* Obtain status */
+ val = (val & MCU_IPC_CMD_REPLY_MASK) >> MCU_IPC_CMD_REPLY_SHIFT;
+ if (val)
+ ret = CMD_RET_FAILURE;
+
+ return ret;
+}
+
+static char *get_next_param(char **str, char *delimiter)
+{
+ char *ret = strsep(str, delimiter);
+
+ if (!ret)
+ return NULL;
+
+ return strim(ret);
+}
+
+static int count_chars(char *str, char delimiter)
+{
+ int count = 0;
+
+ if (!str || (!strlen(str)))
+ return 0;
+
+ do {
+ if (*str == delimiter)
+ count++;
+
+ str++;
+ } while (*str);
+
+ /* Need to consider leftovers (if any) after the last delimiter */
+ count++;
+
+ return count;
+}
+
+static int do_setup(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ struct global_header global;
+ struct log_setup setup = {0};
+ u8 *temp;
+ u8 *ptr = NULL;
+ char *buf;
+ u32 line_no = 0;
+ u32 expected_meta_size;
+ u32 data_size;
+ u32 log_offset = 0;
+ u32 num_recs = 0;
+ u32 addr;
+ int global_ready = 0;
+ int ret = CMD_RET_FAILURE;
+ int force = 0;
+
+ if (argc < MIN_ARGS_COUNT || argc > MAX_ARGS_COUNT)
+ return CMD_RET_USAGE;
+
+ /* Usage: addr or (-s/-c) mbox0_addr [data_size] [force] */
+ crmu_mbox0_address = simple_strtoul(argv[2], NULL, 16);
+ if (!crmu_mbox0_address) {
+ pr_err("Invalid CRMU mbox0 address\n");
+ return ret;
+ }
+
+ if (argc == MIN_ARGS_COUNT) {
+ if (strncmp(argv[1], SUPPORTED_CMD, 2) == 0)
+ return send_crmu_log_cmd(MCU_IPC_MCU_CMD_ELOG_SETUP,
+ LOG_SETUP_CMD_CHECK,
+ 0
+ );
+
+ if (strncmp(argv[1], CHK_HDR_CMD, 2) == 0)
+ return send_crmu_log_cmd(MCU_IPC_MCU_CMD_ELOG_SETUP,
+ (uintptr_t)&setup,
+ 1
+ );
+ }
+
+ /* Expect 1st parameter as a pointer to metadata */
+ addr = simple_strtoul(argv[1], NULL, 16);
+ if (!addr) {
+ pr_err("Address 0x0 is not allowed\n");
+ return ret;
+ }
+
+ data_size = simple_strtoul(argv[3], NULL, 16);
+ if (!data_size) {
+ pr_err("Invalid source size\n");
+ return ret;
+ }
+
+ if (argc == MAX_ARGS_COUNT)
+ force = simple_strtoul(argv[MAX_ARGS_COUNT - 1], NULL, 10);
+
+ memset(&global, 0, sizeof(struct global_header));
+ expected_meta_size = sizeof(struct global_header) +
+ MAX_REC_COUNT * sizeof(struct meta_record);
+
+ temp = (u8 *)malloc(expected_meta_size);
+ if (!temp)
+ return ret;
+
+ memset(temp, 0, expected_meta_size);
+
+ ptr = temp;
+ buf = (char *)(uintptr_t)addr;
+ /* End of file mark */
+ buf[data_size] = '\0';
+
+ do {
+ char *type_name;
+ u32 entry_type;
+ char *line;
+ char *value;
+
+ line_no++;
+ line = get_next_param(&buf, "\n");
+ if (!line)
+ break;
+
+ if ((!strlen(line)) || line[0] == '#')
+ continue;
+
+ value = get_next_param(&line, "=");
+
+ if (!value || (!strlen(value))) {
+ pr_err("Invalid format of line %d\n", line_no);
+ goto free_params;
+ }
+
+ type_name = GLOBAL_NAME;
+
+ if (!strncasecmp(value, type_name, strlen(GLOBAL_NAME))) {
+ entry_type = PARAM_GLOBAL;
+ } else {
+ type_name = RECORD_NAME;
+ if (!strncasecmp(value,
+ type_name,
+ strlen(RECORD_NAME)
+ )
+ ) {
+ entry_type = PARAM_REC;
+ } else {
+ pr_err("Unknown type %s, line %d\n",
+ value,
+ line_no
+ );
+ goto free_params;
+ }
+ }
+
+ if (global_ready && entry_type == PARAM_GLOBAL) {
+ /* Multiple globals are not allowed */
+ pr_err("Multiple GLOBAL records, line %d\n", line_no);
+ goto free_params;
+ }
+
+ if (entry_type == PARAM_GLOBAL) {
+ /* Parse Global and create global record in outfile */
+ if (count_chars(line, ',') != GLOBAL_PARAM_COUNT) {
+ pr_err("Invalid GLOBAL format, line %d\n",
+ line_no
+ );
+ goto free_params;
+ }
+
+ global.sector_size =
+ simple_strtoul
+ (get_next_param(&line, SEP_STR),
+ NULL,
+ 0
+ );
+ global.revision =
+ (u8)simple_strtoul
+ (get_next_param(&line, SEP_STR),
+ NULL,
+ 0
+ );
+ log_offset = simple_strtoul
+ (get_next_param(&line, SEP_STR),
+ NULL,
+ 0
+ );
+
+ if (!global.sector_size) {
+ pr_err("Invalid GLOBAL, line %d\n", line_no);
+ goto free_params;
+ }
+
+ global.signature = GLOBAL_META_HDR_SIG;
+ global_ready = 1;
+
+ /* Shift to the first RECORD header */
+ log_offset += 2 * global.sector_size;
+ ptr += sizeof(struct global_header);
+ } else {
+ struct meta_record rec = {0};
+ char *desc;
+ char *rec_addr_str;
+ int auto_addr = 0;
+
+ if (count_chars(line, ',') != REC_PARAM_COUNT) {
+ pr_err("Invalid RECORD, line %d\n", line_no);
+ goto free_params;
+ }
+
+ rec.record_type = (u8)simple_strtoul
+ (get_next_param(&line, SEP_STR),
+ NULL,
+ 0
+ );
+ rec.record_format = (u8)simple_strtoul
+ (get_next_param(&line, SEP_STR),
+ NULL,
+ 0
+ );
+ rec.src_mem_type = (u8)simple_strtoul
+ (get_next_param(&line, SEP_STR),
+ NULL,
+ 0
+ );
+ rec.alt_src_mem_type = (u8)simple_strtoul
+ (get_next_param(&line, SEP_STR),
+ NULL,
+ 0
+ );
+ rec.nvm_type = (u8)simple_strtoul
+ (get_next_param(&line, SEP_STR),
+ NULL,
+ 0
+ );
+ desc = get_next_param(&line, SEP_STR);
+ if (desc)
+ desc = strim(desc);
+
+ rec.src_mem_addr = (u64)simple_strtoul
+ (get_next_param(&line, SEP_STR),
+ NULL,
+ 0
+ );
+ rec.alt_src_mem_addr = (u64)simple_strtoul
+ (get_next_param(&line, SEP_STR),
+ NULL,
+ 0
+ );
+ rec_addr_str = get_next_param(&line, SEP_STR);
+ if (rec_addr_str)
+ rec_addr_str = strim(rec_addr_str);
+
+ if (!strcmp(rec_addr_str, "auto"))
+ auto_addr = 1;
+ else
+ rec.rec_addr = (u64)simple_strtoul
+ (rec_addr_str,
+ NULL,
+ 0
+ );
+
+ rec.rec_size = simple_strtoul
+ (get_next_param(&line, SEP_STR),
+ NULL,
+ 0
+ );
+ rec.sector_size = simple_strtoul
+ (get_next_param(&line, SEP_STR),
+ NULL,
+ 0
+ );
+ if (auto_addr) {
+ rec.rec_addr = (u64)log_offset;
+ log_offset += rec.rec_size;
+ }
+
+ /* Sanity checks */
+ if (rec.record_type > MAX_REC_COUNT ||
+ rec.record_format > MAX_REC_FORMAT ||
+ (rec.nvm_type > MAX_NVM_TYPE &&
+ rec.nvm_type != NVM_DEFAULT) ||
+ !rec.rec_size ||
+ !rec.sector_size ||
+ (!desc || !strlen(desc) ||
+ (strlen(desc) > sizeof(rec.rec_desc) + 1)
+ )
+ ) {
+ pr_err("Invalid RECORD %s, line %d\n",
+ desc ? desc : " (no desc)", line_no
+ );
+ goto free_params;
+ }
+
+ memset(rec.rec_desc, ' ', sizeof(rec.rec_desc));
+ memcpy(rec.rec_desc, desc, strlen(desc));
+ memcpy(ptr, &rec, sizeof(struct meta_record));
+ ptr += sizeof(struct meta_record);
+ num_recs++;
+ }
+
+ } while (buf && ((uintptr_t)buf < (addr + data_size)));
+
+ if (!num_recs) {
+ pr_err("No RECORD entry!\n");
+ goto free_params;
+ }
+
+ if (!global_ready) {
+ pr_err("Global entry was not found!\n");
+ goto free_params;
+ }
+
+ if (num_recs > MAX_REC_COUNT) {
+ pr_err("Too many records (max %d)\n", MAX_REC_COUNT);
+ goto free_params;
+ }
+
+ /* Recalculate new expected size */
+ if (num_recs != MAX_REC_COUNT) {
+ expected_meta_size = sizeof(struct global_header) +
+ num_recs * sizeof(struct meta_record);
+ }
+
+ global.rec_count = num_recs;
+ memcpy(temp, &global, sizeof(struct global_header));
+
+ setup.params[0] = (uintptr_t)temp;
+ setup.params[1] = expected_meta_size;
+
+ /* Apply the command via CRMU mailboxes and read the result. */
+ setup.cmd = (force) ? LOG_SETUP_CMD_WRITE_META :
+ LOG_SETUP_CMD_VALIDATE_META;
+
+ /* Finally, request the MCU patch to perform the command */
+ ret = send_crmu_log_cmd(MCU_IPC_MCU_CMD_ELOG_SETUP,
+ (uintptr_t)(&setup),
+ 1
+ );
+
+free_params:
+ free(temp);
+
+ return ret;
+}
+
+U_BOOT_CMD(logsetup, 5, 1, do_setup, "setup Broadcom MCU logging subsystem",
+ "\taddr mbox0_addr [data_size] [force]\nor\n\t -s(or -c) mbox0_addr"
+ );