Jump to content

U-Boot porting: Difference between revisions

From postmarketOS Wiki
Dsankouski (talk | contribs)
No edit summary
Dsankouski (talk | contribs)
No edit summary
Line 30: Line 30:
* 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_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_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 <code>_debug_uart_init</code> 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 ===
=== Building ===
{{todo|Add what init code to comment out}}
==== Make u-boot ====
Install arm cross compiler
Install arm cross compiler
Build:
Build:
Line 39: Line 43:
  make <$board_name>_defconfig
  make <$board_name>_defconfig
  make
  make
{{todo| How to assemble android boot image }}
==== 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 ==
== Getting uart debug output ==
Line 53: Line 59:


=== Using debug() function ===
=== 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.


Line 98: Line 98:


== Creating basic device tree ==
== Creating basic device tree ==
{{todo|Add Template and examples}}
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! ==
== Congrats!  You should now get a u-boot console prompt! ==

Revision as of 13:49, 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.
  • 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!