Hi,
I am encountering a privilege violation exception on MCF52259 MCU.
Whenever I am trying to disable/enable the interrupts, it throws this exception even the supervisor bit is set in Status register.
Here is my code: -
DWORD set_ipl( DWORD newIPL )
{
DWORD curIPL;
asm
{
// I am getting error on this line of code.
move.w SR,D7
andi.l #0x0700,D7
lsr.l #8,D7
move.l D7,curIPL
move.l newIPL,D7
andi.l #0x07,D7
lsl.l #8,D7
move.w SR,D6
andi.l #0x0000F8FF,D6
or.l D7,D6
move.w D6,SR
}
return curIPL;
}
Function Call
set_ipl(7);
set_ipl(0);
Any help will be highly appreciated.
Thanks in advance.
Regards.
That code looks the same as that provided by Freescale that I'm using with gcc:
asm_set_ipl:
_asm_set_ipl:
link a6,#-8
movem.l d6-d7,(sp)
move.w sr,d7 /* current sr */
move.l d7,d0 /* prepare return value */
andi.l #0x0700,d0 /* mask out IPL */
lsr.l #8,d0 /* IPL */
... and so on ...
The Manual states:
3.3.4.5 Privilege Violation
The attempted execution of a supervisor mode instruction while in user mode generates a privilege
violation exception. See ColdFire Programmer’s Reference Manual for a list of supervisor-mode
instructions.
So it seems like the only way to get that error is to be in User mode and try to access something that isn't allowed.
The manual also warns:
The V2 ColdFire processor uses an imprecise reporting mechanism for access errors on operand writes.
Because the actual write cycle may be decoupled from the processor’s issuing of the operation, the
signaling of an access error appears to be decoupled from the instruction that generated the write.
Accordingly, the PC contained in the exception stack frame merely represents the location in the program
when the access error was signaled. All programming model updates associated with the write instruction
are completed. The NOP instruction can collect access errors for writes.
You may be triggering the error earlier somehow, and the "blame" might be delayed, so the one it says it as fault may not be. So I'd suggest putting lots of NOPs between all of the instructions in that function and see if the exception "blames" something else.
I would suggest you also manually decode the Exception Frame that it pushes on the stack, and if you need more help, provide both the RAW and decoded exception frames in any further posts.
Disassemble that function and see if the compiler did anything unexpected too.
Tom
Hi Tom,
Thanks for the reply.
I further investigated it and found out that the supervisor bit somehow got cleared.
I further digged down and found that when i am enabling/disabling the interrupts, just before moving the value from data register to status register. I got interrupted by interrupt handler which changes the value in the data register. So when the interrupt processing is done, i am moving the wrong value from the data register to status register.
I have proved it by using different registers which are not used in the interrupt handler and it worked perfectly.
Now, my question is that can I use strldsr instruction for enabling/disabling the interrupts.
Thanks for your help.
Regards.
> I got interrupted by interrupt handler which changes the value in the data register.
That's not the real cause of your problems. If you compare the code sample I provided with yours you'll see a "movem.l d6-d7,(sp)" line which saves the registers that the function is using.
I should have spotted this problem in your code, but I didn't look closely enough.
Every compiler has a standard that has to be followed for writing functions. This is its "ABI". That specifies which register is used for the stack and frame pointer, how parameters are passed to functions (on the stack, in registers or both) and most importantly, which registers have to be preserved across a function call and which ones are free for use within the function. When you're writing in "C" the compiler handles all of this for you, but when you write an assembly function you have to get this right.
But you're not writing standalone assembly code, you're using inline assembly within a C function. "C" will save and restore the registers it is using, but your assembly code hasn't "told the function" that it is using D6 and D7, so it didn't save them. The usual fix for this is either to replace your use of "D6" and "D7" with variables declared in the function, only use the "unpreserved" registers in your function (usually D0 and D1 and D2 for CW, but not GCC) or to push them on the stack and restore them. That won't work if the C function is also using those registers though. The in-line assembly syntax might also have a way of indicating which registers are used. Gcc has this, but it is very complicated and hard to get right.
Inline assembly is dangerous. It is very easy to write fragile code with bugs in it that the compiler can't save you from. You only have to change one line of code in a function that includes inline assembly to get this sort of register clash happening. Or you might change some compiler flags and cause a problem. You're better off with real assembly code in a separate file.
Are you sure CodeWarrior doesn't already provide an equivalent to your "set_ipl()" function already? The codebase we're using with gcc has an "mcf5xxx.S" file which provides the functions detailed in this post:
If you're using CodeWarrior, they changed the default ABI from stack-based to register-based parameters between 7.1 and 7.2 according to this post:
Re: Updating CW 7.1 to 7.2, using MQX 3.3, missing librarys
This post details the ABI, but you should be able to find this in the documentation somewhere:
Coldfire register ABI documentation
Tom