From b4dc9364a2b8553a33aaf0ba1d1068d911fa7809 Mon Sep 17 00:00:00 2001 From: yufeng <321353225@qq.com> Date: Wed, 12 Jun 2024 03:49:25 +0800 Subject: [PATCH] feat: add percpu mode --- .gitignore | 4 +- Cargo.lock | 22 ----- Cargo.toml | 1 - example/Cargo.lock | 22 ----- example/Makefile | 15 ++- example/linker/linker-riscv64.ld | 14 --- polyhal-macro/src/lib.rs | 159 ++++++++++++++++++++++++++++++- polyhal-macro/src/percpu.rs | 69 ++++++++++++++ src/lib.rs | 4 +- src/percpu.rs | 126 ++++++++++++++++++++++++ src/riscv64/interrupt.rs | 7 +- src/riscv64/mod.rs | 17 ++-- src/x86_64/gdt.rs | 4 +- src/x86_64/interrupt.rs | 10 +- src/x86_64/mod.rs | 7 +- 15 files changed, 395 insertions(+), 86 deletions(-) create mode 100644 polyhal-macro/src/percpu.rs create mode 100644 src/percpu.rs diff --git a/.gitignore b/.gitignore index 1de5659..cd649cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -target \ No newline at end of file +target +output.log +qemu.log diff --git a/Cargo.lock b/Cargo.lock index f0aae7d..c8808ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,27 +130,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" -[[package]] -name = "percpu" -version = "0.1.0" -source = "git+https://github.com/Byte-OS/percpu.git#a71e60e376ac28cdf1a46312e2705b864c07732e" -dependencies = [ - "cfg-if", - "percpu_macros", - "spin", - "x86", -] - -[[package]] -name = "percpu_macros" -version = "0.1.0" -source = "git+https://github.com/Byte-OS/percpu.git#a71e60e376ac28cdf1a46312e2705b864c07732e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "polyhal" version = "0.1.0" @@ -165,7 +144,6 @@ dependencies = [ "log", "loongArch64", "multiboot", - "percpu", "polyhal-macro", "raw-cpuid 11.0.1", "riscv", diff --git a/Cargo.toml b/Cargo.toml index 4d50f5c..0352d70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ log = "0.4" fdt = "0.1.5" bitflags = "2.0.2" cfg-if = "1.0.0" -percpu = { git = "https://github.com/Byte-OS/percpu.git" } polyhal-macro = { path = "polyhal-macro" } [target.'cfg(target_arch = "riscv64")'.dependencies] diff --git a/example/Cargo.lock b/example/Cargo.lock index c5c238a..e02841d 100644 --- a/example/Cargo.lock +++ b/example/Cargo.lock @@ -161,27 +161,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" -[[package]] -name = "percpu" -version = "0.1.0" -source = "git+https://github.com/Byte-OS/percpu.git#a71e60e376ac28cdf1a46312e2705b864c07732e" -dependencies = [ - "cfg-if", - "percpu_macros", - "spin", - "x86", -] - -[[package]] -name = "percpu_macros" -version = "0.1.0" -source = "git+https://github.com/Byte-OS/percpu.git#a71e60e376ac28cdf1a46312e2705b864c07732e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "polyhal" version = "0.1.0" @@ -196,7 +175,6 @@ dependencies = [ "log", "loongArch64", "multiboot", - "percpu", "polyhal-macro", "raw-cpuid 11.0.1", "riscv", diff --git a/example/Makefile b/example/Makefile index ac09207..f775e98 100644 --- a/example/Makefile +++ b/example/Makefile @@ -1,7 +1,10 @@ # Building ARCH := riscv64 -QEMU_EXEC := timeout 40 +TEST := false +ifeq ($(TEST), true) +QEMU_EXEC := timeout 40 +endif ifeq ($(ARCH), x86_64) TARGET := x86_64-unknown-none @@ -62,11 +65,17 @@ clean: run: run-inner -QEMU_EXEC += -nographic -smp 1 >output.log 2>&1 +QEMU_EXEC += -nographic -smp 1 +QEMU_EXEC += -D qemu.log -d in_asm,int,pcall,cpu_reset,guest_errors +ifeq ($(TEST), true) +QEMU_EXEC += >output.log 2>&1 || echo "QEMU exited" +endif run-inner: build - $(QEMU_EXEC) || echo "QEMU exited" + $(QEMU_EXEC) +ifeq ($(TEST), true) grep "\[kernel\] Hello, world!" output.log +endif test: make ARCH=aarch64 run diff --git a/example/linker/linker-riscv64.ld b/example/linker/linker-riscv64.ld index eb4a595..91db022 100644 --- a/example/linker/linker-riscv64.ld +++ b/example/linker/linker-riscv64.ld @@ -48,20 +48,6 @@ SECTIONS _ebss = .; } - . = ALIGN(4K); - _percpu_start = .; - .percpu 0x0 : AT(_percpu_start) { - _percpu_load_start = .; - *(.percpu .percpu.*) - _percpu_load_end = .; - . = ALIGN(64); - _percpu_size_aligned = .; - - . = _percpu_load_start + _percpu_size_aligned * 1; - } - . = _percpu_start + SIZEOF(.percpu); - _percpu_end = .; - PROVIDE(end = .); /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) diff --git a/polyhal-macro/src/lib.rs b/polyhal-macro/src/lib.rs index 9ea6e9d..87fda76 100644 --- a/polyhal-macro/src/lib.rs +++ b/polyhal-macro/src/lib.rs @@ -1,6 +1,9 @@ +mod percpu; + use proc_macro::TokenStream; -use quote::quote; -use syn::{parse_macro_input, ItemFn}; +use proc_macro2::Span; +use quote::{format_ident, quote}; +use syn::{parse_macro_input, Error, ItemFn, ItemStatic}; #[proc_macro_attribute] pub fn arch_entry(_input: TokenStream, annotated_item: TokenStream) -> TokenStream { @@ -19,3 +22,155 @@ pub fn arch_interrupt(_input: TokenStream, annotated_item: TokenStream) -> Token #annotated_item }) } + +/// Defines a per-CPU data structure. +/// +/// It should be used on a `static` variable. +/// +/// See the [crate-level documentation](../percpu/index.html) for more details. +#[proc_macro_attribute] +pub fn def_percpu(attr: TokenStream, item: TokenStream) -> TokenStream { + if !attr.is_empty() { + return compiler_error(Error::new( + Span::call_site(), + "expect an empty attribute: `#[def_percpu]`", + )); + } + + let ast = syn::parse_macro_input!(item as ItemStatic); + + let attrs = &ast.attrs; + let vis = &ast.vis; + let name = &ast.ident; + let ty = &ast.ty; + let init_expr = &ast.expr; + + let inner_symbol_name = &format_ident!("__PERCPU_{}", name); + let struct_name = &format_ident!("{}_WRAPPER", name); + + let ty_str = quote!(#ty).to_string(); + let is_primitive_int = ["bool", "u8", "u16", "u32", "u64", "usize"].contains(&ty_str.as_str()); + + // Do not generate `fn read_current()`, `fn write_current()`, etc for non primitive types. + let read_write_methods = if is_primitive_int { + quote! { + /// Returns the value of the per-CPU data on the current CPU. + /// + /// # Safety + /// + /// Caller must ensure that preemption is disabled on the current CPU. + #[inline] + pub unsafe fn read_current_raw(&self) -> #ty { + unsafe { *self.current_ptr() } + } + + /// Set the value of the per-CPU data on the current CPU. + /// + /// # Safety + /// + /// Caller must ensure that preemption is disabled on the current CPU. + #[inline] + pub unsafe fn write_current_raw(&self, val: #ty) { + unsafe { *self.current_ref_mut_raw() = val }; + } + + /// Returns the value of the per-CPU data on the current CPU. Preemption will + /// be disabled during the call. + pub fn read_current(&self) -> #ty { + unsafe { self.read_current_raw() } + } + + /// Set the value of the per-CPU data on the current CPU. Preemption will + /// be disabled during the call. + pub fn write_current(&self, val: #ty) { + unsafe { self.write_current_raw(val) } + } + } + } else { + quote! {} + }; + + let current_ptr = percpu::gen_current_ptr(inner_symbol_name, ty); + quote! { + #[cfg_attr(not(target_os = "macos"), link_section = "percpu")] // unimplemented on macos + #[used(linker)] + #(#attrs)* + static mut #inner_symbol_name: #ty = #init_expr; + + #[doc = concat!("Wrapper struct for the per-CPU data [`", stringify!(#name), "`]")] + #[allow(non_camel_case_types)] + #vis struct #struct_name {} + + #(#attrs)* + #vis static #name: #struct_name = #struct_name {}; + + impl #struct_name { + /// Returns the offset relative to the per-CPU data area base on the current CPU. + #[inline] + pub fn offset(&self) -> usize { + extern "Rust" { + fn __start_percpu(); + } + unsafe { + &#inner_symbol_name as *const _ as usize - __start_percpu as usize + } + } + + /// Returns the raw pointer of this per-CPU data on the current CPU. + /// + /// # Safety + /// + /// Caller must ensure that preemption is disabled on the current CPU. + #[inline] + pub unsafe fn current_ptr(&self) -> *const #ty { + #current_ptr + } + + /// Returns the reference of the per-CPU data on the current CPU. + /// + /// # Safety + /// + /// Caller must ensure that preemption is disabled on the current CPU. + #[inline] + pub unsafe fn current_ref_raw(&self) -> &#ty { + &*self.current_ptr() + } + + /// Returns the mutable reference of the per-CPU data on the current CPU. + /// + /// # Safety + /// + /// Caller must ensure that preemption is disabled on the current CPU. + #[inline] + #[allow(clippy::mut_from_ref)] + pub unsafe fn current_ref_mut_raw(&self) -> &mut #ty { + &mut *(self.current_ptr() as *mut #ty) + } + + /// Manipulate the per-CPU data on the current CPU in the given closure. + /// Preemption will be disabled during the call. + pub fn with_current(&self, f: F) -> T + where + F: FnOnce(&mut #ty) -> T, + { + f(unsafe { self.current_ref_mut_raw() }) + } + + #read_write_methods + } + } + .into() +} + +#[doc(hidden)] +#[proc_macro] +pub fn percpu_symbol_offset(item: TokenStream) -> TokenStream { + let symbol = &format_ident!("{}", item.to_string()); + let offset = percpu::gen_offset(symbol); + quote!({ #offset }).into() +} + + +fn compiler_error(err: Error) -> TokenStream { + err.to_compile_error().into() +} diff --git a/polyhal-macro/src/percpu.rs b/polyhal-macro/src/percpu.rs new file mode 100644 index 0000000..44f218c --- /dev/null +++ b/polyhal-macro/src/percpu.rs @@ -0,0 +1,69 @@ +use quote::quote; +use syn::{Ident, Type}; + +pub fn gen_offset(symbol: &Ident) -> proc_macro2::TokenStream { + quote! { + let value: usize; + unsafe { + cfg_match! { + cfg(target_arch = "x86_64") => { + ::core::arch::asm!( + "movabs {0}, offset {VAR}", + out(reg) value, + VAR = sym #symbol, + ); + } + cfg(target_arch = "aarch64") => { + ::core::arch::asm!( + "movz {0}, #:abs_g0_nc:{VAR}", + out(reg) value, + VAR = sym #symbol, + ); + } + cfg(any(target_arch = "riscv32", target_arch = "riscv64")) => { + ::core::arch::asm!( + "lui {0}, %hi({VAR})", + "addi {0}, {0}, %lo({VAR})", + out(reg) value, + VAR = sym #symbol, + ); + } + cfg(target_arch = "loongarch64") => { + ::core::arch::asm!( + "la.abs {0}, {VAR}", + out(reg) value, + VAR = sym #symbol, + ); + } + } + } + value + } +} + +pub fn gen_current_ptr(symbol: &Ident, ty: &Type) -> proc_macro2::TokenStream { + quote! { + let base: usize; + #[cfg(target_arch = "x86_64")] + { + // `__PERCPU_SELF_PTR` stores GS_BASE, which is defined in crate `percpu`. + ::core::arch::asm!( + "mov {0}, gs:[offset __PERCPU_SELF_PTR]", + "add {0}, offset {VAR}", + out(reg) base, + VAR = sym #symbol, + ); + base as *const #ty + } + #[cfg(not(target_arch = "x86_64"))] + { + #[cfg(target_arch = "aarch64")] + ::core::arch::asm!("mrs {}, TPIDR_EL1", out(reg) base); + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + ::core::arch::asm!("mv {}, gp", out(reg) base); + #[cfg(target_arch = "loongarch64")] + ::core::arch::asm!("move {}, $r21", out(reg) base); + (base + self.offset()) as *const #ty + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 4696f2f..e2bc165 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,8 @@ #![feature(asm_const)] #![feature(cfg_version)] #![feature(decl_macro)] +#![feature(cfg_match)] +#![feature(used_with_arg)] #![cfg_attr(not(version("1.79")), feature(stdsimd))] #![feature(const_mut_refs)] #![feature(const_slice_from_raw_parts_mut)] @@ -152,6 +154,7 @@ pub mod mem; pub mod multicore; pub mod once; pub mod pagetable; +pub mod percpu; pub mod time; use core::mem::size_of; @@ -163,7 +166,6 @@ use fdt::Fdt; use irq::IRQVector; use once::LazyInit; use pagetable::PageTable; -pub use percpu; #[cfg_attr(target_arch = "riscv64", path = "riscv64/mod.rs")] #[cfg_attr(target_arch = "aarch64", path = "aarch64/mod.rs")] diff --git a/src/percpu.rs b/src/percpu.rs new file mode 100644 index 0000000..e8806a3 --- /dev/null +++ b/src/percpu.rs @@ -0,0 +1,126 @@ +use core::{alloc::Layout, cell::UnsafeCell, mem::size_of, ptr::copy_nonoverlapping}; + +use alloc::alloc::alloc; + +use crate::{debug::println, PAGE_SIZE}; + +static BOOT_PERCPU_DATA_AREA: [u8; PAGE_SIZE] = [0; PAGE_SIZE]; + +/// Returns the base address of the per-CPU data area on the given CPU. +/// +/// if `cpu_id` is 0, it returns the base address of all per-CPU data areas. +pub fn percpu_area_init(cpu_id: usize) -> usize { + // Get initial per-CPU data area + extern "Rust" { + fn __start_percpu(); + fn __stop_percpu(); + } + let start = __start_percpu as usize; + let size = __stop_percpu as usize - start; + // use polyhal_macro::percpu_symbol_offset; + // let start = percpu_symbol_offset!(__start_percpu); + // let size = percpu_symbol_offset!(__stop_percpu) - percpu_symbol_offset!(__start_percpu); + + println!("start: {:#x} size: {:#x}", start, size); + + // Get the base address of the per-CPU data area + // If cpu_id is boot,core then use BOOT_PERCPU_DATA_AREA. + // else alloc area. + let dst = if cpu_id == 0 { + BOOT_PERCPU_DATA_AREA.as_ptr() as usize as *mut u8 + } else { + let layout = + Layout::from_size_align(size, size_of::()).expect("percpu area align failed"); + unsafe { alloc(layout) } + }; + + // Init the area with original data. + unsafe { + copy_nonoverlapping(start as *const u8, dst, size); + } + + dst as usize +} + +/// Read the architecture-specific thread pointer register on the current CPU. +pub fn get_local_thread_pointer() -> usize { + let tp; + unsafe { + cfg_if::cfg_if! { + if #[cfg(target_arch = "x86_64")] { + tp = x86::msr::rdmsr(x86::msr::IA32_GS_BASE) as usize + } else if #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] { + core::arch::asm!("mv {}, gp", out(reg) tp) + } else if #[cfg(target_arch = "aarch64")] { + core::arch::asm!("mrs {}, TPIDR_EL1", out(reg) tp) + } else if #[cfg(target_arch = "loongarch64")] { + core::arch::asm!("move {}, $r21", out(reg) tp) + } + } + } + tp +} + +/// Set the architecture-specific thread pointer register to the per-CPU data +/// area base on the current CPU. +/// +/// `cpu_id` indicates which per-CPU data area to use. +pub fn set_local_thread_pointer(cpu_id: usize) { + let tp = percpu_area_init(cpu_id); + unsafe { + cfg_if::cfg_if! { + if #[cfg(target_arch = "x86_64")] { + x86::msr::wrmsr(x86::msr::IA32_GS_BASE, tp as u64); + SELF_PTR.write_current_raw(tp); + } else if #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] { + core::arch::asm!("mv gp, {}", in(reg) tp) + } else if #[cfg(target_arch = "aarch64")] { + core::arch::asm!("msr TPIDR_EL1, {}", in(reg) tp) + } else if #[cfg(target_arch = "loongarch64")] { + core::arch::asm!("move $r21, {}", in(reg) tp) + } + } + } +} + +/// On x86, we use `gs:SELF_PTR` to store the address of the per-CPU data area base. +#[cfg(target_arch = "x86_64")] +#[no_mangle] +#[polyhal_macro::def_percpu] +static SELF_PTR: usize = 0; + +// 思路: 这里存放的 usize 数据,如果是 u8, u16, u32, u64, 或者 size 小于等于 u64 的直接存取 +// 否则这里仅表示指针。 +pub struct PerCpu { + value: UnsafeCell, +} + +unsafe impl Sync for PerCpu {} + +impl PerCpu { + pub const fn new(value: T) -> Self { + PerCpu { + value: UnsafeCell::new(value), + } + } + + pub fn get(&self) -> &T { + unsafe { &*self.value.get() } + } + + pub fn write(&self, value: T) { + unsafe { + core::ptr::write_volatile(self.value.get(), value); + } + } + + // pub fn offset(&self) -> usize { + // extern "Rust" { + // #[link_name = "__start_per_cpu"] + // fn SECION_START(); + // } + // self.value.get() as usize - SECION_START as usize + // } +} + +pub fn init_per_cpu() {} diff --git a/src/riscv64/interrupt.rs b/src/riscv64/interrupt.rs index 2237063..0c17b85 100644 --- a/src/riscv64/interrupt.rs +++ b/src/riscv64/interrupt.rs @@ -1,7 +1,8 @@ use core::arch::{asm, global_asm}; use riscv::register::{ - scause::{self, Exception, Interrupt, Trap}, stval, stvec, + scause::{self, Exception, Interrupt, Trap}, + stval, stvec, }; use crate::{instruction::Instruction, TrapFrame, TrapType, VIRT_ADDR_START}; @@ -73,11 +74,11 @@ global_asm!( ); #[no_mangle] -#[percpu::def_percpu] +#[polyhal_macro::def_percpu] static KERNEL_RSP: usize = 0; #[no_mangle] -#[percpu::def_percpu] +#[polyhal_macro::def_percpu] static USER_RSP: usize = 0; // 设置中断 diff --git a/src/riscv64/mod.rs b/src/riscv64/mod.rs index 3704ed9..e7674fd 100644 --- a/src/riscv64/mod.rs +++ b/src/riscv64/mod.rs @@ -18,9 +18,7 @@ pub use consts::*; pub use context::TrapFrame; pub use entry::{kernel_page_table, switch_to_kernel_page_table}; use fdt::Fdt; -pub use interrupt::{ - run_user_task, run_user_task_forever, -}; +pub use interrupt::{run_user_task, run_user_task_forever}; use sbi::*; pub use sbi::shutdown; @@ -38,7 +36,7 @@ use crate::{ CPU_NUM, DTB_BIN, MEM_AREA, }; -#[percpu::def_percpu] +#[polyhal_macro::def_percpu] static CPU_ID: usize = 0; static DTB_PTR: LazyInit = LazyInit::new(); @@ -46,14 +44,17 @@ static DTB_PTR: LazyInit = LazyInit::new(); pub(crate) fn rust_main(hartid: usize, device_tree: usize) { crate::clear_bss(); // Init allocator - percpu::init(4); - percpu::set_local_thread_pointer(hartid); + crate::percpu::set_local_thread_pointer(hartid); + println!("CPU_ID offset: {:#x}", CPU_ID.offset()); + println!("init success, CPU_ID: {}", CPU_ID.read_current()); CPU_ID.write_current(hartid); - + // println!("NEWCPU_ID offset: {}", NEW_CPU_ID.offset()); interrupt::init_interrupt(); let (_hartid, device_tree) = boards::init_device(hartid, device_tree | VIRT_ADDR_START); + println!("CPU_ID offset: {:#x}", CPU_ID.offset()); + // 开启 SUM unsafe { // 开启浮点运算 @@ -93,7 +94,7 @@ pub(crate) fn rust_main(hartid: usize, device_tree: usize) { } pub(crate) extern "C" fn rust_secondary_main(hartid: usize) { - percpu::set_local_thread_pointer(hartid); + crate::percpu::set_local_thread_pointer(hartid); CPU_ID.write_current(hartid); interrupt::init_interrupt(); diff --git a/src/x86_64/gdt.rs b/src/x86_64/gdt.rs index 306c48c..dd303ea 100644 --- a/src/x86_64/gdt.rs +++ b/src/x86_64/gdt.rs @@ -7,10 +7,10 @@ use x86_64::structures::gdt::{Descriptor, DescriptorFlags}; use x86_64::structures::{tss::TaskStateSegment, DescriptorTablePointer}; use x86_64::{addr::VirtAddr, PrivilegeLevel}; -#[percpu::def_percpu] +#[polyhal_macro::def_percpu] pub(super) static GDT: Once = Once::new(); -#[percpu::def_percpu] +#[polyhal_macro::def_percpu] pub(super) static TSS: Once = Once::new(); /// A wrapper of the Global Descriptor Table (GDT) with maximum 16 entries. diff --git a/src/x86_64/interrupt.rs b/src/x86_64/interrupt.rs index 6d6c1db..bec0602 100644 --- a/src/x86_64/interrupt.rs +++ b/src/x86_64/interrupt.rs @@ -45,15 +45,15 @@ global_asm!( global_asm!(include_str!("trap.S")); #[no_mangle] -#[percpu::def_percpu] +#[polyhal_macro::def_percpu] static USER_RSP: usize = 0; #[no_mangle] -#[percpu::def_percpu] +#[polyhal_macro::def_percpu] static KERNEL_RSP: usize = 0; #[no_mangle] -#[percpu::def_percpu] +#[polyhal_macro::def_percpu] static USER_CONTEXT: usize = 0; bitflags! { @@ -104,7 +104,9 @@ fn kernel_callback(context: &mut TrapFrame) { TrapType::Time } // PIC IRQS - 0x20..=0x2f => TrapType::Irq(crate::irq::IRQVector(context.vector as usize - PIC_VECTOR_OFFSET as usize)), + 0x20..=0x2f => TrapType::Irq(crate::irq::IRQVector( + context.vector as usize - PIC_VECTOR_OFFSET as usize, + )), _ => { panic!( "Unhandled exception {} (error_code = {:#x}) @ {:#x}:\n{:#x?}", diff --git a/src/x86_64/mod.rs b/src/x86_64/mod.rs index c7873a0..25a5501 100644 --- a/src/x86_64/mod.rs +++ b/src/x86_64/mod.rs @@ -40,10 +40,11 @@ use crate::{ debug::{display_info, println}, multicore::MultiCore, once::LazyInit, + percpu::PerCpu, CPU_NUM, DTB_BIN, MEM_AREA, }; -#[percpu::def_percpu] +#[polyhal_macro::def_percpu] static CPU_ID: usize = 1; pub fn shutdown() -> ! { @@ -60,8 +61,8 @@ fn rust_tmp_main(magic: usize, mboot_ptr: usize) { apic::init(); sigtrx::init(); // Init allocator - percpu::init(1); - percpu::set_local_thread_pointer(0); + crate::percpu::init(1); + crate::percpu::set_local_thread_pointer(0); gdt::init(); interrupt::init_syscall(); time::init_early();