// Copyright (c) 2023-2026 Chris (boreddevnl) // This software is released under the GNU General Public License v3.0. See LICENSE file for details. // This header needs to maintain in any file it is present in, as per the GPL license terms. #include "smp.h" #include "limine.h" #include "memory_manager.h" #include "gdt.h" #include "idt.h" #include "platform.h" #include "paging.h" #include "process.h" #include "work_queue.h" #include "core/kutils.h" extern void serial_write(const char *str); extern void serial_write_num(uint32_t n); extern void serial_write_hex(uint64_t n); static cpu_state_t *cpu_states = NULL; static uint32_t total_cpus = 0; static uint32_t bsp_lapic_id = 0; static cpu_state_t *bsp_cpu_state = NULL; #define MSR_GS_BASE 0xC0000101 #define MSR_KERNEL_GS_BASE 0xC0000102 static inline void wrmsr(uint32_t msr, uint64_t value) { uint32_t low = (uint32_t)value; uint32_t high = (uint32_t)(value >> 32); asm volatile("wrmsr" : : "c"(msr), "a"(low), "d"(high)); } static uint32_t read_lapic_id(void) { extern uint64_t hhdm_offset; volatile uint32_t *lapic = (volatile uint32_t *)(hhdm_offset + 0xFEE00000ULL); return (lapic[0x020 / 4] >> 24) & 0xFF; } uint32_t smp_this_cpu_id(void) { if (!cpu_states || total_cpus == 0) return 0; uint32_t lapic = read_lapic_id(); if (lapic == bsp_lapic_id) return 0; cpu_state_t *state = NULL; asm volatile("movq %%gs:0, %0" : "=r"(state) : : "memory"); if (state && state->lapic_id == lapic) return state->cpu_id; for (uint32_t i = 0; i < total_cpus; i++) { if (cpu_states[i].online && cpu_states[i].lapic_id == lapic) return i; } return 0; } uint32_t smp_cpu_count(void) { return total_cpus; } cpu_state_t *smp_get_cpu(uint32_t cpu_id) { if (cpu_id >= total_cpus) return NULL; return &cpu_states[cpu_id]; } static void ap_entry(struct limine_smp_info *info) { uint32_t my_id = (uint32_t)(info->extra_argument); uint64_t cr0; asm volatile("mov %%cr0, %0" : "=r"(cr0)); cr0 &= ~(1ULL << 2); cr0 |= (1ULL << 1); cr0 |= (1ULL << 5); asm volatile("mov %0, %%cr0" : : "r"(cr0)); uint64_t cr4; asm volatile("mov %%cr4, %0" : "=r"(cr4)); cr4 |= (1ULL << 9); cr4 |= (1ULL << 10); asm volatile("mov %0, %%cr4" : : "r"(cr4)); asm volatile("fninit"); extern struct gdt_ptr gdtr; extern void gdt_flush(uint64_t); gdt_flush((uint64_t)&gdtr); gdt_load_ap_tss(my_id); extern void idt_load(void); idt_load(); extern void syscall_init(void); syscall_init(); uint64_t kernel_cr3 = paging_get_pml4_phys(); asm volatile("mov %0, %%cr3" : : "r"(kernel_cr3)); extern void lapic_enable(void); lapic_enable(); cpu_states[my_id].self = &cpu_states[my_id]; cpu_states[my_id].online = true; cpu_states[my_id].kernel_syscall_stack = cpu_states[my_id].kernel_stack; wrmsr(MSR_GS_BASE, (uint64_t)&cpu_states[my_id]); wrmsr(MSR_KERNEL_GS_BASE, (uint64_t)&cpu_states[my_id]); serial_write("[SMP] AP "); serial_write_num(my_id); serial_write(" online (LAPIC "); serial_write_num(cpu_states[my_id].lapic_id); serial_write(")\n"); process_t *ap_idle = process_create(NULL, false); ap_idle->cpu_affinity = my_id; ap_idle->is_idle = true; k_strcpy(ap_idle->name, "idle:"); char id_s[8]; k_itoa(my_id, id_s); k_strcpy(ap_idle->name + 5, id_s); process_set_current_for_cpu(my_id, ap_idle); asm volatile("sti"); work_queue_drain_loop(); } void smp_init_bsp(void) { static cpu_state_t bsp_state_static = {0}; bsp_state_static.cpu_id = 0; bsp_lapic_id = read_lapic_id(); bsp_state_static.lapic_id = bsp_lapic_id; bsp_state_static.self = &bsp_state_static; bsp_state_static.online = true; wrmsr(MSR_GS_BASE, (uint64_t)&bsp_state_static); wrmsr(MSR_KERNEL_GS_BASE, (uint64_t)&bsp_state_static); bsp_cpu_state = &bsp_state_static; } // --- SMP Initialization --- uint32_t smp_init(struct limine_smp_response *smp_resp) { if (!smp_resp || smp_resp->cpu_count <= 1) { total_cpus = 1; cpu_states = (cpu_state_t *)kmalloc(sizeof(cpu_state_t)); if (!cpu_states) return 1; extern void mem_memset(void *, int, size_t); mem_memset(cpu_states, 0, sizeof(cpu_state_t)); cpu_states[0].cpu_id = 0; cpu_states[0].lapic_id = read_lapic_id(); cpu_states[0].online = true; serial_write("[SMP] Single CPU mode\n"); return 1; } total_cpus = (uint32_t)smp_resp->cpu_count; bsp_lapic_id = smp_resp->bsp_lapic_id; serial_write("[SMP] Detected "); serial_write_num(total_cpus); serial_write(" CPUs. BSP LAPIC ID: "); serial_write_num(bsp_lapic_id); serial_write("\n"); cpu_states = (cpu_state_t *)kmalloc(total_cpus * sizeof(cpu_state_t)); if (!cpu_states) { serial_write("[SMP] ERROR: Failed to allocate CPU state array!\n"); total_cpus = 1; return 1; } extern void mem_memset(void *, int, size_t); mem_memset(cpu_states, 0, total_cpus * sizeof(cpu_state_t)); gdt_init_ap_tss(total_cpus); uint32_t bsp_index = 0; for (uint32_t i = 0; i < total_cpus; i++) { struct limine_smp_info *cpu = smp_resp->cpus[i]; cpu_states[i].cpu_id = i; cpu_states[i].lapic_id = cpu->lapic_id; if (cpu->lapic_id == bsp_lapic_id) { cpu_states[i] = *bsp_cpu_state; // Copy early BSP state cpu_states[i].self = &cpu_states[i]; cpu_states[i].kernel_stack = 0; // Limine stack for now cpu_states[i].kernel_syscall_stack = 0; bsp_index = i; wrmsr(MSR_GS_BASE, (uint64_t)&cpu_states[i]); wrmsr(MSR_KERNEL_GS_BASE, (uint64_t)&cpu_states[i]); serial_write("[SMP] BSP CPU "); serial_write_num(i); serial_write(" (LAPIC "); serial_write_num(cpu->lapic_id); serial_write(") online\n"); } else { void *ap_stack = kmalloc_aligned(65536, 65536); if (!ap_stack) { serial_write("[SMP] ERROR: Failed to allocate AP stack!\n"); continue; } cpu_states[i].kernel_stack = (uint64_t)ap_stack + 65536; cpu_states[i].kernel_stack_alloc = ap_stack; cpu_states[i].online = false; cpu->extra_argument = i; serial_write("[SMP] Starting AP "); serial_write_num(i); serial_write(" (LAPIC "); serial_write_num(cpu->lapic_id); serial_write(")...\n"); __atomic_store_n(&cpu->goto_address, ap_entry, __ATOMIC_SEQ_CST); } } volatile uint32_t timeout = 10000000; uint32_t online_count = 0; while (timeout-- > 0) { online_count = 0; for (uint32_t i = 0; i < total_cpus; i++) { if (cpu_states[i].online) online_count++; } if (online_count == total_cpus) break; asm volatile("pause"); } serial_write("[SMP] All "); serial_write_num(online_count); serial_write(" of "); serial_write_num(total_cpus); serial_write(" CPUs online\n"); return online_count; }