summaryrefslogtreecommitdiff
path: root/src/soc/intel/common/block/crashlog/crashlog.c
blob: daa5b763cf3524dc3a9b3b8d82390daa5ad529b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
/* SPDX-License-Identifier: GPL-2.0-only */

#include <cbmem.h>
#include <console/console.h>
#include <delay.h>
#include <device/mmio.h>
#include <intelblocks/crashlog.h>
#include <intelblocks/pmc_ipc.h>
#include <string.h>

int __weak cl_get_cpu_record_size(void)
{
	return 0;
}

int __weak cl_get_pmc_record_size(void)
{
	return 0;
}

u32 __weak cl_get_cpu_bar_addr(void)
{
	return 0;
}

u32 __weak cl_get_cpu_tmp_bar(void)
{
	return 0;
}

u32 __weak cl_get_cpu_mb_int_addr(void)
{
	return 0;
}

int __weak cl_get_total_data_size(void)
{
	return cl_get_cpu_record_size() + cl_get_pmc_record_size();
}

bool __weak cl_pmc_sram_has_mmio_access(void)
{
	return false;
}

bool __weak cpu_crashlog_support(void)
{
	return false;
}

bool __weak pmc_crashlog_support(void)
{
	return false;
}

bool __weak cl_cpu_data_present(void)
{
	return false;
}

bool __weak cl_pmc_data_present(void)
{
	return false;
}

__weak void reset_discovery_buffers(void) {}

__weak void update_new_pmc_crashlog_size(u32 *pmc_crash_size) {}

__weak void update_new_cpu_crashlog_size(u32 *cpu_crash_size) {}

pmc_ipc_discovery_buf_t __weak cl_get_pmc_discovery_buf(void)
{
	pmc_ipc_discovery_buf_t discov_buf;
	memset(&discov_buf, 0, sizeof(pmc_ipc_discovery_buf_t));
	return discov_buf;
}

pmc_crashlog_desc_table_t __weak cl_get_pmc_descriptor_table(void)
{
	pmc_crashlog_desc_table_t desc_tab;
	memset(&desc_tab, 0, sizeof(pmc_crashlog_desc_table_t));
	return desc_tab;
}

cpu_crashlog_discovery_table_t __weak cl_get_cpu_discovery_table(void)
{
	cpu_crashlog_discovery_table_t cpu_disc_tab;
	memset(&cpu_disc_tab, 0, sizeof(cpu_crashlog_discovery_table_t));
	return cpu_disc_tab;
}

int cpu_cl_poll_mailbox_ready(u32 cl_mailbox_addr)
{
	cpu_crashlog_mailbox_t cl_mailbox_interface;
	u16 stall_cnt = 0;

	do {
		cl_mailbox_interface.data = read32((u32 *)cl_mailbox_addr);
		udelay(CPU_CRASHLOG_MAILBOX_WAIT_STALL);
		stall_cnt++;
	} while ((cl_mailbox_interface.fields.busy == 1)
		 && stall_cnt < CPU_CRASHLOG_MAILBOX_WAIT_TIMEOUT);

	if ((cl_mailbox_interface.fields.busy == 1)
	    && (stall_cnt >= CPU_CRASHLOG_MAILBOX_WAIT_TIMEOUT)) {
		printk(BIOS_ERR, "CPU crashlog mailbox timed out.\n");
		return 0;
	}

	return 1;
}

int cpu_cl_mailbox_cmd(u8 cmd, u8 param)
{
	cpu_crashlog_mailbox_t cl_mailbox_intf;
	u32 cl_base_addr;

	memset(&cl_mailbox_intf, 0, sizeof(cpu_crashlog_mailbox_t));

	cl_base_addr = cl_get_cpu_bar_addr();

	cl_mailbox_intf.fields.command = cmd;
	cl_mailbox_intf.fields.param = param;
	cl_mailbox_intf.fields.busy = 1;

	write32((u32 *)(cl_base_addr + cl_get_cpu_mb_int_addr()),
		cl_mailbox_intf.data);

	cpu_cl_poll_mailbox_ready(cl_base_addr + cl_get_cpu_mb_int_addr());

	return 1;
}

int cpu_cl_clear_data(void)
{
	return cpu_cl_mailbox_cmd(CPU_CRASHLOG_CMD_CLEAR, 0);
}

