Git Product home page Git Product logo

arm64-sysreg-lib's Introduction

Arm64 System Register Library

Introduction

arm64-sysreg-lib is a header-only C library for reading/writing 64-bit Arm registers, automatically generated by parsing the AArch64 System Register XML.

Key features:

  • Bit structs defined for 263 system registers
  • Safe values defined for all writeable registers, with all currently/previously RES1 bits set to 1 and all currently/previously RES0 bits cleared to 0
  • Accessors for read, unsafe write, safe write, and read-modify-write sequences defined according to register accessibility
  • All library calls optimise down to an average of between one and four A64 assembly instructions and are inlined with no branches and no static storage
  • Support for both gcc and clang, each with -Wall -Wextra -pedantic -Werror flags and supporting both -std=c99 and -std=c11

Prerequisites

To build the library:

  • Python 3.8+
  • Beautiful Soup 4 (pip3.8 install beautifulsoup4)

To use the library in your own projects:

  • C compiler supporting at least C99 with C11 extensions

Known working compilers:

  • aarch64-none-elf-gcc (GCC) 7.2.0:

    • C99: -Wall -Wextra -pedantic -Werror -std=c99 -O2
    • C11: -Wall -Wextra -pedantic -Werror -std=c11 -O2
  • Apple clang version 11.0.3 (clang-1103.0.32.62): *

    • C99: -Wall -Wextra -pedantic -Werror -std=c99 -O2 --target=aarch64-none-elf
    • C11: -Wall -Wextra -pedantic -Werror -std=c11 -O2 --target=aarch64-none-elf

* Apple Clang also tested with --target=aarch64-linux-gnu.

High-level functionality

Bit structs

Availability: All system registers.

All parsed system registers define a union of the form:

union sctlr_el1
{
    u64 _;
    struct
    {
        u64 m : 1;
        u64 a : 1;
        u64 c : 1;
        u64 sa : 1;
        ...
    };
};

The ._ member allows for raw access to the underlying register value while the anonymous struct member allows for manipulation of the register's constituent bit fields.

Safe values

Availability: All writeable system registers.

The safe value for a writeable system register has all currently or previously RES1 fields set to 1 and all currently or previously RES0 fields cleared to 0. These values are used by the safe_write_<reg>() convenience macros, or you can use them yourself when manually constructing a value to write into a register using unsafe_write_<reg>().

Example:

static const union sctlr_el3 SCTLR_EL3_SAFEVAL =
{
    .res1_5_4 = 3,
    .eos = 1,
    .res1_16 = 1,
    .res1_18 = 1,
    .eis = 1,
    .res1_23 = 1,
    .res1_29_28 = 3,
};

Some of these fields are currently RES1 bits, such as .res1_5_4 for bits [5:4] and .res1_16 for bit [16]. Setting these to 1 in the safe value ensures portability when running on future CPU implementations where those bits have been repurposed into new fields, as in these cases a value of 1 will give the old behaviour while a value of 0 will give the new behaviour, and we don't want to inadvertently enable the new behaviour by clearing them.

This is why the .eos and .eis fields are also set to 1; these fields were previosuly RES1 bits but were repurposed into new fields in later revisions of the architecture. The user can choose to clear these to 0 explicitly if they want the new behaviour, but the library defaults to setting them to 1 in the safe value and, by extension, the safe_write_<reg>() convenience macro.

Reads

Availability: All readable system registers.

Read the current value of a system register into a field-accessible structure:

/* C code */
#include "sysreg/mpidr_el1.h"
u64 foo( void )
{
    return read_mpidr_el1().aff0;
}

/* Compiler output */
mrs     x8, mpidr_el1
and     x0, x8, #0xff
ret

Writes

Unsafe write

Availability: All writeable system registers.

Write a field-accessible structure into a system register. This function is prefixed unsafe_ to emphasise the fact that it has no provision for helping to ensure that any currently or previously RES1 fields are set to 1; when using this function it is the responsibility of the programmer to ensure that any such bits are set appropriately so as to ensure both correct behaviour and future portability. For this reason, it is recommended that you instead use safe_write_<reg>() wherever possible, or use <REG>_SAFEVAL as a basis for the value being constructed.

/* C code */
#include "sysreg/sctlr_el1.h"
void foo( void )
{
    union sctlr_el1 val = { .m=1, .c=1, .i=1 };
    unsafe_write_sctlr_el1(val);
}

/* Compiler output */
mov     w8, #0x1005         // Danger! No RES1 bits set! See safe_write_<reg>()
msr     sctlr_el1, x8
ret

Use the union's ._ member to write a raw value:

/* C code */
#include "sysreg/sctlr_el1.h"
void foo( u64 raw )
{
    union sctlr_el1 val = { ._=raw };
    unsafe_write_sctlr_el1(val);
}

/* Compiler output */
msr     sctlr_el1, x0
ret

Safe write

Availability: All writeable system registers.

Use safe_write_<reg>() to set a variadic list of fields. Any fields not specified in the variadic list that are currently or previously RES1 will be set to 1, and all other fields will be cleared to 0.

For example:

/* C code */
#include "sysreg/sctlr_el1.h"
void foo( void )
{
    safe_write_sctlr_el1( .m=1, .c=1, .i=1 );
}

/* compiler output */
mov     w8, #0x1985
movk    w8, #0x30d0, lsl #16
msr     sctlr_el1, x8
ret

Note how while we only specified .m=1, .c=1, and .i=1, the value written to SCTLR_EL1 also has all currently or previously RES1 fields set to 1, such as .itd=1, .sed=1, .eos=1, etc.

Repeating the same, but this time explicitly clearing one of those currently or previously RES1 fields to 0 in the variadic list:

/* in C */
#include "sysreg/sctlr_el1.h"
void foo( void )
{
    safe_write_sctlr_el1( .m=1, .c=1, .i=1, .itd=0 );
}

/* compiler output */
mov     w8, #0x1905
movk    w8, #0x30d0, lsl #16
msr     sctlr_el1, x8
ret

Here we can see bit [7] corresponding to .itd in the first MOV has been cleared; the value moved into w8 is now 0x1905 vs 0x1985 in the earlier example.

Read-modify-write

Availability: All system registers that are both readable and writeable.

Use read_modify_write_<reg>() to read the current value of a system register, set a variadic list of fields in that value, then write the result back.

/* in C */
void foo( void )
{
    read_modify_write_sctlr_el1( .m=1, .c=1, .i=1 );
}

/* compiler output */
mrs     x8, sctlr_el1
mov     w9, #0x1005
orr     x8, x8, x9
msr     sctlr_el1, x8
ret

NOTE: Many AArch64 system registers have architecturally UNKNOWN values at reset, meaning you should not perform a read-modify-write sequence when first initializing them. Instead, use safe_write_<reg>() to guarantee that all currently or previously RES1 fields are set to 1 and all other unspecified fields are cleared to 0.

Building and testing the library

NOTE: The library has already been built for you using the June 2020 release of the AArch64 System Register XML (SysReg_xml_v86A-2020-06). You can simply add -I/path/to/arm64-sysreg-lib/include to your compiler flags to begin using the library in your own projects straight away. You can also run the run-tests.py script to build the compilation tests using your chosen compiler. More detailed instructions for running the compilation tests can be found at the end of this document.

Step 1) Obtain AArch64 System Register XML

Download and extract the AArch64 System Register XML from the Arm A-Profile CPU architecture exploration tools page.

Alternatively, use curl to download the Armv8.6-A XML published in June 2020:

curl -O https://developer.arm.com/-/media/developer/products/architecture/armv8-a-architecture/2020-06/SysReg_xml_v86A-2020-06.tar.gz
tar xf SysReg_xml_v86A-2020-06.tar.gz

Step 2) Build

Run the provided run-build.py script, pointing it at the AArch64 System Register XML downloaded and extracted earlier:

python3.8 run-build.py /path/to/SysReg_xml_v86A-2020-06

Step 3) Test

Run the provided run-tests.py script, pointing it at your chosen compiler:

python3.8 run-tests.py [--keep] COMPILER_PATH [COMPILER_FLAGS]

For example:

python3.8 run-tests.py /path/to/aarch64-none-elf-gcc 

It is assumed the compiler uses the same switches as gcc and clang, and the script always invokes the compiler with the following flags:

-Wall -Wextra -pedantic -Werror

You may pass additional flags to the run-tests.py script which will be passed in turn to the compiler, for example:

python3.8 run-tests.py /path/to/aarch64-none-elf-gcc -std=c99 -O3  

If no -std flag is provided, the script defaults to -std=c11.

If no -O flag is provided, the script defaults to -O2.

If the compiler path contains substring clang and no --target flag is provided, the script defaults to --target=aarch64-none-elf.

By default the script will cleanup the generated .o object files after finishing the test run; pass the --keep flag before the compiler path if you wish to keep these files.

Known issues and limitations

This library is still in development and is not yet able to parse all files included in the AArch64 System Register XML.

Of the 485 AArch64-* files included in the SysReg_xml_v86A-2020-06 release that actually describe system register encodings:

  • 126 are skipped as they correspond to instructions that use the system register encoding space (ic, dc, tlbi, at, cfp, cpp, and dvp)
  • 263 successfully build
  • 96 fail to parse

