Skip to content

Commit 5a559a1

Browse files
kernel: add cpu_local proc macro
Its like the `thread_local` macro; makes the code much cleaner :^) Signed-off-by: Anhad Singh <[email protected]>
1 parent bc4c8b3 commit 5a559a1

File tree

15 files changed

+205
-76
lines changed

15 files changed

+205
-76
lines changed

src/.cargo/kernel.ld

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ SECTIONS
3030
*(.rodata .rodata.*)
3131
} :rodata
3232

33+
.cpu_local : {
34+
__cpu_local_start = .;
35+
KEEP(*(.cpu_local_self_ptr));
36+
KEEP(*(.cpu_local_tss));
37+
KEEP(*(.cpu_local));
38+
__cpu_local_end = .;
39+
}
40+
3341
/* Move to the next memory page for .data */
3442
. += CONSTANT(MAXPAGESIZE);
3543

src/aero_kernel/src/arch/x86_64/apic.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
use core::ptr;
1919
use core::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
2020

21+
use crate::arch::interrupts;
2122
use crate::arch::interrupts::InterruptStack;
22-
use crate::arch::{interrupts, tls};
2323
use crate::mem::paging::{PhysAddr, VirtAddr};
2424
use raw_cpuid::{CpuId, FeatureInfo};
2525
use spin::Once;
@@ -108,6 +108,9 @@ fn lapic_error_handler(_stack: &mut InterruptStack) {
108108
log::error!("ESR={:#0x}", self::get_local_apic().get_esr());
109109
}
110110

