From 0ee212a448186598f17dafd07a776b9c0235a389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Sun, 11 Aug 2019 00:00:00 +0000 Subject: [PATCH] d7a24ea2002547350e9b23f2a8f468b06dbd0836 --- index.html | 186 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 167 insertions(+), 19 deletions(-) diff --git a/index.html b/index.html index 0084a5d..a7ff123 100644 --- a/index.html +++ b/index.html @@ -1210,10 +1210,11 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
  • 20.4. Userland multithreading
  • @@ -19953,6 +19959,9 @@ qemu-system-aarch64 -M virt -cpu cortex-a57 -nographic -smp 1 -kernel output/ima
    xdg-open "$(./getvar --arch aarch64 --gem5-build-id ruby gem5_build_build_dir)/ARM/mem/protocol/html/index.html"
    +
    +

    A minimized ruby config which was not merged upstream can be found for study at: https://gem5-review.googlesource.com/c/public/gem5/+/13599/1

    +
    @@ -20502,16 +20511,6 @@ git -C "$(./getvar qemu_source_dir)" checkout -
    -
  • -

    malloc

    - -
  • @@ -20554,9 +20553,28 @@ git -C "$(./getvar qemu_source_dir)" checkout -
    -

    20.1.1. GCC C extensions

    +

    20.1.1. malloc

    + +
    + +
    +
    +

    LInux 5.1 / glibc 2.29 implements it with the mmap system call.

    +
    +
    +
    +

    20.1.2. GCC C extensions

    -
    20.1.1.1. C empty struct
    +
    20.1.2.1. C empty struct
    @@ -20568,7 +20586,7 @@ git -C "$(./getvar qemu_source_dir)" checkout -
    -
    20.1.1.2. OpenMP
    +
    20.1.2.2. OpenMP

    GCC implements the OpenMP threading implementation: https://stackoverflow.com/questions/3949901/pthreads-vs-openmp

    @@ -20737,6 +20755,40 @@ git -C "$(./getvar qemu_source_dir)" checkout -

    getconf is also specified by POSIX at: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html but not the -a option which shows all configurations.

    +
    +

    20.3.4. mmap

    +
    +

    The mmap system call allows advanced memory operations:

    +
    +
    + +
    +
    +

    mmap is notably used to implement the malloc ANSI C function, replacing the previously used break system call.

    +
    +
    +

    Linux adds has several POSIX extension flags to it.

    +
    +
    +
    20.3.4.1. brk
    +
    +

    Previously POSIX, but was deprecated in favor of malloc

    +
    + +
    +

    The example allocates two ints and uses them, and then deallocates back.

    +
    + +
    +

    20.4. Userland multithreading

    @@ -26925,13 +26977,49 @@ IN: main

    26.8.4. ARM timer

    -

    TODO get working. Attempt at: baremetal/arch/aarch64/timer.c

    +

    The ARM timer is the simplest way to generate hardware interrupts periodically, and therefore serves as the simples example of ARM GIC usage.

    -

    The timer is documented at: ARMv8 architecture reference manual db Chapter D10 "The Generic Timer in AArch64 state"

    +

    Working on QEMU: baremetal/arch/aarch64/timer.c

    +
    +
    +
    +
    ./run --arch aarch64 --baremetal baremetal/arch/aarch64/timer.c
    +
    -

    The key registers to keep in mind are:

    +

    Output at lkmc d8dae268c0a3e4e361002aca3b382fedd77f2567 + 1:

    +
    +
    +
    +
    cntv_ctl_el0 0x0
    +cntfrq_el0 0x3B9ACA0
    +cntv_cval_el0 0x0
    +cntvct_el0 0x105113
    +cntvct_el0 0x1080BC
    +cntvct_el0 0x10A118
    +
    +IRQ number 0x1B
    +cntvct_el0 0x14D25B
    +cntv_cval_el0 0x3CE9CD6
    +
    +IRQ number 0x1B
    +cntvct_el0 0x3CF516F
    +cntv_cval_el0 0x7893217
    +
    +IRQ number 0x1B
    +cntvct_el0 0x789B733
    +cntv_cval_el0 0xB439642
    +
    +
    +
    +

    and new IRQ number section appears every second, when a clock interrupt is raised!

    +
    +
    +

    Once an interrupt is raised, the interrupt itself sets up a new interrupt to happen in one second in the future after cntv_cval_el0 is reached by the counter.

    +
    +
    +

    The timer is part of the aarch64 specification itself and is documented at: ARMv8 architecture reference manual db Chapter D10 "The Generic Timer in AArch64 state". The key registers to keep in mind are:

      @@ -26963,6 +27051,66 @@ IN: main
    +

    Due to QEMU’s non-determinism, each consecutive run has slightly different output values.

    +
    +
    +

    From the terminal output, we can see that the initial clock frequency is 0x3B9ACA0 == 62500000 Hz == 62.5MHz. Grepping QEMU source for that string leads us to:

    +
    +
    +
    +
    /* Scale factor for generic timers, ie number of ns per tick.
    + * This gives a 62.5MHz timer.
    + */
    +#define GTIMER_SCALE 16
    +
    +
    +
    +

    which in turn is used to set the initial reset value of the clock:

    +
    +
    +
    +
        { .name = "CNTFRQ_EL0", .state = ARM_CP_STATE_AA64,
    +      .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0,
    +      .access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access,
    +      .fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq),
    +      .resetvalue = (1000 * 1000 * 1000) / GTIMER_SCALE,
    +
    +
    +
    +

    where (1000 * 1000 * 1000) / 16 == 62500000.

    +
    +
    +

    Trying to set the frequency on QEMU by writing to the CNTFRQ register does change the value of future reads, but has no effect on the actual clock frequency as commented on the QEMU source code https://github.com/qemu/qemu/blob/v4.0.0/target/arm/helper.c#L2647

    +
    +
    +
    +
    static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
    +    /* Note that CNTFRQ is purely reads-as-written for the benefit
    +     * of software; writing it doesn't actually change the timer frequency.
    +     * Our reset value matches the fixed frequency we implement the timer at.
    +     */
    +    { .name = "CNTFRQ", .cp = 15, .crn = 14, .crm = 0, .opc1 = 0, .opc2 = 0,
    +      .type = ARM_CP_ALIAS,
    +      .access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access,
    +      .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_cntfrq),
    +    },
    +
    +
    +
    +

    At each interrupt, we increase the compare value CVAL by about 1x the clock frequency 0x3B9ACA0 so that it will fire again in one second, e.g. 0x3CE9CD6 - 0x14D25B == 3B9CA7B. The increment is not perfect because the counter keeps ticking even while our register read and print instructions are running inside the interrupt handler!

    +
    +
    +

    We then observe that the next interrupt happens soon after CNTV_CVAL_EL0 is reached by CNTVCT_EL0:

    +
    +
    +
    +
    cntv_cval_el0 0x3CE9CD6
    +
    +IRQ number 0x1B
    +cntvct_el0 0x3CF516F
    +
    +
    +

    Bibliography: