WIP x86 SMP
This commit is contained in:
@@ -3,6 +3,7 @@ Checks: >
|
||||
-*,
|
||||
bugprone-*,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-reserved-identifier,
|
||||
|
||||
clang-diagnostic-*,
|
||||
-clang-diagnostic-unused-command-line-argument,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <arch/x86.h>
|
||||
#include <inttypes.h>
|
||||
#include <lk/compiler.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
28
arch/x86/include/arch/x86/mp.h
Normal file
28
arch/x86/include/arch/x86/mp.h
Normal 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
42
arch/x86/mp.c
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user