Search code examples
armu-boot

How to use the U-Boot API from a bare-metal ARM program loaded by U-Boot?


Is there a way to call U-Boot API functions from within an ARM program loaded with the go command ?

I see that the U-Boot API is available after startup at an address defined in the environment variable api_address but i can't make sens of how to use it.

For example, if i want to call the getc function that ones see in U-Boot api/api.c, how do i actually call it from within an assembly program ?

I tried this (on ARM1176JZF-S board) but this crash :

.global _start

_start:
    ldr r0, =0x1BB89148 ; api_address value
    ldr r1, [r0, #32] ; offset to get getc location
    bx r1 ; call getc

The rational was to call the second function (API_getc) from the syscall_table initialized in U-Boot api.c with r0 being the value of api_address.


Solution

  • Looks like i was misguided with api_address as it is not the address of the jump table but the api_signature struct defined in api/api.c, this hold a syscall member which is the address of the syscall function defined in that same file, this function can be used to call the U-Boot API (the api example show that)

    So for API call this works :

    // U-Boot API call
    ldr r4, =0x1bba50b0 // "api_address" value (U-Boot env. variable)
    mov r0, #1 // U-Boot syscall number (getc)
    mov r1, #0 // retval argument
    add r2, sp, #4 // address of getc returned value
    mov lr, pc
    ldr pc, [r4, #16] // call syscall(1, 0, &v)
    ldr r3, [sp, #4] // get getc returned value in r3
    

    This works to call any API functions defined in include/api_public.h below the api_signature struct

    The standalone example works a bit differently doc/README.standalone and has much less features than the API, the examples/standalone/stubs.c show some assembly code, this code use the U-Boot global data pointer stored in R9 on ARM (global_data struct in include/asm-generic/global_data.h) plus some computed offset to locate the jump table (struct jt member) which contains pointers to U-Boot exported functions, it then call a function stored in that table.

    So in this case this works :

    .global _start
    
    _start:
        // U-Boot call
        adr lr, halt // get back to "halt" after calling getc
        ldr ip, [r9, #124] // load jump table address (global data pointer is in r9 + offset to the *jt* member)
        ldr pc, [ip, #4] // call getc then r0 hold getc returned value
        //
    
    halt:
        wfe
        b halt
    

    Note that the offset to locate the jump table may change with different U-Boot config. options so it may be better to use the U-Boot API since the API address is available in an environment variable.