September 14, 2018

The Nanopi Fire3 - The path to a U-Boot for network booting

Friendly Arm provides sources to their U-Boot on Github. I did a lot of work with this. I learned about U-Boot and found that the designware driver supports the network section of the S5P6818 chip. I ended this effort with that version of U-Boot heavily modified, detecting the network chip, and correctly detecting a 1000 Mbit connection. It failed with ARM alignment errors when trying to build a network packet.

On thing that proved to be a total disctraction was studying the U-Boot setup used by Friendly ARM to boot Linux. This uses some bl1 second stage loader that no source is available for, as well as two other intermediate loaders that do some kind of secure mode thing with FIP authenticated packages. No source code is provided for any of this. It turns out that all of this can just be ignored and done away with. By using the bl1 boot loader that was available for the NanoPi M3 (also a S5P6818 based board) along with a version of U-Boot, I can have everything I want, and source code for every bit of it.

Several useful things did come out of studying the boot setup. The first is that much of it can be ignored and simplified, as just mentioned. The other is that USB booting is very handy. With a proper host side utility (which is pretty simple on a linux system) you can send executables to the in chip bootrom and have them get run in on-chip static ram (at 0xffff0000). This requires setting up a NSIH header that is 512 bytes in size. It gets this name from the 4 bytes magic number "NSIH" in the last 4 bytes. The bl1 boot loader has this header, and expects to boot something else with this header (which can be U-Boot, or some other executable if you want to dispense with U-Boot). In a system that runs from SD card, U-Boot could be done away with entirely. For my purposes, U-Boot offers only the convenience of network booting.

The network alignment business seemed to be the final hurdle in attempting to use the U-Boot sources provided by Friendly ARM. The ARM processor has a bit in the SCTRL register that will allow improperly aligned accesses. But this only works when the D cache is enabled. That in turn requires the MMU to be configured, enabled, and configured correctly. Enabling the D cache (and hence the MMU) can be done by a single setting in a hidden config file (in include/configs, not configs), but even that did not work. Linux can do networking just fine on this hardware, so this is not a hardware issue. I discovered much later that this was due to a faulty MMU setup. Only the first half of RAM was being configured as non-device memory. This could be called a bug in U-Boot (given that it was asked to setup 0x40000000 to 0x7df00000 as RAM. Or it could be called a flaw in the config file. I learned much later that if the CONFIG file correctly specifies the size of RAM as 0x40000000, U-boot will properly set up the MMU. The heart of the issue is that the MMU is mapping 512M using a single page table entry -- so it can either map half of ram or all of it, and it rounds down, mapping only half of it if the size is given as 0x3df00000 as it was. The network buffers are in the second half of RAM, so we get alignment errors, even with the D cache and MMU enabled.

If you look at include/configs/s5p6818_nanopi3.h you will see this:

#define CONFIG_SYS_MEM_SIZE             0x40000000
#define CONFIG_SYS_RESERVE_MEM_SIZE     0x02500000 /* 37MB */
#define CONFIG_SYS_SDRAM_BASE           0x40000000
#define CONFIG_SYS_SDRAM_SIZE           (CONFIG_SYS_MEM_SIZE - CONFIG_SYS_RESERVE_MEM_SIZE)
There is no telling why 37M is being reserved and what for, but this is what screws up the MMU initialization code in U-Boot. Apparently the Artik code reserves only 32M. Maybe this is being reserved for a frame buffer? Who knows. Whatever the case, if it needs to be reserved, it should be done in a different way that does not confuse the MMU initialization code. I simply set SDRAM size equal to MEM size and got networking to work. If this is something video related, I simply don't care as I have no interest in video. For that matter, I doubt that U-Boot has any interest in video either, so nothing really needs to be reserved here for U-Boot. Any operating system subsequently loaded that may use video may do the right thing.

Before learning all of what was just described, I turned from the Friendly ARM U-Boot sources to the Samsung Artik 710 U-Boot sources. My hope was that the Artik might have networking enabled out of the box. It did, but several changes needed to be made. First the serial console for the Artik is on port 3, and this needs to be changed to port 0. Second the ethernet Phy for the Artik is at address 3 and this needs to be changed to 0 for the Fire3. At this point U-Boot is talking to the serial console and announcing that the ethernet is being detected. But again, a synchronous exception due to an alignment error is being thrown when a network packet is getting set up. Digging into this revealed the issue described above where only the first half of ram is being properly initialized. Enabling the D cache and changing the ram size to a proper value of 0x40000000 (1 G) did the trick and we have a working U-Boot with networking. Enable the DHCP command and we are in business.

It might well be possible to return to the Friendly ARM sources and do the same, but I see little point and will use the hacked Artik 710 U-Boot for my work.

With this, I can put the bl1 second stage loader along with U-Boot onto and SD card and by simply hitting reset, be loading and running new versions of software I want to develop. Note that I have entirely abandoned the secure/nonsecure FIP boot setup that Friendly ARM uses to boot Linux. I see no advantage to that for what I am doing, there are many inscrutable binary packages on that path, so the heck with it.


Have any comments? Questions? Drop me a line!

Tom's electronics pages / tom@mmto.org