1 /* SPDX-License-Identifier: GPL-2.0-only */
2
3 #include <assert.h>
4 #include <arch/mmu.h>
5 #include <cbfs.h>
6 #include <cbmem.h>
7 #include <commonlib/bsd/mem_chip_info.h>
8 #include <console/cbmem_console.h>
9 #include <console/console.h>
10 #include <fmap.h>
11 #include <mrc_cache.h>
12 #include <reset.h>
13 #include <security/vboot/misc.h>
14 #include <soc/mmu.h>
15 #include <soc/mmu_common.h>
16 #include <soc/qclib_common.h>
17 #include <soc/symbols_common.h>
18 #include <string.h>
19 #include <vb2_api.h>
20
21 #define QCLIB_VERSION 0
22
23 /* store QcLib return data until CBMEM_CREATION_HOOK runs */
24 static struct mem_chip_info *mem_chip_info;
25
write_mem_chip_information(struct qclib_cb_if_table_entry * te)26 static void write_mem_chip_information(struct qclib_cb_if_table_entry *te)
27 {
28 struct mem_chip_info *info = (void *)te->blob_address;
29 if (te->size > sizeof(struct mem_chip_info) &&
30 te->size == mem_chip_info_size(info->num_entries)) {
31 /* Save mem_chip_info in global variable ahead of hook running */
32 mem_chip_info = info;
33 }
34 }
35
add_mem_chip_info(int unused)36 static void add_mem_chip_info(int unused)
37 {
38 void *mem_region_base = NULL;
39 size_t size;
40
41 if (!mem_chip_info || !mem_chip_info->num_entries ||
42 mem_chip_info->struct_version != MEM_CHIP_STRUCT_VERSION) {
43 printk(BIOS_ERR, "Did not receive valid mem_chip_info from QcLib!\n");
44 return;
45 }
46
47 size = mem_chip_info_size(mem_chip_info->num_entries);
48
49 /* Add cbmem table */
50 mem_region_base = cbmem_add(CBMEM_ID_MEM_CHIP_INFO, size);
51 ASSERT(mem_region_base != NULL);
52
53 /* Migrate the data into CBMEM */
54 memcpy(mem_region_base, mem_chip_info, size);
55 }
56
57 CBMEM_CREATION_HOOK(add_mem_chip_info);
58
59 struct qclib_cb_if_table qclib_cb_if_table = {
60 .magic = QCLIB_MAGIC_NUMBER,
61 .version = QCLIB_INTERFACE_VERSION,
62 .num_entries = 0,
63 .max_entries = QCLIB_MAX_NUMBER_OF_ENTRIES,
64 .global_attributes = 0,
65 .reserved = 0,
66 };
67
qclib_file_default(enum qclib_cbfs_file file)68 const char *qclib_file_default(enum qclib_cbfs_file file)
69 {
70 switch (file) {
71 case QCLIB_CBFS_PMICCFG:
72 return CONFIG_CBFS_PREFIX "/pmiccfg";
73 case QCLIB_CBFS_QCSDI:
74 return CONFIG_CBFS_PREFIX "/qcsdi";
75 case QCLIB_CBFS_QCLIB:
76 return CONFIG_CBFS_PREFIX "/qclib";
77 case QCLIB_CBFS_DCB:
78 return CONFIG_CBFS_PREFIX "/dcb";
79 default:
80 die("unknown QcLib file %d", file);
81 }
82 }
83
84 const char *qclib_file(enum qclib_cbfs_file file)
85 __attribute__((weak, alias("qclib_file_default")));
86
qclib_add_if_table_entry(const char * name,void * base,uint32_t size,uint32_t attrs)87 void qclib_add_if_table_entry(const char *name, void *base,
88 uint32_t size, uint32_t attrs)
89 {
90 struct qclib_cb_if_table_entry *te =
91 &qclib_cb_if_table.te[qclib_cb_if_table.num_entries++];
92 assert(qclib_cb_if_table.num_entries <= qclib_cb_if_table.max_entries);
93 strncpy(te->name, name, sizeof(te->name) - 1);
94 te->blob_address = (uintptr_t)base;
95 te->size = size;
96 te->blob_attributes = attrs;
97 }
98
write_ddr_information(struct qclib_cb_if_table_entry * te)99 static void write_ddr_information(struct qclib_cb_if_table_entry *te)
100 {
101 uint64_t ddr_size;
102
103 /* Save DDR info in SRAM region to share with ramstage */
104 ddr_region->offset = te->blob_address;
105 ddr_size = te->size;
106 ddr_region->size = ddr_size * MiB;
107
108 /* Use DDR info to configure MMU */
109 qc_mmu_dram_config_post_dram_init(
110 (void *)(uintptr_t)region_offset(ddr_region), region_sz(ddr_region));
111 }
112
write_qclib_log_to_cbmemc(struct qclib_cb_if_table_entry * te)113 static void write_qclib_log_to_cbmemc(struct qclib_cb_if_table_entry *te)
114 {
115 int i;
116 char *ptr = (char *)te->blob_address;
117
118 for (i = 0; i < te->size; i++) {
119 char c = *ptr++;
120 if (c != '\r')
121 __cbmemc_tx_byte(c);
122 }
123 }
124
write_table_entry(struct qclib_cb_if_table_entry * te)125 static void write_table_entry(struct qclib_cb_if_table_entry *te)
126 {
127 if (!strncmp(QCLIB_TE_DDR_INFORMATION, te->name,
128 sizeof(te->name))) {
129 write_ddr_information(te);
130
131 } else if (!strncmp(QCLIB_TE_DDR_TRAINING_DATA, te->name,
132 sizeof(te->name))) {
133 assert(!mrc_cache_stash_data(MRC_TRAINING_DATA, QCLIB_VERSION,
134 (const void *)te->blob_address, te->size));
135
136 } else if (!strncmp(QCLIB_TE_LIMITS_CFG_DATA, te->name,
137 sizeof(te->name))) {
138 assert(fmap_overwrite_area(QCLIB_FR_LIMITS_CFG_DATA,
139 (const void *)te->blob_address, te->size));
140
141 } else if (!strncmp(QCLIB_TE_QCLIB_LOG_BUFFER, te->name,
142 sizeof(te->name))) {
143 write_qclib_log_to_cbmemc(te);
144
145 } else if (!strncmp(QCLIB_TE_MEM_CHIP_INFO, te->name,
146 sizeof(te->name))) {
147 write_mem_chip_information(te);
148
149 } else {
150 printk(BIOS_WARNING, "%s write not implemented\n", te->name);
151 printk(BIOS_WARNING, " blob_address[%llx]..size[%x]\n",
152 te->blob_address, te->size);
153 }
154 }
155
dump_te_table(void)156 static void dump_te_table(void)
157 {
158 struct qclib_cb_if_table_entry *te;
159 int i;
160
161 for (i = 0; i < qclib_cb_if_table.num_entries; i++) {
162 te = &qclib_cb_if_table.te[i];
163 printk(BIOS_DEBUG, "[%s][%llx][%x][%x]\n",
164 te->name, te->blob_address,
165 te->size, te->blob_attributes);
166 }
167 }
168
qclib_soc_override(struct qclib_cb_if_table * table)169 __weak int qclib_soc_override(struct qclib_cb_if_table *table) { return 0; }
170
qclib_load_and_run(void)171 void qclib_load_and_run(void)
172 {
173 int i;
174 ssize_t data_size;
175 struct mmu_context pre_qclib_mmu_context;
176
177 /* zero ddr_information SRAM region, needs new data each boot */
178 memset(ddr_region, 0, sizeof(struct region));
179
180 /* output area, QCLib copies console log buffer out */
181 if (CONFIG(CONSOLE_CBMEM))
182 qclib_add_if_table_entry(QCLIB_TE_QCLIB_LOG_BUFFER,
183 _qclib_serial_log,
184 REGION_SIZE(qclib_serial_log), 0);
185
186 /* output area, QCLib fills in DDR details */
187 qclib_add_if_table_entry(QCLIB_TE_DDR_INFORMATION, NULL, 0, 0);
188
189 /* Attempt to load DDR Training Blob */
190 data_size = mrc_cache_load_current(MRC_TRAINING_DATA, QCLIB_VERSION,
191 _ddr_training, REGION_SIZE(ddr_training));
192 if (data_size < 0) {
193 printk(BIOS_ERR, "Unable to load previous training data.\n");
194 memset(_ddr_training, 0, REGION_SIZE(ddr_training));
195 }
196 qclib_add_if_table_entry(QCLIB_TE_DDR_TRAINING_DATA,
197 _ddr_training, REGION_SIZE(ddr_training), 0);
198
199 /* Address and size of this entry will be filled in by QcLib. */
200 qclib_add_if_table_entry(QCLIB_TE_MEM_CHIP_INFO, NULL, 0, 0);
201
202 /* Attempt to load PMICCFG Blob */
203 data_size = cbfs_load(qclib_file(QCLIB_CBFS_PMICCFG),
204 _pmic, REGION_SIZE(pmic));
205 if (!data_size) {
206 printk(BIOS_ERR, "[%s] /pmiccfg failed\n", __func__);
207 goto fail;
208 }
209 qclib_add_if_table_entry(QCLIB_TE_PMIC_SETTINGS, _pmic, data_size, 0);
210
211 /* Attempt to load DCB Blob */
212 data_size = cbfs_load(qclib_file(QCLIB_CBFS_DCB),
213 _dcb, REGION_SIZE(dcb));
214 if (!data_size) {
215 printk(BIOS_ERR, "[%s] /dcb failed\n", __func__);
216 goto fail;
217 }
218 qclib_add_if_table_entry(QCLIB_TE_DCB_SETTINGS, _dcb, data_size, 0);
219
220 /* Enable QCLib serial output, based on Kconfig */
221 if (CONFIG(CONSOLE_SERIAL))
222 qclib_cb_if_table.global_attributes =
223 QCLIB_GA_ENABLE_UART_LOGGING;
224
225 if (CONFIG(QC_SDI_ENABLE) && (!CONFIG(VBOOT) ||
226 !vboot_is_gbb_flag_set(VB2_GBB_FLAG_RUNNING_FAFT))) {
227 struct prog qcsdi =
228 PROG_INIT(PROG_REFCODE,
229 qclib_file(QCLIB_CBFS_QCSDI));
230
231 /* Attempt to load QCSDI elf */
232 if (cbfs_prog_stage_load(&qcsdi))
233 goto fail;
234
235 qclib_add_if_table_entry(QCLIB_TE_QCSDI,
236 prog_entry(&qcsdi), prog_size(&qcsdi), 0);
237 printk(BIOS_INFO, "qcsdi.entry[%p]\n", qcsdi.entry);
238 }
239
240 /* hook for SoC specific binary blob loads */
241 if (qclib_soc_override(&qclib_cb_if_table)) {
242 printk(BIOS_ERR, "qclib_soc_override failed\n");
243 goto fail;
244 }
245
246 dump_te_table();
247
248 /* Attempt to load QCLib elf */
249 struct prog qclib =
250 PROG_INIT(PROG_REFCODE, qclib_file(QCLIB_CBFS_QCLIB));
251
252 if (cbfs_prog_stage_load(&qclib))
253 goto fail;
254
255 prog_set_entry(&qclib, prog_entry(&qclib), &qclib_cb_if_table);
256
257 printk(BIOS_DEBUG, "\n\n\nQCLib is about to Initialize DDR\n");
258 printk(BIOS_DEBUG, "Global Attributes[%#x]..Table Entries Count[%d]\n",
259 qclib_cb_if_table.global_attributes,
260 qclib_cb_if_table.num_entries);
261 printk(BIOS_DEBUG, "Jumping to QCLib code at %p(%p)\n",
262 prog_entry(&qclib), prog_entry_arg(&qclib));
263
264 /* back-up mmu context before disabling mmu and executing qclib */
265 mmu_save_context(&pre_qclib_mmu_context);
266 /* disable mmu before jumping to qclib. mmu_disable also
267 flushes and invalidates caches before disabling mmu. */
268 mmu_disable();
269
270 prog_run(&qclib);
271
272 /* Before returning, QCLib flushes cache and disables mmu.
273 Explicitly disable mmu (flush, invalidate and disable mmu)
274 before re-enabling mmu with backed-up mmu context */
275 mmu_disable();
276 mmu_restore_context(&pre_qclib_mmu_context);
277 mmu_enable();
278
279 if (qclib_cb_if_table.global_attributes & QCLIB_GA_FORCE_COLD_REBOOT) {
280 printk(BIOS_NOTICE, "QcLib requested cold reboot\n");
281 board_reset();
282 }
283
284 /* step through I/F table, handling return values */
285 for (i = 0; i < qclib_cb_if_table.num_entries; i++)
286 if (qclib_cb_if_table.te[i].blob_attributes &
287 QCLIB_BA_SAVE_TO_STORAGE)
288 write_table_entry(&qclib_cb_if_table.te[i]);
289
290 /* confirm that we received valid ddr information from QCLib */
291 assert((uintptr_t)_dram == region_offset(ddr_region) &&
292 region_sz(ddr_region) >= (u8 *)cbmem_top() - _dram);
293
294 printk(BIOS_DEBUG, "QCLib completed\n\n\n");
295
296 return;
297
298 fail:
299 die("Couldn't run QCLib.\n");
300 }
301