rulimine/common/sys/smp.c

280 lines
8.7 KiB
C
Raw Normal View History

2020-09-18 15:39:29 +03:00
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <lib/libc.h>
2020-09-18 15:39:29 +03:00
#include <lib/acpi.h>
2020-09-21 13:15:55 +03:00
#include <sys/cpu.h>
2022-08-27 00:44:47 +03:00
#include <lib/misc.h>
2020-09-18 20:02:47 +03:00
#include <lib/print.h>
2020-09-18 21:02:10 +03:00
#include <sys/smp.h>
#include <sys/lapic.h>
#include <sys/gdt.h>
2020-09-21 13:15:55 +03:00
#include <mm/vmm.h>
2020-09-20 13:03:44 +03:00
#include <mm/pmm.h>
2020-09-18 15:39:29 +03:00
struct madt {
struct sdt header;
2020-09-18 15:39:29 +03:00
uint32_t local_controller_addr;
uint32_t flags;
char madt_entries_begin[];
} __attribute__((packed));
struct madt_header {
uint8_t type;
uint8_t length;
} __attribute__((packed));
struct madt_lapic {
struct madt_header header;
uint8_t acpi_processor_uid;
2020-09-18 15:39:29 +03:00
uint8_t lapic_id;
uint32_t flags;
} __attribute__((packed));
struct madt_x2apic {
struct madt_header header;
uint8_t reserved[2];
uint32_t x2apic_id;
uint32_t flags;
uint32_t acpi_processor_uid;
} __attribute__((packed));
extern symbol smp_trampoline_start;
extern size_t smp_trampoline_size;
struct trampoline_passed_info {
uint8_t smp_tpl_booted_flag;
uint8_t smp_tpl_target_mode;
uint32_t smp_tpl_pagemap;
2021-03-07 08:50:04 +03:00
uint32_t smp_tpl_info_struct;
struct gdtr smp_tpl_gdt;
2022-03-18 03:47:04 +03:00
uint64_t smp_tpl_hhdm;
} __attribute__((packed));
2020-09-18 15:39:29 +03:00
static bool smp_start_ap(uint32_t lapic_id, struct gdtr *gdtr,
2020-09-18 15:39:29 +03:00
struct smp_information *info_struct,
bool longmode, bool lv5, uint32_t pagemap,
bool x2apic, bool nx, uint64_t hhdm, bool wp) {
2020-09-18 15:39:29 +03:00
// Prepare the trampoline
static void *trampoline = NULL;
if (trampoline == NULL) {
trampoline = conv_mem_alloc(smp_trampoline_size);
memcpy(trampoline, smp_trampoline_start, smp_trampoline_size);
}
static struct trampoline_passed_info *passed_info = NULL;
if (passed_info == NULL) {
passed_info = (void *)(((uintptr_t)trampoline + smp_trampoline_size)
- sizeof(struct trampoline_passed_info));
}
2021-03-07 08:50:04 +03:00
passed_info->smp_tpl_info_struct = (uint32_t)(uintptr_t)info_struct;
passed_info->smp_tpl_booted_flag = 0;
passed_info->smp_tpl_pagemap = pagemap;
passed_info->smp_tpl_target_mode = ((uint32_t)x2apic << 2)
2022-09-18 12:30:21 +03:00
| ((uint32_t)lv5 << 1)
| ((uint32_t)nx << 3)
| ((uint32_t)wp << 4)
| ((uint32_t)longmode << 0);
passed_info->smp_tpl_gdt = *gdtr;
2022-03-18 03:47:04 +03:00
passed_info->smp_tpl_hhdm = hhdm;
asm volatile ("" ::: "memory");
2020-09-18 15:39:29 +03:00
// Send the INIT IPI
if (x2apic) {
x2apic_write(LAPIC_REG_ICR0, ((uint64_t)lapic_id << 32) | 0x4500);
} else {
lapic_write(LAPIC_REG_ICR1, lapic_id << 24);
lapic_write(LAPIC_REG_ICR0, 0x4500);
}
delay(10000000);
2020-09-18 15:39:29 +03:00
// Send the Startup IPI
if (x2apic) {
x2apic_write(LAPIC_REG_ICR0, ((uint64_t)lapic_id << 32) |
((size_t)trampoline / 4096) | 0x4600);
} else {
lapic_write(LAPIC_REG_ICR1, lapic_id << 24);
lapic_write(LAPIC_REG_ICR0, ((size_t)trampoline / 4096) | 0x4600);
}
2020-09-18 15:39:29 +03:00
2020-09-18 20:02:47 +03:00
for (int i = 0; i < 100; i++) {
if (locked_read(&passed_info->smp_tpl_booted_flag) == 1) {
2020-09-18 15:39:29 +03:00
return true;
}
delay(10000000);
2020-09-18 15:39:29 +03:00
}
return false;
}
2020-10-25 14:41:13 +03:00
struct smp_information *init_smp(size_t header_hack_size,
void **header_ptr,
size_t *cpu_count,
uint32_t *_bsp_lapic_id,
2020-09-18 15:39:29 +03:00
bool longmode,
bool lv5,
2020-09-18 15:39:29 +03:00
pagemap_t pagemap,
2021-07-15 17:20:29 +03:00
bool x2apic,
2022-03-18 03:47:04 +03:00
bool nx,
uint64_t hhdm,
bool wp) {
if (!lapic_check())
return NULL;
2020-09-18 15:39:29 +03:00
// Search for MADT table
struct madt *madt = acpi_get_table("APIC", 0);
if (madt == NULL)
return NULL;
2021-03-07 08:50:04 +03:00
struct gdtr gdtr = gdt;
2020-09-18 15:39:29 +03:00
uint32_t eax, ebx, ecx, edx;
if (!cpuid(1, 0, &eax, &ebx, &ecx, &edx))
return NULL;
uint8_t bsp_lapic_id = ebx >> 24;
2020-09-18 15:39:29 +03:00
x2apic = x2apic && x2apic_enable();
uint32_t bsp_x2apic_id = 0;
if (x2apic) {
// The Intel manual recommends checking if leaf 0x1f exists first, and
// using that in place of 0xb if that's the case
if (!cpuid(0x1f, 0, &eax, &ebx, &ecx, &edx))
if (!cpuid(0xb, 0, &eax, &ebx, &ecx, &edx))
return NULL;
bsp_x2apic_id = edx;
*_bsp_lapic_id = bsp_x2apic_id;
} else {
*_bsp_lapic_id = bsp_lapic_id;
}
*cpu_count = 0;
// Count the MAX of startable APs and allocate accordingly
size_t max_cpus = 0;
for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
(uintptr_t)madt_ptr < (uintptr_t)madt + madt->header.length;
madt_ptr += *(madt_ptr + 1)) {
switch (*madt_ptr) {
case 0: {
// Processor local xAPIC
struct madt_lapic *lapic = (void *)madt_ptr;
// Check if we can actually try to start the AP
if ((lapic->flags & 1) ^ ((lapic->flags >> 1) & 1))
max_cpus++;
continue;
}
case 9: {
// Processor local x2APIC
if (!x2apic)
continue;
2021-07-06 06:17:18 +03:00
struct madt_x2apic *x2lapic = (void *)madt_ptr;
// Check if we can actually try to start the AP
2021-07-06 06:17:18 +03:00
if ((x2lapic->flags & 1) ^ ((x2lapic->flags >> 1) & 1))
max_cpus++;
continue;
}
}
}
2020-10-25 14:41:13 +03:00
*header_ptr = ext_mem_alloc(
header_hack_size + max_cpus * sizeof(struct smp_information));
struct smp_information *ret = *header_ptr + header_hack_size;
*cpu_count = 0;
// Try to start all APs
2020-09-18 15:39:29 +03:00
for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
(uintptr_t)madt_ptr < (uintptr_t)madt + madt->header.length;
2020-09-18 15:39:29 +03:00
madt_ptr += *(madt_ptr + 1)) {
switch (*madt_ptr) {
case 0: {
// Processor local xAPIC
struct madt_lapic *lapic = (void *)madt_ptr;
2020-09-26 18:12:52 +03:00
// Check if we can actually try to start the AP
if (!((lapic->flags & 1) ^ ((lapic->flags >> 1) & 1)))
continue;
struct smp_information *info_struct = &ret[*cpu_count];
2020-09-20 11:28:39 +03:00
info_struct->acpi_processor_uid = lapic->acpi_processor_uid;
info_struct->lapic_id = lapic->lapic_id;
2020-09-18 15:39:29 +03:00
// Do not try to restart the BSP
if (lapic->lapic_id == bsp_lapic_id) {
2020-09-20 11:28:39 +03:00
(*cpu_count)++;
2020-09-18 15:39:29 +03:00
continue;
2020-09-20 11:28:39 +03:00
}
2020-09-18 15:39:29 +03:00
2021-05-11 07:46:42 +03:00
printv("smp: [xAPIC] Found candidate AP for bring-up. LAPIC ID: %u\n", lapic->lapic_id);
2020-09-18 15:39:29 +03:00
// Try to start the AP
if (!smp_start_ap(lapic->lapic_id, &gdtr, info_struct,
2021-03-02 12:23:43 +03:00
longmode, lv5, (uintptr_t)pagemap.top_level,
x2apic, nx, hhdm, wp)) {
print("smp: FAILED to bring-up AP\n");
continue;
}
2021-05-11 07:46:42 +03:00
printv("smp: Successfully brought up AP\n");
(*cpu_count)++;
continue;
}
case 9: {
// Processor local x2APIC
if (!x2apic)
continue;
2021-07-06 06:17:18 +03:00
struct madt_x2apic *x2lapic = (void *)madt_ptr;
// Check if we can actually try to start the AP
2021-07-06 06:17:18 +03:00
if (!((x2lapic->flags & 1) ^ ((x2lapic->flags >> 1) & 1)))
continue;
struct smp_information *info_struct = &ret[*cpu_count];
2021-07-06 06:17:18 +03:00
info_struct->acpi_processor_uid = x2lapic->acpi_processor_uid;
info_struct->lapic_id = x2lapic->x2apic_id;
// Do not try to restart the BSP
2021-07-06 06:17:18 +03:00
if (x2lapic->x2apic_id == bsp_x2apic_id) {
(*cpu_count)++;
continue;
}
2021-07-06 06:17:18 +03:00
printv("smp: [x2APIC] Found candidate AP for bring-up. LAPIC ID: %u\n", x2lapic->x2apic_id);
// Try to start the AP
2021-07-06 06:17:18 +03:00
if (!smp_start_ap(x2lapic->x2apic_id, &gdtr, info_struct,
2021-03-02 12:23:43 +03:00
longmode, lv5, (uintptr_t)pagemap.top_level,
true, nx, hhdm, wp)) {
2020-09-18 15:39:29 +03:00
print("smp: FAILED to bring-up AP\n");
continue;
}
2021-05-11 07:46:42 +03:00
printv("smp: Successfully brought up AP\n");
2020-09-18 15:39:29 +03:00
(*cpu_count)++;
2020-09-20 11:28:39 +03:00
continue;
2020-09-18 15:39:29 +03:00
}
}
}
return ret;
}