Mathias,
Thanks for all the tips! While waiting for somebody to answer my former question, I've reached almost your same conclusion, but your post definitely confirmed my ideas.
The trap handlers must be handwritten in the target assembly in any case, let alone with this weird sparc register arrangement.
I've come across the same - initially unexplainable - odd behaviors you mentioned in your post.
I think that the main pitfall is just the fact that the traps are disabled while executing a trap handler. In this way, it is impossible to nest another trap handler execution (i.e. a window overflow handler), thus avoiding the possibility to use the output registers.
Follows in importance the fact that the "scratch" registers like %g1 must be preserved across the procedure call (as gcc follows its own execution flow which does not plan a jump to the trap handler).
Going into the details, I have to write a handler that will forward a new addtional instruction set (interspersed with the existing v8 ISA) to a coprocessor coupled with the leon3. Unfortunately, this handler has a quite complicated logic (it makes use of translation tables, for example), and I think it will take me a lot of time to implement in sparc assembly, given the fact that I am an absolute beginner with this architecture, and the amount of constraints I have.
I was going to reserve some memory locations to work around the problem of the lack of available registers, but I actually do not know whether the MMU is implemented / active or not in my setup (i'm not the hardware developer). Do you know, by any chance, if the default LEON2/3 setup with leonbare library allows the MMU / paging?
Thanks a lot for your accurate and specific answers,
J.
--- On Sun, 11/21/10, m.rosenfelder <m.rosenfelder-/***@public.gmane.org> wrote:
From: m.rosenfelder <m.rosenfelder-/***@public.gmane.org>
Subject: [leon_sparc] Re: How to install a trap handler
To: leon_sparc-***@public.gmane.org
Date: Sunday, November 21, 2010, 3:54 PM
Hey there,
Post by josef_ahmCould anyone suggest me the best way to install correctly a trap handler at runtime?
I'd like to develop a C program able to substitute the original Illegal Instruction trap handler with its own handler. To do that, I think the simplest way is to overwrite the 4 instructions relative to the trap table entry of the Illegal Instruction exception, with new instructions that direct the processor to my handler procedure (the classic jmp).
The problem is that the handler procedure must be a leaf procedure, ended by "rett", and I'm wondering if there's a way to implement that procedure in C (the translation of a C function is a normal procedure, surrounded by the save/restore instructions).
If I may give you one advice: Don't write your trap handler in C code!
The compiler is intended for code generation for applications and not for trap handlers. This implies that it uses many conventions from the SPARC V8 ABI, especially concerning the register usage. For example, the SPARC ABI says that the register %g1 is not expected to retain its content during a procedure call. The called procedure is allowed to overwrite the content. But a trap handler should preserve any registers visible to an application, so it should also preserve %g1! Thus, if you let the compiler choose in which register to put your local variables in your C trap procedure, you cannot guarantee that %g1 is being preserved.
Other problems are the output registers %o0 to %o7. The SPARC register window is circular yielding that the window that is being rotated to by the hardware when the trap occurs may be the last window that is free. Its output registers overlap with the input registers of the last window. "Last" in this contexts means the window that is the oldest one in the register window of the processor that has not yet been saved onto the stack.
Thus, you may only use the outs if you are sure that there is at least one additional free register window available. That means that your trap code should either avoid using the outs at all or you should check the current window pointer (CWP) and the window invalid mask (WIM) if there is another free window available. Only then you are sure that you may use the output registers in your trap code!
Again, the compiler assumes that it can use all outs as it usually can do for application code. This may overwrite other register data and this kind of error is very hard to spot. It is only visible if a trap occurs after your procedure call chain has reached a certain depth (i.e. no more additional register windows free for the application, i.e. any SAVE instruction will cause an overflow trap). It may work for most cases but will sporadically produre strange behaviour. I experienced this kind of bug in my current project some time ago and after long debugging sessions it turned out that it was not my code that was erronous but the trap handler that was written by someone else who was unable to write trap handler code properly.
And - of course - don't use any input registers. These are the output registers of the application where the trap occurs and must be reserved by the trap handler. As the HW is putting the PC and the nPC of the trap in registers %l1 and %l2 of the new window when a trap occurs, the only six registers that your trap handling code may safely use at the beginning are %l0, %l3-%l7. You may then check if there is another register window free and if it is may use additionally the eight output registers as mentioned above.
A third issue with trap handlers are traps itself. If a trap occurs, the processor automatically disables traps, i.e. ET (enable traps) in the PSR (processor status register) is set to zero. That is because the handling of the current trap shall not be interrupted by other traps (especially interrupts) before it didn't finish the current one. Generating another trap while ET is zero will cause the processor to enter error mode (as defined in the SPARC V8 instruction set architecture). This is very dangerous because what follows then is undefined behaviour! The SPARC V8 ISA even mentions that the hardware may typically trigger an external reset for the processor which means that all information of the system is lost! Thus, causing traps itself while being in a trap handler must absolutely be avoided!
Therefore, you cannot use the normal register over- or underflow handlers. So don't blindly execute SAVE or RESTORE instructions without checking the WIM before. Don't execute any memory load- or store-instructions if you have an MMU and it is enabled before not checking the page tables (use the LDA instruction with ASI bypass for physical memory addresses to walk the page tables in software before doing ANY normal memory access).
In summary, traps are evil on the SPARC. Write them yourself in assembler and not in C. Or even better: Don't write your own trap handlers if you are unsure of your capabilities and use tested and proven code from someone else (e.g. the Linux kernel). Or try to solve your problems in other ways and avoid writing your own trap handlers if possible. You will shoot youself in the foot if your are not very careful!
regards, matthias
[Non-text portions of this message have been removed]
------------------------------------