U-Boot porting

From postmarketOS
Jump to navigation Jump to search


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.
  • CONFIG_DEBUG_UART=y
  • CONFIG_DEBUG_UART_SKIP_INIT=y - make sure to comment _debug_uart_init function code in you serial driver with that option
  • CONFIG_DEBUG_UART_BASE=0x13820000 // your uart address from downstream device tree
  • CONFIG_LOGLEVEL=8 // log all


Building

Make u-boot

Install arm cross compiler Build:

export CROSS_COMPILE=aarch64-linux-gnu- 
make <$board_name>_defconfig
make

Create android boot image

mkbootimg --base 0x40000000 --kernel_offset 0x00008000 --ramdisk_offset 0x01000000 --tags_offset 0x00000100 --pagesize 2048 --second_offset 0x00f00000 --kernel {path to u-boot.bin} -o {output file}

Replace offsets to your offsets, found from downstream boot.img.

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

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

Will contain node for uart driver, fixed clock and chosen.

a5y17lte device tree example

exynos7880.dtsi:

// SPDX-License-Identifier: GPL-2.0+

/dts-v1/;
#include "skeleton.dtsi"
/ {
	compatible = "samsung,exynos7880";

	fin_pll: xxti {
		compatible = "fixed-clock";
		clock-output-names = "fin_pll";
		u-boot,dm-pre-reloc;
		#clock-cells = <0>;
	};

	uart2: serial@13820000 {
		compatible = "samsung,exynos4210-uart";
		reg = <0x13820000 0x100>;
		u-boot,dm-pre-reloc;
	};
};

a5y17lte.dts:

// SPDX-License-Identifier: GPL-2.0+

/dts-v1/;
#include "exynos7880.dtsi"
/ {
	compatible = "samsung,exynos7880";

    aliases {
    	console = &uart2;
    };

    chosen {
        stdout-path = &uart2;
    };
};
 
&fin_pll {
	clock-frequency = <26000000>;
};


Final changes

You may need to comment some serial driver initialization code, as it may not work properly without clock and pinctrl drivers.

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

Testing future u-boot builds

Once you entered in u-boot console, you may upload new u-boot build in RAM via uart and run it

Upload a file into RAM

  • on u-boot console: loadb <address>. If no address specified, file will be loaded at CONFIG_SYS_LOAD_ADDR
  • exit u-boot console to release tty device
  • install ckermit
  • send file:
$kermit
kermit> set port /dev/ttyUSB0
kermit> set speed 115200
kermit> set carrier-watch off
kermit> set flow-control none
kermit> set prefixing all

Source

TODO Figure out, how to transfer files from picocom with ymodem or xmodem or gkermit

Run uboot test version

On u-boot console: go <address of loaded file>