NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

port-arm/48710: Fixes for kernel ARMv7 cache invalidation routine

>Number:         48710
>Category:       port-arm
>Synopsis:       Fixes for kernel ARMv7 cache invalidation routine
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    port-arm-maintainer
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Apr 04 22:00:00 +0000 2014
>Originator:     Julius Werner
>Release:        Topmost SVN revision
I never actually built NetBSD, sorry.

I'm not really interested in NetBSD itself, but I recently needed some 
BSD-compatible ARMv7 cache invalidation routines for the coreboot project and 
decided to take yours (the function armv7_dcache_wbinv_all from 
src/sys/arch/arm/arm/cpufunc_asm_armv7.S). It's a nice piece of code, but it 
has some bugs in it which you probably want to fix as well:

1. The code uses R3 to store the current cache level, and counts upwards 
towards the Level of Coherency to iterate through all of them. Unfortunately, 
it already initializes R3 to LoC, so you are essentially "done" immediately and 
never really run the main loop. You want to start at 0 instead.

2. At the end of the invalidation loop, you try to extract the set from the 
way/set/level mask in R0. You shift it left to drop the way (10 highest bits), 
and then right to drop the level (4 lowest bits), but the second shift didn't 
account for the fact that the register was already shifted once. It should 
shift by 14, not 4.

3. Right after that you use conditional execution to check if the leftover set 
bits are zero or not, but in order to do that (at least in ARM mode) you need 
to use instructions that explictly set the condition code. The second shift 
should be LSRS, not just LSR.

Here's a diff that should fix all those problems (and some whitespace issues on 
top). It also reorders the way .Lnext_level_wbinv works to reuse a bit of that 
code in the initialization part. I have tested this code in coreboot and 
confirmed that it works as it should, but I have not actually compiled the 
NetBSD version (it should apply fine, though). You probably also want similar 
fixes in armv7_dcache_inv_all and armv7_icache_inv_all, but I did not take a 
close look at them.

--- a/src/sys/arch/arm/arm/cpufunc_asm_armv7.S
+++ b/src/sys/arch/arm/arm/cpufunc_asm_armv7.S
@@ -400,15 +400,20 @@
 /* * LINTSTUB: void armv7_dcache_wbinv_all(void); */
+       dsb
+       mov     r3, #-2                 @ initialize level so that we start at 0
+       add     r3, r3, #2              @ increment level
        mrc     p15, 1, r0, c0, c0, 1   @ read CLIDR
-       ands    r3, r0, #0x07000000
-       beq     .Ldone_wbinv
-       lsr     r3, r3, #23             @ left align loc (low 4 bits)
+       and     ip, r0, #0x07000000     @ narrow to LoC
+       lsr     ip, ip, #23             @ left align LoC (low 4 bits)
+       cmp     r3, ip                  @ compare
+       bge     .Ldone_wbinv            @ else fall through (r0 == CLIDR)

-       mov     r1, #0
-       add     r2, r3, r3, lsr #1      @ r2 = level * 3 / 2
+       add     r2, r3, r3, lsr #1      @ r2 = (level << 1) * 3 / 2
        mov     r1, r0, lsr r2          @ r1 = cache type
-       bfc     r1, #3, #29
+       and     r1, r1, #7
        cmp     r1, #2                  @ is it data or i&d?
        blt     .Lnext_level_wbinv      @ nope, skip level

@@ -420,14 +425,14 @@ ENTRY_NP(armv7_dcache_wbinv_all)
        add     ip, ip, #4              @ apply bias
        ubfx    r2, r0, #13, #15        @ get numsets - 1 from CCSIDR
        lsl     r2, r2, ip              @ shift to set position
-       orr     r3, r3, r2              @ merge set into way/set/level 
+       orr     r3, r3, r2              @ merge set into way/set/level
        mov     r1, #1
        lsl     r1, r1, ip              @ r1 = set decr

        ubfx    ip, r0, #3, #10         @ get numways - 1 from [to be 
discarded] CCSIDR
        clz     r2, ip                  @ number of bits to MSB of way
        lsl     ip, ip, r2              @ shift by that into way position
-       mov     r0, #1                  @
+       mov     r0, #1
        lsl     r2, r0, r2              @ r2 now contains the way decr
        mov     r0, r3                  @ get sets/level (no way yet)
        orr     r3, r3, ip              @ merge way into way/set/level
@@ -438,20 +443,12 @@ ENTRY_NP(armv7_dcache_wbinv_all)
 1:     mcr     p15, 0, r3, c7, c14, 2  @ writeback and invalidate line
        cmp     r3, #15                 @ are we done with this level (way/set 
== 0)
        bls     .Lnext_level_wbinv      @ yes, go to next level
-       lsl     r0, r3, #10             @ clear way bits leaving only set/level 
-       lsr     r0, r0, #4              @ clear level bits leaving only set bits
+       lsr     r0, r3, #4              @ clear level bits leaving only way/set 
+       lsls    r0, r0, #14             @ clear way bits leaving only set bits
        subne   r3, r3, r1              @ non-zero?, decrement set #
        subeq   r3, r3, r2              @ zero?, decrement way # and restore 
set count
        b       1b

-       mrc     p15, 1, r0, c0, c0, 1   @ read CLIDR
-       and     ip, r0, #0x07000000     @ narrow to LoC
-       lsr     ip, ip, #23             @ left align LoC (low 4 bits)
-       add     r3, r3, #2              @ go to next level
-       cmp     r3, ip                  @ compare
-       blt     .Lstart_wbinv           @ not done, next level (r0 == CLIDR)
        mov     r0, #0                  @ default back to cache level 0
        mcr     p15, 2, r0, c0, c0, 0   @ select cache level

Home | Main Index | Thread Index | Old Index