Grand Unified Kernel
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.
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.
|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|
|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)|