111+
#[cpu_local]
112+
static mut LAPIC_TIMER_FREQUENCY: u32 = 0;
113+
111114
pub struct LocalApic {
112115
address: VirtAddr,
113116
apic_type: ApicType,
@@ -194,7 +197,7 @@ impl LocalApic {
194197
pub fn timer_oneshot(&mut self, vec: u8, us: usize) {
195198
self.timer_stop();
196199

197-
let lapic_timer_frequency = tls::get_percpu().lapic_timer_frequency;
200+
let lapic_timer_frequency = unsafe { *LAPIC_TIMER_FREQUENCY };
198201
let ticks = us * (lapic_timer_frequency / 1000000) as usize;
199202

200203
unsafe {
@@ -225,7 +228,7 @@ impl LocalApic {
225228
let pit_ticks = initial_pit_tick - final_pit_tick;
226229
let timer_frequency = (SAMPLES / pit_ticks as u32) * time::PIT_DIVIDEND as u32;
227230

228-
tls::get_percpu().lapic_timer_frequency = timer_frequency;
231+
*LAPIC_TIMER_FREQUENCY = timer_frequency;
229232
}
230233

231234
self.timer_stop();
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (C) 2021-2023 The Aero Project Developers.
2+
//
3+
// This file is part of The Aero Project.
4+
//
5+
// Aero is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// Aero is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with Aero. If not, see <https://www.gnu.org/licenses/>.
17+
18+
use core::alloc::Layout;
19+
use core::ops::{Deref, DerefMut};
20+
21+
use crate::mem::paging::VirtAddr;
22+
use crate::utils::LinkerSymbol;
23+
24+
use super::io;
25+
26+
extern "C" {
27+
static __cpu_local_start: LinkerSymbol<u8>;
28+
static __cpu_local_end: LinkerSymbol<u8>;
29+
}
30+
31+
#[repr(C)]
32+
pub struct CpuLocal<T>(T);
33+
34+
impl<T> CpuLocal<T> {
35+
pub const fn new(val: T) -> Self {
36+
Self(val)
37+
}
38+
39+
pub fn addr(&self) -> VirtAddr {
40+
let val: u64;
41+
42+
unsafe {
43+
// gs:[0] -> SELF_PTR
44+
asm!(
45+
"mov {}, qword ptr gs:[0]",
46+
lateout(reg) val,
47+
options(nostack, preserves_flags, pure, readonly),
48+
);
49+
}
50+
51+
let self_addr = VirtAddr::new(self as *const _ as u64);
52+
let section_addr = VirtAddr::new(unsafe { &__cpu_local_start as *const _ as u64 });
53+
54+
let offset = self_addr - section_addr;
55+
VirtAddr::new(val) + offset
56+
}
57+
}
58+
59+
impl<T> Deref for CpuLocal<T> {
60+
type Target = T;
61+
62+
fn deref(&self) -> &Self::Target {
63+
unsafe { &*self.addr().as_ptr() }
64+
}
65+
}
66+
67+
impl<T> DerefMut for CpuLocal<T> {
68+
fn deref_mut(&mut self) -> &mut Self::Target {
69+
unsafe { &mut *self.addr().as_mut_ptr() }
70+
}
71+
}
72+
73+
/// The GS register holds a pointer to CPU-local data at a fixed offset of 0. While this approach
74+
/// requires an additional memory lookup for accessing the data, it enables better code optimization
75+
/// since the subsequent access is just a normal memory access. Considering the size of the
76+
/// CPU-local data, this optimization is beneficial.
77+
#[cpu_local(subsection = "self_ptr")]
78+
static SELF_PTR: u64 = 0;
79+
80+
#[cpu_local]
81+
static mut CPUID: usize = 0;
82+
83+
pub fn init(cpu_id: usize) {
84+
unsafe {
85+
let start = VirtAddr::new(&__cpu_local_start as *const _ as u64);
86+
let end = VirtAddr::new(&__cpu_local_end as *const _ as u64);
87+
let size = end - start;
88+
89+
let layout = Layout::from_size_align_unchecked(size as _, 64);
90+
let data = alloc::alloc::alloc_zeroed(layout);
91+
92+
core::ptr::copy_nonoverlapping::<u8>(start.as_ptr(), data, size as usize);
93+
*data.cast::<u64>() = data as u64;
94+
95+
io::wrmsr(io::IA32_GS_BASE, data as u64);
96+
*CPUID = cpu_id;
97+
}
98+
}

src/aero_kernel/src/arch/x86_64/gdt.rs

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ use core::mem;
2929

3030
use alloc::alloc::alloc_zeroed;
3131

32-
use crate::arch::tls::PerCpuData;
33-
34-
use super::{io, tls};
35-
3632
bitflags::bitflags! {
3733
/// Specifies which element to load into a segment from
3834
/// descriptor tables (i.e., is a index to LDT or GDT table
@@ -283,12 +279,16 @@ pub struct Tss {
283279
pub iomap_base: u16, // offset 0x66
284280
}
285281

286-
// Processor Control Region
287-
#[repr(C)]
288-
pub struct Kpcr {
289-
pub tss: Tss,
290-
pub cpu_local: PerCpuData,
291-
}
282+
#[cpu_local(subsection = "tss")]
283+
pub static mut TSS: Tss = Tss {
284+
reserved: 0,
285+
rsp: [0; 3],
286+
reserved2: 0,
287+
ist: [0; 7],
288+
reserved3: 0,
289+
reserved4: 0,
290+
iomap_base: 0,
291+
};
292292

293293
/// Initialize the bootstrap GDT which is required to initialize TLS (Thread Local Storage)
294294
/// support so, after the kernel heap we will map the TLS section and initialize the *actual* GDT
@@ -315,14 +315,6 @@ pub fn init_boot() {
315315
}
316316
}
317317

318-
pub fn get_task_state_segment() -> &'static mut Tss {
319-
&mut get_kpcr().tss
320-
}
321-
322-
pub fn get_kpcr() -> &'static mut Kpcr {
323-
unsafe { &mut *(io::rdmsr(io::IA32_GS_BASE) as *mut Kpcr) }
324-
}
325-
326318
static STK: [u8; 4096 * 16] = [0; 4096 * 16];
327319

328320
/// Initialize the *actual* GDT stored in TLS.
@@ -345,14 +337,13 @@ pub fn init() {
345337
gdt.copy_from_slice(&GDT);
346338

347339
unsafe {
348-
let tss_ref = get_task_state_segment();
349-
let tss_ptr = tss_ref as *mut Tss;
340+
let tss_ptr = TSS.addr().as_mut_ptr::<Tss>();
350341

351342
gdt[GdtEntryType::TSS as usize].set_offset(tss_ptr as u32);
352343
gdt[GdtEntryType::TSS as usize].set_limit(mem::size_of::<Tss>() as u32);
353344
gdt[GdtEntryType::TSS_HI as usize].set_raw((tss_ptr as u64) >> 32);
354345

355-
tss_ref.rsp[0] = STK.as_ptr().offset(4096 * 16) as u64;
346+
TSS.rsp[0] = STK.as_ptr().offset(4096 * 16) as u64;
356347

357348
let gdt_descriptor = GdtDescriptor::new(
358349
(mem::size_of::<[GdtEntry; GDT_ENTRY_COUNT]>() - 1) as u16,
@@ -371,9 +362,9 @@ pub fn init() {
371362
load_tss(SegmentSelector::new(GdtEntryType::TSS, Ring::Ring0));
372363
}
373364

374-
// Now we update the per-cpu storage to store a reference
375-
// to the per-cpu GDT.
376-
tls::get_percpu().gdt = gdt;
365+
// // Now we update the per-cpu storage to store a reference
366+
// // to the per-cpu GDT.
367+
// tls::get_percpu().gdt = gdt;
377368
}
378369

379370
#[inline(always)]

src/aero_kernel/src/arch/x86_64/interrupts/exceptions.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818
use super::{io, InterruptErrorStack};
1919

2020
use crate::arch::controlregs;
21-
use crate::arch::tls::get_percpu;
22-
use crate::mem::paging::PageFaultErrorCode;
21+
use crate::mem::paging::{PageFaultErrorCode, VirtAddr};
2322

2423
use crate::unwind;
2524
use crate::userland::scheduler;
2625

26+
#[cpu_local]
27+
pub static mut PF_RESUME: VirtAddr = VirtAddr::new(0);
28+
2729
const LOG_PF_PTABLE: bool = true;
2830

2931
macro interrupt_exception(fn $name:ident() => $message:expr) {
@@ -121,9 +123,9 @@ pub fn breakpoint(stack: &mut InterruptErrorStack) {
121123
}
122124

123125
pub(super) fn page_fault(stack: &mut InterruptErrorStack) {
124-
let pf_resume = get_percpu().pf_resume.as_ptr::<u8>();
125-
if !pf_resume.is_null() {
126-
stack.stack.iret.rip = pf_resume as u64;
126+
let pf_resume = unsafe { *PF_RESUME };
127+
if !pf_resume.is_zero() {
128+
stack.stack.iret.rip = pf_resume.as_u64();
127129
return;
128130
}
129131

src/aero_kernel/src/arch/x86_64/interrupts/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// You should have received a copy of the GNU General Public License
1616
// along with Aero. If not, see <https://www.gnu.org/licenses/>.
1717

18-
mod exceptions;
18+
pub mod exceptions;
1919
mod idt;
2020

2121
use core::sync::atomic::{AtomicUsize, Ordering};

src/aero_kernel/src/arch/x86_64/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
// You should have received a copy of the GNU General Public License
1616
// along with Aero. If not, see <https://www.gnu.org/licenses/>.
1717

18+
pub mod cpu_local;
19+
1820
pub mod apic;
1921
pub mod controlregs;
2022
pub mod gdt;
@@ -181,7 +183,8 @@ extern "C" fn arch_aero_main() -> ! {
181183
acpi::init(rsdp);
182184
log::info!("loaded ACPI");
183185

184-
tls::init(0);
186+
tls::init();
187+
cpu_local::init(0);
185188
log::info!("loaded TLS");
186189

187190
crate::unwind::set_panic_hook_ready(true);
@@ -209,7 +212,8 @@ extern "C" fn x86_64_aero_ap_main(boot_info: *const SmpInfo) -> ! {
209212
gdt::init_boot();
210213
log::info!("AP{}: loaded boot GDT", ap_id);
211214

212-
tls::init(ap_id);
215+
tls::init();
216+
cpu_local::init(ap_id);
213217
log::info!("AP{}: loaded TLS", ap_id);
214218

215219
gdt::init();

src/aero_kernel/src/arch/x86_64/syscall_handler.asm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ extern x86_64_do_syscall
2323
extern x86_64_check_sysenter
2424
global x86_64_syscall_handler
2525

26-
%define TSS_TEMP_USTACK_OFF 0x1c
27-
%define TSS_RSP0_OFF 0x04
26+
%define TSS_TEMP_USTACK_OFF 0x24
27+
%define TSS_RSP0_OFF 0xc
2828

2929
%define USERLAND_SS 0x23
3030
%define USERLAND_CS 0x2b

src/aero_kernel/src/arch/x86_64/task.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ pub fn arch_task_spinup(from: &mut ArchTask, to: &ArchTask) {
548548
unsafe {
549549
// Load the new thread's kernel stack pointer everywhere it's needed.
550550
let kstackp = to.context_switch_rsp.as_u64();
551-
super::gdt::get_task_state_segment().rsp[0] = kstackp;
551+
super::gdt::TSS.rsp[0] = kstackp;
552552
io::wrmsr(io::IA32_SYSENTER_ESP, kstackp);
553553

554554
// Preserve and restore the %fs, %gs bases.

src/aero_kernel/src/arch/x86_64/tls.rs

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,8 @@
2323
//! * <https://wiki.osdev.org/Thread_Local_Storage>
2424
//! * <https://doc.rust-lang.org/std/thread/struct.LocalKey.html>
2525
26-
use core::alloc::Layout;
27-
28-
use alloc::alloc::alloc_zeroed;
2926
use alloc::vec::Vec;
3027

31-
use super::gdt::*;
32-
use super::io;
33-
34-
use crate::mem::paging::VirtAddr;
3528
use crate::utils::sync::Mutex;
3629

3730
use raw_cpuid::FeatureInfo;
@@ -110,37 +103,11 @@ pub struct CpuInfo {
110103
pub features: Vec<&'static &'static str>,
111104
}
112105

113-
#[repr(C)]
114-
pub struct PerCpuData {
115-
pub cpuid: usize,
116-
pub lapic_timer_frequency: u32,
117-
118-
pub(super) gdt: &'static mut [GdtEntry],
119-
pub(super) pf_resume: VirtAddr,
120-
}
121-
122-
/// SAFETY: The GS base should point to the kernel PCR.
123106
pub fn get_cpuid() -> usize {
124-
get_percpu().cpuid
107+
0
125108
}
126109

127-
/// SAFETY: The GS base should point to the kernel PCR.
128-
pub fn get_percpu() -> &'static mut PerCpuData {
129-
&mut get_kpcr().cpu_local
130-
}
131-
132-
pub fn init(cpuid: usize) {
133-
// NOTE: Inside kernel space, the GS base will always point to the CPU local data and when
134-
// jumping to userland `swapgs` is called making the GS base point to the userland TLS data.
135-
unsafe {
136-
let kpcr_layout = Layout::new::<Kpcr>();
137-
138-
let kpcr_ptr = alloc_zeroed(kpcr_layout) as *mut Kpcr;
139-
io::wrmsr(io::IA32_GS_BASE, kpcr_ptr as u64);
140-
}
141-
142-
get_percpu().cpuid = cpuid;
143-
110+
pub fn init() {
144111
let cpuid = raw_cpuid::CpuId::new();
145112

146113
let features = cpuid

0 commit comments

Comments
 (0)