int pmc_cl_gen_descriptor_table(u32 desc_table_addr,
				pmc_crashlog_desc_table_t *descriptor_table)
{
	int total_data_size = 0;
	descriptor_table->numb_regions = read32((u32 *)desc_table_addr);
	printk(BIOS_DEBUG, "CL PMC desc table: numb of regions is 0x%x at addr 0x%x\n",
	       descriptor_table->numb_regions, desc_table_addr);
	for (int i = 0; i < descriptor_table->numb_regions; i++) {
		if (i >= ARRAY_SIZE(descriptor_table->regions)) {
			printk(BIOS_ERR, "Maximum number of PMC crashLog descriptor table exceeded (%u/%zu)\n",
			descriptor_table->numb_regions,
			ARRAY_SIZE(descriptor_table->regions));
			break;
		}
		desc_table_addr += 4;
		descriptor_table->regions[i].data = read32((u32 *)(desc_table_addr));
		total_data_size += descriptor_table->regions[i].bits.size * sizeof(u32);
		printk(BIOS_DEBUG, "CL PMC desc table: region 0x%x has size 0x%x at offset 0x%x\n",
			i, descriptor_table->regions[i].bits.size,
			descriptor_table->regions[i].bits.offset);
	}
	return total_data_size;
}

bool __weak pmc_cl_discovery(void)
{
	return false;
}

bool __weak cpu_cl_discovery(void)
{
	return false;
}

int cl_pmc_re_arm_after_reset(void)
{

	const struct pmc_ipc_buffer req = { 0 };
	struct pmc_ipc_buffer res;
	uint32_t cmd_reg;
	int r;

	cmd_reg = pmc_make_ipc_cmd(PMC_IPC_CMD_CRASHLOG,
				PMC_IPC_CMD_ID_CRASHLOG_RE_ARM_ON_RESET,
				PMC_IPC_CMD_SIZE_SHIFT);

	r = pmc_send_ipc_cmd(cmd_reg, &req, &res);

	if (r < 0) {
		printk(BIOS_ERR, "pmc_send_ipc_cmd failed in %s\n", __func__);
		return 0;
	}

	return r;
}

/* Sends PMC IPC to clear CrashLog from PMC SSRAM area */
int cl_pmc_clear(void)
{
	const struct pmc_ipc_buffer req = { 0 };
	struct pmc_ipc_buffer res;
	uint32_t cmd_reg;
	int r;

	cmd_reg = pmc_make_ipc_cmd(PMC_IPC_CMD_CRASHLOG,
				PMC_IPC_CMD_ID_CRASHLOG_ERASE,
				PMC_IPC_CMD_SIZE_SHIFT);

	r = pmc_send_ipc_cmd(cmd_reg, &req, &res);

	if (r < 0) {
		printk(BIOS_ERR, "pmc_send_ipc_cmd failed in %s\n", __func__);
		return 0;
	}

	return r;
}

/*
 * Sends PMC IPC to populate CrashLog on all reboot.
 * The SSRAM area will be cleared on G3 by PMC automatically
 */
int cl_pmc_en_gen_on_all_reboot(void)
{
	const struct pmc_ipc_buffer req = { 0 };
	struct pmc_ipc_buffer res;
	uint32_t cmd_reg;
	int r;

	cmd_reg = pmc_make_ipc_cmd(PMC_IPC_CMD_CRASHLOG,
				PMC_IPC_CMD_ID_CRASHLOG_ON_RESET,
				PMC_IPC_CMD_SIZE_SHIFT);

	r = pmc_send_ipc_cmd(cmd_reg, &req, &res);

	if (r < 0) {
		printk(BIOS_ERR, "pmc_send_ipc_cmd failed in %s\n", __func__);
		return 0;
	}

	return r;
}

bool discover_crashlog(void)
{
	bool cpu_cl_discovered = false, pmc_cl_discovered = false;

	reset_discovery_buffers();

	/* PCH crashLog discovery */
	pmc_cl_discovered = pmc_cl_discovery();

	/* CPU crashLog discovery */
	cpu_cl_discovered = cpu_cl_discovery();

	return (cpu_cl_discovered || pmc_cl_discovered);
}

bool cl_copy_data_from_sram(u32 src_bar,
				u32 offset,
				u32 size,
				u32 *dest_addr,
				u32 buffer_index,
				bool pmc_sram)
{
	if (src_bar == 0) {
		printk(BIOS_ERR, "Invalid bar 0x%x and offset 0x%x for %s\n",
		       src_bar, offset, __func__);
		return false;
	}

	u32 src_addr = src_bar + offset;

	u32 data =  read32((u32 *)src_addr);
	/* PMC: copy if 1st DWORD in buffer is not zero and its 31st bit is not set */
	if (pmc_sram && !(data && !(data & BIT(31)))) {
		printk(BIOS_DEBUG, "Invalid data 0x%x at offset 0x%x from addr 0x%x"
			" of PMC SRAM.\n", data, offset, src_bar);
		return false;
	}
	/*CPU: don't copy if 1st DWORD in first buffer is zero */
	if (!pmc_sram && !data && (buffer_index == 0)) {
		printk(BIOS_DEBUG, "Invalid data 0x%x at offset 0x%x from addr 0x%x"
			" of telemetry SRAM.\n", data, offset, src_bar);
		return false;
	}

	u32 copied = 0;
	while (copied < size) {
		/* DW by DW copy: byte access to PMC SRAM not allowed */
		*dest_addr = read32((u32 *)src_addr);
		dest_addr++;
		src_addr += 4;
		copied++;
	}
	return true;
}

