Browse Source

[librm] Add facility to provide register and stack dump for CPU exceptions

When DEBUG=librm_mgmt is enabled, intercept CPU exceptions and provide
a register and stack dump, then drop to an emergency shell.  Exiting
from the shell will almost certainly not work, but this provides an
opportunity to view the register and stack dump and carry out some
basic debugging.

Note that we can intercept only the first 8 CPU exceptions, since a
PXE ROM is not permitted to rebase the PIC.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 6 years ago
parent
commit
89e31f8491

+ 44
- 0
src/arch/x86/include/librm.h View File

@@ -376,6 +376,50 @@ struct interrupt_vector {
376 376
 /** "jmp" instruction */
377 377
 #define JMP_INSN 0xe9
378 378
 
379
+/** 32-bit interrupt wrapper stack frame */
380
+struct interrupt_frame32 {
381
+	uint32_t esp;
382
+	uint32_t ss;
383
+	uint32_t gs;
384
+	uint32_t fs;
385
+	uint32_t es;
386
+	uint32_t ds;
387
+	uint32_t ebp;
388
+	uint32_t edi;
389
+	uint32_t esi;
390
+	uint32_t edx;
391
+	uint32_t ecx;
392
+	uint32_t ebx;
393
+	uint32_t eax;
394
+	uint32_t eip;
395
+	uint32_t cs;
396
+	uint32_t eflags;
397
+} __attribute__ (( packed ));
398
+
399
+/** 64-bit interrupt wrapper stack frame */
400
+struct interrupt_frame64 {
401
+	uint64_t r15;
402
+	uint64_t r14;
403
+	uint64_t r13;
404
+	uint64_t r12;
405
+	uint64_t r11;
406
+	uint64_t r10;
407
+	uint64_t r9;
408
+	uint64_t r8;
409
+	uint64_t rbp;
410
+	uint64_t rdi;
411
+	uint64_t rsi;
412
+	uint64_t rdx;
413
+	uint64_t rcx;
414
+	uint64_t rbx;
415
+	uint64_t rax;
416
+	uint64_t rip;
417
+	uint64_t cs;
418
+	uint64_t rflags;
419
+	uint64_t rsp;
420
+	uint64_t ss;
421
+} __attribute__ (( packed ));
422
+
379 423
 extern void set_interrupt_vector ( unsigned int intr, void *vector );
380 424
 
381 425
 /** A page table */

+ 27
- 6
src/arch/x86/transitions/librm.S View File

@@ -1363,20 +1363,19 @@ flatten_dummy:
1363 1363
 	.code32
1364 1364
 	.globl interrupt_wrapper
1365 1365
 interrupt_wrapper:
1366
-	/* Preserve registers (excluding already-saved %eax and
1367
-	 * otherwise unused registers which are callee-save for both
1368
-	 * 32-bit and 64-bit ABIs).
1369
-	 */
1366
+	/* Preserve registers (excluding already-saved %eax) */
1370 1367
 	pushl	%ebx
1371 1368
 	pushl	%ecx
1372 1369
 	pushl	%edx
1373 1370
 	pushl	%esi
1374 1371
 	pushl	%edi
1372
+	pushl	%ebp
1375 1373
 
1376 1374
 	/* Expand IRQ number to whole %eax register */
1377 1375
 	movzbl	%al, %eax
1378 1376
 
1379 1377
 .if64 ; /* Skip transition to long mode, if applicable */
1378
+	xorl	%edx, %edx
1380 1379
 	movw	%cs, %bx
1381 1380
 	cmpw	$LONG_CS, %bx
1382 1381
 	je	1f
@@ -1391,24 +1390,45 @@ interrupt_wrapper:
1391 1390
 
1392 1391
 	/* Switch to virtual addressing */
1393 1392
 	call	intr_to_prot
1393
+
1394
+	/* Pass 32-bit interrupt frame pointer in %edx */
1395
+	movl	%esp, %edx
1396
+	xorl	%ecx, %ecx
1394 1397
 .if64
1395 1398
 	/* Switch to long mode */
1396 1399
 	call	prot_to_long
1397 1400
 	.code64
1398 1401
 
1399
-1:	/* Preserve long-mode caller-save registers */
1402
+1:	/* Preserve long-mode registers */
1400 1403
 	pushq	%r8
1401 1404
 	pushq	%r9
1402 1405
 	pushq	%r10
1403 1406
 	pushq	%r11
1407
+	pushq	%r12
1408
+	pushq	%r13
1409
+	pushq	%r14
1410
+	pushq	%r15
1404 1411
 
1405 1412
 	/* Expand IRQ number to whole %rdi register */
1406 1413
 	movl	%eax, %edi
1414
+
1415
+	/* Pass 32-bit interrupt frame pointer (if applicable) in %rsi */
1416
+	testl	%edx, %edx
1417
+	je	1f
1418
+	movl	%edx, %esi
1419
+	addl	virt_offset, %esi
1420
+1:
1421
+	/* Pass 64-bit interrupt frame pointer in %rdx */
1422
+	movq	%rsp, %rdx
1407 1423
 .endif
1408 1424
 	/* Call interrupt handler */
1409 1425
 	call	interrupt
1410 1426
 .if64
1411
-	/* Restore long-mode caller-save registers */
1427
+	/* Restore long-mode registers */
1428
+	popq	%r15
1429
+	popq	%r14
1430
+	popq	%r13
1431
+	popq	%r12
1412 1432
 	popq	%r11
1413 1433
 	popq	%r10
1414 1434
 	popq	%r9
@@ -1432,6 +1452,7 @@ interrupt_wrapper:
1432 1452
 	popl	%ds
1433 1453
 
1434 1454
 1:	/* Restore registers */
1455
+	popl	%ebp
1435 1456
 	popl	%edi
1436 1457
 	popl	%esi
1437 1458
 	popl	%edx

+ 91
- 1
src/arch/x86/transitions/librm_mgmt.c View File

@@ -13,6 +13,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
13 13
 #include <ipxe/profile.h>
14 14
 #include <realmode.h>
15 15
 #include <pic8259.h>
16
+#include <ipxe/shell.h>
16 17
 
17 18
 /*
18 19
  * This file provides functions for managing librm.
@@ -43,6 +44,9 @@ struct idtr64 idtr64 = {
43 44
 	.limit = ( sizeof ( idt64 ) - 1 ),
44 45
 };
45 46
 
47
+/** Length of stack dump */
48
+#define STACK_DUMP_LEN 128
49
+
46 50
 /** Timer interrupt profiler */
47 51
 static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" };
48 52
 
@@ -159,15 +163,101 @@ static struct profiler * interrupt_profiler ( int intr ) {
159 163
 	}
160 164
 }
