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
 /** "jmp" instruction */
376
 /** "jmp" instruction */
377
 #define JMP_INSN 0xe9
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
 extern void set_interrupt_vector ( unsigned int intr, void *vector );
423
 extern void set_interrupt_vector ( unsigned int intr, void *vector );
380
 
424
 
381
 /** A page table */
425
 /** A page table */

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

1363
 	.code32
1363
 	.code32
1364
 	.globl interrupt_wrapper
1364
 	.globl interrupt_wrapper
1365
 interrupt_wrapper:
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
 	pushl	%ebx
1367
 	pushl	%ebx
1371
 	pushl	%ecx
1368
 	pushl	%ecx
1372
 	pushl	%edx
1369
 	pushl	%edx
1373
 	pushl	%esi
1370
 	pushl	%esi
1374
 	pushl	%edi
1371
 	pushl	%edi
1372
+	pushl	%ebp
1375
 
1373
 
1376
 	/* Expand IRQ number to whole %eax register */
1374
 	/* Expand IRQ number to whole %eax register */
1377
 	movzbl	%al, %eax
1375
 	movzbl	%al, %eax
1378
 
1376
 
1379
 .if64 ; /* Skip transition to long mode, if applicable */
1377
 .if64 ; /* Skip transition to long mode, if applicable */
1378
+	xorl	%edx, %edx
1380
 	movw	%cs, %bx
1379
 	movw	%cs, %bx
1381
 	cmpw	$LONG_CS, %bx
1380
 	cmpw	$LONG_CS, %bx
1382
 	je	1f
1381
 	je	1f
1391
 
1390
 
1392
 	/* Switch to virtual addressing */
1391
 	/* Switch to virtual addressing */
1393
 	call	intr_to_prot
1392
 	call	intr_to_prot
1393
+
1394
+	/* Pass 32-bit interrupt frame pointer in %edx */
1395
+	movl	%esp, %edx
1396
+	xorl	%ecx, %ecx
1394
 .if64
1397
 .if64
1395
 	/* Switch to long mode */
1398
 	/* Switch to long mode */
1396
 	call	prot_to_long
1399
 	call	prot_to_long
1397
 	.code64
1400
 	.code64
1398
 
1401
 
1399
-1:	/* Preserve long-mode caller-save registers */
1402
+1:	/* Preserve long-mode registers */
1400
 	pushq	%r8
1403
 	pushq	%r8
1401
 	pushq	%r9
1404
 	pushq	%r9
1402
 	pushq	%r10
1405
 	pushq	%r10
1403
 	pushq	%r11
1406
 	pushq	%r11
1407
+	pushq	%r12
1408
+	pushq	%r13
1409
+	pushq	%r14
1410
+	pushq	%r15
1404
 
1411
 
1405
 	/* Expand IRQ number to whole %rdi register */
1412
 	/* Expand IRQ number to whole %rdi register */
1406
 	movl	%eax, %edi
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
 .endif
1423
 .endif
1408
 	/* Call interrupt handler */
1424
 	/* Call interrupt handler */
1409
 	call	interrupt
1425
 	call	interrupt
1410
 .if64
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
 	popq	%r11
1432
 	popq	%r11
1413
 	popq	%r10
1433
 	popq	%r10
1414
 	popq	%r9
1434
 	popq	%r9
1432
 	popl	%ds
1452
 	popl	%ds
1433
 
1453
 
1434
 1:	/* Restore registers */
1454
 1:	/* Restore registers */
1455
+	popl	%ebp
1435
 	popl	%edi
1456
 	popl	%edi
1436
 	popl	%esi
1457
 	popl	%esi
1437
 	popl	%edx
1458
 	popl	%edx

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

13
 #include <ipxe/profile.h>
13
 #include <ipxe/profile.h>
14
 #include <realmode.h>
14
 #include <realmode.h>
15
 #include <pic8259.h>
15
 #include <pic8259.h>
16
+#include <ipxe/shell.h>
16
 
17
 
17
 /*
18
 /*
18
  * This file provides functions for managing librm.
19
  * This file provides functions for managing librm.
43
 	.limit = ( sizeof ( idt64 ) - 1 ),
44
 	.limit = ( sizeof ( idt64 ) - 1 ),
44
 };
45
 };
45
 
46
 
47
+/** Length of stack dump */
48
+#define STACK_DUMP_LEN 128
49
+
46
 /** Timer interrupt profiler */
50
 /** Timer interrupt profiler */
47
 static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" };
51
 static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" };
48
 
52
 
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
  * Interrupt handler
238
  * Interrupt handler
164
  *
239
  *
165
  * @v intr		Interrupt number
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
 	struct profiler *profiler = interrupt_profiler ( intr );
248
 	struct profiler *profiler = interrupt_profiler ( intr );
169
 	uint32_t discard_eax;
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
 	/* Reissue interrupt in real mode */
261
 	/* Reissue interrupt in real mode */
172
 	profile_start ( profiler );
262
 	profile_start ( profiler );
173
 	__asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t"
263
 	__asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t"

Loading…
Cancel
Save