summaryrefslogtreecommitdiff
path: root/core/minute-ia/panic.c
blob: 177480a6475bbdd8d7eb69dfe3fa219b68c05dee (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
/* Copyright 2016 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "common.h"
#include "console.h"
#include "cpu.h"
#include "host_command.h"
#include "mia_panic_internal.h"
#include "panic.h"
#include "printf.h"
#include "software_panic.h"
#include "system.h"
#include "task.h"
#include "timer.h"
#include "util.h"

/*
 * This array maps an interrupt vector number to the corresponding
 * exception name. See see "Intel 64 and IA-32 Architectures Software
 * Developer's Manual", Volume 3A, Section 6.15.
 */
const static char *panic_reason[] = {
	"Divide By Zero",
	"Debug Exception",
	"NMI Interrupt",
	"Breakpoint Exception",
	"Overflow Exception",
	"BOUND Range Exceeded Exception",
	"Invalid Opcode Exception",
	"Device Not Available Exception",
	"Double Fault Exception",
	"Coprocessor Segment Overrun",
	"Invalid TSS Exception",
	"Segment Not Present",
	"Stack Fault Exception",
	"General Protection Fault",
	"Page Fault",
	"Reserved",
	"Math Fault",
	"Alignment Check Exception",
	"Machine Check Exception",
	"SIMD Floating Point Exception",
	"Virtualization Exception",
};

/*
 * Print panic data. This may be called either from the report_panic
 * procedure (below) while handling a panic, or from the panicinfo
 * console command.
 */
void panic_data_print(const struct panic_data *pdata)
{
	if (pdata->x86.vector == PANIC_SW_WATCHDOG)
		panic_printf("Reason: Watchdog Expiration\n");
	else if (pdata->x86.vector <= 20)
		panic_printf("Reason: %s\n", panic_reason[pdata->x86.vector]);
	else if (panic_sw_reason_is_valid(pdata->x86.vector)) {
		panic_printf("Software panic reason %s\n",
			     panic_sw_reasons[pdata->x86.vector -
					      PANIC_SW_BASE]);
		panic_printf("Software panic info 0x%x\n",
			     pdata->x86.error_code);
	}
	else
		panic_printf("Interrupt vector number: 0x%08X (unknown)\n",
			     pdata->x86.vector);
	panic_printf("\n");
	panic_printf("Error Code = 0x%08X\n", pdata->x86.error_code);
	panic_printf("EIP        = 0x%08X\n", pdata->x86.eip);
	panic_printf("CS         = 0x%08X\n", pdata->x86.cs);
	panic_printf("EFLAGS     = 0x%08X\n", pdata->x86.eflags);
	panic_printf("EAX        = 0x%08X\n", pdata->x86.eax);
	panic_printf("EBX        = 0x%08X\n", pdata->x86.ebx);
	panic_printf("ECX        = 0x%08X\n", pdata->x86.ecx);
	panic_printf("EDX        = 0x%08X\n", pdata->x86.edx);
	panic_printf("ESI        = 0x%08X\n", pdata->x86.esi);
	panic_printf("EDI        = 0x%08X\n", pdata->x86.edi);
	panic_printf("EC Task    = %s\n", task_get_name(pdata->x86.task_id));
}

/**
 * Default exception handler, which reports a panic.
 *
 * The first parameter should be pushed by a software routine aware of
 * the interrupt vector number (see DEFINE_EXN_HANDLER macro in
 * interrupts.c).
 *
 * The remaining parameters (error_code, eip, cs, eflags) are in the
 * order pushed to the stack by hardware: see "Intel 64 and IA-32
 * Architectures Software Developer's Manual", Volume 3A, Figure 6-4.
 */
void exception_panic(
	uint32_t vector,
	uint32_t error_code,
	uint32_t eip,
	uint32_t cs,
	uint32_t eflags)
{
	/*
	 * If a panic were to occur during the reset procedure, we want
	 * to make sure that this panic will certainly cause a hard
	 * reset, rather than aontaskfw reset. Track if paniced once
	 * already.
	 */
	static int panic_once;

	register uint32_t eax asm("eax");
	register uint32_t ebx asm("ebx");
	register uint32_t ecx asm("ecx");
	register uint32_t edx asm("edx");
	register uint32_t esi asm("esi");
	register uint32_t edi asm("edi");

	struct panic_data * const pdata = get_panic_data_write();

	/* Save registers to global panic structure */
	pdata->x86.eax = eax;
	pdata->x86.ebx = ebx;
	pdata->x86.ecx = ecx;
	pdata->x86.edx = edx;
	pdata->x86.esi = esi;
	pdata->x86.edi = edi;

	/*
	 * Convert watchdog timer vector number to be a SW
	 * Watchdog. This is for code that is in
	 * system_common_pre_init
	 */
	if (IS_ENABLED(CONFIG_WATCHDOG) && vector == CONFIG_MIA_WDT_VEC)
		vector = PANIC_SW_WATCHDOG;

	/* Save stack data to global panic structure */
	pdata->x86.vector = vector;
	pdata->x86.error_code = error_code;
	pdata->x86.eip = eip;
	pdata->x86.cs = cs;
	pdata->x86.eflags = eflags;

	/* Save task information */
	pdata->x86.task_id = task_get_current();

	/* Initialize panic data */
	pdata->arch = PANIC_ARCH_X86;
	pdata->struct_version = 2;
	pdata->magic = PANIC_DATA_MAGIC;

	/* Display the panic and reset */
	if (panic_once)
		panic_printf("\nWhile resetting from a panic, another panic"
			     " occurred!");

	panic_printf("\n========== PANIC ==========\n");
	panic_data_print(pdata);
	panic_printf("\n");
	panic_printf("Resetting system...\n");
	panic_printf("===========================\n");

	/*
	 * Post increment panic_once to make sure we only go through
	 * once before we resort to a hard reset
	 */
	if (panic_once++)
		system_reset(SYSTEM_RESET_HARD);
	else if (vector == PANIC_SW_WATCHDOG)
		system_reset(SYSTEM_RESET_AP_WATCHDOG);
	else if (panic_sw_reason_is_valid(vector))
		system_reset(SYSTEM_RESET_MANUALLY_TRIGGERED);
	else
		system_reset(0);

	__builtin_unreachable();
}

noreturn
void software_panic(uint32_t reason, uint32_t info)
{
	uint16_t code_segment;

	/* Get the current code segment */
	__asm__ volatile ("movw  %%cs, %0":"=m" (code_segment));

	exception_panic(reason,
			info,
			(uint32_t)__builtin_return_address(0),
			code_segment,
			0);

	__builtin_unreachable();
}

void panic_set_reason(uint32_t reason, uint32_t info, uint8_t exception)
{
	struct panic_data * const pdata = get_panic_data_write();

	/* Setup panic data structure */
	memset(pdata, 0, CONFIG_PANIC_DATA_SIZE);
	pdata->magic = PANIC_DATA_MAGIC;
	pdata->struct_size = CONFIG_PANIC_DATA_SIZE;
	pdata->struct_version = 2;
	pdata->arch = PANIC_ARCH_X86;

	/* Log panic cause */
	pdata->x86.vector = reason;
	pdata->x86.error_code = info;
	pdata->x86.eflags = exception;
}

void panic_get_reason(uint32_t *reason, uint32_t *info, uint8_t *exception)
{
	struct panic_data * const pdata = panic_get_data();

	if (pdata && pdata->struct_version == 2) {
		*reason = pdata->x86.vector;
		*info = pdata->x86.error_code;
		*exception = pdata->x86.eflags;
	} else {
		*reason = *info = *exception = 0;
	}
}