Podcast explains Linux kernel ports
Jan 3, 2008 — by Eric Brown — from the LinuxDevices Archive — 2 viewsTimesys's 24th and 25th podcast episodes explain how to port a Linux kernel to a new hardware platform. Focusing on ARM-based hardware supported by a reference board, hosts Gene Sally and Maciej Halasz explain how to choose kernels and cross-compilers, set up bootloaders and machine IDs, and map devices.
Together, the podcasts last well over an hour, but listeners can easily skip the first seven minutes of the first podcast and the first six minutes of the second. (This is to take nothing away from Sally and Halasz, who are, as usual, informative and entertaining company.) The following is a summary of the two podcasts:
Finding a kernel (start of episode 24)
Sally and Halasz recommend visiting kernel.org and downloading one of the mainline releases. Typically, the mainline kernel will not include device drivers for all peripherals on the board, but if most are supported, and/or the developer is using only a small subset of the peripherals, they recommend starting with a mainline kernel. They also recommend using the latest mainline version, to take advantage of the latest fixes, performance improvements, and security changes. Once the kernel release version is chosen, there is high resistance to switching versions, they say.
If there are too many unsupported devices, say Sally and Halasz, developers may need to try the semiconductor vendor's kernel. If this version is out of date, however, they may need to patch forward the drivers that are specific to that board and compile them outside the kernel tree. At the very least, some reconciliation will be required between the kernel's header lines and whatever drivers the board vendor provides.
Other kernel sources include various open source projects that focus on specific processors, features, functions, or boards. In the end, developers may need a combination of all three sources — kernel.org, semi vendor, and open source sites.
Compile like a programmer
Sally and Halasz recommend that developers use the same toolchain for compiling both the kernel and the entire system. Cross-compilers are often available from the board vendor, or developers can use a GCC version or try various cross-tool projects. Processor-specific commercial cross-compilers are becoming increasingly popular, they say, due to the difficulty of finding free toolchains that are optimized for new processors. Optimization is often desired for hardware acceleration, such as floating point operations, shuttling data to a digital signal processor (DSP), or video processing. Typically, developers don't need C libraries in the toolchain, they say. The kernel is autonomous, so library functions such as printf and printk are all already implemented.
Internal structure and function blocks
Once the kernel and toolchain are in place, developers need to address the internal structure of the kernel space, starting with the file system block. The file systems built into most kernels take up a lot of memory, so the first step is to clip out those that won't be needed. Typically developers don't need to modify the file systems themselves, but developers may want to add different ones.
The next step is to look at the function blocks for process management and scheduling, followed by memory management. After that, developers need to address device control, which typically breaks down into networking, character, and block types. Fortunately, the semi vendor already handles the core support, which is very difficult to modify.
Bootloaders (start of episode 25)
The next step in porting the kernel, explain Sally and Halasz, is to configure the bootloader, which loads the kernel into a particular memory address and starts it running. If the kernel is compressed, developers may need to run a compiled decompression program so the bootloader knows where to place the kernel. The program, typically Gzip, commandeers a chunk of memory, expands the kernel, and then resets the pointer and begins running the architecture-specific start-up code. The kernel then commences higher level start-up processes such as configuring devices, mapping interrupt handlers, and starting the memory manager.
Architecture-specific machine code
The next stage is to find the appropriate machine code in the Linux kernel tree. In the “arch” subdirectory, say Sally and Halasz, there are typically loads of architecture- and processor-specific folders full of code that developers can reuse and modify. In the ARM folders, one can find MAC subdirectories that include processor-specific subdirectories of machine initialization code. Developers should also investigate the “includes” subdirectories, which stores definitions and head of files for specific platforms.
Memory management has yet to start at this point, they explain, so any address one is seeing is an actual physical address. The bootloader leaves some cookie crumbs to help developers see what's going on, typically via registers initialized for certain addresses.
Device mapping
To map devices, developers can choose to: modify the kernel for a new driver; modify existing drivers for performance; or adopt third-party device drivers. First, the machine needs to be registered. Each machine has a unique numerical identifier that allows the kernel to configure certain pieces of code for it. For ARM users, these machine IDs are typically stored in a single file, usually following the path, arch/arm/tools/mac. If the reference platform is well done, developers won't need to do much modification of the IDs, they say. However, if it's a new board, it may require adding a new ID.
Sometimes the bootloader stores the value for the machine ID in a certain register, so the kernel can check for it when it starts booting. Depending on that value, it executes a different initialization code for each machine. Developers can use this to support multiple designs with a single binary kernel image.
ARM directory structure
At this point, the porting process involves investigating a number of kernel subdirectories to see if files need to be modified, deleted, or replaced. For example, the Mm subdirectory provides memory management dependent code responsible for initializing the cache pending direct memory access. Developers should also evaluate the tlb (translation lookaside buffer) init.
The kernel subdirectory holds primary initialization code for kernel set-up, such as the system code, definitions of different APIs, and the assembly code for the kernel initialization. If developers plan to work extensively at the device driver level, however, parts of the code may exist in other subdirectories. The “lips” subdirectory covers string handling, memory copy, and I/O operations. Some of the helper functions may need to be modified, say Sally and Halasz, if there are special interrupt handling or dispatching needs.
Developers may now want to prepare the serial console to start communicating with various devices. If developers plan to change their board's serial ports, they will need to look at this code, which maps the console to physical memory addresses. The code is typically split between the include subdirectory and an architecture-specific subdirectory.
Machine Detection
After initializing the serial console, the Linux kernel checks for the processor ID, and then calls the associated function. There's also a machine start structure that defines information such as the maintainer, as well as pointers to the interrupt map, boot vector, and the name of the routine that does the machine initialization. Ideally, developers can copy the default I/O mappings, but changes may be needed if the board requires particular boot parameter changes.
One of the functions calls the machine start code, which then defines how devices and interrupts are called. The interrupt definitions are usually stored inside the include file and an architecture-specific IRQS header file. If developers are attaching a new device or changing how devices are connected to the processor, they may want to reshuffle the interrupt map.
Finally, developers should look at the memory map, which defines how devices talk to the processor. Developers can stick with the default unless they want the buses to interconnect different peripherals, which would require redefining the memory map. As always, suggest Sally and Halasz, it's better to first look at what the reference board already provides before wasting one's time coding from scratch.
Both episodes of the Linux kernel port podcasts are available here.
This article was originally published on LinuxDevices.com and has been donated to the open source community by QuinStreet Inc. Please visit LinuxToday.com for up-to-date news and articles about Linux and open source.