161 165
 
166
+/**
167
+ * Display interrupt stack dump (for debugging)
168
+ *
169
+ * @v intr		Interrupt number
170
+ * @v frame32		32-bit interrupt wrapper stack frame (or NULL)
171
+ * @v frame64		64-bit interrupt wrapper stack frame (or NULL)
172
+ */
173
+static __attribute__ (( unused )) void
174
+interrupt_dump ( int intr, struct interrupt_frame32 *frame32,
175
+		 struct interrupt_frame64 *frame64 ) {
176
+	unsigned long sp;
177
+	void *stack;
178
+
179
+	/* Do nothing unless debugging is enabled */
180
+	if ( ! DBG_LOG )
181
+		return;
182
+
183
+	/* Print register dump */
184
+	if ( ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) || frame32 ) {
185
+		sp = ( frame32->esp + sizeof ( *frame32 ) -
186
+		       offsetof ( typeof ( *frame32 ), esp ) );
187
+		DBGC ( &intr, "INT%d at %04x:%08x (stack %04x:%08lx):\n",
188
+		       intr, frame32->cs, frame32->eip, frame32->ss, sp );
189
+		DBGC ( &intr, "cs = %04x  ds = %04x  es = %04x  fs = %04x  "
190
+		       "gs = %04x  ss = %04x\n", frame32->cs, frame32->ds,
191
+		       frame32->es, frame32->fs, frame32->gs, frame32->ss );
192
+		DBGC ( &intr, "eax = %08x  ebx = %08x  ecx = %08x  "
193
+		       "edx = %08x  flg = %08x\n", frame32->eax, frame32->ebx,
194
+		       frame32->ecx, frame32->edx, frame32->eflags );
195
+		DBGC ( &intr, "esi = %08x  edi = %08x  ebp = %08x  "
196
+		       "esp = %08lx  eip = %08x\n", frame32->esi, frame32->edi,
197
+		       frame32->ebp, sp, frame32->eip );
198
+		stack = ( ( ( void * ) frame32 ) + sizeof ( *frame32 ) );
199
+	} else {
200
+		DBGC ( &intr, "INT%d at %04llx:%016llx (stack "
201
+		       "%04llx:%016llx):\n", intr,
202
+		       ( ( unsigned long long ) frame64->cs ),
203
+		       ( ( unsigned long long ) frame64->rip ),
204
+		       ( ( unsigned long long ) frame64->ss ),
205
+		       ( ( unsigned long long ) frame64->rsp ) );
206
+		DBGC ( &intr, "rax = %016llx  rbx = %016llx  rcx = %016llx\n",
207
+		       ( ( unsigned long long ) frame64->rax ),
208
+		       ( ( unsigned long long ) frame64->rbx ),
209
+		       ( ( unsigned long long ) frame64->rcx ) );
210
+		DBGC ( &intr, "rdx = %016llx  rsi = %016llx  rdi = %016llx\n",
211
+		       ( ( unsigned long long ) frame64->rdx ),
212
+		       ( ( unsigned long long ) frame64->rsi ),
213
+		       ( ( unsigned long long ) frame64->rdi ) );
214
+		DBGC ( &intr, "rbp = %016llx  rsp = %016llx  flg = %016llx\n",
215
+		       ( ( unsigned long long ) frame64->rbp ),
216
+		       ( ( unsigned long long ) frame64->rsp ),
217
+		       ( ( unsigned long long ) frame64->rflags ) );
218
+		DBGC ( &intr, "r8  = %016llx  r9  = %016llx  r10 = %016llx\n",
219
+		       ( ( unsigned long long ) frame64->r8 ),
220
+		       ( ( unsigned long long ) frame64->r9 ),
221
+		       ( ( unsigned long long ) frame64->r10 ) );
222
+		DBGC ( &intr, "r11 = %016llx  r12 = %016llx  r13 = %016llx\n",
223
+		       ( ( unsigned long long ) frame64->r11 ),
224
+		       ( ( unsigned long long ) frame64->r12 ),
225
+		       ( ( unsigned long long ) frame64->r13 ) );
226
+		DBGC ( &intr, "r14 = %016llx  r15 = %016llx\n",
227
+		       ( ( unsigned long long ) frame64->r14 ),
228
+		       ( ( unsigned long long ) frame64->r15 ) );
229
+		sp = frame64->rsp;
230
+		stack = phys_to_virt ( sp );
231
+	}
232
+
233
+	/* Print stack dump */
234
+	DBGC_HDA ( &intr, sp, stack, STACK_DUMP_LEN );
235
+}
236
+
162 237
 /**
163 238
  * Interrupt handler
164 239
  *
165 240
  * @v intr		Interrupt number
241
+ * @v frame32		32-bit interrupt wrapper stack frame (or NULL)
242
+ * @v frame64		64-bit interrupt wrapper stack frame (or NULL)
243
+ * @v frame		Interrupt wrapper stack frame
166 244
  */
167
-void __attribute__ (( regparm ( 1 ) )) interrupt ( int intr ) {
245
+void __attribute__ (( regparm ( 3 ) ))
246
+interrupt ( int intr, struct interrupt_frame32 *frame32,
247
+	    struct interrupt_frame64 *frame64 ) {
168 248
 	struct profiler *profiler = interrupt_profiler ( intr );
169 249
 	uint32_t discard_eax;
170 250
 
251
+	/* Trap CPU exceptions if debugging is enabled.  Note that we
252
+	 * cannot treat INT8+ as exceptions, since we are not
253
+	 * permitted to rebase the PIC.
254
+	 */
255
+	if ( DBG_LOG && ( intr < IRQ_INT ( 0 ) ) ) {
256
+		interrupt_dump ( intr, frame32, frame64 );
257
+		DBG ( "CPU exception: dropping to emergency shell\n" );
258
+		shell();
259
+	}
260
+
171 261
 	/* Reissue interrupt in real mode */
172 262
 	profile_start ( profiler );
173 263
 	__asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t"

Loading…
Cancel
Save