void cl_get_pmc_sram_data(void)
{
	u32 *dest = NULL;
	u32 tmp_bar_addr = cl_get_cpu_tmp_bar();
	u32 pmc_crashLog_size = cl_get_pmc_record_size();

	if (!cl_pmc_sram_has_mmio_access() || !tmp_bar_addr)
		return;

	pmc_ipc_discovery_buf_t discovery_buf = cl_get_pmc_discovery_buf();

	if (discovery_buf.bits.supported != 1) {
		printk(BIOS_DEBUG, "PCH crashlog feature not supported.\n");
		goto pmc_send_re_arm_after_reset;
	}

	/* Get the size of data to copy */
	if (discovery_buf.bits.discov_mechanism == 1) {
		if (discovery_buf.bits.base_offset & BIT(31)) {
			printk(BIOS_DEBUG, "PCH discovery to be used is disabled.\n");
			goto pmc_send_re_arm_after_reset;
		}
		printk(BIOS_DEBUG, "PMC crashLog size in discovery mode : 0x%X\n",
		       pmc_crashLog_size);
	} else {
		if (discovery_buf.bits.dis) {
			printk(BIOS_DEBUG, "PCH crashlog is disabled in legacy mode.\n");
			return;
		}
		pmc_crashLog_size = (discovery_buf.bits.size != 0) ?
					discovery_buf.bits.size : 0xC00;
		printk(BIOS_DEBUG, "PMC crashLog size in legacy mode : 0x%X\n",
		       pmc_crashLog_size);
	}

	/* allocate mem for the record to be copied */
	unsigned long pmc_cl_cbmem_addr;

	pmc_cl_cbmem_addr = (unsigned long)cbmem_add(CBMEM_ID_PMC_CRASHLOG,
						pmc_crashLog_size);
	if (!pmc_cl_cbmem_addr) {
		printk(BIOS_ERR, "Unable to allocate CBMEM PMC crashLog entry.\n");
		return;
	}

	memset((void *)pmc_cl_cbmem_addr, 0, pmc_crashLog_size);
	dest = (u32 *)(uintptr_t)pmc_cl_cbmem_addr;
	bool pmc_sram = true;
	pmc_crashlog_desc_table_t descriptor_table =  cl_get_pmc_descriptor_table();
	if (discovery_buf.bits.discov_mechanism == 1) {
		for (int i = 0; i < descriptor_table.numb_regions; i++) {
			if (cl_copy_data_from_sram(tmp_bar_addr,
						  descriptor_table.regions[i].bits.offset,
						  descriptor_table.regions[i].bits.size,
						  dest,
						  i,
						  pmc_sram)) {
				dest = (u32 *)((u32)dest +
						(descriptor_table.regions[i].bits.size
						* sizeof(u32)));
			} else {
				pmc_crashLog_size -= descriptor_table.regions[i].bits.size *
							sizeof(u32);
				printk(BIOS_DEBUG, "discover mode PMC crashlog size adjusted"
					" to: 0x%x\n", pmc_crashLog_size);
			}
		}
	} else {
		if (!cl_copy_data_from_sram(tmp_bar_addr,
					   discovery_buf.bits.base_offset,
					   discovery_buf.bits.size,
					   dest,
					   0,
					   pmc_sram)) {
			pmc_crashLog_size -= discovery_buf.bits.size * sizeof(u32);
			printk(BIOS_DEBUG, "legacy mode PMC crashlog size adjusted to: 0x%x\n",
			       pmc_crashLog_size);
		}
	}

	update_new_pmc_crashlog_size(&pmc_crashLog_size);

pmc_send_re_arm_after_reset:
	/* when bit 7 of discov cmd resp is set -> bit 2 of size field */
	if (discovery_buf.bits.size & BIT(2))
		cl_pmc_re_arm_after_reset();

	/* Clear the SSRAM region after copying the error log */
	cl_pmc_clear();

}

