4.8 KiB
LAPIC devices
This driver is defined inside shelter/lib/include/devs/apic/lapic.h and implemented inside shelter/lib/src/devs/apic/lapic.c.
Driver initialization
During the MADT parsing, all LAPIC entries are counted. Then, a call to sh_lapic_init_devs(sh_uint16 max_lapic_id,sh_uint16 max_acpi_processor_id) is made to initialize the driver internal state. This call allocates two arrays: one indexed by LAPIC id and one indexed by ACPI processors id.
Device initialization
After that, each time a LAPIC entry is parsed, a call to sh_lapic_init(sh_uint64 lapic_phys,sh_uint8 spurious_vector,sh_uint8 apic_id,sh_uint8 apic_processor_id,sh_uint32 flags,sh_lapic_DEVICE *lapic) is made. This does two things:
- Identity map the memory space for the LAPIC
- Fill the structure for the LAPIC device
Then, a call to sh_lapic_bind(sh_lapic_DEVICE *lapic_dev) is made, registering the LAPIC structure in both arrays.
Finally, when the MADT parsing is over, a call to sh_lapic_init_dev(sh_uint8 spurious_vector,sh_lapic_DEVICE *lapic) initialize the LAPIC by setuping a few of its registers.
Basic LAPIC management
For basic LAPIC management, the following functions are provided:
sh_lapic_eoi(sh_lapic_DEVICE *lapic): send a End Of Interrupt (EOI) to the LAPICsh_lapic_get_dev_apic_id(sh_uint64 apic_id): return ash_lapic_DEVICE*pointing to the LAPIC structure corresponding to the provided APIC idsh_lapic_get_dev_acpi_cpu_id(sh_uint64 acpi_processor_id): return ash_lapic_DEVICE*pointing to the LAPIC structure corresponding to the provided ACPI processor idsh_lapic_get_by_apic_id_array(): return ash_lapic_DEVICE**of the LAPIC devices pointers array, indexed by LAPIC idsh_lapic_get_max_apic_id(): return the max LAPIC idsh_lapic_get_max_acpi_processor_id(): return the max ACPI processor idsh_lapic_get_lapic_count(): return the number of registered LAPICssh_lapic_get_current_core(): return the LAPIC id of the current core, return -1 if error happened
LAPIC timer
To launch a one shot timer without knowing the frequency, the sh_lapic_timer_one_shot(sh_lapic_DEVICE *lapic_dev,sh_uint32 initial_value) can be used.
But the LAPIC driver provides a way to calibrate the LAPIC in order to obtain its frequency. For this, we use the following process:
- Launch a timer with max value
- Read the start TSC and current LAPIC value, iterate for 10 millions times, read the end TSC and current LAPIC value.
- Compute
tsc_deltaandlapic_delta - Using the following formula, estimate the LAPIC frequency:
lapic_freq = (lapic_delta * cpu_freq) / tsc_delta
This calibration process is implemented in sh_lapic_calibrate(sh_lapic_DEVICE *lapic_dev,sh_uint64 cpu_freq). But the TSC frequency estimation must have been achevied before that.
When the LAPIC frequency is obtained, two new functions are unlocked:
sh_lapic_get_frequency(): return the estimated LAPIC frequencysh_lapic_timer_one_shot_us(sh_lapic_DEVICE *lapic_dev,sh_uint64 microseconds_count): launch a one shot timer by taking a value in microsecond. Compute the initial value using the estimated frequency before hand.
IPI management
The LAPIC driver provides a helper to know if the LAPIC IPI feature is busy: sh_lapic_ipi_is_busy(sh_lapic_DEVICE *lapic_dev).
Sending a fixed IPI
The primitive to send a fixed IPI is:
sh_lapic_send_fixed_ipi(sh_lapic_DEVICE *lapic_dev,sh_uint8 vector,sh_uint32 destination_mode,sh_int16 target_lapic_id);
lapic_dev must be the struct of the LAPIC of the current CPU.
destination_mode can either be SH_LAPIC_IPI_DESTINATION_SPECIFIC, SH_LAPIC_IPI_DESTINATION_SELF, SH_LAPIC_IPI_DESTINATION_ALL or SH_LAPIC_IPI_DESTINATION_ALL_EXCLUDING_SELF.
If destination_mode isn't SH_LAPIC_IPI_DESTINATION_SPECIFIC, target_lapic_id must be SH_LAPIC_IPI_NO_DESTINATION.
Sending any others IPI
The primitive to send any other IPI than a fixed IPI is:
sh_lapic_send_ipi(sh_lapic_DEVICE *lapic_dev,sh_uint32 ipi_type,sh_uint32 destination_mode,sh_int16 target_lapic_id,sh_page_PHYSICAL_ADDRESS start_address);
lapic_dev must be the struct of the LAPIC of the current CPU.
ipi_type can either be SH_LAPIC_IPI_TYPE_INIT, SH_LAPIC_IPI_TYPE_STARTUP or SH_LAPIC_IPI_TYPE_NMI.
If ipi_type is SH_LAPIC_IPI_TYPE_STARTUP, start_address must contain the starting address. Address constraint (compatible with 16 bits mode) are checked.
If ipi_type isn't SH_LAPIC_IPI_TYPE_STARTUP, start_address must be 0.
destination_mode can either be SH_LAPIC_IPI_DESTINATION_SPECIFIC, SH_LAPIC_IPI_DESTINATION_SELF, SH_LAPIC_IPI_DESTINATION_ALL or SH_LAPIC_IPI_DESTINATION_ALL_EXCLUDING_SELF.
If destination_mode isn't SH_LAPIC_IPI_DESTINATION_SPECIFIC, target_lapic_id must be SH_LAPIC_IPI_NO_DESTINATION.