Grand Unified Kernel

From LinuxMIPS
Jump to: navigation, search

Aim

The aim of this project is to produce a single kernel binary that can run on multiple machines. There are multiple benefits to doing this:

  • Adding new platforms should become easier as no board/platform level code should be required.
  • The amount of board/platform level code we need to maintain will be reduced significantly.
  • The number of kernel binaries that need to be built & tested can be reduced significantly.
  • Distributions will be able to include a kernel binary built for a given architecture revision that will run on any board with a CPU implementing that architecture revision.

The Plan

Episode I: The Board Code Menace

Convert boards to use device tree

Device trees will serve as the mechanism by which hardware differences are abstracted, and remove the need for each board to have C code to probe device drivers. With a suitable device tree it should be possible for boards to be supported with no custom code, beyond drivers, in the kernel.

Episode II: Attack of the Generic Platform

Add the generic platform to the kernel

This is a platform which the kernel can be built for which does nothing board-specific, instead relying entirely upon the device tree for board-specific information. In order to support boards with legacy boot protocols, where we can't rely upon the bootloader providing a device tree, exceptions to this are allowed. Such systems (eg. Malta & SEAD-3) provide a relatively small amount of code which takes a device tree embedded into the kernel & adjusts it appropriately for the running system early in the boot process. For example the system may need to tweak the amount of memory indicated in the device tree, or adjust the interrupt configuration to reflect the presence or absence of a GIC. In newer systems (eg. Boston) the bootloader will be expected to do this & provide an accurate device tree to the kernel. In a typical completed product there should be little need to tweak the device tree at all, since the configuration of the product will be largely fixed.

Episode III: Revenge of the Memory Map

Map the generic kernel via the TLB

Historically, MIPS kernels have been run from kseg0 or similar unmapped regions of the virtual address space. This makes things nice & simple, since the bootloader can load the kernel trivially to the addresses from which it will run & the kernel has no need to set up the TLB until relatively late in the boot process when it has already detected various properties of the CPU & the rest of the system. However, this has downsides:

  • The kernel has to be linked to an unmapped address which is valid for the system it will run on. Typically this is in kseg0, which means systems end up requiring DDR in the lowest 512MB of the physical address space for the kernel to run from. For a generic kernel this means all target systems need to have DDR available in the same region of that kseg0-accessible region of physical memory. For a generic kernel this isn't ideal, as if a system chose not to have DDR available there the kernel wouldn't be able to run.
  • We have no way to provide memory protection for kernel text or data. If the kernel is run unmapped then we don't have TLB entries which could mark regions of kernel code or read-only data non-writeable, or regions of kernel data non-executable. The ability to do this would be great for security, and indeed recent versions of Linux (v4.6+) complain during boot that MIPS isn't currently able to do this with a message stating "This architecture does not have kernel memory protection".

On the other hand, mapping the kernel via the TLB will have a cost - pressure on the TLB will be increased, kernel code may take TLB refill exceptions, and startup code for the kernel will be more complex. The first of these we should be able to mitigate to some extent - for example we will need a wired TLB entry to cover at least the TLB exception vector code, and we could attempt to extend this wired entry to cover as much kernel code as possible such that the covered code no longer takes TLB refill exceptions.

Episode IV: A New Relocation

Relocate the kernel virtually & physically

With the kernel mapped via the TLB we can decouple the virtual & physical addresses that it runs at, allowing it to be relocated in both the virtual & physical address spaces with differing offsets. We should even be able to map the kernel non-contiguously, allowing it to be jumbled throughout the physical address space if we desire. This would need to be balanced with the desire described above to cover a large amount of it with a single wired TLB entry though: that region will need to remain contiguous.

Episode V: EVA Strikes Back

Sanitise EVA memory maps

One of the hurdles to including Malta in the generic kernel is its use of EVA. EVA support in Linux currently makes the memory map board-specific, and relies upon board code to configure the segmentation control registers & change various macros indicating the memory map to the kernel. For a generic kernel this is not ideal.

With the kernel mapped it should become possible to run with no regions of the virtual address space unmapped, and with no need for a large uncached region of the address space. This should allow for us to map the entire virtual address space, leaving no unmapped regions at all. This would be good for security since all memory access becomes mediated via the TLB, with the associated access protections applied. We could however choose to keep one segment unmapped & cached for use as lowmem. Either way we ought to be able to standardise the memory map for EVA somewhat with the kernel being mapped to an address high up, perhaps an unmapped & cached lowmem region next to it that the kernel could map to anywhere that DDR is present in the physical memory map, and the rest of the address space mapped for use by user code.

A bonus here would be to allow kernels to build with EVA support & disable it at runtime, eg. by patching the EVA memory instructions with their non-EVA equivalents if EVA isn't implemented by the CPU. This would allow a kernel to use EVA when it's present but still run anywhere.

Tasks

Essential tasks
Add generic platform Done in v4.9 (Paul)
Genericise SEAD3 Done in v4.9 (Paul)
Add Boston board In Progress (Paul) Basic support, PCI/SATA/MMC
pch_gbe ethernet support
Genericise memory layout In Progress (Paul) Genericise MIPS64 xkphys/CAC_BASE CCA
Genericise PAGE_OFFSET, PHYS_OFFSET, HIGHMEM_START
Genericise EVA segment mapping
Allow EVA kernels to run on non-EVA systems
Allow XPA kernels to run on non-XPA systems
Convert Malta to DT In Progress (Paul) Parallel Flash
RTC
Interrupt Controllers
UARTs
System controllers & PCI (gt64120, bonito & MSC)
Convert Malta to generic platform In Progress (Paul)
Convert pistachio to generic platform In Progress (Paul)
Convert ingenic (jz4740,jz4780) to generic platform In Progress (Paul)
Convert xilfpga/MIPSfpga to generic platform TODO
Genericise cpu_has_* macros (allow kernels to build for specific platforms or multiple platforms) TODO (Paul)
Run (conservatively) on unrecognised MIPSr6 CPUs TODO (Paul)