I'm aiming for a cpeek that catches the data faults on sparc32, so I've coded up the attached test code.
The fault handler here is almost trivially simple: set a new instruction pointer and return to it. All the magic is done in push_trap_handler and do_trap_handler, and they act similar to setjmp/longjmp. Nesting is allowed, so it should be possible to use this at the top level for printing out pretty error messages for any fault just like OBP.
I'm looking for any comments and suggestions. This proof of concept only handles mmu data faults, but is extendable. Example tests: deadbeef try-fault returns false (0), 10000 try-fault returns true (-1) and the value at 0x10000
Bob
On 15/06/2013 19:50, Bob Breuer wrote:
I'm aiming for a cpeek that catches the data faults on sparc32, so I've coded up the attached test code.
The fault handler here is almost trivially simple: set a new instruction pointer and return to it. All the magic is done in push_trap_handler and do_trap_handler, and they act similar to setjmp/longjmp. Nesting is allowed, so it should be possible to use this at the top level for printing out pretty error messages for any fault just like OBP.
I'm looking for any comments and suggestions. This proof of concept only handles mmu data faults, but is extendable. Example tests: deadbeef try-fault returns false (0), 10000 try-fault returns true (-1) and the value at 0x10000
Bob
If you only need to check MMU data faults, you can alternatively use the "No Fault" flag in the MMU Control register (SparcV8 standard, appendix H, page 253).
Your solution is certainly more flexible, though.
Clearing the fault by reading the "MMU fault status register" may be useful, some OSes could be disturbed during boot by a still pending MMU fault.
Olivier
On 15/06/13 18:50, Bob Breuer wrote:
I'm aiming for a cpeek that catches the data faults on sparc32, so I've coded up the attached test code.
The fault handler here is almost trivially simple: set a new instruction pointer and return to it. All the magic is done in push_trap_handler and do_trap_handler, and they act similar to setjmp/longjmp. Nesting is allowed, so it should be possible to use this at the top level for printing out pretty error messages for any fault just like OBP.
I'm looking for any comments and suggestions. This proof of concept only handles mmu data faults, but is extendable. Example tests: deadbeef try-fault returns false (0), 10000 try-fault returns true (-1) and the value at 0x10000
Bob
I took a peek at the OpenBOOT source code to see what that does, and it seems that under the hood it is based on a trap handling solution. The key is newguarded-execute which on SPARC64 does the following:
* Save the current contents of traps 30, 1f, 32 and 34 0x30 = Data Access Exception 0x1f = Level 15 interrupt 0x32 = Data Access Error 0x34 = Unaligned Access Error
* Invoke the Forth word, throwing an a Forth exception upon error
* Restore the contents of the above traps
From the SPARCv8 manual we could say that these map to the following SPARC32 traps:
* 0x09 Data Access Exception * 0x1f Level 15 interrupt * 0x29 Data Access Error * 0x07 Unaligned Access Error
In terms of the routine itself, have you had a look at the work I did a while back to catch the I/D-MMU miss traps on SPARC64? In particular I added SAVE_CPU_STATE(x) and RESTORE_CPU_STATE(x) macros to arch/sparc64/vectors.S that do all of the context save/restore work so that the code required to get back into C outside of this is fairly minimal (see reload_DMMU_tlb in the same file).
ATB,
Mark.
On 6/16/2013 4:38 AM, Mark Cave-Ayland wrote:
On 15/06/13 18:50, Bob Breuer wrote:
I'm aiming for a cpeek that catches the data faults on sparc32, so I've coded up the attached test code.
<snip>
Got some good suggestions, thanks guys. So I need to read the fault status register to clear it before the OS boots, and may as well use the status itself as the flag to indicate if a fault happened. Also the interrupt handler I had posted is overkill, so I'll narrow the focus.
I took a peek at the OpenBOOT source code to see what that does, and it seems that under the hood it is based on a trap handling solution. The key is newguarded-execute which on SPARC64 does the following:
Save the current contents of traps 30, 1f, 32 and 34 0x30 = Data Access Exception 0x1f = Level 15 interrupt 0x32 = Data Access Error 0x34 = Unaligned Access Error
Invoke the Forth word, throwing an a Forth exception upon error
Restore the contents of the above traps
From the SPARCv8 manual we could say that these map to the following SPARC32 traps:
- 0x09 Data Access Exception
- 0x1f Level 15 interrupt
- 0x29 Data Access Error
- 0x07 Unaligned Access Error
So I fired up my SS-20 and did a little poking around to see how the SuperSparc-II behaves here. * page not mapped: read/write = Data Access Exception * sbus timeout: read = Data Access Exception, write = Level 15 int * afterwards, SFSR will always be cleared to 0
For cpeek/cpoke, OBP will catch the Access Exception and Access Error, but it won't catch the Unaligned Access or Level 15 int.
So how about if I setup an interrupt handler that skips over the faulting instruction when a global flag is set, otherwise continues as is today. Then cpeek can become: - set skip_datafault flag - clear SFSR - chain to c@ - clear skip_datafault flag - check SFSR - if fault, fixup stack and return false, else return true
Now, I might be able to code up this cpeek version in forth, or just leave it in C?
In terms of the routine itself, have you had a look at the work I did a while back to catch the I/D-MMU miss traps on SPARC64? In particular I added SAVE_CPU_STATE(x) and RESTORE_CPU_STATE(x) macros to arch/sparc64/vectors.S that do all of the context save/restore work so that the code required to get back into C outside of this is fairly minimal (see reload_DMMU_tlb in the same file).
We can probably come back to this later, but I don't see any similar code for sparc32.
ATB,
Mark.
On 2013-Jun-19 10:44 , Bob Breuer wrote: [...]
So I fired up my SS-20 and did a little poking around to see how the SuperSparc-II behaves here.
- page not mapped: read/write = Data Access Exception
- sbus timeout: read = Data Access Exception, write = Level 15 int
- afterwards, SFSR will always be cleared to 0
For cpeek/cpoke, OBP will catch the Access Exception and Access Error, but it won't catch the Unaligned Access or Level 15 int.
Yeah - on current generation sparcs, I/O write failures aren't notified until long after the instruction has gone past. That generally means that PIO write failures generally don't get reported at the OK prompt - they just get dropped invisibly.
On 6/19/2013 9:44 AM, Bob Breuer wrote:
So how about if I setup an interrupt handler that skips over the faulting instruction when a global flag is set, otherwise continues as is today. Then cpeek can become:
- set skip_datafault flag
- clear SFSR
- chain to c@
- clear skip_datafault flag
- check SFSR
- if fault, fixup stack and return false, else return true
Now, I might be able to code up this cpeek version in forth, or just leave it in C?
Here's what I've got. If it looks ok, I will finish it off by also catching trap 0x29, modify wpeek/lpeek to also use the wrapper, and make a similar change for the poke commands.
Index: forth/device/other.fs =================================================================== --- forth/device/other.fs (revision 1160) +++ forth/device/other.fs (working copy) @@ -20,8 +20,12 @@
\ 5.3.7.1 Peek/poke
+defer (peek-wrapper) +: (peek-default) execute true ; +['] (peek-default) to (peek-wrapper) + : cpeek ( addr -- false | byte true ) - c@ true + ['] c@ (peek-wrapper) ;
: wpeek ( waddr -- false | w true ) Index: arch/sparc32/vectors.S =================================================================== --- arch/sparc32/vectors.S (revision 1160) +++ arch/sparc32/vectors.S (working copy) @@ -41,6 +41,9 @@ #define WINDOW_FILL \ rd %psr, %l0; rd %wim, %l3; b fill_window_entry; nop;
+#define TRAP_DFAULT(lvl) \ + rd %psr, %l0; rd %wim, %l3; b do_dfault; mov lvl, %l7; + #define BTRAP(lvl) ba bug; mov lvl, %g1; nop; nop; #define BTRAPS(x) BTRAP(x) BTRAP(x+1) BTRAP(x+2) BTRAP(x+3) BTRAP(x+4) BTRAP(x+5) BTRAP(x+6) BTRAP(x+7) #define TRAP_ENTRY_INTERRUPT(int_level) \ @@ -54,7 +57,11 @@ t_wovf: WINDOW_SPILL /* Window Overflow */ t_wunf: WINDOW_FILL /* Window Underflow */ BTRAP(0x7) - BTRAPS(0x8) + BTRAP(0x8) + TRAP_DFAULT(0x9) + BTRAP(0xa) BTRAP(0xb) BTRAP(0xc) BTRAP(0xd) BTRAP(0xe) BTRAP(0xf) + + #if 0 BAD_TRAP(0x10) t_irq1: TRAP_ENTRY_INTERRUPT(1) /* IRQ Software/SBUS Level 1 */ @@ -206,6 +213,26 @@ #include "wof.S" #include "wuf.S"
+/* data fault handler */ + .data + .align 4 + .global allow_dfault +allow_dfault: + .word 0 + + .text + .align 4 +do_dfault: + /* if allow_dfault is 0, do old behavior */ + set allow_dfault, %l4 + ld [ %l4 ], %l4 + tst %l4 + bz,a bug + mov %l7, %g1 + /* skip the faulting instruction */ + jmp %l2 + rett %l2+4 + .section .rodata _BUG_message_0: .string "Unhandled Exception 0x" Index: arch/sparc32/lib.c =================================================================== --- arch/sparc32/lib.c (revision 1160) +++ arch/sparc32/lib.c (working copy) @@ -302,6 +302,45 @@ DPRINTF("obp_dumb_memfree 0x%p (size %d)\n", va, sz); }
+#include "asm/crs.h" + +extern unsigned int allow_dfault; + +static unsigned int get_sfsr(void) +{ + unsigned int sfsr; + __asm__ __volatile__( + "lda [%1] %2, %0" + : "=r" (sfsr) + : "r" (AC_M_SFSR), "i" (ASI_M_MMUREGS)); + + return sfsr; +} + +static void sparc_peek(void) +{ + unsigned int sfsr; + + get_sfsr(); /* clear any stale fault status */ + allow_dfault = 1; + + fword("execute"); + + allow_dfault = 0; + sfsr = get_sfsr(); + + if (sfsr == 0) + { + PUSH(-1); /* true */ + } + else + { + /* drop failed read, return false */ + POP(); + PUSH(0); + } +} + void ob_init_mmu(void) { @@ -360,6 +399,9 @@ bind_func("pgmap@", pgmap_fetch); bind_func("pgmap!", pgmap_store); bind_func("map-pages", ob_map_pages); + + PUSH_xt( bind_noname_func(sparc_peek) ); + feval("to (peek-wrapper)"); }
/*
On 25/06/13 16:40, Bob Breuer wrote:
On 6/19/2013 9:44 AM, Bob Breuer wrote:
So how about if I setup an interrupt handler that skips over the faulting instruction when a global flag is set, otherwise continues as is today. Then cpeek can become:
- set skip_datafault flag
- clear SFSR
- chain to c@
- clear skip_datafault flag
- check SFSR
- if fault, fixup stack and return false, else return true
Now, I might be able to code up this cpeek version in forth, or just leave it in C?
Here's what I've got. If it looks ok, I will finish it off by also catching trap 0x29, modify wpeek/lpeek to also use the wrapper, and make a similar change for the poke commands.
Hi Bob,
Great work - this is definitely looking closer to what I was expecting :) My initial feeling was that we could try and do a bit more in Forth, so I've had a go at converting spark_peek() into Forth (minus the SFSR register access), the result of which I've attached to this email.
I made a few other changes too: I renamed allow_dfault to ignore_dfault (as that seemed to better reflect its behaviour in that we don't fault if it is non-zero), removed the get_sfsr() function (as a similar prototype already existed in pgtsrmmu.h) and mapped the address of ignore_dfault to the ignore-dfault word.
By unifying C/Forth in this way you get the advantage that you can control the fault handler from either language, e.g.
0 > -1 ignore-dfault ! ok 0 > 0 c@ ok 1 > u. ffffffff ok 0 > 0 ignore-dfault ! ok 0 > 0 c@ Unhandled Exception 0x00000009 PC = 0xffd074f0 NPC = 0xffd074f4 Stopping execution
Anyway I'm happy that the patch is on the right track if you would like to fill in the missing bits - thanks once again!
ATB,
Mark.
On Sat, Jun 15, 2013 at 5:50 PM, Bob Breuer breuerr@mc.net wrote:
I'm aiming for a cpeek that catches the data faults on sparc32, so I've coded up the attached test code.
The fault handler here is almost trivially simple: set a new instruction pointer and return to it. All the magic is done in push_trap_handler and do_trap_handler, and they act similar to setjmp/longjmp. Nesting is allowed, so it should be possible to use this at the top level for printing out pretty error messages for any fault just like OBP.
Instead of (perhaps not so portable) returns_twice function, the trap handler could just set a flag if a pointer to the flag is not NULL and return to the caller after skipping the faulting instruction.
I'm looking for any comments and suggestions. This proof of concept only handles mmu data faults, but is extendable. Example tests: deadbeef try-fault returns false (0), 10000 try-fault returns true (-1) and the value at 0x10000
Bob