uint8_t fpu_state[FPU_STATE_SIZE] __attribute__((aligned(FPU_STATE_ALIGN)));
};
-// Global pointer to the currently running process
+// Per-CPU pointer to the currently running process.
+// On x86 SMP this reads from the GS-based percpu_data; on non-x86 it
+// falls back to a plain global (single-CPU only).
+#ifdef __i386__
+#include "arch/x86/percpu.h"
+#define current_process percpu_current()
+#else
extern struct process* current_process;
+#endif
// Initialize the multitasking system
void process_init(void);
push %eax
push %gs
- /* Load Kernel Data Segment for DS/ES/FS.
- * GS is NOT touched — it holds the per-CPU selector set by
- * percpu_setup_gs() and must survive ISR entry/exit. */
+ /* Load Kernel Data Segment for DS/ES/FS */
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
+ /* Reload per-CPU GS: read LAPIC ID from MMIO, look up GS selector.
+ * This is necessary because user mode may have changed GS (e.g. TLS).
+ * LAPIC_ID register is at KVA_LAPIC (0xC0400000) + 0x20.
+ * The ID is in bits 31:24 of the register value. */
+ mov 0xC0400020, %eax
+ shr $24, %eax
+ lea _percpu_gs_lut, %ecx
+ movzwl (%ecx,%eax,2), %eax
+ mov %ax, %gs
+
/* Call C handler */
push %esp /* Pass pointer to stack structure */
call isr_handler
add $4, %esp
- /* Restore GS (per-CPU) then DS/ES/FS */
+ /* Restore user GS then DS/ES/FS */
pop %gs
pop %eax
mov %ax, %ds
static struct percpu_data g_percpu[SMP_MAX_CPUS];
+/* Lookup table: LAPIC ID → percpu GS selector.
+ * Used by the ISR entry stub (interrupts.S) to reload the correct
+ * percpu GS without needing GS itself (chicken-and-egg).
+ * Indexed by LAPIC ID (0–255), entries are 16-bit GDT selectors. */
+uint16_t _percpu_gs_lut[256] __attribute__((aligned(4)));
+
/* We use GDT entries 6..6+N for per-CPU GS segments.
* GDT layout: 0=null, 1=kcode, 2=kdata, 3=ucode, 4=udata, 5=TSS, 6+=percpu */
#define PERCPU_GDT_BASE 6
uint32_t ncpus = smp_get_cpu_count();
if (ncpus > SMP_MAX_CPUS) ncpus = SMP_MAX_CPUS;
+ /* Clear lookup table — default to BSP's GS selector as fallback */
+ uint16_t bsp_sel = (uint16_t)(PERCPU_GDT_BASE * 8);
+ for (int j = 0; j < 256; j++) _percpu_gs_lut[j] = bsp_sel;
+
for (uint32_t i = 0; i < ncpus; i++) {
const struct cpu_info* ci = smp_get_cpu(i);
g_percpu[i].cpu_index = i;
/* Create a GDT entry for this CPU's GS segment */
set_percpu_gdt_entry(PERCPU_GDT_BASE + i, (uint32_t)(uintptr_t)&g_percpu[i]);
+
+ /* Populate LAPIC-ID → GS-selector lookup table */
+ uint16_t sel = (uint16_t)((PERCPU_GDT_BASE + i) * 8);
+ if (ci) _percpu_gs_lut[ci->lapic_id] = sel;
}
kprintf("[PERCPU] Initialized for %u CPU(s).\n", (unsigned)ncpus);
void hal_cpu_set_tls(uintptr_t base) {
/* GDT entry 22: user TLS segment (ring 3, data RW) */
gdt_set_gate_ext(22, (uint32_t)base, 0xFFFFF, 0xF2, 0xCF);
- __asm__ volatile(
- "mov $0xB3, %%ax\n"
- "mov %%ax, %%gs\n" : : : "ax"
- ); /* selector = 22*8 | RPL=3 = 0xB3 */
+ /* Do NOT reload GS here — kernel GS must stay as percpu selector.
+ * The user TLS GS (selector 0xB3) is loaded when returning to ring3
+ * via the saved register state on the interrupt/syscall stack. */
}
#else
#include "sched_pcpu.h"
#include <stddef.h>
+#ifndef __i386__
struct process* current_process = NULL;
+#endif
struct process* ready_queue_head = NULL;
struct process* ready_queue_tail = NULL;
static uint32_t next_pid = 1;
}
kernel_proc->kernel_stack = (uint32_t*)kstack0;
- current_process = kernel_proc;
+ percpu_set_current(kernel_proc);
ready_queue_head = kernel_proc;
ready_queue_tail = kernel_proc;
kernel_proc->next = kernel_proc;
return;
}
- current_process = next;
+ percpu_set_current(next);
current_process->state = PROCESS_RUNNING;
current_process->time_slice = SCHED_TIME_SLICE;