void cl_get_cpu_sram_data(void)
{
	u32 tmp_bar_addr = 0;
	u32 *dest = NULL;
	u32 m_cpu_crashLog_size = cl_get_cpu_record_size();
	cpu_crashlog_discovery_table_t cpu_cl_disc_tab = cl_get_cpu_discovery_table();

	if (m_cpu_crashLog_size < 1) {
		printk(BIOS_DEBUG, "%s: no data to collect.\n", __func__);
		return;
	}

	printk(BIOS_DEBUG, "CPU crash data size: 0x%X bytes in 0x%X region(s).\n",
	       m_cpu_crashLog_size, cpu_cl_disc_tab.header.fields.count);

	/* allocate memory buffers for CPU crashog data to be copied */
	unsigned long cpu_crashlog_cbmem_addr;
	cpu_crashlog_cbmem_addr = (unsigned long)cbmem_add(CBMEM_ID_CPU_CRASHLOG,
								m_cpu_crashLog_size);
	if (!cpu_crashlog_cbmem_addr) {
		printk(BIOS_ERR, "Failed to add CPU main crashLog entries to CBMEM.\n");
		return;
	}

	memset((void *)cpu_crashlog_cbmem_addr, 0, m_cpu_crashLog_size);
	tmp_bar_addr = cl_get_cpu_bar_addr();
	dest = (u32 *)(uintptr_t)cpu_crashlog_cbmem_addr;
	bool pmc_sram = false;

	for (int i = 0 ; i < cpu_cl_disc_tab.header.fields.count ; i++) {
		if (cl_copy_data_from_sram(tmp_bar_addr,
					   cpu_cl_disc_tab.buffers[i].fields.offset,
					   cpu_cl_disc_tab.buffers[i].fields.size,
					   dest,
					   i,
					   pmc_sram)) {
			dest = (u32 *)((u32)dest +
					(cpu_cl_disc_tab.buffers[i].fields.size * sizeof(u32)));
		} else {
			m_cpu_crashLog_size -= cpu_cl_disc_tab.buffers[i].fields.size
						* sizeof(u32);

			/* for CPU skip all buffers if the 1st one is not valid */
			if (i == 0) {
				m_cpu_crashLog_size = 0;
				break;
			}
		}
	}

	update_new_cpu_crashlog_size(&m_cpu_crashLog_size);

	/* clear telemetry SRAM region */
	cpu_cl_clear_data();
}

void collect_pmc_and_cpu_crashlog_from_srams(void)
{
	if (pmc_crashlog_support() && cl_pmc_data_present()
		&& (cl_get_pmc_record_size() > 0)) {
		if (CONFIG(SOC_INTEL_CRASHLOG_ON_RESET)) {
			cl_pmc_en_gen_on_all_reboot();
			printk(BIOS_DEBUG, "Crashlog collection enabled on every reboot.\n");
		}
		cl_get_pmc_sram_data();
	} else {
		printk(BIOS_DEBUG, "Skipping PMC crashLog collection. Data not present.\n");
	}

	printk(BIOS_DEBUG, "m_cpu_crashLog_size : 0x%X bytes\n", cl_get_cpu_record_size());

	if (cpu_crashlog_support() && cl_cpu_data_present()
		&& (cl_get_cpu_record_size() > 0)) {
		printk(BIOS_DEBUG, "CPU crashLog present.\n");
		cl_get_cpu_sram_data();
	} else {
		printk(BIOS_DEBUG, "Skipping CPU crashLog collection. Data not present.\n");
	}
}

bool cl_fill_cpu_records(void *cl_record)
{
	void *cl_src_addr = NULL;

	u32 m_cpu_crashLog_size = cl_get_cpu_record_size();

	if (!cl_cpu_data_present() || m_cpu_crashLog_size == 0) {
		printk(BIOS_DEBUG, "CPU crashLog not present, skipping.\n");
		return false;
	}

	printk(BIOS_DEBUG, "CPU crash data collection.\n");
	cl_src_addr = cbmem_find(CBMEM_ID_CPU_CRASHLOG);
	memcpy(cl_record, cl_src_addr, m_cpu_crashLog_size);

	return true;
}

bool cl_fill_pmc_records(void *cl_record)
{
	void *cl_src_addr = NULL;

	u32 m_pmc_crashLog_size = cl_get_pmc_record_size();

	if (!cl_pmc_data_present() || m_pmc_crashLog_size == 0) {
		printk(BIOS_DEBUG, "PMC crashLog not present, skipping.\n");
		return false;
	}

	printk(BIOS_DEBUG, "PMC crash data collection.\n");
	cl_src_addr = cbmem_find(CBMEM_ID_PMC_CRASHLOG);
	memcpy(cl_record, cl_src_addr, m_pmc_crashLog_size);

	return true;
}