https://docs.banana-pi.org/en/BPI-F3/BananaPi_BPI-F3
https://docs.banana-pi.org/en/BPI-F3/GettingStarted_BPI-F3
https://wiki.banana-pi.org/Banana_Pi_BPI-F3
Attempts to get something to run via Ubuntu's/Debian's fastboot
(version 28.0.2-debian
) directly only results in hanging.
There is a script in the SpacemiT download archives though that invokes fastboot
directly:
https://archive.spacemit.com/image/k1/flash-all.zip
A newer version of fastboot
will do; get it here if your distro doesn't have it:
https://developer.android.com/tools/releases/platform-tools
People are puzzled.
https://forum.banana-pi.org/t/dfu-and-directly-flashing-the-emmc-using-simpler-tools/18049
I found notes on the usage of flashserver
here:
https://forum.banana-pi.org/t/emmc-image-installation-fail-fastboot-charmap-exception/18276/3
Download and extract the latest titanflasher: https://archive.spacemit.com/tools/titanflasher/
From the titantools binary, extract the flashserver
binary; it's essentially fastboot, but modified.
./titantools_for_linux-1.0.35-beta.AppImage --appimage-extract resources/app/flashserver
The file will then be at squashfs-root/resources/app/flashserver
.
I recommend putting it elsewhere and have it in your $PATH
.
NOTE: The path may change with a newer version of the AppImage.
You can mount it like this, which yields the mountpoint, so you can search for it:
./titantools_for_linux-1.0.35-beta.AppImage --appimage-mount
That will print a path like /tmp/.mount_titantFtuAIM
. When done, press Ctrl+C to unmount.
TODO: reverse it.
Here is my fork with a few extra changes to get the code to compile (mind the branch): https://github.com/orangecms/spacemit-u-boot/tree/v2022.10-k1
SpacemiT U-Boot creates a file named FSBL.bin
in the root directory.
That is what we can run now using flasherver
; after -cmd
, provide the fastboot command to run:
flashserver -cmd "stage FSBL.bin"
flashserver -cmd "continue"
For each command, it will ask you to select the port. With just one device attached, that is just 1
.
You can also do flasherver --list
and from there take the portPath
, which is something like [3, 2]
.
Join the two numbers with a -
, and provide that as the -port
:
flashserver -port 3-2 -cmd "getvar version-brom"
Alternatively, with a recent version of fastboot
(this here is 35.0.1-11580240
):
fastboot stage FSBL.bin
fastboot continue
Here is a log from U-Boot SPL with debug logging (loglevel > 0) enabled in drivers/ddr/spacemit/k1x/lpddr4_silicon_init.c
:
fastboot_handle_command: 0002b0a0
Starting download of 176288 bytes
usb_tx_bytes : len= 65 pBuf= 0xc083fe88
usb_rx_bytes : len= 176288 pBuf= 0xc0800000
usb_tx_bytes : len= 65 pBuf= 0xc083fe88
usb_rx_bytes : len= 4096 pBuf= 0xc0838720
SETUP: 0x80 0x6 0x300
SETUP: 0x80 0x6 0x30a
SETUP: 0x80 0x6 0x300
SETUP: 0x80 0x6 0x30a
fastboot_handle_command: continue
usb_tx_bytes : len= 65 pBuf= 0xc083fe88
j...
U-Boot SPL 2022.10spacemit-g0e3d5c910-dirty (Jul 11 2024 - 03:00:49 +0200)
ADDR[0xc0000304]=0x00800400 !!!!
PHY INIT done
wait DRAM INIT
DRAM INIT done
DRAM Mode register Init done.....
DEBUG-ADDR[0xc0000200]:0xf0001
DEBUG-ADDR[0xc0000204]:0x0
DEBUG-ADDR[0xc0000208]:0x800f0001
DEBUG-ADDR[0xc000020c]:0x0
DEBUG-ADDR[0xc0000220]:0x5030632
DEBUG-ADDR[0xc0000224]:0x5030632
ddr density: 4096 MB
enter self refresh start .....
enter self refresh start done .....
c0000000, 0, 2
Training start....
Training init....
dump margin and setting Before Training....
Write Leveling.....
Read Gate Training.....
Read_gate_training PASS!!
Read_gate_training PASS!!
read gate code[0xc0040070]=0x00012121
read gate code[0xc0040170]=0x00012222
read gate code[0xc0041070]=0x00011f1f
read gate code[0xc0041170]=0x00012020
Read Training.....
each RX Vref corresponding min margin = 22 22 22 23 23 24 24 24 23 23 23 22 21 18 17 17
optimize Rx Vref adjust=5 ,corresponding best margin=24
Again!!! training optimize Fine Rx vref step = 5
Write Training.....
each TX Vref corresponding min margin = 19 19 19 20 20 20 20 20 20 20 21 21 21 21 21 21
optimize Tx Vref adjust=31 ,corresponding best margin=21
Again!!! training optimize Fine Tx vref step = 31
Training status[0xC0058000]=0x00000000
change to 1600
frequency change done!!!!
enter self refresh start .....
enter self refresh start done .....
c0000000, 1, 2
Training start....
Training init....
dump margin and setting Before Training....
Write Leveling.....
Read Gate Training.....
Read_gate_training PASS!!
Read_gate_training PASS!!
read gate code[0xc0044070]=0x00012121
read gate code[0xc0044170]=0x00012222
read gate code[0xc0045070]=0x00011f1f
read gate code[0xc0045170]=0x00012020
Read Training.....
each RX Vref corresponding min margin = 14 15 15 16 17 17 17 17 16 16 15 15 14 12 10 9
optimize Rx Vref adjust=4 ,corresponding best margin=17
Again!!! training optimize Fine Rx vref step = 4
Write Training.....
each TX Vref corresponding min margin = 12 12 12 12 12 12 12 12 13 13 13 13 14 14 14 14
optimize Tx Vref adjust=33 ,corresponding best margin=14
Again!!! training optimize Fine Tx vref step = 33
Training status[0xC0058000]=0x00000000
change to 2400
frequency change done!!!!
enter self refresh start .....
enter self refresh start done .....
c0000000, 2, 2
Training start....
Training init....
dump margin and setting Before Training....
Write Leveling.....
Read Gate Training.....
Read_gate_training PASS!!
Read_gate_training PASS!!
read gate code[0xc0048070]=0x00012323
read gate code[0xc0048170]=0x00012323
read gate code[0xc0049070]=0x00012121
read gate code[0xc0049170]=0x00012121
Read Training.....
each RX Vref corresponding min margin = 8 8 9 9 10 11 11 11 10 10 9 8 8 4 0 0
optimize Rx Vref adjust=5 ,corresponding best margin=11
Again!!! training optimize Fine Rx vref step = 5
Write Training.....
each TX Vref corresponding min margin = 9 9 9 9 10 10 10 10 9 9 9 9 9 9 9 9
optimize Tx Vref adjust=25 ,corresponding best margin=10
Again!!! training optimize Fine Tx vref step = 25
Training status[0xC0058000]=0x00000000
change to 2400
frequency change done!!!!
lpddr4_silicon_init consume 263ms
After SPL, you can load the U-Boot itb image:
fastboot stage u-boot.itb
fastboot continue
continued log:
.........Boot from fit configuration k1-x_deb1
## Checking hash(es) for config conf_2 ... OK
## Checking hash(es) for Image uboot ... crc32+ OK
## Checking hash(es) for Image fdt_2 ... crc32+ OK
U-Boot 2022.10spacemit-g0e3d5c910-dirty (Jul 15 2024 - 22:42:54 +0200)
CPU: rv64imafdcvsu_zicsr_zifencei_zicbom_zihintpause_zba_zbb_zbc_zbs_svpbmt_sstc_sscofpmf
Model: spacemit k1-x deb1 board
DRAM: DDR size = 4096 MB
DDR size = 4096 MB
DDR size = 4096 MB
4 GiB
[RESET]probe start
[RESET]probe finish
DCDC_REG1@dcdc1: ; enabling
DCDC_REG2@dcdc2: ; enabling
DCDC_REG3@dcdc3: ; enabling
DCDC_REG4@dcdc4: ; enabling
DCDC_REG5@dcdc5: ; enabling
DCDC_REG6@dcdc6: ; enabling
LDO_REG1@ldo1: ; enabling
LDO_REG2@ldo2: ; enabling
LDO_REG3@ldo3: ; enabling
LDO_REG4@ldo4: ; enabling
LDO_REG5@ldo5: ; enabling
LDO_REG6@ldo6: ; enabling
LDO_REG7@ldo7: ; enabling
LDO_REG8@ldo8: ; enabling
LDO_REG9@ldo9: ; enabling
LDO_REG10@ldo10: ; enabling
LDO_REG11@ldo11: ; enabling
SWITCH_REG1@switch1: ; enabling
Core: 395 devices, 22 uclasses, devicetree: board
MMC: [RESET]spacemit_reset_set assert=0, id=71
[RESET]spacemit_reset_set assert=0, id=72
sdh@d4280000: probe done.
[RESET]spacemit_reset_set assert=0, id=71
[RESET]spacemit_reset_set assert=0, id=83
sdh@d4281000: probe done.
sdh@d4280000: 0, sdh@d4281000: 2
Loading Environment from nowhere... OK
pcie_dw_k1x_probe, 662
[RESET]spacemit_reset_set assert=0, id=90
Now init Rterm...
pcie prot id = 1, porta_init_done = 0
Now waiting portA resister tuning done...
porta redonly_reg2: 00005d47
pcie_rcal = 0x00005d47
pcie port id = 1, lane num = 2
Now int init_puphy...
waiting pll lock...
Now finish init_puphy....
pcie_dw_k1x pcie@ca400000: Unable to get phy0pcie_dw_k1x pcie@ca400000: Unable to get phy1PCIEn
In: serial
Out: serial
Err: serial
ddr_freq_change: ddr frequency change from level 0 to 6
Change DDR data rate to 2400MT/s
[RESET]spacemit_reset_set assert=0, id=38
Default to 100kHz
EEPROM: TlvInfo v1 len=32
valid ethaddr: fe:fe:fe:e4:09:17
Serial number is valid.
Cannot find TLV data: product_name
Cannot find TLV data: manufacture_date
Cannot find TLV data: manufacturer
Cannot find TLV data: device_version
Cannot find TLV data: sdk_version
k1xci_udc: phy_init
k1xci_udc probe
k1xci_udc: pullup 1
-- suspend --
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x0 value 0x100 length 0x40
handle setup SET_ADDRESS, 0x0, 0x5 index 0x0 value 0xe length 0x0
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x0 value 0x100 length 0x12
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x0 value 0x200 length 0x9
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x0 value 0x200 length 0x20
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x0 value 0x300 length 0xff
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x409 value 0x302 length 0xff
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x409 value 0x301 length 0xff
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x409 value 0x303 length 0xff
handle setup SET_CONFIGURATION, 0x0, 0x9 index 0x0 value 0x1 length 0x0
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x409 value 0x302 length 0xff
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x409 value 0x304 length 0xff
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x0 value 0x300 length 0x4
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x409 value 0x301 length 0xff
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x0 value 0x300 length 0x4
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x409 value 0x302 length 0xff
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x0 value 0x300 length 0x4
handle setup GET_DESCRIPTOR, 0x80, 0x6 index 0x409 value 0x303 length 0xff
Now press Ctrl + C to drop into the U-Boot shell. Enjoy! :-)
DRAM training
I took the SpacemiT U-Boot fork, which is not open source - it has a binary blob in a header in the DRAM / DDR controller driver. Dumped that, and since U-Boot loads it and has the load address, I could open it up in Ghidra with that base address.
I then went ahead and adjusted some things Ghidra couldn't fully figure out, mainly function args. You will see in U-Boot that a 10-entry u64 array is passed, with actually 5 values only, including the DDR controller base, something they call boot_pp, the CS number, and, by default silenced to void, a printf implementation, plus a blob which is actually part of the payload header that the mask ROM reads above your payload to it. Turns out that's non-sense anyway and, AFAICT, unused.
So back to Ghidra.
Since I knew the args, I could now trace them through the function calls within that binary, seeing that a lot of stack allocation and unnecessary copying is going on, which is why it gets confusing. Probably that is just what the compiler does without too much optimization. Anyway, I could still figure out a lot.
As I currently happen to understand it, the SRAM is heavily used for a bunch of stuff, but... I may be mistaken. There is a >4k chunk of memory allocated on the stack that is being written to in nested loops across multiple functions, especially in read and write training.
The controller base address is
0xc000_0000
.PHY0 is at
0xc004_0000
, and they use PHY0 base + boot_pp * 0x4000 a bunch of times (boot_pp is set to 0, 1, 2, the blob called 3 times).And there are other blocks involved, e.g. at
0xc005_8000
.SRAM seems to start at
0xc080_0000
, which is where your payload with its header is loaded, but since the header is included, you really start at0xc080_1000
(i.e. at 4k offset). That is also the address where U-Boot links the SPL binary.