Search code examples
assemblyarmraspberry-pi3

Assembler error when using 'dccmvac' in a kernel module


When I try to compile the following code in a kernel module on a Raspberry Pi 3B+ (ARMv8a, AARCH32), I get a 'bad instruction' error. According to ARM Reference Manual for ARMv8a, this instruction is undefined at Exception Level 0 (EL0) for AARCH32, but I'm using it in a kernel module so I don't understand the issue.

__asm(
"mov r9,%0 \n\t"
"mov r10,%1 \n\t"
"mov r1,#0 \n\t
"str r1,[r9] \n\t"
"str r1,[r10] \n\t"
"DCCMVAC r9 \n\t"
"DCCMVAC r10 \n\t"
:
:"r" (firstPtr),
 "r" (secondPtr)
:"r1","r9","r10","memory"
);

The assembly-related message is as follows,

/tmp/ccCmW5TY.s: Assembler messages:
/tmp/ccCmW5TY.s:221: Error: bad instruction `dccmvac r9'
/tmp/ccCmW5TY.s:222: Error: bad instruction `dccmvac r10'

Solution

  • The DCCMVAC instruction is likely not recognized by GNU as: According to Arm documentation, the DCCMVAC instruction is being performed by performing an operation on the CP15 coprocessor:

    MCR{<c>}{<q>} <coproc>, {#}<opc1>, <Rt>, <CRn>, <CRm>{, {#}<opc2>} 
    with the following values:
    coproc  opc1    CRn     CRm     opc2
    0b1111  0b000   0b0111  0b1010  0b001
    

    There is an implementation in the CMSIS_5 project in the form of the following define/function/macro, in files CMSIS/Core_A/Include/cmsis_gcc.h and CMSIS/Core_A/Include/cmsis_cp15.h.

    We can re-use some code for building an example program dccmvac.c:

    #include <stdint.h>
      
    #define __ASM                                  __asm
    #define __set_CP(cp, op1, Rt, CRn, CRm, op2) __ASM volatile("MCR p" # cp ", " # op1 ", %0, c" # CRn ", c" # CRm ", " # op2 : : "r" (Rt) : "memory" )
    #define __STATIC_FORCEINLINE                   __attribute__((always_inline)) static inline
    
    /** \brief  Set DCCMVAC
        
      Data cache clean
     */
    __STATIC_FORCEINLINE void __set_DCCMVAC(uint32_t value)
    {
      __set_CP(15, 0, value, 7, 10, 1);
    }
    
    int main()
    {
      __set_DCCMVAC(0x12345678U);
    
      return 0;
    }
    
    
    /opt/arm/9/gcc-arm-9.2-2019.12-x86_64-arm-none-eabi/bin/arm-none-eabi-gcc -c -o dccmvac.o dccmvac.c
    /opt/arm/9/gcc-arm-9.2-2019.12-x86_64-arm-none-eabi/bin/arm-none-eabi-objdump -D  dccmvac.o
    
    
    
    dccmvac.o:     file format elf32-littlearm
    
    
    Disassembly of section .text:
    
    00000000 <main>:
       0:   e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
       4:   e28db000        add     fp, sp, #0
       8:   e24dd00c        sub     sp, sp, #12
       c:   e59f3020        ldr     r3, [pc, #32]   ; 34 <main+0x34>
      10:   e50b3008        str     r3, [fp, #-8]
      14:   e51b3008        ldr     r3, [fp, #-8]
      18:   ee073f3a        mcr     15, 0, r3, cr7, cr10, {1}
      1c:   e1a00000        nop                     ; (mov r0, r0)
      20:   e3a03000        mov     r3, #0
      24:   e1a00003        mov     r0, r3
      28:   e28bd000        add     sp, fp, #0
      2c:   e49db004        pop     {fp}            ; (ldr fp, [sp], #4)
      30:   e12fff1e        bx      lr
      34:   12345678        eorsne  r5, r4, #120, 12        ; 0x7800000
    

    Value 0x123456781 is being loaded into r3prior to mcr 15, 0, r3, cr7, cr10, {1} being executed.

    Disassembling 0xee073f3ausing shell-storm gives:

    0x0000000000000000: EE 07 3F 3A mcr p15, #0, r3, c7, c10, #1

    You should now have the information you need to use the DCCMVAC instruction in your code - just keep in mind that Arm CMSIS_5 code is licensed under the Apache-2.0 license, and therefore cannot just be copied/pasted into a Linux kernel module - you should probably use the Linux dccmvac macro defined in arch/arm/mm/cache-v7m.S instead, or write your own equivalent code.