WIP x86 SMP

This commit is contained in:
Travis Geiselbrecht
2024-12-06 00:03:23 -08:00
parent 6f32a0f377
commit fd79fccdde
13 changed files with 203 additions and 80 deletions

View File

@@ -3,6 +3,7 @@ Checks: >
-*,
bugprone-*,
-bugprone-easily-swappable-parameters,
-bugprone-reserved-identifier,
clang-diagnostic-*,
-clang-diagnostic-unused-command-line-argument,

View File

@@ -421,7 +421,7 @@ bool arch_mmu_supports_user_aspaces(void) { return false; }
void x86_mmu_early_init(void) {
/* Set WP bit in CR0*/
volatile uint32_t cr0 = x86_get_cr0();
uint32_t cr0 = x86_get_cr0();
cr0 |= X86_CR0_WP;
x86_set_cr0(cr0);

View File

@@ -624,15 +624,13 @@ bool arch_mmu_supports_ns_mappings(void) { return false; }
bool arch_mmu_supports_user_aspaces(void) { return false; }
void x86_mmu_early_init(void) {
volatile uint64_t efer_msr, cr0, cr4;
/* Set WP bit in CR0*/
cr0 = x86_get_cr0();
uint64_t cr0 = x86_get_cr0();
cr0 |= X86_CR0_WP;
x86_set_cr0(cr0);
/* Setting the SMEP & SMAP bit in CR4 */
cr4 = x86_get_cr4();
uint64_t cr4 = x86_get_cr4();
if (x86_feature_test(X86_FEATURE_SMEP))
cr4 |= X86_CR4_SMEP;
if (x86_feature_test(X86_FEATURE_SMAP))
@@ -640,7 +638,7 @@ void x86_mmu_early_init(void) {
x86_set_cr4(cr4);
/* Set NXE bit in MSR_EFER*/
efer_msr = read_msr(X86_MSR_IA32_EFER);
uint64_t efer_msr = read_msr(X86_MSR_IA32_EFER);
efer_msr |= X86_EFER_NXE;
write_msr(X86_MSR_IA32_EFER, efer_msr);

View File

@@ -204,6 +204,11 @@ highaddr:
/* set up the idt */
call setup_idt
/* set up the percpu data structure pointer for the boot cpu */
xor %edi, %edi
xor %esi, %esi
call x86_percpu_init_early
/* call the main module */
call lk_main

View File

@@ -19,35 +19,31 @@
#include <kernel/vm.h>
#include <platform.h>
#include <sys/types.h>
#include <string.h>
/* Describe how start.S sets up the MMU.
* These data structures are later used by vm routines to lookup pointers
* to physical pages based on physical addresses.
*/
struct mmu_initial_mapping mmu_initial_mappings[] = {
#if ARCH_X86_64
/* 64GB of memory mapped where the kernel lives */
/* 64GB of the first 64GB of memory mapped 1:1 */
{
.phys = MEMBASE,
.virt = KERNEL_ASPACE_BASE,
.size = PHYSMAP_SIZE, /* x86-64 maps first 64GB by default, 1GB on x86-32 */
.size = PHYSMAP_SIZE, /* x86-64 maps first 64GB by default, 1GB on x86-32, 16MB in legacy mode */
.flags = 0,
.name = "physmap"
},
#endif
/* 1GB of memory mapped where the kernel lives */
#if ARCH_X86_64
/* Another linear map of the first GB of memory where the kernel image
* lives at the top of the address space. */
{
.phys = MEMBASE,
.virt = KERNEL_BASE,
#if X86_LEGACY
.size = 16*MB, /* only map the first 16MB on legacy x86 due to page table usage */
#else
.size = 1*GB, /* x86 maps first 1GB by default */
#endif
.size = 1*GB,
.flags = 0,
.name = "kernel"
},
#endif
/* null entry to terminate the list */
{ 0 }
@@ -70,6 +66,7 @@ void arch_early_init(void) {
/* enable caches here for now */
clear_in_cr0(X86_CR0_NW | X86_CR0_CD);
/* configure the system TSS */
#if ARCH_X86_32
system_tss.esp0 = 0;
system_tss.ss0 = DATA_SELECTOR;
@@ -78,9 +75,10 @@ void arch_early_init(void) {
system_tss.eflags = 0x00003002;
system_tss.bitmap = offsetof(tss_32_t, tss_bitmap);
system_tss.trace = 1; // trap on hardware task switch
#elif ARCH_X86_64
/* nothing to be done here, a fully zeroed TSS is a good starting point */
#endif
set_global_desc(TSS_SELECTOR, &system_tss, sizeof(system_tss), 1, 0, 0, SEG_TYPE_TSS, 0, 0);
x86_set_gdt_descriptor(TSS_SELECTOR, &system_tss, sizeof(system_tss), 1, 0, 0, SEG_TYPE_TSS, 0, 0);
x86_ltr(TSS_SELECTOR);
x86_feature_early_init();

View File

@@ -5,62 +5,89 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <lk/compiler.h>
#include <arch/x86/descriptor.h>
/* not the best way to do this, but easy for now */
typedef union {
struct {
uint16_t limit_15_0;
uint16_t base_15_0;
uint8_t base_23_16;
#include <assert.h>
#include <lk/compiler.h>
uint8_t type : 4;
uint8_t s : 1;
uint8_t dpl : 2;
uint8_t p : 1;
extern uint64_t _gdt[];
uint8_t limit_19_16 : 4;
uint8_t avl : 1;
uint8_t reserved_0 : 1;
uint8_t d_b : 1;
uint8_t g : 1;
uint8_t base_31_24;
} __PACKED seg_desc_legacy;
struct {
uint32_t base_63_32;
uint32_t reserved_1;
} __PACKED seg_desc_64;
} __PACKED seg_desc_t;
extern seg_desc_t _gdt[];
void set_global_desc(seg_sel_t sel, void *base, uint32_t limit,
void x86_set_gdt_descriptor(seg_sel_t sel, void *base, uint32_t limit,
uint8_t present, uint8_t ring, uint8_t sys, uint8_t type, uint8_t gran, uint8_t bits) {
// convert selector into index
typedef struct {
struct {
uint16_t limit_15_0;
uint16_t base_15_0;
uint8_t base_23_16;
uint8_t type : 4;
uint8_t s : 1;
uint8_t dpl : 2;
uint8_t p : 1;
uint8_t limit_19_16 : 4;
uint8_t avl : 1;
uint8_t reserved : 1;
uint8_t d_b : 1;
uint8_t g : 1;
uint8_t base_31_24;
} seg_desc_legacy;
#if ARCH_X86_64
// some descriptors have additional fields for x86-64
struct {
uint32_t base_63_32;
uint32_t reserved;
} seg_desc_64;
#endif
} seg_desc_t;
#if ARCH_X86_64
static_assert(sizeof(seg_desc_t) == 16, "seg_desc_t size mismatch");
#else
static_assert(sizeof(seg_desc_t) == 8, "seg_desc_t size mismatch");
#endif
seg_desc_t desc = {0};
desc.seg_desc_legacy.limit_15_0 = limit & 0x0000ffff;
desc.seg_desc_legacy.limit_19_16 = (limit & 0x000f0000) >> 16;
desc.seg_desc_legacy.base_15_0 = ((uintptr_t) base) & 0x0000ffff;
desc.seg_desc_legacy.base_23_16 = (((uintptr_t) base) & 0x00ff0000) >> 16;
desc.seg_desc_legacy.base_31_24 = ((uintptr_t) base) >> 24;
desc.seg_desc_legacy.type = type & 0x0f; // segment type
desc.seg_desc_legacy.s = sys != 0; // system / non-system
desc.seg_desc_legacy.dpl = ring & 0x03; // descriptor privilege level
desc.seg_desc_legacy.p = present != 0; // present
desc.seg_desc_legacy.avl = 0;
desc.seg_desc_legacy.reserved = 0;
desc.seg_desc_legacy.d_b = bits != 0; // 16 / 32 bit
desc.seg_desc_legacy.g = gran != 0; // granularity
// convert selector into index, which are always 8 byte indexed
uint16_t index = sel >> 3;
_gdt[index].seg_desc_legacy.limit_15_0 = limit & 0x0000ffff;
_gdt[index].seg_desc_legacy.limit_19_16 = (limit & 0x000f0000) >> 16;
_gdt[index].seg_desc_legacy.base_15_0 = ((uintptr_t) base) & 0x0000ffff;
_gdt[index].seg_desc_legacy.base_23_16 = (((uintptr_t) base) & 0x00ff0000) >> 16;
_gdt[index].seg_desc_legacy.base_31_24 = ((uintptr_t) base) >> 24;
_gdt[index].seg_desc_legacy.type = type & 0x0f; // segment type
_gdt[index].seg_desc_legacy.p = present != 0; // present
_gdt[index].seg_desc_legacy.dpl = ring & 0x03; // descriptor privilege level
_gdt[index].seg_desc_legacy.g = gran != 0; // granularity
_gdt[index].seg_desc_legacy.s = sys != 0; // system / non-system
_gdt[index].seg_desc_legacy.d_b = bits != 0; // 16 / 32 bit
seg_desc_t *entry = (seg_desc_t *)&_gdt[index];
entry->seg_desc_legacy = desc.seg_desc_legacy;
#ifdef ARCH_X86_64
if (TSS_SELECTOR == sel) {
_gdt[index + 1].seg_desc_64.base_63_32 = (uint32_t)((uintptr_t) base >> 32);
_gdt[index + 1].seg_desc_64.reserved_1 = 0;
if (sys == 0) {
// some of the system descriptors have two more words
switch (type) {
case SEG_TYPE_TSS:
case SEG_TYPE_TSS_BUSY:
case SEG_TYPE_LDT:
case SEG_TYPE_CALL_GATE:
// copy the lower 32 bits of the descriptor (base and limit)
desc.seg_desc_64.base_63_32 = (uint32_t)((uintptr_t) base >> 32);
desc.seg_desc_64.reserved = 0;
// copy the upper 64 bits of the descriptor
entry->seg_desc_64 = desc.seg_desc_64;
break;
}
}
#endif
}

View File

@@ -123,7 +123,10 @@ _tss_gde:
.byte 0x89 /* P(1) DPL(00) S(0) TYPE(9) */
.byte 0x80 /* G(1) D/B(0) L(0) AVL(0) limit 19:16 */
.byte 0 /* base 31:24 */
#if ARCH_X86_64
/* 64-bit TSSs are 16 bytes long */
.quad 0x0000000000000000
#endif
.set i, i+1
.endr

View File

@@ -18,8 +18,13 @@
/* based on how start.S sets up the physmap */
#if ARCH_X86_64
#define PHYSMAP_SIZE (64ULL*GB)
#define PHYSMAP_SIZE (64ULL*1024*1024*1024)
#elif X86_LEGACY
/* Only map the first 16MB on legacy x86 due to page table usage
* due to lack of 4MB pages. */
#define PHYSMAP_SIZE (16ULL*1024*1024)
#elif ARCH_X86_32
#define PHYSMAP_SIZE (1ULL*GB)
/* Map 1GB by default for x86-32 */
#define PHYSMAP_SIZE (1ULL*1024*1024*1024)
#endif

View File

@@ -25,17 +25,33 @@
#define USER_CODE_64_SELECTOR 0x38
#define USER_DATA_64_SELECTOR 0x40
/* base selector for a list of TSSes, one per cpu (SMP_MAX_CPUS) */
#define TSS_SELECTOR 0x48
/*
* Descriptor Types
*/
#define SEG_TYPE_TSS 0x9
#define SEG_TYPE_TSS_BUSY 0xb
#define SEG_TYPE_TASK_GATE 0x5
#define SEG_TYPE_INT_GATE 0xe // 32 bit
#define SEG_TYPE_DATA_RW 0x2
#define SEG_TYPE_CODE_RW 0xa
/* code/data segment types (S = 1) */
/* bit 0 is accessed */
#define SEG_TYPE_DATA_RO 0x0
#define SEG_TYPE_DATA_RW 0x2
#define SEG_TYPE_DATA_RO_EXPAND_DOWN 0x4
#define SEG_TYPE_DATA_RW_EXPAND_DOWN 0x6
#define SEG_TYPE_CODE_XO 0x8
#define SEG_TYPE_CODE_RO 0xa
#define SEG_TYPE_CODE_XO_CONFORMING 0xc
#define SEG_TYPE_CODE_RO_CONFORMING 0xe
/* system segment types (S = 0) */
#define SEG_TYPE_TSS_16 0x1
#define SEG_TYPE_LDT 0x2
#define SEG_TYPE_TSS_16_BUSY 0x3
#define SEG_TYPE_CALL_GATE_16 0x4
#define SEG_TYPE_TASK_GATE 0x5
#define SEG_TYPE_INT_GATE_16 0x6
#define SEG_TYPE_TRAP_GATE_16 0x7
#define SEG_TYPE_TSS 0x9
#define SEG_TYPE_TSS_BUSY 0xb
#define SEG_TYPE_CALL_GATE 0xc
#define SEG_TYPE_INT_GATE 0xe
#define SEG_TYPE_TRAP_GATE 0xf
#ifndef ASSEMBLY
@@ -43,7 +59,7 @@
typedef uint16_t seg_sel_t;
void set_global_desc(seg_sel_t sel, void *base, uint32_t limit,
void x86_set_gdt_descriptor(seg_sel_t sel, void *base, uint32_t limit,
uint8_t present, uint8_t ring, uint8_t sys, uint8_t type, uint8_t gran, uint8_t bits);
#endif

View File

@@ -24,7 +24,6 @@
#pragma once
#include <arch/x86.h>
#include <inttypes.h>
#include <lk/compiler.h>
#include <stdbool.h>
#include <assert.h>

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2024 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once
#include <sys/types.h>
// per cpu pointer pointed to by a segment register on x86
typedef struct x86_percpu {
// pointer back to ourselves so we can get a raw pointer via segment:0
struct x86_percpu *self;
uint cpu_num;
uint apic_id;
struct thread *current_thread;
// XXX add more stuff:
// per cpu TSS
// per cpu doublefault/nmi stacks
} x86_percpu_t;
// called extremely early on the boot cpu and each secondary cpu
void x86_percpu_init_early(uint cpu_num, uint apic_id);

42
arch/x86/mp.c Normal file
View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2024 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <arch/x86/mp.h>
#include <assert.h>
#include <arch/x86.h>
#include <sys/types.h>
// the boot cpu's percpu struct
static x86_percpu_t x86_boot_percpu;
// pointer to an array of percpu structs for each of the secondary cpus
static x86_percpu_t **x86_ap_percpus;
static x86_percpu_t *percpu_for_cpu(uint cpu_num) {
DEBUG_ASSERT(cpu_num < SMP_MAX_CPUS);
if (cpu_num == 0) {
return &x86_boot_percpu;
}
DEBUG_ASSERT(x86_ap_percpus);
return x86_ap_percpus[cpu_num - 1];
}
void x86_percpu_init_early(uint cpu_num, uint apic_id) {
x86_percpu_t *percpu = percpu_for_cpu(cpu_num);
percpu->self = percpu;
percpu->cpu_num = cpu_num;
percpu->apic_id = apic_id;
// XXX load into gs:/fs/etc
#if ARCH_X86_64
write_msr(X86_MSR_IA32_KERNEL_GS_BASE, 0);
write_msr(X86_MSR_IA32_GS_BASE, (uint64_t)percpu);
#else
//#error implement
#endif
}

View File

@@ -58,6 +58,7 @@ MODULE_SRCS += \
$(LOCAL_DIR)/faults.c \
$(LOCAL_DIR)/feature.c \
$(LOCAL_DIR)/gdt.S \
$(LOCAL_DIR)/mp.c \
$(LOCAL_DIR)/thread.c \
# legacy x86's dont have fpu support