AlmuHS / GNUMach_SMP

SMP implementation in GNU Mach
GNU General Public License v2.0
25 stars 7 forks source link
gnumach hurd mach smp

Hurd_SMP project

Objective

The objective of this project is to fix and complete SMP support (multiprocessing) in GNU/Hurd. This support must be implemented in GNU/Hurd's microkernel (aka GNU Mach)

Original status:

GNU/Hurd includes a tiny SMP support, as this FAQ explain.
The GNU Mach source code includes many special cases for multiprocessor, controlled by #if NCPUS > 1 macro.

But this support is very limited:

Solution

To solve this, we need to implement some routines to detect the number of processors, assign an identifier to each processor, and configure the lapic and IPI support. These routines must been executed during Mach boot.

"Really, all the support you want to get from the hardware is just getting the number of processors, initializing them, and support for interprocessor interrupts (IPI) for signaling." - Samuel Thibault link

"The process scheduler probably already has the support. What is missing is the hardware driver for SMP: enumeration and initialization." - Samuel Thibault link

The current necessary functions are cpu_number() (in kern/cpu_number.h) and intel_startCPU(). Another not-implemented function, but don't critical, is cpu_control() Reference

Other interesting files are pmap.c and sched_prim.c

Added to this, we have to build an isolated environment to execute the non-thread-safe drivers.

"Yes, this is a real concern. For the Linux drivers, the long-term goal is to move them to userland anyway. For Mach drivers, quite often they are not performance-sensitive, so big locks would be enough." - Samuel Thibault link

Project draft

You can read the full project draft in Hurd SMP Project draft

How to test

To test the software you will need:

More info in: https://www.gnu.org/software/hurd/microkernel/mach/gnumach/building.html

Task done

Current status

Implementation

Summary

Recover old gnumach APIC headers

We have recovered the apic.h header, original from Mach 4, with Local APIC and IOAPIC structs, and an old implementation of cpu_number().

CPU detection and enumeration

In this step, we find the Local APIC and IOAPIC registers in the ACPI tables, and enumerate them.

The implementation of this step is based in Min_SMP acpi.c implementation. The main function is acpi_setup(), who call to other functions to go across ACPI tables.

To adapt the code to gnumach, It was necessary some changes:

Implementation of cpu_number() function

Once get the lapic pointer, we could use this pointer to access to the Local APIC of the current processor. Using this, we have implemented cpu_number() function, which search in machine_slot[] array the apic_id of the current processor, and return the index as kernel ID.

A newer implementation get the Kernel ID from the apic2kernel[] array, using the apic_id as index.

This function will be used later to get the cpu currently working.

CPU enabling using StartUp IPI

In this step, we enable the cpus using the StartUp IPI. To do this, we need to write the ICR register in the Local APIC of the processor who raise the IPI (in this case, the BSP raise the IPI to each processor).

To implement this step, we have been inspired in Min_SMP mp.c and cpu.c files, and based in the existent work in i386/i386/mp_desc.c

We have split this task in some steps:

Add interrupt stack to cpus

To allow cpus execute interrupt handlers, It's needed a interrupt stack. Each cpu has its own interrupt stack.

To get this, we've added a call to interrupt_stack_alloc() to initialize the cpus interrupt stack array before call to mp_desc_init().

This step don't shows any new effect yet.

Enable paging in the cpus (WIP)

Before add the cpus to the kernel, we need to configure paging in them, to allow fully access to the memory.

To enable paging, we need to initialize CR0, CR3 and CR4 registers. in a similar way to this.

This code has been copied in paging_setup() function, in mp_desc.c. The processor, at starts, isn't capable to read the content from a pointer, so we copied the memory address of kernel_page_dir and pdpbase in two temporary integer variables: kernel_page_dir_addr, and pdpbase_addr.

The paging initialization also requires a temporary mapping in some low memory address. We keep the temporary mapping done in BSP processor until all AP will be enabled.

Add AP processors to the kernel

Once paging is enabled, each cpu will can to read its own Local APIC, using the *lapic pointer. It also allows to execute cpu_number() function, which is necessary to execute the slave_main() function to add the cpu to the kernel.

Before call to slave_main(), we need to load the final GDT and IDT, to get the same value than BSP processor, and be able to load correctly the LDT entries.

To do this, we call to gdt_init() and idt_init() in cpu_setup(), just before call to slave_main().

Once final GDT and IDT are loaded, slave_main() finish successfully, and the AP processors are added to the kernel.

Gratitude

References