This page is intended to provide an informal overview of the internals of the RISC OS kernel, to help inform OS maintainers of how everything fits together. h3. Source files and branches The kernel source is located at "RiscOS/Sources/Kernel":https://gitlab.riscosopen.org/RiscOS/Sources/Kernel in gitlab. Currently the main development branch is the master branch, but there are many other branches which have performed key roles over the years. As a summary: * During the time of Acorn, there were several branches used by the different development teams. E.g. the Spinner branch was used for NCOS development, and Ursula was used for RISC OS 4 development, while the main branch was effectively just a snapshot of the last desktop OS release. * After the collapse of Acorn many of the branches and features were merged back to trunk, some obsolete features were removed, and work started on creating a 32bit version of the kernel. * In 2000 the HAL branch was created, in order to add hardware abstraction to the 32bit version of the kernel that was present in trunk. Trunk development essentially halted and the HAL branch became the main area of development for the next 15+ years. * In 2016 the HAL branch was merged back to trunk, making trunk the main development branch again. This merge was also followed by another cleanup period, with support for non-HAL, 26bit and STB builds being removed, along with the removal of many minor feature switches. There are five main parts to the kernel source: * The AMBControl source, located in the s.AMBControl folder. AMBControl is the "application memory block" code that is used for lazy task swapping by the Wimp. * The VDU driver source, located in the s.vdu folder. This contains all the code for handling VDU sequences, character printing, MODE 7 emulation, and the GraphicsV and HAL video interfaces. * The PMF source, located in the s.PMF folder. It's probable that most of this code was originally written by Paul Fellows (head of the Arthur project team), hence the folder name being his initials. It's a collection of mostly disjointed functionality for things such as: ** IIC support ** OS_Byte, OS_Word SWIs ** Mouse & keyboard handling ** BBC-compatible buffers for the printer, serial, mouse, sound, speech, etc. ** Real-time clock and date/time handling * The MMU interface. This deals with the different page table formats required by the type of MMU in the host machine. There are several choices of interface/driver available, but the choice of which one to use must be made at compile time. Each one corresponds to a single source file (and a header file in HdrSrc): ** s.ARM600 - Used for the integrated MMU that was introduced in the ARM600. The code is suitable for machines from ARMv3 to ARMv5. ** s.VMSAv6 - Used for VMSAv6-compatible MMUs, i.e. those found in most ARMv6 and ARMv7 CPUs. ** Past versions of the kernel have also contained the files s.MEMC1 and s.MEMC2, which were the interfaces used for the MEMC1 and MEMC2 chips. * "The rest". This is everything else in the 's' folder not listed above, including but not limited to: ** Dealing with startup and resets ** The HAL interface ** IRQ, SWI, service call and vector dispatch ** Cache/TLB maintenance functions ** All kinds of memory allocation (dynamic areas, heaps, etc.) ** The command line interpreter and system variables h3. Kernel workspace The kernel workspace is defined in hdr.KernelWS. There are two workspace layouts available: # The legacy layout. This uses the first 32K of RAM for storing the bulk of the kernel workspace and the processor vectors. This is currently the default mode for all machine types, and provides the maximum amount of compatibility with previous versions of RISC OS. # The "high processor vectors"/"zero page relocation" layout. With this layout, the first 16K of workspace and the processor vectors are relocated to &FFFF0000. The 16K of scratch space located at &4000 remains in its legacy location. This mode is only available on CPUs that support the high processor vectors feature (any ARMv6+, and some ARMv5, e.g. the IOP321 used in the Iyonix). The type of workspace in use is selected via the HighProcVecs option in hdr.Options. Although the majority of the kernel workspace is kept private, there are several locations exported publicly to the rest of the OS in hdr.PublicWS. However only the legacy locations are exported here; for compatibility with kernels that use zero page relocation, programs should first attempt to query the address using [[OS_ReadSysInfo 6]] before falling back on the legacy address. Significant workspace locations are listed below: h4. "Zero page" locations * *IRQsema* - Although the name suggests that it's a semaphore, it's actually more than that. It's the head of a linked list of all processing that's been interrupted by IRQs. Various bits of code check IRQsema as method of working out whether the current code is executing in the foreground or the background, or for checking whether specific code sections have been interrupted. See the comments at the top of s.PMF.IIC for a rough guide as to how the linked list is formatted. * *DomainId* - The Wimp uses this to store the active task handle. There are still a few pieces of code that rely on this instead of using [[Wimp_ReadSysInfo]] 5. * *ESC_Status* - This stores the escape status, as returned by [[OS_ReadEscapeState]]. There are still pieces of code which read this value directly, but the only significant bit of the byte appears to be bit 6, which is what OS_ReadEscapeState checks for. * *RISCOSLibWord*, *CLibWord* - These are used by the shared C library to store pointers to the modules workspace. This allows the ROM version of the module to continue to function even after its been killed off (the module must continue to function because any C code in the ROM image will have been statically linked against it) * *FPEAnchor* - FPEmulator uses this location to store a pointer to its workspace, to allow it to be easily fetched while in exception handling code. * *DebuggerSpace* - Used by the Debugger module to store breakpoint handlers. When the debugger places a breakpoint it simply places a MOV PC,#xx instruction which jumps to the appropriate handler code in zero page. * *CLibCounter* - Used by the tmpnam() implementation in the shared C library * *Modula2_Private* - Obsolete entry used by Acorn's long-abandoned Modula2 project. * *HeapSavedReg_**, *Heap_ReturnedReg_** - Register save/restore blocks to allow [[OS_Heap]] to be re-entered. * *DUMPER*, *Abort32_dumparea* - Register save blocks used by the abort handlers. * *MMUControlSoftCopy* - Soft copy of the MMU control register, aka the CP15 system control register. This is required due to the register being write-only on ARMv3 machines. * *Page_Size* - This is used to store the page size of the system. It was vital during the MEMC1/MEMC2 era, but pretty much obsolete now that 4K page size is standard (and lots of code assumes 4K page size anyway). * *ScratchSpace* - A 16K block (from &4000 to &8000) of temporary workspace used for various purposes by many different modules. * *PhysRamTable* - Each entry is a pair of words. The first word contains the base physical address of a RAM block, and the second word contains the size and flags. The flags are only stored in the lower 12 bits of the word; masking them out will leave you with the size value (measured in bytes). The flags are the same flags as specified by the HAL when it calls [[RISCOS_AddRAM]]. Note that versions of RISC OS prior to 5.19 didn't store the flags at all. h4. CursorChunkAddress CursorChunkAddress marks the start of a second area of workspace (currently) located at &FAFF0000. This area is used for the cursor & sound DMA buffers, along with the OSCLI workspace and SWI/IRQ dispatchers. h4. SWIDespatch The SWI despatcher is a small routine that gets copied out of ROM (see SVCDespatcher in s.Kernel). ** This code is branched to directly from the SWI processor vector. (TODO - Check this!) ** A lookup table directly follows the dispatch code, allowing quick access to the kernel SWIs. ** Non-kernel SWIs drop through to the NotMainMOSSWI routine within the ROM image, where a hash table is used to quickly find the appropriate module. ** Theoretically the SWI dispatcher will cope with SWI calls from Thumb mode, but it's unknown if this has ever been used or is fully reliable. ** The kernel SWI dispatcher stores the SPSR in R14, allowing the SWIs to easily update the return flags. ** The RAM-based SLVK_* routines are used to return from the SWI dispatcher. h4. DefaultIRQ1V The default IRQ dispatcher is a small routine that gets copied out of ROM (see DefaultIRQ1Vcode in s.NewIRQs). ** This is the routine that can be overridden by [[OS_ClaimProcessorVector]]. As such, it doesn't need to preserve r0-r3,r11 or r12, or worry about IRQsema or triggering callbacks (Initial_IRQ_code handles that). ** The DefaultIRQ1V code is next to the table of IRQ handlers ([[OS_ClaimDeviceVector]]/[[OS_ReleaseDeviceVector]]). h4. The CAM The CAM is a data structure used to track the allocation of physical RAM pages. The name refers to the "Content-Addressable Memory":https://en.wikipedia.org/wiki/Content-addressable_memory that the MEMC chips used to store the memory map. In MEMC, the CAM wasn't readable by the CPU - so to avoid losing track of what memory was being used where, the OS had to maintain its own copy of the data. This is the origin of the RAM-based "CAM soft copy" that is described here. Modern ARMs handle memory mapping in a different way, using TLBs and RAM-based page tables, but the "CAM" name in the OS sources has stuck, and the data structure still serves a useful purpose. This RAM based CAM is implemented as a simple table indexed by the physical (RAM) page number. Each CAM entry is four words long. PhysRamTable is used to map the physical RAM page numbers to and from physical addresses. |_<^{width:4em}. Word |_<^. Meaning | |<^. 0 |<^. Logical address the page is currently mapped to, or 'DuffEntry' if unmapped | |<^. 1 |<^. PPL ("Page protection level") and additional flags (see below table) | |<^. 2 |<^. Physical memory pool (actually, DANode pointer). Invalid if PMP flag not set. | |<^. 3 |<^. Page index in physical memory pool. Invalid if PMP flag not set. | PPL/page flags: |_<^{width:4em}. Bit(s) |_<^. Meaning | |<^. 0-14 |<^. PPL, cacheable/noncacheable, flags, etc. Same as bits 0-14 of the [[Memory Page Access Flags]]. | |<^. 15 |<^. "Unavailable" flag. Used to mark pages that have been requested by a dynamic area PreGrow handler, but haven't yet been moved. | |<^. 16-19 |<^. "Temporary count of uncacheability", used when pages are made temporarily uncacheable via [[OS_Memory 0]] | |<^. 20 |<^. Bit 20 of [[Memory Page Access Flags]] | |<^. 21 |<^. "Required" flag. Used to mark pages that have been specifically requested by dynamic areas. This guarantees that the page won't be used for another purpose until the dynamic area releases it, e.g. for hardware IO buffers. | The above flags are all defined in s.ChangeDyn, at the DynamicAreaSWI code. Older kernels (prior to the introduction of physical memory pools) only used two words per CAM entry, and had different flag allocations.