This is a status update about my work on Linux for Cortex-M4 inside Freescale Vybrid SoC’s. While I managed to boot Linux on the M4 core since fall last year, several improvements have been made since then.
MSCM interrupt router driver
Peripheral interrupts need to be routed to the core requiring them. The routing is done by the interrupt router inside MSCM (Miscellaneous System Control Module). So far, the boot loader (U-Boot) configured all interrupts to the primary core. A proper driver for the interrupt router was missing, hence to get Linux running on the secondary core, manual fiddling of the interrupt router registers was needed.
The IRQ domain hierarchy feature merged in 3.19 was an excellent fit to write a driver for the MSCM interrupt router within Vybrid: The hierarchy allows to model the interrupt controllers as stacked entities, which is exactly how the interrupt controllers are arranged in Vybrid. A interrupt needs to be routed to the CPU local interrupt controller through the MSCM interrupt router. Hence the standard device-trees for the Cortex-A5 CPU stack the MSCM-IR and GIC whereas the device-tree for the Cortex-M4 CPU stack the MSCM-IR and the NVIC interrupt controller.
The driver has been merged in the v4.1 merge window, however this version only works with the GIC (and hence the primary CPU). But patches to enable the driver in conjunction with NVIC will likely make it in the v4.2 merge window.
Cortex-M4 mini-loader
The mini-loader for the Cortex-M4 makes the hacked Linux kernel entry code (head-nommu.S) obsolete. It essentially copies the pointer to the device tree file from SRC_GPR3 (PERSISTENT_ARG1) to the M4 CPU’s r2 register. The mini loader is included within the m4boot utility for Linux and part of the U-Boot support for Cortex-M4 (see below).
Caches support
There are no standard cache controllers for the Cortex-M4, also caches are completely optional. The Vybrid SoC provides caches for the Cortex-M4 through the Local Memory Controller. Enabling caches especially boosts performance when running code from the relatively slow DDR memory. First I had some troubles after enabling caches, but it turned out that it works flawless when turning them on correctly. Latest versions of the vf610m4bootldr mini-loader enable both, data and instruction caches, which boosts performance from 4.30 to blazing 110.48 BogoMIPS, according to Linux 😀
Boot directly from U-Boot
A new U-Boot command m4boot allows to boot Linux on the Cortex-M4 directly from U-Boot. The first argument of the command is a pointer to a FIT image, which is currently the only image format which is supported. I opted for the FIT image since it allowed most flexibility. However, FIT image support is somewhat different from the standard FIT image support known from the bootm. Especially the entry points and load addresses which are required need to be carefully choosen when preparing the FIT image:
- Kernel image:
- Need to be a XIP (eXecute In-Place image)
- The CONFIG_XIP_PHYS_ADDR defines the text base of the kernel and hence the target memory location of the image
- Load Address is the target memory location from the Cortex-A5 viewpoint (e.g. 0x8f000080)
- Entry Point is the same memory location from the Cortex-M4 viewpoint, plus 1 due to Thumb-2 jump address (e.g. 0x0f000081)
- Initrd image (optional)
- Load Address is the target memory location from the Cortex-A5 viewpoint where U-Boot copies the image to
- Need to be in the memory area accessible by Linux (e.g. 0x8d000000)
- Device Tree file
- Load Address is the target memory location from the Cortex-A5 viewpoint where U-Boot copies the device tree to
- Need to be in the memory area accessible by Linux (e.g. 0x8df00000)
A complete FIT image input file which works for the Linux vf610m4_defconfig standard configuration:
/dts-v1/; / { description = "Linux for Vybrid's Cortex-M4"; #address-cells = ; images { kernel@1 { description = "Linux Kernel 4.1-rc1"; data = /incbin/("./xipImage"); type = "kernel"; arch = "arm"; os = "linux"; compression = "none"; load = ; entry = ; hash@1 { algo = "md5"; }; }; ramdisk@1 { description = "BusyBox based initrd"; data = /incbin/("./initramfs.cpio.lzo"); type = "ramdisk"; arch = "arm"; os = "linux"; compression = "lzo"; load = ; hash@1 { algo = "md5"; }; }; fdt@1 { description = "Vybrid VF610 for Cortex-M4"; data = /incbin/("./vf610m4-colibri.dtb"); type = "flat_dt"; arch = "arm"; compression = "none"; load = ; hash@1 { algo = "md5"; }; }; }; configurations { default = "config@1"; config@1 { description = "Linux for Vybrid's Cortex-M4"; kernel = "kernel@1"; ramdisk = "ramdisk@1"; fdt = "fdt@1"; }; }; };
The FIT image can then be generated using mkimage:
$ mkimage -f vybridm4.its vybridm4.itb ...
Booting the FIT image using TFTP is then just a matter of two commands:
Colibri VFxx # tftp m4/vybridm4.itb && m4boot ${loadaddr}#config@1 Using FEC device TFTP from server 192.168.10.1; our IP address is 192.168.10.2 Filename 'm4/vybridm4.itb'. Load address: 0x80008000 Loading: ################################################################# ################################################################# ################################################################# ########## 4.9 MiB/s done Bytes transferred = 3002188 (2dcf4c hex) ## Loading kernel from FIT Image at 80008000 ... Using 'config@1' configuration Trying 'kernel@1' kernel subimage Description: Linux Kernel 4.1-rc1 Type: Kernel Image Compression: uncompressed Data Start: 0x800080d8 Data Size: 1357828 Bytes = 1.3 MiB Architecture: ARM OS: Linux Load Address: 0x8f000080 Entry Point: 0x0f000081 Hash algo: md5 Hash value: 1fdfa34a7dbc821f1e0eb413b5026921 Loading kernel from 0x800080d8 to 0x8f000080 ## Loading ramdisk from FIT Image at 80008000 ... ... Loading fdt from 0x802e21bc to 0x8df00000 Using Device Tree in place at 8df00000, end 8df05a50 Booting Cortex-M4 @0x0f000001
Resources:
0 Comments.