Jump to content

U-Boot porting: Difference between revisions

From postmarketOS Wiki
Dsankouski (talk | contribs)
Dsankouski (talk | contribs)
No edit summary
Line 6: Line 6:
# Before starting uboot porting, you should get access to the stock bootloader uart port. Make sure you can see stock bootloader logs.
# Before starting uboot porting, you should get access to the stock bootloader uart port. Make sure you can see stock bootloader logs.
# Search uboot code for a uart driver, compatible with your SOC. It is good, if it support debug(i.e. include debug_uart.h)
# Search uboot code for a uart driver, compatible with your SOC. It is good, if it support debug(i.e. include debug_uart.h)
# Get familiar with you phone RAM map
# Some useful links:
- [http://xillybus.com/tutorials/uboot-hacking-howto-3 bring up process]<br/>
- [http://xillybus.com/tutorials/uboot-hacking-howto-1 configuration process] 


== Create basic configuration and build ==
== Create basic configuration and build ==
=== Add board files ===
=== Add board files ===
{{todo|add more specific info}}
Use [https://elinux.org/images/2/2a/Schulz-how-to-support-new-board-u-boot-linux.pdf guide from Free Electrons pdf] or [https://www.youtube.com/watch?v=5E0sdYkvq-Q video version]
Add simple board, see examples.
{{todo|Create template}}
==== Examples ====
==== Examples ====
# Add [https://gitlab.denx.de/u-boot/u-boot/-/commit/6c15a2a996214574e8145bff69d110a302edf277 exynos7420 espresso board]
# Add [https://gitlab.denx.de/u-boot/u-boot/-/commit/6c15a2a996214574e8145bff69d110a302edf277 exynos7420 espresso board]
# Add [https://gitlab.denx.de/u-boot/u-boot/-/commit/e39448e8be8389f5ddeabae0ec9c6a3b7b8a2ca6 exynos7420 SOC support]
# Add [https://gitlab.denx.de/u-boot/u-boot/-/commit/e39448e8be8389f5ddeabae0ec9c6a3b7b8a2ca6 exynos7420 SOC support]
=== Configure ===
U-boot configuration system comes from linux kernel. Put config options not supposed to be changed by user in <code>/include/configs/.*.h</code> files, in <code>*_defconfig</code> otherwise 
==== Options, you DO NOT need ====
* All SPL related options. SPL(Secondary Program Loader) splits u-boot in two parts. You don't need this, since stock bootloader will load whole u-boot image into RAM.
* CONFIG_BOARD_EARLY_INIT_F
==== Options, you need ====
* CONFIG_SYS_MALLOC_LEN - size of early C runtime environment heap
* CONFIG_SYS_INIT_SP_ADDR - address of early C runtime environment stack (should in RAM address space, but NOT at the bottom, because stack is growing DOWN. see u-boot readme memory management section)
* CONFIG_SYS_TEXT_BASE - u-boot base address. See instructions below, how to find it.


=== Building ===
=== Building ===
{{todo|Add what init code to comment out}}
Install arm cross compiler
Install arm cross compiler
Build:
Build:
Line 25: Line 42:


== Getting uart debug output ==
== Getting uart debug output ==
{{todo|add raw c and assemble examples. Also config options for debug_uart.h What init code to comment out}}
=== Raw ===
This may be useful, when no C runtime environment set up yet. You should find uart address to write
==== C ====
*(volatile unsigned char *)(0x13820020/*uart register to write characters*/) = 0x48; // print H letter
==== Assembly ====
mov x26, #0x20 // move 0x13820020 low 16 bit to x26 register
movk x26, #0x1382, lsl #16 // move 0x13820020 high 16 bit to x26 register
mov x27, #0x48 // H letter ascii code
str x27, [x26] // move H to uart register
 
=== Using debug() function ===
Enable options:
* CONFIG_DEBUG_UART=y
* CONFIG_DEBUG_UART_SKIP_INIT=y
* CONFIG_DEBUG_UART_BASE=0x13820000 // your uart address from downstream device tree
* CONFIG_LOGLEVEL=8 // log all
 
You can get debug output from particular *.c file by adding <code>#define DEBUG</code> at the top of file, and calling <code>debug("debug output");</code> where you need.
You can get debug output from particular *.c file by adding <code>#define DEBUG</code> at the top of file, and calling <code>debug("debug output");</code> where you need.


== Finding base address ==
== Finding base address ==
{{todo|How to dump PC register on ARM, to know RAM range to dump}}


You should tell u-boot base address, i.e. address in RAM, where stock bootloader load u-boot. Do it with CONFIG_SYS_TEXT_BASE option.
You should tell u-boot base address, i.e. address in RAM, where stock bootloader load u-boot. Keep in mind, that stock bootloader may ignore offsets, set in boot.img. This section describes, how to find it.
Without that knowledge u-boot will fail on `/common/board_f.c` file in function `init_sequence_f`. This function contains list of initcall addresses, and if CONFIG_SYS_TEXT_BASE does not match with address u-boot resides, you CPU will jump in wrong address and execute irrelevant code, leaving you with unpredictable uart output and results.
 
Without the knowledge of base address u-boot will fail on <code>/common/board_f.c</code> file when calling functions from <code>init_sequence_f</code>. This is a list of initcall addresses, and if CONFIG_SYS_TEXT_BASE does not match with address u-boot resides, you CPU will jump in wrong address and execute irrelevant code, leaving you with unpredictable uart output and results.
 
To find u-boot base address, you should dump RAM, where u-boot presumably resides, and find(hex search), at what address uboot image starts.
=== Enable debug ===
- put <code>#define DEBUG</code> at the top of <code>/common/board_f.c</code> file
=== Narrow down RAM range u-boot resides in ===
- read PC(program counter) or LR(link register) at board_init_f function
uint64_t pc;
asm volatile ("ADR %0, ." : "=r"(pc) ::);
debug("pc reg at board_init_f is: %p", pc);
Here we are [https://developer.arm.com/architectures/learn-the-architecture/armv8-a-instruction-set-architecture/registers-in-aarch64-other-registers reading pc reg] from [https://stackoverflow.com/questions/53550891/read-a-register-in-arm64-using-c-using-the-qnx-compiler C]
 
Once you have PC register value, u-boot base address 100% in range PC-uboot_size - PC+uboot_size.
=== How to dump ram ===
- put ram dump code at the beginning of <code>board_init_f</code> function.


To find u-boot base address, you should dump RAM, where u-boot presumably resides, and find, at what address uboot image starts. Result should be placed in CONFIG_SYS_TEXT_BASE


Ram dump code example:
Ram dump code example:
     char *val;
     char *val;
     val = 0x40001000;
     val = 0x40001000; // dump start address
     debug("0x40001000 - 0x40101000 \n");
     debug("0x40001000 - 0x40101000 \n");
     for(int i=0 ; i < 0x100000; i++) {
     for(int i=0 ; i < 0x100000 /* dump size*/; i++) {
             if ((i & 0xf) == 0) {
             if ((i & 0xf) == 0) {// new line every 16 symbols
                 debug("\n%.8x: ", val + i); // new line every 16 symbols
                 debug("\n%.8x: ", val + i); // current address stamp
             }
             }
             debug("%.2x ", *(val + i));
             debug("%.2x ", *(val + i));
         }
         }
     debug("\n");
     debug("\n");
=== Find uboot start address in dump ===
Find first few bytes of compiled u-boot image in RAM dump. Result will be u-boot base address.


== Creating basic device tree ==
== Creating basic device tree ==

Revision as of 13:14, 10 October 2020

It is possible to port u-boot on your phone, even if there is no SOC support in uboot tree (because stock bootloader initialized hardware for us). First thing to focus on should be getting u-boot shell prompt.

Preparation

  1. Find uboot code
  2. Before starting uboot porting, you should get access to the stock bootloader uart port. Make sure you can see stock bootloader logs.
  3. Search uboot code for a uart driver, compatible with your SOC. It is good, if it support debug(i.e. include debug_uart.h)
  4. Get familiar with you phone RAM map
  5. Some useful links:

- bring up process
- configuration process

Create basic configuration and build

Add board files

Use guide from Free Electrons pdf or video version

Examples

  1. Add exynos7420 espresso board
  2. Add exynos7420 SOC support

Configure

U-boot configuration system comes from linux kernel. Put config options not supposed to be changed by user in /include/configs/.*.h files, in *_defconfig otherwise

Options, you DO NOT need

  • All SPL related options. SPL(Secondary Program Loader) splits u-boot in two parts. You don't need this, since stock bootloader will load whole u-boot image into RAM.
  • CONFIG_BOARD_EARLY_INIT_F

Options, you need

  • CONFIG_SYS_MALLOC_LEN - size of early C runtime environment heap
  • CONFIG_SYS_INIT_SP_ADDR - address of early C runtime environment stack (should in RAM address space, but NOT at the bottom, because stack is growing DOWN. see u-boot readme memory management section)
  • CONFIG_SYS_TEXT_BASE - u-boot base address. See instructions below, how to find it.


Building

TODO TODO: Add what init code to comment out

Install arm cross compiler Build:

export CROSS_COMPILE=aarch64-linux-gnu- 
make <$board_name>_defconfig
make
TODO TODO: How to assemble android boot image

Getting uart debug output

Raw

This may be useful, when no C runtime environment set up yet. You should find uart address to write

C

*(volatile unsigned char *)(0x13820020/*uart register to write characters*/) = 0x48; // print H letter

Assembly

mov x26, #0x20 // move 0x13820020 low 16 bit to x26 register
movk x26, #0x1382, lsl #16 // move 0x13820020 high 16 bit to x26 register
mov x27, #0x48 // H letter ascii code
str x27, [x26] // move H to uart register

Using debug() function

Enable options:

  • CONFIG_DEBUG_UART=y
  • CONFIG_DEBUG_UART_SKIP_INIT=y
  • CONFIG_DEBUG_UART_BASE=0x13820000 // your uart address from downstream device tree
  • CONFIG_LOGLEVEL=8 // log all

You can get debug output from particular *.c file by adding #define DEBUG at the top of file, and calling debug("debug output"); where you need.

Finding base address

You should tell u-boot base address, i.e. address in RAM, where stock bootloader load u-boot. Keep in mind, that stock bootloader may ignore offsets, set in boot.img. This section describes, how to find it.

Without the knowledge of base address u-boot will fail on /common/board_f.c file when calling functions from init_sequence_f. This is a list of initcall addresses, and if CONFIG_SYS_TEXT_BASE does not match with address u-boot resides, you CPU will jump in wrong address and execute irrelevant code, leaving you with unpredictable uart output and results.

To find u-boot base address, you should dump RAM, where u-boot presumably resides, and find(hex search), at what address uboot image starts.

Enable debug

- put #define DEBUG at the top of /common/board_f.c file

Narrow down RAM range u-boot resides in

- read PC(program counter) or LR(link register) at board_init_f function

uint64_t pc;
asm volatile ("ADR %0, ." : "=r"(pc) ::);
debug("pc reg at board_init_f is: %p", pc); 

Here we are reading pc reg from C

Once you have PC register value, u-boot base address 100% in range PC-uboot_size - PC+uboot_size.

How to dump ram

- put ram dump code at the beginning of board_init_f function.


Ram dump code example:

   char *val;
   val = 0x40001000; // dump start address
   debug("0x40001000 - 0x40101000 \n");
   for(int i=0 ; i < 0x100000 /* dump size*/; i++) {
           if ((i & 0xf) == 0) {// new line every 16 symbols
               debug("\n%.8x: ", val + i); // current address stamp
           }
           debug("%.2x ", *(val + i));
       }
   debug("\n");

Find uboot start address in dump

Find first few bytes of compiled u-boot image in RAM dump. Result will be u-boot base address.

Creating basic device tree

TODO TODO: Add Template and examples

Congrats! You should now get a u-boot console prompt!