Skip to content

Commit c60bb2e

Browse files
committed
address all concerns except trying new enum/trait-based design
1 parent 7999e49 commit c60bb2e

File tree

3 files changed

+44
-43
lines changed

3 files changed

+44
-43
lines changed

kernel/ap_start/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ pub fn kstart_ap(
102102
nmi_lint,
103103
nmi_flags,
104104
).unwrap();
105-
105+
106106
// Now that the Local APIC has been initialized for this CPU, we can initialize the
107107
// per-CPU storage, tasking, and create the idle task for this CPU.
108108
per_cpu::init(cpu_id).unwrap();

kernel/cpu_local/src/lib.rs

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ pub struct FixedCpuLocal {
3535
}
3636
// NOTE: These fields must be kept in sync with `cpu_local::FixedCpuLocal`.
3737
impl FixedCpuLocal {
38-
const SELF_PTR_OFFSET: usize = 0;
3938
pub const CPU_ID: Self = Self { offset: 8, size: 4, align: 4 };
4039
pub const PREEMPTION_COUNT: Self = Self { offset: 12, size: 1, align: 1 };
4140
pub const TASK_SWITCH_PREEMPTION_GUARD: Self = Self { offset: 16, size: 8, align: 4 };
@@ -120,7 +119,7 @@ impl<const OFFSET: usize, T> CpuLocal<OFFSET, T> {
120119
unsafe {
121120
#[cfg(target_arch = "x86_64")]
122121
asm!(
123-
concat!("mov {}, gs:[0]"), // the SELF_PTR_OFFSET is 0
122+
"mov {}, gs:[0]", // the SELF_PTR_OFFSET is 0
124123
lateout(reg) self_ptr,
125124
options(nostack, preserves_flags, readonly, pure)
126125
);
@@ -135,7 +134,7 @@ impl<const OFFSET: usize, T> CpuLocal<OFFSET, T> {
135134
impl<const OFFSET: usize, T: Copy> CpuLocal<OFFSET, T> {
136135
/// Returns a copy of this `CpuLocal`'s inner value of type `T`.
137136
///
138-
/// This is a convenience functiononly available for types where `T: Copy`.
137+
/// This is a convenience function only available for types where `T: Copy`.
139138
pub fn get(&self) -> T {
140139
self.with(|v| *v)
141140
}
@@ -144,67 +143,66 @@ impl<const OFFSET: usize, T: Copy> CpuLocal<OFFSET, T> {
144143

145144
/// The underlying memory region for each CPU's per-CPU data.
146145
#[derive(Debug)]
147-
struct CpuLocalDataImage(MappedPages);
148-
impl CpuLocalDataImage {
149-
/// This function does 3 main things:
150-
/// 1. Allocates a new CPU-local data image for this CPU.
151-
/// 2. Sets the self pointer value such that it can be properly accessed.
152-
/// 3. Sets this CPU's base register (e.g., GsBase on x86_64) to the address
153-
/// of this new data image, making it "currently active" and accessible.
154-
fn new() -> Result<CpuLocalDataImage, &'static str> {
155-
// 1. Allocate a single page to store each CPU's local data.
156-
let mut mp = memory::create_mapping(1, PteFlags::new().writable(true).valid(true))?;
157-
158-
// 2. Set up the self pointer for the new data image.
159-
let self_ptr_value = mp.start_address().value();
160-
let self_ptr_dest = mp.as_type_mut::<usize>(0)?;
161-
*self_ptr_dest = self_ptr_value;
146+
struct CpuLocalDataRegion(MappedPages);
147+
impl CpuLocalDataRegion {
148+
/// Allocates a new CPU-local data image.
149+
fn new(size_of_per_cpu_data: usize) -> Result<CpuLocalDataRegion, &'static str> {
150+
let mp = memory::create_mapping(
151+
size_of_per_cpu_data,
152+
PteFlags::new().writable(true).valid(true),
153+
)?;
154+
Ok(CpuLocalDataRegion(mp))
155+
}
162156

163-
// 3. Set the base register used for CPU-local data.
164-
{
165-
#[cfg(target_arch = "x86_64")] {
166-
let gsbase_val = VirtAddr::new_truncate(self_ptr_value as u64);
167-
log::warn!("Writing value {:#X} to GSBASE", gsbase_val);
168-
GsBase::write(gsbase_val);
169-
}
157+
/// Sets this CPU's base register (e.g., GsBase on x86_64) to the address
158+
/// of this CPU-local data image, making it "currently active" and accessible.
159+
fn set_as_current_cpu_local_base(&self) {
160+
let self_ptr_value = self.0.start_address().value();
170161

171-
#[cfg(not(target_arch = "x86_64"))]
172-
todo!("CPU-local variable access is not yet implemented on this architecture")
162+
#[cfg(target_arch = "x86_64")] {
163+
let gsbase_val = VirtAddr::new_truncate(self_ptr_value as u64);
164+
log::warn!("Writing value {:#X} to GSBASE", gsbase_val);
165+
GsBase::write(gsbase_val);
173166
}
174167

175-
Ok(CpuLocalDataImage(mp))
168+
#[cfg(not(target_arch = "x86_64"))]
169+
todo!("CPU-local variable access is not yet implemented on this architecture")
176170
}
177171
}
178172

179173

180-
/// Initializes the CPU-local data image for this CPU.
174+
/// Initializes the CPU-local data region for this CPU.
181175
///
182176
/// Note: this is invoked by the `per_cpu` crate;
183177
/// other crates do not need to invoke this.
184178
pub fn init<P>(
185179
cpu_id: u32,
180+
size_of_per_cpu_data: usize,
186181
per_cpu_data_initializer: impl FnOnce(usize) -> P
187182
) -> Result<(), &'static str> {
188183
/// The global set of all per-CPU data regions.
189-
static CPU_LOCAL_DATA_REGIONS: Mutex<BTreeMap<u32, CpuLocalDataImage>> = Mutex::new(BTreeMap::new());
184+
static CPU_LOCAL_DATA_REGIONS: Mutex<BTreeMap<u32, CpuLocalDataRegion>> = Mutex::new(BTreeMap::new());
190185

191186
let mut regions = CPU_LOCAL_DATA_REGIONS.lock();
192187
let entry = regions.entry(cpu_id);
193-
let data_image = match entry {
194-
Entry::Vacant(v) => v.insert(CpuLocalDataImage::new()?),
188+
let data_region = match entry {
189+
Entry::Vacant(v) => v.insert(CpuLocalDataRegion::new(size_of_per_cpu_data)?),
195190
Entry::Occupied(_) => return Err("BUG: cannot init CPU-local data more than once"),
196191
};
197-
let self_ptr = data_image.0.start_address().value();
198192

199-
// Run the given initializer function on the per-CPU data.
200-
let new_data_image = CpuLocal::<{FixedCpuLocal::SELF_PTR_OFFSET}, P>(PhantomData);
201-
new_data_image.with_mut(|per_cpu_data_mut| {
193+
// Run the given initializer function to initialize the per-CPU data region.
194+
{
195+
let self_ptr = data_region.0.start_address().value();
202196
let initial_value = per_cpu_data_initializer(self_ptr);
203-
let existing_junk = core::mem::replace(per_cpu_data_mut, initial_value);
204-
// The memory contents we just replaced was random junk, so it'd be invalid
205-
// to run any destructors on it. Thus, we must forget it here.
206-
core::mem::forget(existing_junk);
207-
});
197+
// SAFETY:
198+
// ✅ We just allocated memory for the self ptr above, it is only accessible by us.
199+
// ✅ That memory is mutable (writable) and is aligned to a page boundary.
200+
// ✅ The memory is at least as large as `size_of::<P>()`.
201+
unsafe { core::ptr::write(self_ptr as *mut P, initial_value) };
202+
}
203+
204+
// Set the new CPU-local data region as active and ready to be used on this CPU.
205+
data_region.set_as_current_cpu_local_base();
208206

209207
// TODO Remove, temp tests
210208
if true {

kernel/per_cpu/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ pub struct PerCpuData {
6161
/// This has a different initial value for each CPU's data image, of course.
6262
///
6363
/// We use this to allow writes to this entire structure (for initialization),
64-
/// and also to allow faster access to large fields herein () accelerate accesses to large items
64+
/// and also to allow faster access to large fields herein, as they don't need to be
65+
/// loaded in full before accessing a single sub-field. See this for more:
66+
/// <https://github.com/rust-osdev/x86_64/pull/257#issuecomment-849514649>.
6567
self_ptr: usize,
6668
// NOTE: These fields must be kept in sync with `cpu_local::FixedCpuLocal`.
6769
/// The unique ID of this CPU.
@@ -103,6 +105,7 @@ impl PerCpuData {
103105
pub fn init(cpu_id: CpuId) -> Result<(), &'static str> {
104106
cpu_local::init(
105107
cpu_id.value(),
108+
core::mem::size_of::<PerCpuData>(),
106109
|self_ptr| PerCpuData::new(self_ptr, cpu_id),
107110
)
108111
}

0 commit comments

Comments
 (0)