27

I know that Linux is available and has been ported for many different platforms such as for X86, ARM, PowerPC etc.

However, in terms of porting, what is required exactly?

My understanding is that Linux is software written in C. Therefore when porting Linux originally from X86 to ARM or others for example, is it not just a matter of re-compiling the code with the compiler for the specific target architecture?

Putting device drivers for different peripherals aside, what else would need to be done when porting Linux to a new architecture. Does the compiler not take care of everything for us?

closed as too broad by Michael Homer, Rui F Ribeiro, Thomas Dickey, mosvy, Dmitry Grigoryev Mar 1 at 12:17

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

56

Even though most of the code in the Linux kernel is written in C, there are still many parts of that code that are very specific to the platform where it's running and need to account for that.

One particular example of this is virtual memory, which works in similar fashion on most architectures (hierarchy of page tables) but has specific details for each architecture (such as the number of levels in each architecture, and this has been increasing even on x86 with introduction of new larger chips.) The Linux kernel code introduces macros to handle traversing these hierarchies that can be elided by the compiler on architectures which have fewer levels of page tables (so that code is written in C, but takes details of the architecture into consideration.)

Many other areas are very specific to each architecture and need to be handled with arch-specific code. Most of these involve code in assembly language though. Examples are:

  • Context Switching: Context switching involves saving the value of all registers for the process being switched out and restoring the registers from the saved set of the process scheduled into the CPU. Even the number and set of registers is very specific to each architecture. This code is typically implemented in assembly, to allow full access to the registers and also to make sure it runs as fast as possible, since performance of context switching can be critical to the system.

  • System Calls: The mechanism by which userspace code can trigger a system call is usually specific to the architecture (and sometimes even to the specific CPU model, for instance Intel and AMD introduced different instructions for that, older CPUs might lack those instructions, so details for those will still be unique.)

  • Interrupt Handlers: Details of how to handle interrupts (hardware interrupts) are usually platform-specific and usually require some assembly-level glue to handle the specific calling conventions in use for the platform. Also, primitives for enabling/disabling interrupts are usually platform-specific and require assembly code as well.

  • Initialization: Details of how initialization should happen also usually include details that are specific to the platform and often require some assembly code to handle the entry point to the kernel. On platforms that have multiple CPUs (SMP), details on how to bring other CPUs online are usually platform-specific as well.

  • Locking Primitives: Implementation of locking primitives (such as spinlocks) usually involve platform-specific details as well, since some architectures provide (or prefer) different CPU instructions to efficiently implement those. Some will implement atomic operations, some will provide a cmpxchg that can atomically test/update (but fail if another writer got in first), others will include a "lock" modifier to CPU instructions. These will often involve writing assembly code as well.

There are probably other areas where platform- or architecture-specific code is needed in a kernel (or, specifically, in the Linux kernel.) Looking at the kernel source tree, there are architecture-specific subtrees under arch/ and under include/arch/ where you can find more examples of this.

Some are actually surprising, for instance you'll see that the number of system calls available on each architecture is distinct and some system calls will exist in some architectures and not others. (Even on x86, the list of syscalls differs between a 32-bit and a 64-bit kernel.)

In short, there's plenty of cases a kernel needs to be aware that are specific to a platform. The Linux kernel tries to abstract most of those, so higher-level algorithms (such as how memory management and scheduling works) can be implemented in C and work the same (or mostly the same) on all architectures.

  • 7
    Very nice write-up! The variation in the number of syscalls is mostly related to history: new ports include the valid syscalls at the time of the port, they don’t bother with the historical baggage present in older ports, so deprecated syscalls typically aren’t present in ports newer than the deprecation. (That doesn’t cover all the scenarios...) – Stephen Kitt Feb 28 at 10:44
10

In addition to porting the Linux kernel, you will need to define the application binary interface (ABI) for "user space" programs and port the lowest layers of the user space software stack. Linux is typically used with low-level user space components from the GNU project, of which the most critical are:

  • The C compiler, assembler, and linker: GCC and GNU Binutils. For an entirely new CPU architecture, you need to port this software before you even start porting the kernel, since the kernel is itself a C program and has to be compiled. If there is already "back end" support for your platform's CPU, just not with Linux as the OS kernel, you have substantially less work to do and you may be able to get away with postponing most of the work until the kernel is up and running.
  • The C runtime library: "GNU libc". This library includes the code that makes system calls and otherwise interacts directly with the kernel.
  • The "foreign function interface" library, libffi, which is an essential component of many high-level language interpreters, and performs one of the few remaining tasks that requires a small amount of hand-written assembly language.

Many other pieces of software have optional platform-dependent components; for instance, Web browsing will be substantially faster if you write hand-optimized cryptographic primitives for NSS and OpenSSL for your new CPU architecture, and just-in-time compilation back-ends for IonMonkey and V8. But these are not essential to bringing up a new platform.

1

You have to tell the kernel about the hardware you’re porting to. The kernel’s job is to directly interface with the hardware, so in order for it to function properly, the kernel needs to know about the CPU, oscillators (clocks), and any peripherals, like the various kinds of serial ports (SPI, CAN, I2C, etc.).

In the old days, you would do this by writing platform specific code that drivers would then use to function. These days, this is done by writing a Device Tree definition.

Not the answer you're looking for? Browse other questions tagged or ask your own question.