I was trying to enable Interrupts for an ARMv7A mcu (Cortex-A8) and I noticed I can't change the I bit in the CPSR register in user mode no matter what I try. I read around a bit and it seems like this is because I am in USR mode.
Is the only solution to call an SWI and change the SPSR in the SWI handler?
Indeed you cannot disable or enable interrupts in user mode. You can only do it in a privileged mode.
It's unusual to switch to user mode with interrupts disabled. Normally you only run a very small amount of code with interrupts disabled, since that delays the handling of the next interrupt. If you run part of an interrupt handler in user mode, first do whatever has to be done before re-enabling interrupts, and then switch to user mode.
Related
Im trying to find out how I can disable and enable interrupts on the STM32L4x6RG Nucleo?
After a bit of googling I found the macros __disble_irq() and __enable_irq() but I'm not convinced these are disabling interrupts.
After more investigation it seems that the cpsid instruction this macro maps to only has effect when it runs in supervisor context. So the question becomes how do I move to supervisor mode to disable interrupts and back again??
I found the macros __disble_irq() and __enable_irq() but I'm not
convinced these are disabling interrupts.
They do, unless you (or the OS you are using) explicitly leave privileged mode with the MSR control, Rn instruction, or the __set_CONTROL() function, which does the same.
So the question becomes how do I move to supervisor mode to disable
interrupts and back again??
The processor is in privileged mode after reset, and stays in it unless you tell it otherwise. It also enters privileged mode temporarily when executing an exception handler.
You can use the SVC instruction to call the SVC exception handler from user code, and run some code in privileged mode. There is a problem though, that the SVC handler invocation would be blocked too by __disable_irq(), so there would be no way to reenable them afterwards. Instead of __disable_irq(), you can adjust the BASEPRI register to selectively disable lower priority interrupts, and set SVC priority higher so that it would not be blocked.
The processor boots in privileged mode so unless you are running your application on top of an operating system or have switched to unprivileged mode yourself you should already be in privileged mode. If you are running you application on top of an OS you should use its services to handle interrupts and if no such service exists you should leave the interrupts alone.
If you have switched to unprivileged-mode yourself, you can use the svc instruction to trigger an svc-exception and the handler for the exception executes in privileged mode.
the basic world switch flow is:
set FIQ to monitor mode
normal world -> FIQ triggered
-> enter monitor mode (do switch to Secure world, restore Secure world context)
-> in Secure world sys mode
-> FIQ is not clear, enter FIQ handler in Secure world
step3 and step 4, after we restore the target context,
arm will trigger the exception to enter the exception
is the behavior correct? (if we dont branch to FIQ handle in monitor mode vector table)
we need flow like below:
(no world context switch case, just enter monitor mode to check if we need world switch, and enter irq exception from monitor mode directly. we need this because of our hw limitation, we only have IRQ in our chip)
set IRQ to monitor mode
normal world user mode -> IRQ triggered
-> enter monitor, do something we want to hook, check if we need context switch, prepare some spsr/lr for IRQ mode
-> enter normal world IRQ mode, irq handling
-> irq done, return back to user mode
for non-world switch case, we would like to let the normal world os does not know about the monitor mode, just though he enters the irq mode directly and return from irq mode.
for world switch case, just switch it in the monitor mode.
or it's just do the irq_handle in the monitor mode?
eq.
normal world OS usr mode -> irq -> usr mode
normal world OS usr mode -> monitor to irq handler -> usr mode
is the flow possible and well design?
is the flow possible and well design?
It is possible. 'well designed' is subjective. It has several fails or non-ideal issues. I guess your system doesn't have a GIC; which is a trustzone aware interrupt controller. The GIC has banked registers which allow the normal world OS to use it (almost) as if it was in the secure world.
It is not clear from you question whether you want the secure world to have interrupts? I guess from the statement 'for non-world switch case...'. If you only have interrupts handled by the normal world, things are simple. Don't branch to monitor mode on an IRQ (or FIQ). There is a register to set this behaviour (SCR/security configuration register).
For the dual world interrupt case, you have two issues.
You need to trust the normal world OS.
Interrupt latency will be increased.
You must always take the interrupt in monitor mode. The monitor must check the interrupt controller source to see what world the interrupt belongs to. It may need to do a world switch depending on the world. This will increase interrupt latency. As well, both the normal and secure world will be dealing with the same interrupt controller registers. So you have malicious security concerns and non-malicious race conditions with multiple interrupt drivers trying to manipulate registers (RMW). Generally, if your chip doesn't have a GIC, but the CPU supports TrustZone, the your system hasn't been well thought through for TrustZone use. The L1/L2 cache controllers must also be TrustZone aware and you possible have issue there as well.
If you have Linux (or some other open source OS in the normal world), it would be better to replace the normal world interrupt driver with a 'virtual' interrupt driver. The normal world virtual IRQ code would use the SMC instruction to set virtual registers and register IRQ routines for specific interrupts. The secure world/monitor IRQ code would then branch directly to the decoded IRQ routine.
With a GIC, set the group 0 (secure world) interrupts as FIQ and group 1 (normal world) as IRQ using the GICC_CTLR bit FIQEnb. Ie, you classify the interrupts with the DIST in the GIC to be either secure or normal (and therefore FIQ/IRQ).
You have to work through scheduling issues and how you want the different OS's to pre-empt. Normally (easiest) is to always have the secure OS running, but this means that some Linux (normal world) interrupts may be very delayed by the secure world (RTOS) main line code.
I have to check whether the ARM processor is in SVC or IRQ mode during kernel bootup. That is, I want to insert some code to check the ARM mode into start_kernel() function before the interrupts are enabled and after the interrupts are enabled.
I know that I need the SPSR or CPSR values(bits) to check the mode in ARM, but how can I write code for that in start_kernel function since the code for reading bits of CPSR/SPSR is in assembly? Where do I put the assembly code to check the ARM mode during bootup time? Is there any way to dump the SPSR/CPSR values?
I don't dare imagine why this should be a concern, but fortunately there's an easy answer:
It's in SVC mode.
The very first thing* the kernel entrypoint does is to forcibly switch into SVC mode and mask interrupts. To somehow be in the wrong mode by the time you reach C code in start_kernel way later would need the system to be unimaginably horribly broken. The only way I can conceive of that even being possible is if there is some Secure firmware running asynchronously (e.g. off a Secure timer interrupt) which deliberately corrupts the Non-secure state, and that's sufficiently ridiculous to discount.
* OK, the second thing if the kernel is built with KVM support and entered in HYP, but hey...
after the boot the ARM processor is in secure SVC mode during this mode you can access CPSR register(first 6 bits) to check in which mode however when the kernel is runing you are in user non-secure mode and in this mode non secure you can't access the the CPSR and SPSR register and also copro 15 register .
The only way is to write a code that generate an exception to switch monitor mode " using SMC assebmbly instruction" to jump to monitor secure mode then in that mode you reset the "NS" bit to switch to non-secure then you generate another exception to switch the SVC mode(SVC instruction assembly call) now you are in supervisor secure mode and you can then access CPSR and SPSR register
In the U-boot start up code (arch/arm/cpu/armv7/start.S), FIQ and IRQs are disabled using CPSR(current program status register).
Can you please tell me why we have to disable these interrupts in the startup code?
I would say for safety. U-boot may not be the first loader in the system, and if the loader is chained from another loaded, it may have already enabled interrupt sources that may occur. Disabling interrupts ensures that U-boot initialization phase is not altered by any unwanted interrupt.
Platform - ARM9
I have a third party device connected via I2C to the ARM9. My problem is the I2C read/write is getting in a twist. It appears the IRQ line is asserted but never de-asserted when there is data to read. The read fails as the third-party device NACKs the address packet. So any subsequent write fails.
I am wondering if my interrupt handling in ok. In the ISR that services the IRQ, I disable interrupts, unregister the interrupt handler and then signal to the task to go read from the I2C bus. Finally, I re-enable the interrupts.
When the task services the signal posted above, I attempt to read data from the I2C bus but this fails. Finally, I always reregister the ISR after ever read attempt. There is no interrupt disabling/enabling that takes place during handling of the read signal.
My question is do I need to disable interrupts when reading/writing to the I2C bus?
Programming language of choice is c using propriety RTOS.
An important thing is whether your RTOS/system is ready to support nested exceptions. Unless there is a good reason to do so, things are simpler if you avoid nested exceptions and disable all interrupts when entering an ISR and re-enabling when leaving.
If you want to allow other higher-priority interrupts to occur while you are serving the I2C interrupt, then disable only the I2C interrupt. It is rather unusual to unregister an interrupt handler when entering an ISR. This may lead to unexpected behaviour, when there is no registered handler, the interrupt itself is enabled and an interrupt occurs. So instead of unregistering the handler, simple disable the I2C interrupt (Perhaps you are already doing so, but as I see it, registering a handler and enabling an interrupt are two different things).
A good strategy to solve your problem will be to try to communicate with the device without interrupts. Try to read/write from it in a serial fashion, doesn't matter if everything blocks - it is just testing. This is much easier to debug and after you are successful you can move to the interrupts version.
Most interrupts need to be acknowledged or cleared. You mention enabling/disabling, registering/unregistering and handling the interrupt. Just check that the interrupt is being acknowledged and/or cleared/reset. Often this involves writing the interrupt number or bit back to the interrupt pending register. Check the specific ARM manual or your RTOS manual.
Whether you need to enable/disable interrupts for your target platform is dependant on your specific hardware/RTOS implementation. Unfortunately, every ARM microcontroller vendor (STMicro, Freescale, Oki, etc) has the ability implement their I2C hardware differently and may have different requirements in how to clear the IRQ.
I'd recommend you find a copy of the hardware datasheet (and/or post the specific hardware part-number here so we can help pour over the vendor documentation, with you).