U-Boot porting: Difference between revisions
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 === | ||
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] | |||
==== 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 == | ||
=== 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 == | ||
You should tell u-boot base address, i.e. address in RAM, where stock bootloader load u-boot. | 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 | |||
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. | |||
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); // | 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
- Find uboot code
- 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)
- Get familiar with you phone RAM map
- 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
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: Add what init code to comment out |
Install arm cross compiler Build:
export CROSS_COMPILE=aarch64-linux-gnu- make <$board_name>_defconfig make
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: Add Template and examples |