On arm platform, the u-boot will invalidate TLBs, icache and BP array at beginning, but what's the reason? Is it necessary?
cpu_init_crit:
/*
* Invalidate L1 I/D
*/
mov r0, #0 # set up for MCR
mcr p15, 0, r0, c8, c7, 0 # invalidate TLBs
mcr p15, 0, r0, c7, c5, 0 # invalidate icache
mcr p15, 0, r0, c7, c5, 6 # invalidate BP array
mcr p15, 0, r0, c7, c10, 4 # DSB
mcr p15, 0, r0, c7, c5, 4 # ISB
A reset may be hard or soft. It is possible for something to jump to a reset vector. If cache is enabled with stale entries, the code may crash resulting in a system not booting. This can be more common than you think as SDRAM may miss behave after a cold power on and mis-read causing a crash. Often a watchdog or unrecoverable faults will jump to the reset vector. Finally, there maybe u-boot systems in XIP NOR flash. Some systems/code may just jump to this code to perform a soft reset.
In all of these cases, the debug available is NIL. You may have a working system in 99.9999% of the cases. Having to trouble shoot a boot failure on particular hardware which only occurs in a hot bar or an ice cream freezer might make you enjoy this extra code. Ie, there are rare circumstances where it is needed.
Hardware failures are much more common as supply rails come on line. Datasheets describe properly behaving systems with power/temperature, etc in range. u-boot may not operate in this ideal world. If your concern is boot time, you are much better to write your own loader (keep this code though) or optimize things like BSS clearing, etc.
On A15/A7/A12 you do not need to do cache/mmu/btb invalidation on post-reset, so this is done just for "paranoia" reasons. However, there might be cores which do not perform auto-invalidate on reset so this code just makes sure that the same behavior is preserved across different core types.
This is necessary in the cases where the invalidation is not done in hardware on a reset (cold or warm). Another more practical reason is that U-Boot is typically not the first piece of code that runs on the hardware. In most cases there would be the ROM code that runs before U-Boot and to avoid making any assumptions regarding the state of the hardware when U-Boot got control of the system it's safer to always try and get the hardware to a known state and proceed from there.
Related
I am trying to evict the memory address in which the stack pointer is pointing to it in an ARM Cortex-A8 processor. I am trying to do that with the below code:
cpy r3, sp
mcr p15, 0x0, r3, cr7, cr6, 0x1
I have run the above code in a loadable kernel module. after running the above code in the kernel, OS crashes and needs a restart. but the above instructions work fine for flushing a variable from the cache.
Can anyone give me any advice to solve the problem?
Thanks to artless noise, actually ARM cortex-a8 has 3 types of command for cache manipulation based on Modified Virtual address:
Invalidate (C6, 1) (just invalidate the cache line)
Clean (C10, 1) (Update memory if the cache line is dirty)
Clean & Invalidate (C14, 1) (Update memory then invalidate cache line)
and as you can see in the question I used Invalidate instruction and it caused that memory to have invalid data for the stack. but after using Clean&Invalidate instruction the problem was solved. so the final code is as below:
cpy r3, sp
mcr p15, 0x0, r3, cr7, cr14, 0x1
DSB SY
I would like to create a data abort and pre-fetch abort to test whether the exception handlers for the same are getting called properly or not. As per my understanding that dereferencing a NULL pointer can cause data abort. But I am not getting how to create a pre-fetch abort for testing. I am working on armv7a. I am not using any OS, working on the boot code.
both of these are bus faults, jumping into unknown code is going to result in an undefined instruction not prefetch abort. I would focus on unaigned accesses do an ldr with bit 1 set and do a bx with bit 1 set (but bit 0 not set) would be where I start. If those dont work it might not be possible with a test fixture inside the chip.
There may be portions of the address space that dont respond, those should simply hang the processor, but you might get lucky and the memory controller returns a fault.
if you have parity or ecc in your system those would be the best way, assuming you have a way to inject an error into those memories to force a parity or ecc fault (also assuming that the memory controller, etc for that design (little to no logic that is relevant to your question is part of the ARM processor) returns a fault on a parity or ecc error).
cortex-m might fault on some address spaces as they to some extent dictate where you are supposed to go.
If one of the newer cores you can use the mmus protection and I dont know if that returns a data/prefetch fault or not, setup the mmu such that some space is at a different access level than the code you are going to hit it with and see what fault you get.
EDIT
Have to look for an armv7 but on an armv6 (pi 1 for example) if I enable alignment checking in the control register, and do an ldr of say address 0x1001 which is an alignment fault then it gives me a data abort.
save a line of code and use address 0x01
mrc p15, 0, r0, c1, c0, 0
orr r0,#2
mcr p15, 0, r0, c1, c0, 0
mov r0,#0x1
ldr r0,[r0]
jumping into an invalid instruction causes an undefined not a prefetch abort, the memory system has to assert the abort, so you can use the mmu for this most likely,
undefined exception
.globl TEST
TEST:
.word 0xFFFFFFFF
bx lr
easiest way to make a prefetch abort (as stated in the ARM ARM).
.globl TEST
TEST:
bkpt
For a data abort you can just attempt to read an unmapped or non-readable memory region. For example try reading/writing a NULL pointer.
For prefetch abort just try jumping to a random address in some unmapped, non-readable or non-executable region (do you have MMU enabled at this stage?):
mov r0, #0xFFFFFFFF ; Some address that is satisfying the above
push r0 ; push it to the stack
pop pc ; jump to that address
Note, that just jumping to a random address might result in an Undefined abort instead, as it might be executable but contain an unknown instruction.
I have written code to enable performance monitoring register as user accessible by setting bit as 1. I getting ARM_BAD_INSTRUCTION at MCR instruction and MRC is going fine.
I am using armv7(cortex a5)
.cfi_startproc
MRC p15, 0, r0, c9, c14, 0 # Read PMUSERENR Register
ORR r0, r0, #0x01 # Set EN bit (bit 0)
MCR p15, 0, r0, c9, c14, 0 # Write PMUSERENR Register
ISB # Synchronize context
BX lr
.cfi_endproc
As per the documentation, PMUSERENR is only writeable from privileged modes, thus an attempt to write to it from unprivileged userspace will indeed raise an undefined instruction exception.
If you want to enable userspace access, you need to do it from the kernel (or possibly from a hypervisor/firmware in the case of a kernel which doesn't know about PMUs itself).
If you don't have control of any such privileged software, well then you're not getting yourself direct access, because that's rather the point of the notion of privilege. What you might have, however, is some userspace API provided by the OS - such as perf events on Linux - to let you profile and measure stuff without the hassle of managing the hardware directly; frankly that's usually the better option even if you could enable direct access, because userspace still has no way to properly handle all the necessary event filtering, scheduling, overflow interrupts, etc. on a multitasking system.
I want to try cache clean and cache invalidate in Raspberry Pi
Can someone guide on this. I just want to do some DMA transfer and try some stuff.
Also if some one can give me a code snippet on how to check if cache has been cleaned in ARM It will be really helpfull
For Eg. I want to see state of cache memory and give instruction
Cache invalidate and see the state of memory
For the Raspberry Pi's ARM1176, cache maintenance is performed via the c7 group of System Control Coprocessor (CP15) operations. Data cache clean+invalidate is mcr p15, 0, r0, c7, c14, 0, and the Cache Dirty Status Register (which simply tells you if the cache is clean or has been written to) can be read with mrc p15, 0, <Rd>, c7, c10, 6.
There's too much information to regurgitate here, so I'd recommend referring to the TRM for the details - if the Raspberry Pi TRM doesn't cover it, you can find the full ARM1176 TRM here (which contains a few example code snippets). As always, the Linux source also provides a handy real-world usage reference.
As for inspecting the actual cache contents, AFAIK you're going to need a JTAG hardware debugger for that, if at all.
According to some tutorials, we will disable MMU and I/D-Caches at the beginning of bootlaoder. If I understand correctly, it aims to use the physical address directly in the program, so please correct me if I'm wrong. Thank you!
Secondly, we do this to disable MMU and Caches:
mrc P15, 0, R0, C1, C0, 0
bic R0, R0, #0x00002300 # clear bits 13, 9:8
bic R0, R0, #0x00000087 # clear bits 7, 2:0
orr R0, R0, #0x00000002 # set bit 2 (A) Align
orr R0, R0, #0x00001000 # set bit 12 (I) I-Cache
mcr P15, 0, R0, C1, C0, 0
D-Cache, MMU and Data Address Alignment Fault Checking have been disabled by clear bits 2:0, but why we enable bit 2 immediately in the following instrument? To make sure this manipulation is valid?
Last question is why D-cache is disabled but I-caches is able? To speed up instrument process?
Last question is why D-cache is disabled but I-caches is able? To speed up instrument process?
The MMU has settings to determine which memory regions are cacheable or not. If you do not have the mmu on but you have the data cache on (if possible) then you cannot safely talk to peripherals. if you read the uart status register for example that goes through the cache just like any other data operation, whatever that status is stays in the cache for subsequent reads until such time as that cache line is evicted and you get one more shot at the actual register. Lets say for example you have some code that polls the uart status register waiting for a character in the rx buffer. If that first read shows there is no character, that status goes in the cache, you will remain in the loop forever since you will never get to talk to the status register again you will simply get the cached copy of the register. if there was a character in there then that status also gets cached, you read the rx register, and perhaps do something, if when you come back again if the status has not been evicted from the data cache then you get the stale status which shows there is a character, you rx buffer read may or may not also be cached so you may get the stale value in the cache, you may get a stale value or whatever the peripheral does when you read and there is no new value or you might get a new value, but what you dont get in these situations is proper access to the peripheral. When the mmu is on, you use the mmu to mark the address space used by that peripheral as non-(data)-cacheable, and you dont have this problem. With the mmu off you need the data cache off for arm systems.
Leaving the I-cache on is okay because instruction fetches only read instructions...Well for a bare metal application that is okay, it helps for example if you are using a flash that has a potential for read disturb (spi or i2c flashes). The problem is this application is a bootloader, so you must take some extra care. For example your bootloader has some code at address 0x8000 that it runs through at least once, then you choose to use it as a bootloader, the bootloader might be at say address 0x10000000 allowing you to load a new program at 0x8000, this load uses data accesses so it does not go through the instruction cache. So there is a potential that the instruction cache has some or all of the code from the last time you were in the 0x8000 area, and when you branch to the bootloaded code at 0x8000 you will get either the old program from cache or a nasty mixture of old program and new program for the parts that are cached and not cached. So if your bootloader allows for the i-cache to be on, you need to invalidate the cache before branching to bootloaded code.
Lastly, if you or anyone using this bootloader wants to use jtag, then you have that same problem but worse, data cycles that do not go through the i-cache are used to write the new program to ram, when you tell the jtag debugger to then run the new program you will get 1) only the new program, 2) a mixture of the new program and old program fragments from cache 3) the old program from cache.
So d-cache is bad without an mmu because of things that are not in ram, peripherals, etc. The i-cache is a use at your own risk kind of thing which you can mitigate except for the times that jtag is used for debugging.
If you have concerns or have confirmed read-disturb in your (external) flash, then I recommend turn on the i-cache, use a tight loop to copy your application to ram, branch to the ram copy and run there, turn off the i-cache (or use at your own risk) and dont touch the flash again, certainly not heavy read accesses to small areas. A tight uart polling loop like you might have for a command line parser, is a really good place to get hit with read-disturb.
You did not specified on which ARM you are working. Capabilities may vary from one ARM to an other (there is a huge gap between an ARM9 and an ARM Cortex A15).
In the given code, bit 2 is cleared and then set, but it does not matter, as those changes are done in R0. There is no change in the ARM behavior until the write in CP15 register (done by the instruction mcr P15, 0, R0, C1, C0, 0).
Concerning d-cache/i-cache enabling, it is only a matter of choice, there is no requirement. On the products I work on, the bootloader enables L1 I-cache, D-cache, L2 cache, and MMU (and it disables all that stuff before jumping on Linux). Be sure to follow ARM documentations about cache invalidation and memory barriers (according to your actual ARM Core) if you use cache and MMU in your bootloader.