Of the 96 that fail to parse:

  • 43 fail as their fields vary, such a when a bit in another register is set to 1
  • 19 fail due to being an arrayed register (<n>)
  • 23 fail due to having arrayed fields (<m>, <n>, or <x>)
  • 11 fail due to having variable length fields

Support for these registers will be added in a future release.

Not counted above are external system registers such as GICD_* and GICR_*, support for which will also be added later.

arm64-sysreg-lib's People

Contributors

ashwio avatar axiqia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

arm64-sysreg-lib's Issues

add `volatile` keyword in asm to suppress the compiler's optimisations

I wrote a simple driver to modify the system register. It includes a header file in arm64-sysreg-lib/include/sysreg/pmcr_el0.h.

        printk(KERN_ALERT "raw variable value: %llx\n", pmcr_val._);
        pmcr_val.e = 1;
        printk(KERN_ALERT "after modifid variable value: %llx\n", pmcr_val._);
        unsafe_write_pmcr_el0(pmcr_val); 
        pmcr_val = read_pmcr_el0();
        printk(KERN_ALERT "read from pmcr_val reg: %llx\n", pmcr_val._);

It seems that the output is not as expected. The output is like below:

[351940.892080] raw variable value: 48006040
[351940.892398] after modifid variable value: 48006041
[351940.892877] read from pmcr_val reg: 48006040

The initial value of variable pmcr_val is 48006040 and we modified it to 48006041. Then, I wrote it into the register and read it out. The current value of the variable should be 48006041, but it is actually 48006040 as the output.

I intermixed the source code with disassembly.

       pmcr_val.e = 1;
  9c:   b2400275        orr     x21, x19, #0x1
        printk(KERN_ALERT "raw variable value: %llx\n", pmcr_val._);
  a0:   aa1303e1        mov     x1, x19
  a4:   90000000        adrp    x0, 0 <init_module>
  a8:   91000000        add     x0, x0, #0x0
  ac:   94000000        bl      0 <printk>
        printk(KERN_ALERT "after modifid variable value: %llx\n", pmcr_val._);
  b0:   aa1503e1        mov     x1, x21
  b4:   90000000        adrp    x0, 0 <init_module>
  b8:   91000000        add     x0, x0, #0x0
  bc:   94000000        bl      0 <printk>
  c0:   d51b9c15        msr     pmcr_el0, x21
        printk(KERN_ALERT "read from pmcr_val reg: %llx\n", pmcr_val._);
  c4:   90000000        adrp    x0, 0 <init_module>
  c8:   aa1303e1        mov     x1, x19
  cc:   91000000        add     x0, x0, #0x0
  d0:   94000000        bl      0 <printk>

It seems that the compiler deleted the read expression.

As a contrast, I added the volatile keyword to read and write the register.

        printk(KERN_ALERT "raw variable value: %llx\n", pmcr_val._);
        pmcr_val.e = 1;
        printk(KERN_ALERT "after modifid variable value: %llx\n", pmcr_val._);
        __asm volatile(
            "MSR s3_3_c9_c12_0, %0"
            : /**/
            : "r"(pmcr_val._));

        __asm volatile(
            "MRS %0, s3_3_c9_c12_0"
            : "=r"(pmcr_val._));
        printk(KERN_ALERT "read from pmcr_val reg: %llx\n", pmcr_val._);

Below is the assembly code

        pmcr_val.e = 1;
  38:   b2400293        orr     x19, x20, #0x1
        printk(KERN_ALERT "raw variable value: %llx\n", pmcr_val._);
  3c:   aa1403e1        mov     x1, x20
  40:   90000000        adrp    x0, 0 <init_module>
  44:   91000000        add     x0, x0, #0x0
  48:   94000000        bl      0 <printk>
        printk(KERN_ALERT "after modifid variable value: %llx\n", pmcr_val._);
  4c:   aa1303e1        mov     x1, x19
  50:   90000000        adrp    x0, 0 <init_module>
  54:   91000000        add     x0, x0, #0x0
  58:   94000000        bl      0 <printk>
        __asm volatile(
  5c:   d51b9c13        msr     pmcr_el0, x19
        __asm volatile(
  60:   d53b9c01        mrs     x1, pmcr_el0
        printk(KERN_ALERT "read from pmcr_val reg: %llx\n", pmcr_val._);
  64:   90000000        adrp    x0, 0 <init_module>

In short, we should add the volatile keyword when reading and writing registers to prevent the compiler from deleting the expression.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.