blob: 82a0b224c670b31e1b7ef5caa6e340e4ab48efd4 [file] [log] [blame]
Sam Protsenko850f1442024-07-23 13:14:36 -05001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2024 Linaro Ltd.
4 * Author: Sam Protsenko <semen.protsenko@linaro.org>
5 *
6 * Firmware loading code.
7 */
8
9#include <part.h>
10#include <linux/arm-smccc.h>
11#include "fw.h"
12
13#define EMMC_IFACE "mmc"
14#define EMMC_DEV_NUM 0
15
16/* LDFW constants */
17#define LDFW_PART_NAME "ldfw"
18#define LDFW_NWD_ADDR 0x88000000
19#define LDFW_MAGIC 0x10adab1e
20#define SMC_CMD_LOAD_LDFW -0x500
21#define SDM_HW_RESET_STATUS 0x1230
22#define SDM_SW_RESET_STATUS 0x1231
23#define SB_ERROR_PREFIX 0xfdaa0000
24
25struct ldfw_header {
26 u32 magic;
27 u32 size;
28 u32 init_entry;
29 u32 entry_point;
30 u32 suspend_entry;
31 u32 resume_entry;
32 u32 start_smc_id;
33 u32 version;
34 u32 set_runtime_entry;
35 u32 reserved[3];
36 char fw_name[16];
37};
38
39static int read_fw(const char *part_name, void *buf)
40{
41 struct blk_desc *blk_desc;
42 struct disk_partition part;
43 unsigned long cnt;
44 int part_num;
45
46 blk_desc = blk_get_dev(EMMC_IFACE, EMMC_DEV_NUM);
47 if (!blk_desc) {
48 debug("%s: Can't get eMMC device\n", __func__);
49 return -ENODEV;
50 }
51
52 part_num = part_get_info_by_name(blk_desc, part_name, &part);
53 if (part_num < 0) {
54 debug("%s: Can't get LDWF partition\n", __func__);
55 return -ENOENT;
56 }
57
58 cnt = blk_dread(blk_desc, part.start, part.size, buf);
59 if (cnt != part.size) {
60 debug("%s: Can't read LDFW partition\n", __func__);
61 return -EIO;
62 }
63
64 return 0;
65}
66
67int load_ldfw(void)
68{
69 const phys_addr_t addr = (phys_addr_t)LDFW_NWD_ADDR;
70 struct ldfw_header *hdr;
71 struct arm_smccc_res res;
72 void *buf = (void *)addr;
73 u64 size = 0;
74 int err, i;
75
76 /* Load LDFW from the block device partition into RAM buffer */
77 err = read_fw(LDFW_PART_NAME, buf);
78 if (err)
79 return err;
80
81 /* Validate LDFW by magic number in its header */
82 hdr = buf;
83 if (hdr->magic != LDFW_MAGIC) {
84 debug("%s: Wrong LDFW magic; is LDFW flashed?\n", __func__);
85 return -EINVAL;
86 }
87
88 /* Calculate actual total size of all LDFW blobs */
89 for (i = 0; hdr->magic == LDFW_MAGIC; ++i) {
90#ifdef DEBUG
91 char name[17] = { 0 };
92
93 strncpy(name, hdr->fw_name, 16);
94 debug("%s: ldfw #%d: version = 0x%x, name = %s\n", __func__, i,
95 hdr->version, name);
96#endif
97
98 size += (u64)hdr->size;
99 hdr = (struct ldfw_header *)((u64)hdr + (u64)hdr->size);
100 }
101 debug("%s: The whole size of all LDFWs: 0x%llx\n", __func__, size);
102
103 /* Load LDFW firmware to SWD (Secure World) memory via EL3 monitor */
104 arm_smccc_smc(SMC_CMD_LOAD_LDFW, addr, size, 0, 0, 0, 0, 0, &res);
105 err = (int)res.a0;
106 if (err == -1 || err == SDM_HW_RESET_STATUS) {
107 debug("%s: Can't load LDFW in dump_gpr state\n", __func__);
108 return -EIO;
109 } else if (err == SDM_SW_RESET_STATUS) {
110 debug("%s: Can't load LDFW in kernel panic (SW RESET) state\n",
111 __func__);
112 return -EIO;
113 } else if (err < 0 && (err & 0xffff0000) == SB_ERROR_PREFIX) {
114 debug("%s: LDFW signature is corrupted! ret=0x%x\n", __func__,
115 (u32)err);
116 return -EIO;
117 } else if (err == 0) {
118 debug("%s: No LDFW is inited\n", __func__);
119 return -EIO;
120 }
121
122#ifdef DEBUG
123 u32 tried = res.a0 & 0xffff;
124 u32 failed = (res.a0 >> 16) & 0xffff;
125
126 debug("%s: %d/%d LDFWs have been loaded successfully\n", __func__,
127 tried - failed, tried);
128#endif
129
130 return 0;
131}