Compare commits
14 commits
fc9fc22261
...
c9481b05c3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9481b05c3 | ||
|
|
c4c810744d | ||
|
|
fe50f8ce74 | ||
|
|
c7d5f18a4f | ||
|
|
3ba16577de | ||
|
|
b407abb094 | ||
|
|
3182f4e7f0 | ||
|
|
57ea368511 | ||
|
|
76506e919d | ||
|
|
30391aa2a1 | ||
|
|
4220f6628e | ||
|
|
8212e794f3 | ||
|
|
eccb202d04 | ||
|
|
bbe0551ac5 |
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
||||||
/.direnv/
|
/.direnv/
|
||||||
|
/kernel/qemu.log
|
||||||
|
|
|
||||||
35
Cargo.lock
generated
Normal file
35
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit_field"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kernel"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bit_field",
|
||||||
|
"bitflags",
|
||||||
|
"rbtree",
|
||||||
|
"seq-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rbtree"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "seq-macro"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
|
||||||
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
[workspace]
|
||||||
|
resolver = "3"
|
||||||
|
|
||||||
|
members = [
|
||||||
|
"crates/*",
|
||||||
|
"kernel",
|
||||||
|
]
|
||||||
7
crates/rbtree/Cargo.lock
generated
Normal file
7
crates/rbtree/Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rbtree"
|
||||||
|
version = "0.1.0"
|
||||||
10
crates/rbtree/Cargo.toml
Normal file
10
crates/rbtree/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "rbtree"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
std = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
9
crates/rbtree/src/lib.rs
Normal file
9
crates/rbtree/src/lib.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#![cfg_attr(not(test), no_std)]
|
||||||
|
#![feature(negative_impls)]
|
||||||
|
#![cfg_attr(test, feature(box_vec_non_null))]
|
||||||
|
|
||||||
|
mod raw_node;
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
pub use raw_node::{RBTree, TreeIter, TreeNodeIter};
|
||||||
1407
crates/rbtree/src/raw_node.rs
Normal file
1407
crates/rbtree/src/raw_node.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -33,6 +33,7 @@
|
||||||
rust-pkg
|
rust-pkg
|
||||||
clang
|
clang
|
||||||
gcc
|
gcc
|
||||||
|
gdb
|
||||||
mold
|
mold
|
||||||
|
|
||||||
limine-full
|
limine-full
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
[profile.dev]
|
|
||||||
codegen-backend = "llvm"
|
|
||||||
|
|
||||||
[unstable]
|
[unstable]
|
||||||
json-target-spec = true # lets us specify a custom target specification file
|
json-target-spec = true # lets us specify a custom target specification file
|
||||||
build-std-features = ["compiler-builtins-mem"]
|
build-std-features = ["compiler-builtins-mem"]
|
||||||
build-std = ["core", "compiler_builtins"]
|
build-std = ["core", "alloc", "compiler_builtins"]
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "x86_64-unknown-kernel.json"
|
target = "x86_64-unknown-kernel.json"
|
||||||
|
|
|
||||||
14
kernel/Cargo.lock
generated
14
kernel/Cargo.lock
generated
|
|
@ -2,6 +2,12 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit_field"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.13.1"
|
version = "2.13.1"
|
||||||
|
|
@ -12,5 +18,13 @@ checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
||||||
name = "kernel"
|
name = "kernel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bit_field",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
"seq-macro",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "seq-macro"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,20 @@ test = false
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
test = false
|
test = true
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "simple"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "stack_overflow"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bit_field = "0.10.3"
|
||||||
bitflags = "2.13.1"
|
bitflags = "2.13.1"
|
||||||
|
seq-macro = "0.3.6"
|
||||||
|
|
||||||
|
rbtree = { path = "../crates/rbtree" }
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo::rerun-if-changed=build.rs");
|
println!("cargo::rerun-if-changed=build.rs");
|
||||||
|
|
||||||
println!("cargo::rustc-link-arg-bin=kernel=-Tkernel.lds");
|
|
||||||
println!("cargo::rerun-if-changed=kernel.lds");
|
println!("cargo::rerun-if-changed=kernel.lds");
|
||||||
|
|
||||||
|
println!("cargo::rustc-link-arg=-Tkernel/kernel.lds");
|
||||||
|
// println!("cargo::rustc-link-arg-tests=-Tkernel.lds");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,41 @@ canonicalised="$(realpath -- "$bin")"
|
||||||
parent="$(dirname -- "$canonicalised")"
|
parent="$(dirname -- "$canonicalised")"
|
||||||
bin_name="$(basename -- "$canonicalised")"
|
bin_name="$(basename -- "$canonicalised")"
|
||||||
|
|
||||||
image="$parent/$bin_name.img"
|
parent_base="$(basename -- "$parent")"
|
||||||
|
|
||||||
|
case "$parent_base" in
|
||||||
|
rustdoctest*)
|
||||||
|
is_doctest=1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
is_doctest=0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$parent" in
|
||||||
|
*/deps|*/deps/)
|
||||||
|
is_test=1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
is_test=$is_doctest
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "Running $bin_name (is_test=$is_test, is_doctest=$is_doctest)"
|
||||||
|
|
||||||
|
if [[ $is_test -eq 1 ]]; then
|
||||||
|
QEMU_TEST_ARGS=("-display" "none" "-device" "isa-debug-exit,iobase=0xf4,iosize=0x04")
|
||||||
|
else
|
||||||
|
QEMU_TEST_ARGS=()
|
||||||
|
fi
|
||||||
|
|
||||||
|
image="$parent/$bin_name.img"
|
||||||
|
|
||||||
if [[ "$canonicalised" -nt "$image" ]]; then
|
if [[ "$canonicalised" -nt "$image" ]]; then
|
||||||
echo "Creating image $image"
|
echo "Creating image $image"
|
||||||
|
|
||||||
cat > /tmp/limine.conf <<EOF
|
cat > /tmp/limine.conf <<EOF
|
||||||
timeout: 0
|
timeout: 0
|
||||||
serial: yes
|
|
||||||
serial_baudrate: 9600
|
|
||||||
|
|
||||||
/Kernel
|
/Kernel
|
||||||
protocol: limine
|
protocol: limine
|
||||||
|
|
@ -39,10 +64,23 @@ EOF
|
||||||
mcopy -i "$image" "$limine_uefi_path" ::/EFI/BOOT/BOOTX64.EFI
|
mcopy -i "$image" "$limine_uefi_path" ::/EFI/BOOT/BOOTX64.EFI
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# cp -n "$OVMF_PATH/FV/OVMF_VARS.fd" "$parent/$bin_name.OVMF_VARS.fd"
|
||||||
|
|
||||||
|
set +e
|
||||||
|
|
||||||
qemu-system-x86_64 \
|
qemu-system-x86_64 \
|
||||||
-drive file="$image",format=raw \
|
-drive file="$image",format=raw \
|
||||||
-machine q35,accel=kvm -enable-kvm \
|
-machine q35,accel=kvm -enable-kvm \
|
||||||
|
-m 2G \
|
||||||
-drive if=pflash,format=raw,readonly=on,file="$OVMF_PATH/FV/OVMF_CODE.fd" \
|
-drive if=pflash,format=raw,readonly=on,file="$OVMF_PATH/FV/OVMF_CODE.fd" \
|
||||||
-serial stdio \
|
-chardev stdio,id=serial0,logfile=qemu.log,signal=on \
|
||||||
-vga std
|
-serial chardev:serial0 \
|
||||||
|
-vga std \
|
||||||
|
${QEMU_TEST_ARGS[@]}
|
||||||
|
|
||||||
|
qemu_ret=$?
|
||||||
|
|
||||||
|
if [[ $is_test -eq 1 ]]; then
|
||||||
|
exit $([ $qemu_ret -eq 33 ])
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
75
kernel/src/bits.rs
Normal file
75
kernel/src/bits.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
use core::ops::{Bound, Range};
|
||||||
|
|
||||||
|
pub const trait Bits {
|
||||||
|
const BITS: usize;
|
||||||
|
fn get_bit(&self, index: usize) -> bool;
|
||||||
|
fn set_bit(&mut self, index: usize, value: bool) -> &mut Self;
|
||||||
|
fn get_bits(&self, range: Range<usize>) -> Self;
|
||||||
|
fn set_bits(&mut self, range: Range<usize>, value: Self) -> &mut Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn range_to_bounds<R: [const] core::ops::RangeBounds<usize>>(
|
||||||
|
range: &R,
|
||||||
|
max: usize,
|
||||||
|
) -> (usize, usize) {
|
||||||
|
let start = match range.start_bound() {
|
||||||
|
Bound::Included(&start) => start,
|
||||||
|
Bound::Excluded(&start) => start + 1,
|
||||||
|
Bound::Unbounded => 0,
|
||||||
|
};
|
||||||
|
let end = match range.end_bound() {
|
||||||
|
Bound::Included(&end) => end + 1,
|
||||||
|
Bound::Excluded(&end) => end,
|
||||||
|
Bound::Unbounded => max,
|
||||||
|
};
|
||||||
|
assert!(
|
||||||
|
start <= end,
|
||||||
|
"Start of range must be less than or equal to end"
|
||||||
|
);
|
||||||
|
assert!(end <= max, "End of range must be less than or equal to max");
|
||||||
|
(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_bits_for {
|
||||||
|
($($t:ty),*) => {
|
||||||
|
$(
|
||||||
|
const impl Bits for $t {
|
||||||
|
const BITS: usize = <$t>::BITS as usize;
|
||||||
|
fn get_bit(&self, index: usize) -> bool {
|
||||||
|
assert!(index < <Self as Bits>::BITS, "Index out of bounds");
|
||||||
|
|
||||||
|
(*self & (1 << index)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_bit(&mut self, index: usize, value: bool) -> &mut Self{
|
||||||
|
assert!(index < <Self as Bits>::BITS, "Index out of bounds");
|
||||||
|
|
||||||
|
if value {
|
||||||
|
*self |= 1 << index;
|
||||||
|
} else {
|
||||||
|
*self &= !(1 << index);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn get_bits(&self, range: Range<usize>) -> Self {
|
||||||
|
let (start, end) = range_to_bounds(&range, <Self as Bits>::BITS);
|
||||||
|
let leading = <Self as Bits>::BITS - end;
|
||||||
|
let bits = (*self << leading) >> (end + start);
|
||||||
|
bits
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_bits(&mut self, range: Range<usize>, value: Self) -> &mut Self{
|
||||||
|
let (start, end) = range_to_bounds(&range, <Self as Bits>::BITS);
|
||||||
|
let mask = ((1 << (end - start)) - 1);
|
||||||
|
let value = (value & mask) << start;
|
||||||
|
|
||||||
|
*self &= !(mask << start);
|
||||||
|
*self |= value;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_bits_for!(u8, u16, u32, u64, u128);
|
||||||
122
kernel/src/boot.rs
Normal file
122
kernel/src/boot.rs
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
use crate::{limine, sync::OnceLock};
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum MemoryRegionType {
|
||||||
|
Usable = 0,
|
||||||
|
Reserved = 1,
|
||||||
|
AcpiReclaimable = 2,
|
||||||
|
AcpiNvs = 3,
|
||||||
|
BadMemory = 4,
|
||||||
|
Framebuffer = 5,
|
||||||
|
FirmwareReserved = 6,
|
||||||
|
FirmwareReclaimable = 7,
|
||||||
|
KernelAndModules = 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<limine::MemMapEntryKind> for MemoryRegionType {
|
||||||
|
fn from(kind: limine::MemMapEntryKind) -> Self {
|
||||||
|
match kind {
|
||||||
|
limine::MemMapEntryKind::Usable => Self::Usable,
|
||||||
|
limine::MemMapEntryKind::Reserved => Self::Reserved,
|
||||||
|
limine::MemMapEntryKind::AcpiReclaimable => Self::AcpiReclaimable,
|
||||||
|
limine::MemMapEntryKind::AcpiNvs => Self::AcpiNvs,
|
||||||
|
limine::MemMapEntryKind::BadMemory => Self::BadMemory,
|
||||||
|
limine::MemMapEntryKind::BootloaderReclaimable => Self::FirmwareReclaimable,
|
||||||
|
limine::MemMapEntryKind::KernelAndModules => Self::KernelAndModules,
|
||||||
|
limine::MemMapEntryKind::Framebuffer => Self::Framebuffer,
|
||||||
|
limine::MemMapEntryKind::ReservedMapped => Self::FirmwareReserved,
|
||||||
|
limine::MemMapEntryKind::Unknown => Self::Reserved,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryRegionType {
|
||||||
|
pub fn should_map(&self) -> bool {
|
||||||
|
!matches!(self, Self::Reserved | Self::BadMemory)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_usable(&self) -> bool {
|
||||||
|
matches!(self, Self::Usable | Self::AcpiReclaimable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct MemoryRegion {
|
||||||
|
pub start: u64,
|
||||||
|
pub length: u64,
|
||||||
|
pub region_type: MemoryRegionType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<limine::MemMapEntry> for MemoryRegion {
|
||||||
|
fn from(entry: limine::MemMapEntry) -> Self {
|
||||||
|
Self {
|
||||||
|
start: entry.base,
|
||||||
|
length: entry.length,
|
||||||
|
region_type: entry.kind().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryRegion {
|
||||||
|
pub fn iter_pages(&self, page_size: usize) -> MemoryRegionIter<'_> {
|
||||||
|
MemoryRegionIter {
|
||||||
|
region: self,
|
||||||
|
cursor: 0,
|
||||||
|
page_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MemoryRegionIter<'a> {
|
||||||
|
region: &'a MemoryRegion,
|
||||||
|
cursor: u64,
|
||||||
|
page_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for MemoryRegionIter<'_> {
|
||||||
|
type Item = u64;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.cursor >= self.region.length {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let addr = self.region.start + self.cursor;
|
||||||
|
self.cursor += self.page_size as u64;
|
||||||
|
Some(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const impl Default for MemoryRegion {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
start: 0,
|
||||||
|
length: 0,
|
||||||
|
region_type: MemoryRegionType::Reserved,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BootInfo {
|
||||||
|
pub hhdm_base: u64,
|
||||||
|
pub memory_map: &'static [MemoryRegion],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static BOOT_INFO: OnceLock<BootInfo> = OnceLock::new();
|
||||||
|
static mut MEMORY_MAP: [MemoryRegion; 128] = [MemoryRegion::default(); 128];
|
||||||
|
|
||||||
|
pub fn init_boot_info<I: Iterator<Item = MemoryRegion>>(hhdm_base: u64, memory_map: I) {
|
||||||
|
BOOT_INFO.initialize(|| {
|
||||||
|
for (i, region) in memory_map.enumerate() {
|
||||||
|
assert!(i < 128, "Memory map has more than 128 entries");
|
||||||
|
unsafe {
|
||||||
|
MEMORY_MAP[i] = region;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok::<_, !>(BootInfo {
|
||||||
|
hhdm_base,
|
||||||
|
memory_map: unsafe { (&raw const MEMORY_MAP).as_ref_unchecked() },
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,34 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(const_trait_impl, const_default)]
|
#![feature(
|
||||||
|
const_trait_impl,
|
||||||
|
const_default,
|
||||||
|
const_range,
|
||||||
|
debug_closure_helpers,
|
||||||
|
allocator_api,
|
||||||
|
ptr_cast_slice,
|
||||||
|
likely_unlikely,
|
||||||
|
never_type
|
||||||
|
)]
|
||||||
|
#![cfg_attr(test, feature(custom_test_frameworks))]
|
||||||
|
#![cfg_attr(test, test_runner(crate::tests::test_runner))]
|
||||||
|
#![cfg_attr(test, no_main)]
|
||||||
|
#![cfg_attr(test, reexport_test_harness_main = "test_main")]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
pub mod bits;
|
||||||
pub mod limine;
|
pub mod limine;
|
||||||
pub mod serial;
|
pub mod serial;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod x86_64;
|
pub mod x86_64;
|
||||||
|
|
||||||
|
pub mod boot;
|
||||||
|
pub mod memory;
|
||||||
|
|
||||||
|
pub mod testing;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
pub unsafe fn volatile_copy<T: Sized>(src: *const T, dst: *mut T, count: usize) {
|
pub unsafe fn volatile_copy<T: Sized>(src: *const T, dst: *mut T, count: usize) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -18,31 +41,22 @@ pub unsafe fn volatile_copy<T: Sized>(src: *const T, dst: *mut T, count: usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
pub struct DropGuard<F: FnOnce()>(::core::mem::ManuallyDrop<F>);
|
||||||
macro_rules! drop_guard {
|
impl<F: FnOnce()> DropGuard<F> {
|
||||||
($($stmts:stmt)*) => {
|
pub fn new(f: F) -> Self {
|
||||||
{
|
DropGuard(::core::mem::ManuallyDrop::new(f))
|
||||||
struct __DropGuard<F: FnOnce()>(::core::mem::ManuallyDrop<F>);
|
}
|
||||||
|
|
||||||
impl<F: FnOnce()> __DropGuard<F> {
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn forget(self) {
|
pub fn forget(self) {
|
||||||
let mut this = ::core::mem::ManuallyDrop::new(self);
|
let mut this = ::core::mem::ManuallyDrop::new(self);
|
||||||
unsafe {
|
unsafe {
|
||||||
ManuallyDrop::drop(&mut this.0);
|
::core::mem::ManuallyDrop::drop(&mut this.0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<F: FnOnce()> Drop for __DropGuard<F> {
|
impl<F: FnOnce()> Drop for DropGuard<F> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { ::core::ptr::read(&*self.0)() }
|
unsafe { ::core::ptr::read(&*self.0)() }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
__DropGuard(::core::mem::ManuallyDrop::new(|| {
|
|
||||||
$($stmts)*
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use core::{cell::UnsafeCell, ptr::NonNull};
|
use core::{cell::UnsafeCell, fmt::Debug, ptr::NonNull};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct BaseRevision(UnsafeCell<[u64; 3]>);
|
pub struct BaseRevision(UnsafeCell<[u64; 3]>);
|
||||||
|
|
@ -44,7 +44,7 @@ pub struct Request<T, U = ()> {
|
||||||
request: U,
|
request: U,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Sync, U: Sync> Sync for Request<T, U>{}
|
unsafe impl<T: Sync, U: Sync> Sync for Request<T, U> {}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Response<T> {
|
pub struct Response<T> {
|
||||||
|
|
@ -165,3 +165,110 @@ impl FramebufferRequest {
|
||||||
.unwrap_or(&[])
|
.unwrap_or(&[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MemMapResponse {
|
||||||
|
count: u64,
|
||||||
|
entries: *const *const MemMapEntry,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for MemMapResponse {}
|
||||||
|
unsafe impl Sync for MemMapResponse {}
|
||||||
|
|
||||||
|
pub type MemMapRequest = Request<MemMapResponse>;
|
||||||
|
|
||||||
|
impl MemMapRequest {
|
||||||
|
const ID: [u64; 2] = [0x67cf3d9d378a806f, 0xe304acdfc50c3c62];
|
||||||
|
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self::new_raw(Self::ID, 0, ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entries<'a>(&self) -> &'a [&'a MemMapEntry] {
|
||||||
|
// SAFETY: limine responses are guaranteed to be valid for the lifetime of the memory mapping.
|
||||||
|
self.response()
|
||||||
|
.map(|response| unsafe {
|
||||||
|
core::slice::from_raw_parts(
|
||||||
|
response.response.entries.cast::<&'a MemMapEntry>(),
|
||||||
|
response.response.count as usize,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or(&[])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u64)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MemMapEntryKind {
|
||||||
|
Usable = 0,
|
||||||
|
Reserved = 1,
|
||||||
|
AcpiReclaimable = 2,
|
||||||
|
AcpiNvs = 3,
|
||||||
|
BadMemory = 4,
|
||||||
|
BootloaderReclaimable = 5,
|
||||||
|
KernelAndModules = 6,
|
||||||
|
Framebuffer = 7,
|
||||||
|
ReservedMapped = 8,
|
||||||
|
Unknown = u64::MAX,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemMapEntryKind {
|
||||||
|
pub fn from_u64(value: u64) -> Self {
|
||||||
|
match value {
|
||||||
|
0 => Self::Usable,
|
||||||
|
1 => Self::Reserved,
|
||||||
|
2 => Self::AcpiReclaimable,
|
||||||
|
3 => Self::AcpiNvs,
|
||||||
|
4 => Self::BadMemory,
|
||||||
|
5 => Self::BootloaderReclaimable,
|
||||||
|
6 => Self::KernelAndModules,
|
||||||
|
7 => Self::Framebuffer,
|
||||||
|
8 => Self::ReservedMapped,
|
||||||
|
_ => Self::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct MemMapEntry {
|
||||||
|
pub base: u64,
|
||||||
|
pub length: u64,
|
||||||
|
pub kind: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemMapEntry {
|
||||||
|
pub fn kind(&self) -> MemMapEntryKind {
|
||||||
|
MemMapEntryKind::from_u64(self.kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for MemMapEntry {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("MemMapEntry")
|
||||||
|
.field("base", &format_args!("{:#x}", self.base))
|
||||||
|
.field("length", &format_args!("{:#x}", self.length))
|
||||||
|
.field("kind", &self.kind())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct HhdmResponse {
|
||||||
|
pub offset: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type HhdmRequest = Request<HhdmResponse>;
|
||||||
|
|
||||||
|
impl HhdmRequest {
|
||||||
|
const ID: [u64; 2] = [0x48dcf1cb8ad2b852, 0x63984e959a98244b];
|
||||||
|
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self::new_raw(Self::ID, 0, ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset(&self) -> Option<u64> {
|
||||||
|
self.response().map(|response| response.response.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,79 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
|
use kernel::{
|
||||||
|
memory::VirtAddr,
|
||||||
|
serial_println,
|
||||||
|
sync::LazyLock,
|
||||||
|
x86_64::{gdt::GlobalDescriptorTable, idt::InterruptDescriptorTable},
|
||||||
|
};
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
kernel::serial_println!("[PANIC] {}", _info);
|
kernel::serial_println!("[PANIC] {}", _info);
|
||||||
kernel::x86_64::halt_loop()
|
kernel::x86_64::halt_loop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IDT: LazyLock<InterruptDescriptorTable> =
|
||||||
|
LazyLock::new(InterruptDescriptorTable::new_default);
|
||||||
|
|
||||||
|
static GDT: LazyLock<GlobalDescriptorTable> = LazyLock::new(GlobalDescriptorTable::new);
|
||||||
|
|
||||||
/// Entry point for the kernel
|
/// Entry point for the kernel
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn _start() -> ! {
|
extern "C" fn _start() -> ! {
|
||||||
kernel::serial_println!("Hello, world!");
|
kernel::serial_println!("Hello, world!");
|
||||||
assert!(limine_requests::LIMINE_BASE_REVISION.is_supported());
|
assert!(limine_requests::LIMINE_BASE_REVISION.is_supported());
|
||||||
|
|
||||||
|
_ = kernel::memory::HHDM_BASE
|
||||||
|
.try_insert(
|
||||||
|
limine_requests::HHDM_REQUEST
|
||||||
|
.offset()
|
||||||
|
.expect("HHDM offset not provided by bootloader"),
|
||||||
|
)
|
||||||
|
.expect("HHDM offset already set");
|
||||||
|
|
||||||
|
kernel::boot::init_boot_info(
|
||||||
|
limine_requests::HHDM_REQUEST
|
||||||
|
.offset()
|
||||||
|
.expect("HHDM offset not provided by bootloader"),
|
||||||
|
limine_requests::MEMMAP_REQUEST
|
||||||
|
.entries()
|
||||||
|
.iter()
|
||||||
|
.map(|&&e| e.into()),
|
||||||
|
);
|
||||||
|
|
||||||
|
kernel::serial_println!(
|
||||||
|
"HHDM offset: 0x{:#x}",
|
||||||
|
kernel::memory::HHDM_BASE.get().unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
kernel::serial_println!(
|
||||||
|
"Memory map: {:#?}",
|
||||||
|
limine_requests::MEMMAP_REQUEST.entries()
|
||||||
|
);
|
||||||
|
|
||||||
|
GDT.load();
|
||||||
|
IDT.load();
|
||||||
|
|
||||||
|
kernel::serial_println!("entry point: 0x{:x}", _start as *const () as usize);
|
||||||
|
kernel::serial_println!(
|
||||||
|
"entry point phy: {:?}",
|
||||||
|
kernel::x86_64::paging::get_physical_addr(VirtAddr(_start as *const () as u64))
|
||||||
|
);
|
||||||
|
|
||||||
|
let pmm = kernel::memory::PhysicalMemoryAllocator::from_memory_map(
|
||||||
|
kernel::boot::BOOT_INFO
|
||||||
|
.get()
|
||||||
|
.expect("Boot info not initialized")
|
||||||
|
.memory_map,
|
||||||
|
);
|
||||||
|
|
||||||
|
let leaf = kernel::x86_64::cpuid::Leaf8000008::get();
|
||||||
|
serial_println!("max phy: {:#?}", leaf);
|
||||||
|
|
||||||
|
// kernel::serial_println!("PMM: {pmm:#?}");
|
||||||
|
|
||||||
let fb = limine_requests::FRAMEBUFFER_REQUEST
|
let fb = limine_requests::FRAMEBUFFER_REQUEST
|
||||||
.framebuffers()
|
.framebuffers()
|
||||||
.first()
|
.first()
|
||||||
|
|
@ -52,6 +113,14 @@ mod limine_requests {
|
||||||
#[unsafe(link_section = ".limine_requests")]
|
#[unsafe(link_section = ".limine_requests")]
|
||||||
pub static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
|
pub static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
|
||||||
|
|
||||||
|
#[used]
|
||||||
|
#[unsafe(link_section = ".limine_requests")]
|
||||||
|
pub static HHDM_REQUEST: kernel::limine::HhdmRequest = kernel::limine::HhdmRequest::new();
|
||||||
|
|
||||||
|
#[used]
|
||||||
|
#[unsafe(link_section = ".limine_requests")]
|
||||||
|
pub static MEMMAP_REQUEST: kernel::limine::MemMapRequest = kernel::limine::MemMapRequest::new();
|
||||||
|
|
||||||
#[used]
|
#[used]
|
||||||
#[unsafe(link_section = ".limine_requests_end")]
|
#[unsafe(link_section = ".limine_requests_end")]
|
||||||
static LIMINE_REQUESTS_END: RequestsEndMarker = REQUESTS_END_MARKER;
|
static LIMINE_REQUESTS_END: RequestsEndMarker = REQUESTS_END_MARKER;
|
||||||
|
|
|
||||||
1128
kernel/src/memory.rs
Normal file
1128
kernel/src/memory.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -300,11 +300,11 @@ pub mod uart_16550 {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub enum BaudRate {
|
pub enum BaudRate {
|
||||||
|
#[default]
|
||||||
Baud115200,
|
Baud115200,
|
||||||
Baud57600,
|
Baud57600,
|
||||||
Baud38400,
|
Baud38400,
|
||||||
Baud19200,
|
Baud19200,
|
||||||
#[default]
|
|
||||||
Baud9600,
|
Baud9600,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -210,9 +210,9 @@ mod once {
|
||||||
// even though we don't have unwinding, for
|
// even though we don't have unwinding, for
|
||||||
// completeness sake we'll poison the lock if
|
// completeness sake we'll poison the lock if
|
||||||
// the closure panics.
|
// the closure panics.
|
||||||
let guard = crate::drop_guard! {
|
let guard = crate::DropGuard::new(|| {
|
||||||
self.state.store(POISONED, Ordering::Release)
|
self.state.store(POISONED, Ordering::Release)
|
||||||
};
|
});
|
||||||
|
|
||||||
let state = OnceState {
|
let state = OnceState {
|
||||||
poisoned: state == POISONED,
|
poisoned: state == POISONED,
|
||||||
|
|
|
||||||
37
kernel/src/testing.rs
Normal file
37
kernel/src/testing.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum QemuExitCode {
|
||||||
|
Success = 0x10,
|
||||||
|
Failed = 0x11,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_qemu(exit_code: QemuExitCode) -> ! {
|
||||||
|
use crate::x86_64::PortU16;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let port = PortU16(0xf4);
|
||||||
|
port.write(exit_code as u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::x86_64::halt_loop()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_panic_handler(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
crate::serial_println!("[PANIC] {}", info);
|
||||||
|
exit_qemu(QemuExitCode::Failed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Testable {
|
||||||
|
fn run(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Testable for T
|
||||||
|
where
|
||||||
|
T: Fn(),
|
||||||
|
{
|
||||||
|
fn run(&self) {
|
||||||
|
crate::serial_print!("{}...\t", core::any::type_name::<T>());
|
||||||
|
self();
|
||||||
|
crate::serial_println!("[ok]");
|
||||||
|
}
|
||||||
|
}
|
||||||
26
kernel/src/tests.rs
Normal file
26
kernel/src/tests.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
use crate::testing::{QemuExitCode, Testable, exit_qemu};
|
||||||
|
|
||||||
|
pub fn test_runner(tests: &[&dyn Testable]) {
|
||||||
|
crate::serial_println!("Running {} tests", tests.len());
|
||||||
|
for test in tests {
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::serial_println!("[ok] All tests passed");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic_thunk(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
crate::testing::test_panic_handler(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(export_name = "_start")]
|
||||||
|
fn main() -> ! {
|
||||||
|
crate::test_main();
|
||||||
|
exit_qemu(QemuExitCode::Success)
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[test_case]
|
||||||
|
// fn panics() {
|
||||||
|
// panic!("This test should panic");
|
||||||
|
// }
|
||||||
|
|
@ -1,11 +1,19 @@
|
||||||
#![cfg(target_arch = "x86_64")]
|
#![cfg(target_arch = "x86_64")]
|
||||||
|
|
||||||
#[inline]
|
pub mod cpuid;
|
||||||
pub fn hlt() {
|
pub mod gdt;
|
||||||
unsafe {
|
pub mod idt;
|
||||||
core::arch::asm!("hlt", options(nomem, nostack, preserves_flags));
|
pub mod instructions;
|
||||||
}
|
pub mod paging;
|
||||||
}
|
pub mod registers;
|
||||||
|
|
||||||
|
use core::{arch::asm, fmt::Debug};
|
||||||
|
|
||||||
|
pub use instructions::hlt;
|
||||||
|
|
||||||
|
use crate::memory::VirtAddr;
|
||||||
|
|
||||||
|
pub const PAGE_SIZE: usize = 4096;
|
||||||
|
|
||||||
pub fn halt_loop() -> ! {
|
pub fn halt_loop() -> ! {
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -23,27 +31,184 @@ impl PortU16 {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn read(&self) -> u16 {
|
pub unsafe fn read(&self) -> u16 {
|
||||||
let value: u16;
|
unsafe { instructions::read_u16(self.0) }
|
||||||
unsafe {
|
|
||||||
core::arch::asm!(
|
|
||||||
"in ax, dx",
|
|
||||||
in("dx") self.0,
|
|
||||||
out("ax") value,
|
|
||||||
options(nomem, nostack, preserves_flags)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn write(&self, value: u16) {
|
pub unsafe fn write(&self, value: u16) {
|
||||||
unsafe {
|
unsafe { instructions::write_u16(self.0, value) }
|
||||||
core::arch::asm!(
|
}
|
||||||
"out dx, ax",
|
}
|
||||||
in("dx") self.0,
|
|
||||||
in("ax") value,
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
options(nomem, nostack, preserves_flags)
|
#[repr(C)]
|
||||||
);
|
// registers in the order they are pushed onto the stack:
|
||||||
|
pub struct Registers {
|
||||||
|
pub r15: u64,
|
||||||
|
pub r14: u64,
|
||||||
|
pub r13: u64,
|
||||||
|
pub r12: u64,
|
||||||
|
pub r11: u64,
|
||||||
|
pub r10: u64,
|
||||||
|
pub r9: u64,
|
||||||
|
pub r8: u64,
|
||||||
|
pub rdi: u64,
|
||||||
|
pub rsi: u64,
|
||||||
|
pub rbp: u64,
|
||||||
|
// rsp is not included here
|
||||||
|
pub rbx: u64,
|
||||||
|
pub rdx: u64,
|
||||||
|
pub rcx: u64,
|
||||||
|
pub rax: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct RFlags: u64 {
|
||||||
|
const CARRY = 1 << 0;
|
||||||
|
const DEFAULT = 1 << 1;
|
||||||
|
const PARITY = 1 << 2;
|
||||||
|
const AUXILIARY_CARRY = 1 << 4;
|
||||||
|
const ZERO = 1 << 6;
|
||||||
|
const SIGN = 1 << 7;
|
||||||
|
const TRAP = 1 << 8;
|
||||||
|
const INTERRUPT_ENABLE = 1 << 9;
|
||||||
|
const DIRECTION = 1 << 10;
|
||||||
|
const OVERFLOW = 1 << 11;
|
||||||
|
const IOPL_LOW = 1 << 12;
|
||||||
|
const IOPL_HIGH = 1 << 13;
|
||||||
|
const NESTED_TASK = 1 << 14;
|
||||||
|
const RESUME = 1 << 16;
|
||||||
|
const VIRTUAL_8086_MODE = 1 << 17;
|
||||||
|
const ALIGNMENT_CHECK = 1 << 18;
|
||||||
|
const VIRTUAL_INTERRUPT_FLAG = 1 << 19;
|
||||||
|
const VIRTUAL_INTERRUPT_PENDING = 1 << 20;
|
||||||
|
const ID_FLAG = 1 << 21;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait VirtAddrExt {
|
||||||
|
const LEVEL5: u8 = 4;
|
||||||
|
const LEVEL4: u8 = 3;
|
||||||
|
const LEVEL3: u8 = 2;
|
||||||
|
const LEVEL2: u8 = 1;
|
||||||
|
const LEVEL1: u8 = 0;
|
||||||
|
|
||||||
|
const PT: u8 = Self::LEVEL1;
|
||||||
|
const PD: u8 = Self::LEVEL2;
|
||||||
|
const PDPT: u8 = Self::LEVEL3;
|
||||||
|
const PML4: u8 = Self::LEVEL4;
|
||||||
|
const PML5: u8 = Self::LEVEL5;
|
||||||
|
|
||||||
|
fn is_canonical(&self) -> bool;
|
||||||
|
fn page_table_index<const LEVEL: u8>(&self) -> u16;
|
||||||
|
fn offset_4k(&self) -> u16;
|
||||||
|
fn offset_2m(&self) -> u32;
|
||||||
|
fn offset_1g(&self) -> u32;
|
||||||
|
fn into_parts<E: sealed::PageSize>(self) -> (E::Entries, E::Offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct L4Entries4K;
|
||||||
|
pub struct L4Entries2M;
|
||||||
|
pub struct L4Entries1G;
|
||||||
|
|
||||||
|
pub struct U12(pub u16);
|
||||||
|
pub struct U21(pub u32);
|
||||||
|
pub struct U30(pub u32);
|
||||||
|
|
||||||
|
pub(crate) mod sealed {
|
||||||
|
use super::{L4Entries1G, L4Entries2M, L4Entries4K, U12, U21, U30};
|
||||||
|
|
||||||
|
pub trait PageSize {
|
||||||
|
type Entries;
|
||||||
|
type Offset;
|
||||||
|
fn decompose(addr_bits: u64) -> (Self::Entries, Self::Offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageSize for L4Entries4K {
|
||||||
|
type Entries = [u16; 4];
|
||||||
|
type Offset = U12;
|
||||||
|
|
||||||
|
fn decompose(addr_bits: u64) -> (Self::Entries, Self::Offset) {
|
||||||
|
let entries = [
|
||||||
|
((addr_bits >> 39) & 0x1FF) as u16,
|
||||||
|
((addr_bits >> 30) & 0x1FF) as u16,
|
||||||
|
((addr_bits >> 21) & 0x1FF) as u16,
|
||||||
|
((addr_bits >> 12) & 0x1FF) as u16,
|
||||||
|
];
|
||||||
|
let offset = U12((addr_bits & 0xFFF) as u16);
|
||||||
|
(entries, offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageSize for L4Entries2M {
|
||||||
|
type Entries = [u16; 3];
|
||||||
|
type Offset = U21;
|
||||||
|
|
||||||
|
fn decompose(addr_bits: u64) -> (Self::Entries, Self::Offset) {
|
||||||
|
let entries = [
|
||||||
|
((addr_bits >> 39) & 0x1FF) as u16,
|
||||||
|
((addr_bits >> 30) & 0x1FF) as u16,
|
||||||
|
((addr_bits >> 21) & 0x1FF) as u16,
|
||||||
|
];
|
||||||
|
let offset = U21((addr_bits & 0x1FFFFF) as u32);
|
||||||
|
(entries, offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageSize for L4Entries1G {
|
||||||
|
type Entries = [u16; 2];
|
||||||
|
type Offset = U30;
|
||||||
|
|
||||||
|
fn decompose(addr_bits: u64) -> (Self::Entries, Self::Offset) {
|
||||||
|
let entries = [
|
||||||
|
((addr_bits >> 39) & 0x1FF) as u16,
|
||||||
|
((addr_bits >> 30) & 0x1FF) as u16,
|
||||||
|
];
|
||||||
|
let offset = U30((addr_bits & 0x3FFFFFFF) as u32);
|
||||||
|
(entries, offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl VirtAddrExt for VirtAddr {
|
||||||
|
fn is_canonical(&self) -> bool {
|
||||||
|
// sign bits are bits 48-63, and they must all be the same as bit 47
|
||||||
|
//
|
||||||
|
// shift right 48 bits puts the 47th bit into the carry flag.
|
||||||
|
// sign-bits + CF should be 0
|
||||||
|
let canonical: u8;
|
||||||
|
unsafe {
|
||||||
|
asm!(
|
||||||
|
"shr {bits}, 48",
|
||||||
|
"adc {bits}, 0",
|
||||||
|
"setz {canonical}",
|
||||||
|
bits = inout(reg) self.0 => _,
|
||||||
|
canonical = out(reg_byte) canonical,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
canonical == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_table_index<const LEVEL: u8>(&self) -> u16 {
|
||||||
|
assert!(LEVEL <= 4, "LEVEL must be in the range 0..=4");
|
||||||
|
let shift = 12 + (LEVEL * 9);
|
||||||
|
((self.0 >> shift) & 0x1FF) as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_4k(&self) -> u16 {
|
||||||
|
(self.0 & 0xFFF) as u16
|
||||||
|
}
|
||||||
|
fn offset_2m(&self) -> u32 {
|
||||||
|
(self.0 & 0x1FFFFF) as u32
|
||||||
|
}
|
||||||
|
fn offset_1g(&self) -> u32 {
|
||||||
|
(self.0 & 0x3FFFFFFF) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_parts<E: sealed::PageSize>(self) -> (E::Entries, E::Offset) {
|
||||||
|
E::decompose(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
454
kernel/src/x86_64/cpuid.rs
Normal file
454
kernel/src/x86_64/cpuid.rs
Normal file
|
|
@ -0,0 +1,454 @@
|
||||||
|
#![allow(clippy::identity_op)]
|
||||||
|
|
||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
use bit_field::BitField;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
pub struct CpuidResult {
|
||||||
|
pub eax: u32,
|
||||||
|
pub ebx: u32,
|
||||||
|
pub ecx: u32,
|
||||||
|
pub edx: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpuid(eax: u32, ecx: u32) -> CpuidResult {
|
||||||
|
let result = core::arch::x86_64::__cpuid_count(eax, ecx);
|
||||||
|
|
||||||
|
CpuidResult {
|
||||||
|
eax: result.eax,
|
||||||
|
ebx: result.ebx,
|
||||||
|
ecx: result.ecx,
|
||||||
|
edx: result.edx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CpuId {
|
||||||
|
max_eax: u32,
|
||||||
|
vendor_id: [u8; 12],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Leaf1(CpuidResult);
|
||||||
|
|
||||||
|
impl Leaf1 {
|
||||||
|
pub fn get() -> Self {
|
||||||
|
let result = cpuid(1, 0);
|
||||||
|
Self(result)
|
||||||
|
}
|
||||||
|
pub fn brand_index(&self) -> u8 {
|
||||||
|
self.0.ebx.get_bits(0..8) as u8
|
||||||
|
}
|
||||||
|
pub fn clflush_line_size(&self) -> u8 {
|
||||||
|
self.0.ebx.get_bits(8..16) as u8
|
||||||
|
}
|
||||||
|
pub fn max_logical_processors(&self) -> u8 {
|
||||||
|
self.0.ebx.get_bits(16..24) as u8
|
||||||
|
}
|
||||||
|
pub fn initial_apic_id(&self) -> u8 {
|
||||||
|
self.0.ebx.get_bits(24..32) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn family_id(&self) -> u8 {
|
||||||
|
self.0.eax.get_bits(8..12) as u8 | ((self.0.eax.get_bits(20..28) as u8) << 4)
|
||||||
|
}
|
||||||
|
pub fn model_id(&self) -> u8 {
|
||||||
|
self.0.eax.get_bits(4..8) as u8 | ((self.0.eax.get_bits(16..20) as u8) << 4)
|
||||||
|
}
|
||||||
|
pub fn stepping_id(&self) -> u8 {
|
||||||
|
self.0.eax.get_bits(0..4) as u8
|
||||||
|
}
|
||||||
|
pub fn processor_type(&self) -> u8 {
|
||||||
|
self.0.eax.get_bits(12..14) as u8
|
||||||
|
}
|
||||||
|
pub fn flags(&self) -> Leaf1Flags {
|
||||||
|
Leaf1Flags::from_bits_truncate(self.0.ecx as u64 | ((self.0.edx as u64) << 32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct Leaf1Flags: u64 {
|
||||||
|
const SSE3 = 1 << 0;
|
||||||
|
const PCLMULQDQ = 1 << 1;
|
||||||
|
const DTES64 = 1 << 2;
|
||||||
|
const MONITOR = 1 << 3;
|
||||||
|
const DS_CPL = 1 << 4;
|
||||||
|
const VMX = 1 << 5;
|
||||||
|
const SMX = 1 << 6;
|
||||||
|
const EIST = 1 << 7;
|
||||||
|
const TM2 = 1 << 8;
|
||||||
|
const SSSE3 = 1 << 9;
|
||||||
|
const CNXT_ID = 1 << 10;
|
||||||
|
const SDBG = 1 << 11;
|
||||||
|
const FMA = 1 << 12;
|
||||||
|
const CMPXCHG16B = 1 << 13;
|
||||||
|
const XTPR_UPDATE_CONTROL = 1 << 14;
|
||||||
|
const PDCM = 1 << 15;
|
||||||
|
const PCID = 1 << 17;
|
||||||
|
const DCA = 1 << 18;
|
||||||
|
const SSE4_1 = 1 << 19;
|
||||||
|
const SSE4_2 = 1 << 20;
|
||||||
|
const X2APIC = 1 << 21;
|
||||||
|
const MOVBE = 1 << 22;
|
||||||
|
const POPCNT = 1 << 23;
|
||||||
|
const TSC_DEADLINE_TIMER = 1 << 24;
|
||||||
|
const AESNI = 1 << 25;
|
||||||
|
const XSAVE = 1 << 26;
|
||||||
|
const OSXSAVE = 1 << 27;
|
||||||
|
const AVX = 1 <<28 ;
|
||||||
|
const F16C = 1 << 29;
|
||||||
|
const RDRAND = 1 << 30;
|
||||||
|
|
||||||
|
const FPU = 1 << (0 + u32::BITS);
|
||||||
|
const VME = 1 << (1 + u32::BITS);
|
||||||
|
const DE = 1 << (2 + u32::BITS);
|
||||||
|
const PSE = 1 << (3 + u32::BITS);
|
||||||
|
const TSC = 1 << (4 + u32::BITS);
|
||||||
|
const MSR = 1 << (5 + u32::BITS);
|
||||||
|
const PAE = 1 << (6 + u32::BITS);
|
||||||
|
const MCE = 1 << (7 + u32::BITS);
|
||||||
|
const CX8 = 1 << (8 + u32::BITS);
|
||||||
|
const APIC = 1 << (9 + u32::BITS);
|
||||||
|
const SEP = 1 << (11 + u32::BITS);
|
||||||
|
const MTRR = 1 << (12 + u32::BITS);
|
||||||
|
const PGE = 1 << (13 + u32::BITS);
|
||||||
|
const MCA = 1 << (14 + u32::BITS);
|
||||||
|
const CMOV = 1 << (15 + u32::BITS);
|
||||||
|
const PAT = 1 << (16 + u32::BITS);
|
||||||
|
const PSE36 = 1 << (17 + u32::BITS);
|
||||||
|
const PSN = 1 << (18 + u32::BITS);
|
||||||
|
const CLFSH = 1 << (19 + u32::BITS);
|
||||||
|
const DS = 1 << (21 + u32::BITS);
|
||||||
|
const ACPI = 1 << (22 + u32::BITS);
|
||||||
|
const MMX = 1 << (23 + u32::BITS);
|
||||||
|
const FXSR = 1 << (24 + u32::BITS);
|
||||||
|
const SSE = 1 << (25 + u32::BITS);
|
||||||
|
const SSE2 = 1 << (26 + u32::BITS);
|
||||||
|
const SS = 1 << (27 + u32::BITS);
|
||||||
|
const HTT = 1 << (28 + u32::BITS);
|
||||||
|
const TM = 1 << (29 + u32::BITS);
|
||||||
|
const PBE = 1 << (31 + u32::BITS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Leaf7(CpuidResult);
|
||||||
|
|
||||||
|
impl Leaf7 {
|
||||||
|
pub fn get() -> Self {
|
||||||
|
let result = cpuid(7, 0);
|
||||||
|
Self(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subleaf1(&self) -> Option<Leaf7Subleaf1> {
|
||||||
|
if self.max_subleaf() >= 1 {
|
||||||
|
let result = cpuid(7, 1);
|
||||||
|
Some(Leaf7Subleaf1(result))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_subleaf(&self) -> u32 {
|
||||||
|
self.0.eax
|
||||||
|
}
|
||||||
|
pub fn flags(&self) -> Leaf7Flags {
|
||||||
|
Leaf7Flags::from_bits_truncate(unsafe {
|
||||||
|
core::mem::transmute::<[[u8; 4]; 4], u128>(
|
||||||
|
[self.0.ebx, self.0.ecx, self.0.edx, 0].map(u32::to_ne_bytes),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn mawau(&self) -> u8 {
|
||||||
|
self.0.ecx.get_bits(17..22) as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct Leaf7Flags: u128{
|
||||||
|
const FSGSBASE = 1 << 0;
|
||||||
|
const IA32_TSC_ADJUST_MSR = 1 << 1;
|
||||||
|
const SGX = 1 << 2;
|
||||||
|
const BMI1 = 1 << 3;
|
||||||
|
const HLE = 1 << 4;
|
||||||
|
const AVX2 = 1 << 5;
|
||||||
|
const SMEP = 1 << 7;
|
||||||
|
const BMI2 = 1 << 8;
|
||||||
|
const ERMS = 1 << 9;
|
||||||
|
const INVPCID = 1 << 10;
|
||||||
|
const RTM = 1 << 11;
|
||||||
|
const PQM = 1 << 12;
|
||||||
|
const FPU_CS_DS_DEPRECATION = 1 << 13;
|
||||||
|
const MPX = 1 << 14;
|
||||||
|
const PQE = 1 << 15;
|
||||||
|
const AVX512F = 1 << 16;
|
||||||
|
const AVX512DQ = 1 << 17;
|
||||||
|
const RDSEED = 1 << 18;
|
||||||
|
const ADX = 1 << 19;
|
||||||
|
const SMAP = 1 << 20;
|
||||||
|
const AVX512IFMA = 1 << 21;
|
||||||
|
const PCOMMIT = 1 << 22;
|
||||||
|
const CLFLUSHOPT = 1 << 23;
|
||||||
|
const CLWB = 1 <<24 ;
|
||||||
|
const INTEL_PT = 1 << 25;
|
||||||
|
const AVX512PF = 1 << 26;
|
||||||
|
const AVX512ER = 1 << 27;
|
||||||
|
const AVX512CD = 1 << 28;
|
||||||
|
const SHA = 1 << 29;
|
||||||
|
const AVX512BW = 1 << 30;
|
||||||
|
const AVX512VL = 1 << 31;
|
||||||
|
|
||||||
|
const PREFETCHWT1 = 1 << (0 + u32::BITS);
|
||||||
|
const AVX512VBMI = 1 << (1 + u32::BITS);
|
||||||
|
const UMIP = 1 << (2 + u32::BITS);
|
||||||
|
const PKU = 1 << (3 + u32::BITS);
|
||||||
|
const OSPKE = 1 << (4 + u32::BITS);
|
||||||
|
const RDPID = 1 << (22 + u32::BITS);
|
||||||
|
const SGX_LC = 1 << (30 + u32::BITS);
|
||||||
|
|
||||||
|
const SGX_KEYS = 1 << (1 + 2 * u32::BITS);
|
||||||
|
const AVX512_4VNNIW = 1 << (2 + 2 * u32::BITS);
|
||||||
|
const AVX512_4FMAPS = 1 << (3 + 2 * u32::BITS);
|
||||||
|
const FSRM = 1 << (4 + 2 * u32::BITS);
|
||||||
|
const UINTR = 1 << (5 + 2 * u32::BITS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Leaf7Subleaf1(CpuidResult);
|
||||||
|
|
||||||
|
impl Leaf7Subleaf1 {
|
||||||
|
pub fn flags(&self) -> Leaf7Subleaf1Flags {
|
||||||
|
Leaf7Subleaf1Flags::from_bits_truncate(unsafe {
|
||||||
|
core::mem::transmute::<[u32; 4], u128>([self.0.eax, self.0.ebx, self.0.ecx, self.0.edx])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// in eax:ebx:ecx:edx order
|
||||||
|
bitflags! {
|
||||||
|
pub struct Leaf7Subleaf1Flags: u128 {
|
||||||
|
const SHA512 = 1 << 0;
|
||||||
|
const SM3 = 1 << 1;
|
||||||
|
const SM4 = 1 << 2;
|
||||||
|
const RAO_INT = 1 << 3;
|
||||||
|
const AVX_VNNI = 1 << 4;
|
||||||
|
const AVX512_BF16 = 1 << 5;
|
||||||
|
const LASS = 1 << 6;
|
||||||
|
const CMPCCXADD = 1 << 7;
|
||||||
|
const ARCHPERFMONTEXT = 1 << 8;
|
||||||
|
const FZRM = 1 << 10;
|
||||||
|
const FSRS = 1 << 11;
|
||||||
|
const RSRCS = 1 << 12;
|
||||||
|
const FRED = 1 << 17;
|
||||||
|
const LKGS = 1 << 18;
|
||||||
|
const WRMSRNS = 1 << 19;
|
||||||
|
const NMI_SRC = 1 << 20;
|
||||||
|
const AMX_FP16 = 1 << 21;
|
||||||
|
const HRESET = 1 << 22;
|
||||||
|
const AVX_IFMA = 1 << 23;
|
||||||
|
const LAM = 1 << 26;
|
||||||
|
const MSRLIST = 1 << 27;
|
||||||
|
const INVD_DISABLE_POST_BIOS_DONE = 1 << 30;
|
||||||
|
const MOVRS = 1 << 31;
|
||||||
|
|
||||||
|
const PPINSTR = 1 << (0 + u32::BITS);
|
||||||
|
const PBNDKB = 1 << (1 + u32::BITS);
|
||||||
|
const CPUIDMAXVAL_LIM_RMV = 1 << (2 + u32::BITS);
|
||||||
|
|
||||||
|
const RDT_M_ASYM = 1 << (0 + 2 * u32::BITS);
|
||||||
|
const RDT_A_ASYM = 1 << (1 + 2 * u32::BITS);
|
||||||
|
const MSR_IMM = 1 << (2 + 2 * u32::BITS);
|
||||||
|
const ACE = 1 << (3 + 2 * u32::BITS);
|
||||||
|
|
||||||
|
const AVX_VNNI_INT8 = 1 << (4 + 3 * u32::BITS);
|
||||||
|
const AVX_NE_CONVERT = 1 << (5 + 3 * u32::BITS);
|
||||||
|
const AMX_COMPLEX = 1 << (8 + 3 * u32::BITS);
|
||||||
|
const AVX_VNNI_INT16 = 1 << (10 + 3 * u32::BITS);
|
||||||
|
const UTMR = 1 << (13 + 3 * u32::BITS);
|
||||||
|
const PREFETCHI = 1 << (14 + 3 * u32::BITS);
|
||||||
|
const USER_MRS = 1 << (15 + 3 * u32::BITS);
|
||||||
|
const UIRET_UIF_FROM_RFLAGS = 1 << (17 + 3 * u32::BITS);
|
||||||
|
const CET_SSS = 1 << (18 + 3 * u32::BITS);
|
||||||
|
const AVX10 = 1 << (19 + 3 * u32::BITS);
|
||||||
|
const APX_F = 1 << (21 + 3 * u32::BITS);
|
||||||
|
const SEC_TEE_ATTESTATION = 1 << (22 + 3 * u32::BITS);
|
||||||
|
const MWAIT = 1 << (23 + 3 * u32::BITS);
|
||||||
|
const SLSM = 1 << (24 + 3 * u32::BITS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct Leaf8000001Flags: u64 {
|
||||||
|
const FPU = 1 << 0;
|
||||||
|
const VME = 1 << 1;
|
||||||
|
const DE = 1 << 2;
|
||||||
|
const PSE = 1 << 3;
|
||||||
|
const TSC = 1 << 4;
|
||||||
|
const MSR = 1 << 5;
|
||||||
|
const PAE = 1 << 6;
|
||||||
|
const MCE = 1 << 7;
|
||||||
|
const CX8 = 1 << 8;
|
||||||
|
const APIC = 1 << 9;
|
||||||
|
const SYSCALL_K9 = 1 << 10;
|
||||||
|
const SYSCALL = 1 << 11;
|
||||||
|
const MTRR = 1 << 12;
|
||||||
|
const PGE = 1 << 13;
|
||||||
|
const MCA = 1 << 14;
|
||||||
|
const CMOV = 1 << 15;
|
||||||
|
const PAT = 1 << 16;
|
||||||
|
const PSE36 = 1 << 17;
|
||||||
|
const ECC_K9 = 1 << 18;
|
||||||
|
const ECC = 1 << 19;
|
||||||
|
const NX = 1 << 20;
|
||||||
|
const MMXEXT = 1 << 22;
|
||||||
|
const MMX = 1 << 23;
|
||||||
|
const FXSR_OPT = 1 << 24;
|
||||||
|
const PDPE1GB = 1 << 26;
|
||||||
|
const RDTSCP = 1 << 27;
|
||||||
|
const REX32 = 1 << 28;
|
||||||
|
const LM = 1 << 29;
|
||||||
|
const _3DNOWEXT = 1 << 30;
|
||||||
|
const _3DNOW = 1 << 31;
|
||||||
|
|
||||||
|
const LAHF_LM = 1 << (0 + u32::BITS);
|
||||||
|
const CMP_LEGACY = 1 << (1 + u32::BITS);
|
||||||
|
const SVM = 1 << (2 + u32::BITS);
|
||||||
|
const EXTAPIC = 1 << (3 + u32::BITS);
|
||||||
|
const CR8_LEGACY = 1 << (4 + u32::BITS);
|
||||||
|
const LZCNT = 1 << (5 + u32::BITS);
|
||||||
|
const SSE4A = 1 << (6 + u32::BITS);
|
||||||
|
const MISALIGNSSE = 1 << (7 + u32::BITS);
|
||||||
|
const _3DNOWPREFETCH = 1 << (8 + u32::BITS);
|
||||||
|
const OSVW = 1 << (9 + u32::BITS);
|
||||||
|
const IBS = 1 << (10 + u32::BITS);
|
||||||
|
const XOP = 1 << (11 + u32::BITS);
|
||||||
|
const SKINIT = 1 << (12 + u32::BITS);
|
||||||
|
const WDT = 1 << (13 + u32::BITS);
|
||||||
|
const LWP = 1 << (15 + u32::BITS);
|
||||||
|
const FMA4 = 1 << (16 + u32::BITS);
|
||||||
|
const TCE = 1 << (17 + u32::BITS);
|
||||||
|
const NODEID_MSR = 1 << (19 + u32::BITS);
|
||||||
|
const TBM = 1 << (21 + u32::BITS);
|
||||||
|
const TOPOEXT = 1 << (22 + u32::BITS);
|
||||||
|
const PERFCTR_CORE = 1 << (23 + u32::BITS);
|
||||||
|
const PERFCTR_NB = 1 << (24 + u32::BITS);
|
||||||
|
const DBX = 1 << (26 + u32::BITS);
|
||||||
|
const PERFTSC = 1 << (27 + u32::BITS);
|
||||||
|
const PCX_L2I = 1 << (28 + u32::BITS);
|
||||||
|
const MONITORX = 1 << (29 + u32::BITS);
|
||||||
|
const ADDR_MASK_EXT = 1 << (30 + u32::BITS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Leaf8000008(CpuidResult);
|
||||||
|
|
||||||
|
impl Leaf8000008 {
|
||||||
|
pub fn get() -> Self {
|
||||||
|
let result = cpuid(0x80000008, 0);
|
||||||
|
Self(result)
|
||||||
|
}
|
||||||
|
pub fn physical_address_bits(&self) -> u8 {
|
||||||
|
self.0.eax.get_bits(0..8) as u8
|
||||||
|
}
|
||||||
|
pub fn num_linear_address_bits(&self) -> u8 {
|
||||||
|
self.0.eax.get_bits(8..16) as u8
|
||||||
|
}
|
||||||
|
pub fn guest_physical_address_bits(&self) -> u8 {
|
||||||
|
self.0.eax.get_bits(16..24) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn num_physical_threads(&self) -> u8 {
|
||||||
|
self.0.ecx.get_bits(0..8) as u8 + 1
|
||||||
|
}
|
||||||
|
pub fn apic_id_size(&self) -> u8 {
|
||||||
|
self.0.ecx.get_bits(12..16) as u8
|
||||||
|
}
|
||||||
|
pub fn performance_timestamp_counter_size(&self) -> u8 {
|
||||||
|
self.0.ecx.get_bits(16..18) as u8
|
||||||
|
}
|
||||||
|
pub fn max_invlpgb_page_count(&self) -> u16 {
|
||||||
|
self.0.edx.get_bits(0..16) as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_rdpru_ecx(&self) -> u16 {
|
||||||
|
self.0.edx.get_bits(16..32) as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flags(&self) -> Leaf8000008Flags {
|
||||||
|
Leaf8000008Flags::from_bits_truncate(self.0.ebx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Leaf8000008 {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("Leaf8000008")
|
||||||
|
.field("physical_address_bits", &self.physical_address_bits())
|
||||||
|
.field("num_linear_address_bits", &self.num_linear_address_bits())
|
||||||
|
.field(
|
||||||
|
"guest_physical_address_bits",
|
||||||
|
&self.guest_physical_address_bits(),
|
||||||
|
)
|
||||||
|
.field("num_physical_threads", &self.num_physical_threads())
|
||||||
|
.field("apic_id_size", &self.apic_id_size())
|
||||||
|
.field(
|
||||||
|
"performance_timestamp_counter_size",
|
||||||
|
&self.performance_timestamp_counter_size(),
|
||||||
|
)
|
||||||
|
.field("max_invlpgb_page_count", &self.max_invlpgb_page_count())
|
||||||
|
.field("max_rdpru_ecx", &self.max_rdpru_ecx())
|
||||||
|
.field("flags", &self.flags())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Leaf8000008Flags: u32 {
|
||||||
|
const CLZERO = 1 << 0;
|
||||||
|
const RETIRED_INSTR = 1 << 1;
|
||||||
|
const XRSTOR_FP_ERR = 1 << 2;
|
||||||
|
const INVLPGB = 1 << 3;
|
||||||
|
const RDPRU = 1 << 4;
|
||||||
|
const XOTEXT = 1 << 5;
|
||||||
|
const MBE = 1 << 6;
|
||||||
|
const MCOMMIT = 1 << 8;
|
||||||
|
const WBNOINVD = 1 << 9;
|
||||||
|
const LBR_EXT_V1 = 1 << 10;
|
||||||
|
const IBPB = 1 << 12;
|
||||||
|
const WBINVD_INT = 1 << 13;
|
||||||
|
const IBRS = 1 << 14;
|
||||||
|
const STIBP = 1 << 15;
|
||||||
|
const IBRS_ALWAYS = 1 << 16;
|
||||||
|
const STIBP_ALWAYS = 1 << 17;
|
||||||
|
const IBRS_PREFERRED = 1 << 18;
|
||||||
|
const IBRS_SAME_MODE_PROT = 1 << 19;
|
||||||
|
const NO_EFER_LMSLE = 1 << 20;
|
||||||
|
const INVLPGB_NESTED = 1 << 21;
|
||||||
|
const LBR_TSX = 1 << 22;
|
||||||
|
const PPIN = 1 << 23;
|
||||||
|
const SSBD = 1 << 24;
|
||||||
|
const SSBD_LEGACY = 1 << 25;
|
||||||
|
const SSBD_NO = 1 << 26;
|
||||||
|
const CPPC = 1 << 27;
|
||||||
|
const PSFD = 1 << 28;
|
||||||
|
const BTC_NO = 1 << 29;
|
||||||
|
const IBPB_RET = 1 << 30;
|
||||||
|
const BRANCH_SAPLING = 1 << 31;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuId {
|
||||||
|
pub fn get() -> Self {
|
||||||
|
let result = cpuid(0, 0);
|
||||||
|
let vendor_id = unsafe {
|
||||||
|
core::mem::transmute::<[u32; 3], [u8; 12]>([result.ebx, result.edx, result.ecx])
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
max_eax: result.eax,
|
||||||
|
vendor_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vendor_id(&self) -> &[u8; 12] {
|
||||||
|
&self.vendor_id
|
||||||
|
}
|
||||||
|
}
|
||||||
516
kernel/src/x86_64/gdt.rs
Normal file
516
kernel/src/x86_64/gdt.rs
Normal file
|
|
@ -0,0 +1,516 @@
|
||||||
|
use core::{
|
||||||
|
arch::asm,
|
||||||
|
fmt::Debug,
|
||||||
|
mem::offset_of,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
};
|
||||||
|
|
||||||
|
use bit_field::BitField;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
serial_println,
|
||||||
|
sync::LazyLock,
|
||||||
|
x86_64::{idt::ToAddress, instructions::*},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct GlobalDescriptorTable {
|
||||||
|
pub null: RawGdtEntry,
|
||||||
|
pub kernel_code: RawGdtEntry,
|
||||||
|
pub kernel_data: RawGdtEntry,
|
||||||
|
pub tss: TssEntry,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlobalDescriptorTable {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let tss = &*TSS;
|
||||||
|
GlobalDescriptorTable {
|
||||||
|
null: RawGdtEntry::new(0, 0, 0, 0),
|
||||||
|
kernel_code: RawGdtEntry::new(0xfffff, 0, 0, 0)
|
||||||
|
.with_segment(true)
|
||||||
|
.with_privilege_level(RING0)
|
||||||
|
.with_present(true)
|
||||||
|
.with_segment_kind(
|
||||||
|
CodeDataSegmentKind::CodeSegment
|
||||||
|
| CodeDataSegmentKind::Executable
|
||||||
|
| CodeDataSegmentKind::ReadWrite
|
||||||
|
| CodeDataSegmentKind::Accessed,
|
||||||
|
)
|
||||||
|
.with_present(true)
|
||||||
|
.with_code_segment64(true)
|
||||||
|
.with_granularity(true),
|
||||||
|
kernel_data: RawGdtEntry::new(0xfffff, 0, 0, 0)
|
||||||
|
.with_segment(true)
|
||||||
|
.with_privilege_level(RING0)
|
||||||
|
.with_present(true)
|
||||||
|
.with_segment_kind(CodeDataSegmentKind::ReadWrite | CodeDataSegmentKind::Accessed)
|
||||||
|
.with_code_segment64(true)
|
||||||
|
.with_granularity(true),
|
||||||
|
tss: TssEntry::new(
|
||||||
|
core::mem::size_of::<TaskStateSegment>() as u32 - 1,
|
||||||
|
tss as *const TaskStateSegment as u64,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(&'static self) {
|
||||||
|
serial_println!("Loading GDT {:#?}", self);
|
||||||
|
unsafe { Self::load_unsafe(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// The caller must ensure that `this` is a valid pointer to a `GlobalDescriptorTable` that remains valid for the lifetime of the gdt.
|
||||||
|
pub unsafe fn load_unsafe(this: *const Self) {
|
||||||
|
let gdt_register = GdtRegister {
|
||||||
|
limit: (core::mem::size_of::<Self>() - 1) as u16,
|
||||||
|
base: this as u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"lgdt [{}]",
|
||||||
|
in(reg) &gdt_register,
|
||||||
|
options(readonly, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
|
||||||
|
let gs = msr::read_msr(msr::MSR_GS_BASE);
|
||||||
|
|
||||||
|
asm!(
|
||||||
|
"push {code_seg}",
|
||||||
|
"lea rax, [rip + 2f]",
|
||||||
|
"push rax",
|
||||||
|
"retfq",
|
||||||
|
"2:",
|
||||||
|
"mov ax, {data_seg}",
|
||||||
|
"mov ds, ax",
|
||||||
|
"mov es, ax",
|
||||||
|
"mov fs, ax",
|
||||||
|
"mov gs, ax",
|
||||||
|
"mov ss, ax",
|
||||||
|
code_seg = const offset_of!(GlobalDescriptorTable, kernel_code),
|
||||||
|
data_seg = const offset_of!(GlobalDescriptorTable, kernel_data),
|
||||||
|
lateout("rax") _,
|
||||||
|
);
|
||||||
|
|
||||||
|
msr::write_msr(msr::MSR_GS_BASE, gs);
|
||||||
|
|
||||||
|
asm!(
|
||||||
|
"mov ax, {tss_seg}",
|
||||||
|
"ltr ax",
|
||||||
|
tss_seg = const offset_of!(GlobalDescriptorTable, tss),
|
||||||
|
lateout("rax") _,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct GdtRegister {
|
||||||
|
limit: u16,
|
||||||
|
base: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GlobalDescriptorTable {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum SystemSegmentKind {
|
||||||
|
Reserved = 0b0000,
|
||||||
|
Ldt = 0b0010,
|
||||||
|
TssAvailable = 0b1001,
|
||||||
|
TssBusy = 0b1011,
|
||||||
|
CallGate = 0b1100,
|
||||||
|
InterruptGate = 0b1110,
|
||||||
|
TrapGate = 0b1111,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const RING0: u8 = 0;
|
||||||
|
pub const RING1: u8 = 1;
|
||||||
|
pub const RING2: u8 = 2;
|
||||||
|
pub const RING3: u8 = 3;
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct CodeDataSegmentKind: u8 {
|
||||||
|
const CodeSegment = 1 << 3;
|
||||||
|
const Executable = 1 << 2;
|
||||||
|
/// `ReadWrite` is set for code segments if they are readable, and for
|
||||||
|
/// data segments if they are writable.
|
||||||
|
const ReadWrite = 1 << 1;
|
||||||
|
const Accessed = 1 << 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemSegmentKind {
|
||||||
|
pub fn into_u8(self) -> u8 {
|
||||||
|
self as u8
|
||||||
|
}
|
||||||
|
pub fn from_u8_or_reserved(value: u8) -> Self {
|
||||||
|
match value {
|
||||||
|
0b0010 => SystemSegmentKind::Ldt,
|
||||||
|
0b1001 => SystemSegmentKind::TssAvailable,
|
||||||
|
0b1011 => SystemSegmentKind::TssBusy,
|
||||||
|
0b1100 => SystemSegmentKind::CallGate,
|
||||||
|
0b1110 => SystemSegmentKind::InterruptGate,
|
||||||
|
0b1111 => SystemSegmentKind::TrapGate,
|
||||||
|
_ => SystemSegmentKind::Reserved,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// the caller must ensure that the lower 4 bits of `value` are a valid `GdtKind` discriminant.
|
||||||
|
pub unsafe fn from_u8_unchecked(value: u8) -> Self {
|
||||||
|
unsafe { core::mem::transmute(value & 0b1111) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct RawGdtEntry {
|
||||||
|
pub limit_low: u16,
|
||||||
|
pub base_low: u16,
|
||||||
|
pub base_middle: u8,
|
||||||
|
pub access: u8,
|
||||||
|
pub flags: u8,
|
||||||
|
pub base_high: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug_commont_gdt_fields<'a, 'b>(
|
||||||
|
entry: &RawGdtEntry,
|
||||||
|
dbg: &'a mut core::fmt::DebugStruct<'a, 'b>,
|
||||||
|
) -> &'a mut core::fmt::DebugStruct<'a, 'b> {
|
||||||
|
dbg.field_with("limit", |f| {
|
||||||
|
if entry.granularity() {
|
||||||
|
f.write_fmt(format_args!("0x{:x} (4KiB blocks)", entry.limit()))
|
||||||
|
} else {
|
||||||
|
f.write_fmt(format_args!("0x{:x} (bytes)", entry.limit()))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.field("segment", &entry.segment());
|
||||||
|
|
||||||
|
if entry.segment() {
|
||||||
|
dbg.field("segment_kind", &entry.segment_kind());
|
||||||
|
} else {
|
||||||
|
dbg.field("system_segment_kind", &entry.system_segment_kind());
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg.field("privilege_level", &entry.privilege_level())
|
||||||
|
.field("present", &entry.present())
|
||||||
|
.field("available", &entry.available())
|
||||||
|
.field("code_segment64", &entry.code_segment64())
|
||||||
|
.field("default_operation_size", &entry.default_operation_size())
|
||||||
|
.field_with("granularity", |f| {
|
||||||
|
if entry.granularity() {
|
||||||
|
f.write_str("4KiB")
|
||||||
|
} else {
|
||||||
|
f.write_str("bytes")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dbg
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for RawGdtEntry {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
let mut dbg = f.debug_struct("RawGdtEntry");
|
||||||
|
|
||||||
|
dbg.field_with("base", |f| {
|
||||||
|
f.write_fmt(format_args!("0x{:x}", self.base32()))
|
||||||
|
});
|
||||||
|
let dbg = debug_commont_gdt_fields(self, &mut dbg);
|
||||||
|
|
||||||
|
dbg.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum DefaultOperationSize {
|
||||||
|
Bit16 = 0,
|
||||||
|
Bit32 = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DefaultOperationSize {
|
||||||
|
pub fn into_u8(self) -> u8 {
|
||||||
|
self as u8
|
||||||
|
}
|
||||||
|
pub fn from_bool(value: bool) -> Self {
|
||||||
|
if value {
|
||||||
|
DefaultOperationSize::Bit32
|
||||||
|
} else {
|
||||||
|
DefaultOperationSize::Bit16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<RawGdtEntry> for TssEntry {
|
||||||
|
fn as_ref(&self) -> &RawGdtEntry {
|
||||||
|
&self.gdt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawGdtEntry {
|
||||||
|
pub fn new(limit: u32, base: u32, access: u8, flags: u8) -> Self {
|
||||||
|
let limit_low = limit as u16;
|
||||||
|
let limit_high = (limit >> 16) as u8;
|
||||||
|
let base_low = base as u16;
|
||||||
|
let base_middle = (base >> 16) as u8;
|
||||||
|
let base_high = (base >> 24) as u8;
|
||||||
|
|
||||||
|
RawGdtEntry {
|
||||||
|
limit_low,
|
||||||
|
base_low,
|
||||||
|
base_middle,
|
||||||
|
access,
|
||||||
|
flags: (flags & 0xf0) | limit_high,
|
||||||
|
base_high,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
RawGdtEntry {
|
||||||
|
limit_low: 0,
|
||||||
|
base_low: 0,
|
||||||
|
base_middle: 0,
|
||||||
|
access: 0,
|
||||||
|
flags: 0,
|
||||||
|
base_high: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_segment_kind(mut self, kind: CodeDataSegmentKind) -> Self {
|
||||||
|
self.set_segment_kind(kind);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_system_segment_kind(mut self, kind: SystemSegmentKind) -> Self {
|
||||||
|
self.set_system_segment_kind(kind);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_segment(mut self, segment: bool) -> Self {
|
||||||
|
self.set_segment(segment);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_privilege_level(mut self, level: u8) -> Self {
|
||||||
|
self.set_privilege_level(level);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_present(mut self, present: bool) -> Self {
|
||||||
|
self.set_present(present);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_segment_kind(&self) -> SystemSegmentKind {
|
||||||
|
SystemSegmentKind::from_u8_or_reserved(self.access.get_bits(0..4))
|
||||||
|
}
|
||||||
|
pub fn set_system_segment_kind(&mut self, kind: SystemSegmentKind) {
|
||||||
|
self.access.set_bits(0..4, kind.into_u8());
|
||||||
|
}
|
||||||
|
/// `segment_kind` is valid if `segment` is set, otherwise
|
||||||
|
/// `system_segment_kind` is valid. `segment_kind` is the type of segment,
|
||||||
|
/// and is only valid for code and data segments.
|
||||||
|
pub fn segment_kind(&self) -> CodeDataSegmentKind {
|
||||||
|
CodeDataSegmentKind::from_bits_truncate(self.access.get_bits(0..4))
|
||||||
|
}
|
||||||
|
pub fn set_segment_kind(&mut self, kind: CodeDataSegmentKind) {
|
||||||
|
self.access.set_bits(0..4, kind.bits());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `segment` is set if this is a code or data segment, and clear if it is a system segment.
|
||||||
|
pub fn segment(&self) -> bool {
|
||||||
|
self.access.get_bit(4)
|
||||||
|
}
|
||||||
|
pub fn set_segment(&mut self, system_segment: bool) {
|
||||||
|
self.access.set_bit(4, system_segment);
|
||||||
|
}
|
||||||
|
pub fn privilege_level(&self) -> u8 {
|
||||||
|
self.access.get_bits(5..7)
|
||||||
|
}
|
||||||
|
pub fn set_privilege_level(&mut self, level: u8) {
|
||||||
|
self.access.set_bits(5..7, level);
|
||||||
|
}
|
||||||
|
pub fn present(&self) -> bool {
|
||||||
|
self.access.get_bit(7)
|
||||||
|
}
|
||||||
|
pub fn set_present(&mut self, present: bool) {
|
||||||
|
self.access.set_bit(7, present);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_available(mut self, available: bool) -> Self {
|
||||||
|
self.set_available(available);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_code_segment64(mut self, code_segment64: bool) -> Self {
|
||||||
|
self.set_code_segment64(code_segment64);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_default_operation_size(mut self, size: DefaultOperationSize) -> Self {
|
||||||
|
self.set_default_operation_size(size);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_granularity(mut self, granularity: bool) -> Self {
|
||||||
|
self.set_granularity(granularity);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_limit_high(mut self, limit_high: u8) -> Self {
|
||||||
|
self.set_limit_high(limit_high);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn limit_high(&self) -> u8 {
|
||||||
|
self.flags.get_bits(0..4)
|
||||||
|
}
|
||||||
|
fn set_limit_high(&mut self, limit_high: u8) {
|
||||||
|
self.flags.set_bits(0..4, limit_high);
|
||||||
|
}
|
||||||
|
pub fn available(&self) -> bool {
|
||||||
|
self.flags.get_bit(4)
|
||||||
|
}
|
||||||
|
pub fn set_available(&mut self, available: bool) {
|
||||||
|
self.flags.set_bit(4, available);
|
||||||
|
}
|
||||||
|
pub fn code_segment64(&self) -> bool {
|
||||||
|
self.flags.get_bit(5)
|
||||||
|
}
|
||||||
|
pub fn set_code_segment64(&mut self, code_segment64: bool) {
|
||||||
|
self.flags.set_bit(5, code_segment64);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `default_operation_size` is the default size of operands for this segment. If `default_operation_size` is set, the default size is 32 bits, otherwise it is 16 bits.
|
||||||
|
/// On 64-bit mode, this should be 1.
|
||||||
|
pub fn default_operation_size(&self) -> DefaultOperationSize {
|
||||||
|
DefaultOperationSize::from_bool(self.flags.get_bit(6))
|
||||||
|
}
|
||||||
|
pub fn set_default_operation_size(&mut self, size: DefaultOperationSize) {
|
||||||
|
self.flags.set_bit(6, size.into_u8() != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `granularity` is the unit of the limit field. If `granularity` is set,
|
||||||
|
/// the limit is in 4KiB blocks, otherwise it is in bytes.
|
||||||
|
pub fn granularity(&self) -> bool {
|
||||||
|
self.flags.get_bit(7)
|
||||||
|
}
|
||||||
|
pub fn set_granularity(&mut self, granularity: bool) {
|
||||||
|
self.flags.set_bit(7, granularity);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn limit(&self) -> u32 {
|
||||||
|
let limit_low = self.limit_low as u32;
|
||||||
|
let limit_high = (self.limit_high() as u32) << 16;
|
||||||
|
limit_low | limit_high
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base32(&self) -> u32 {
|
||||||
|
let base_low = self.base_low as u32;
|
||||||
|
let base_middle = (self.base_middle as u32) << 16;
|
||||||
|
let base_high = (self.base_high as u32) << 24;
|
||||||
|
base_low | base_middle | base_high
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct TssEntry {
|
||||||
|
gdt: RawGdtEntry,
|
||||||
|
pub base_ext: u32,
|
||||||
|
reserved: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for TssEntry {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
let mut dbg = f.debug_struct("TssEntry");
|
||||||
|
|
||||||
|
dbg.field_with("base", |f| {
|
||||||
|
f.write_fmt(format_args!("0x{:x}", self.base64()))
|
||||||
|
});
|
||||||
|
let dbg = debug_commont_gdt_fields(&self.gdt, &mut dbg);
|
||||||
|
|
||||||
|
dbg.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TssEntry {
|
||||||
|
pub fn base64(&self) -> u64 {
|
||||||
|
let base_low = self.gdt.base32() as u64;
|
||||||
|
let base_ext = self.base_ext as u64;
|
||||||
|
(base_ext << 32) | base_low
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(limit: u32, base: u64) -> Self {
|
||||||
|
let base_low = base as u32;
|
||||||
|
let base_ext = (base >> 32) as u32;
|
||||||
|
|
||||||
|
TssEntry {
|
||||||
|
gdt: RawGdtEntry::new(limit, base_low, 0, 0)
|
||||||
|
.with_present(true)
|
||||||
|
.with_privilege_level(RING0)
|
||||||
|
.with_system_segment_kind(SystemSegmentKind::TssAvailable),
|
||||||
|
base_ext,
|
||||||
|
reserved: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for TssEntry {
|
||||||
|
type Target = RawGdtEntry;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.gdt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for TssEntry {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.gdt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed(4))]
|
||||||
|
pub struct TaskStateSegment {
|
||||||
|
_reserved1: [u8; 4],
|
||||||
|
pub privilege_stack_table: [u64; 3],
|
||||||
|
_reserved2: [u8; 8],
|
||||||
|
pub interrupt_stack_table: [u64; 7],
|
||||||
|
_reserved3: [u8; 10],
|
||||||
|
pub iomap_base: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TaskStateSegment {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
privilege_stack_table: [0; 3],
|
||||||
|
interrupt_stack_table: [0; 7],
|
||||||
|
iomap_base: size_of::<TaskStateSegment>() as u16,
|
||||||
|
_reserved1: [0; 4],
|
||||||
|
_reserved2: [0; 8],
|
||||||
|
_reserved3: [0; 10],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const impl Default for TaskStateSegment {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DF_STACK: u8 = 0;
|
||||||
|
pub const NMI_STACK: u8 = 1;
|
||||||
|
pub const MC_STACK: u8 = 2;
|
||||||
|
|
||||||
|
pub static TSS: LazyLock<TaskStateSegment> = LazyLock::new(|| {
|
||||||
|
let mut tss = TaskStateSegment::new();
|
||||||
|
const STACK_SIZE: usize = super::PAGE_SIZE * 5;
|
||||||
|
static mut STACKS: [[u8; STACK_SIZE]; 3] = [[0; STACK_SIZE]; 3];
|
||||||
|
|
||||||
|
let stack_top = |n: usize| unsafe { STACKS[n].as_ptr().add(STACK_SIZE).to_address() };
|
||||||
|
|
||||||
|
tss.interrupt_stack_table[DF_STACK as usize] = stack_top(DF_STACK as usize);
|
||||||
|
tss.interrupt_stack_table[NMI_STACK as usize] = stack_top(NMI_STACK as usize);
|
||||||
|
tss.interrupt_stack_table[MC_STACK as usize] = stack_top(MC_STACK as usize);
|
||||||
|
|
||||||
|
tss
|
||||||
|
});
|
||||||
546
kernel/src/x86_64/idt.rs
Normal file
546
kernel/src/x86_64/idt.rs
Normal file
|
|
@ -0,0 +1,546 @@
|
||||||
|
use core::{arch::naked_asm, fmt::Debug, marker::PhantomData, ops::Deref, ptr::NonNull};
|
||||||
|
|
||||||
|
use bit_field::BitField;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
serial_println,
|
||||||
|
x86_64::{RFlags, Registers, halt_loop},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Entry {
|
||||||
|
pub offset_low: u16,
|
||||||
|
pub selector: u16,
|
||||||
|
pub options: EntryOptions,
|
||||||
|
pub offset_middle: u16,
|
||||||
|
pub offset_high: u32,
|
||||||
|
pub reserved: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Debug for Entry {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("Entry")
|
||||||
|
.field("handler", &self.handler_address())
|
||||||
|
.field("selector", &self.selector)
|
||||||
|
.field("options", &self.options)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ExceptionVector;
|
||||||
|
impl ExceptionVector {
|
||||||
|
pub const DIVIDE_BY_ZERO: u8 = 0;
|
||||||
|
pub const DEBUG: u8 = 1;
|
||||||
|
pub const NON_MASKABLE_INTERRUPT: u8 = 2;
|
||||||
|
pub const BREAKPOINT: u8 = 3;
|
||||||
|
pub const OVERFLOW: u8 = 4;
|
||||||
|
pub const BOUND_RANGE_EXCEEDED: u8 = 5;
|
||||||
|
pub const INVALID_OPCODE: u8 = 6;
|
||||||
|
pub const DEVICE_NOT_AVAILABLE: u8 = 7;
|
||||||
|
pub const DOUBLE_FAULT: u8 = 8;
|
||||||
|
pub const COPROCESSOR_SEGMENT_OVERRUN: u8 = 9;
|
||||||
|
pub const INVALID_TSS: u8 = 10;
|
||||||
|
pub const SEGMENT_NOT_PRESENT: u8 = 11;
|
||||||
|
pub const STACK_SEGMENT_FAULT: u8 = 12;
|
||||||
|
pub const GENERAL_PROTECTION_FAULT: u8 = 13;
|
||||||
|
pub const PAGE_FAULT: u8 = 14;
|
||||||
|
|
||||||
|
pub const X87_FLOATING_POINT_EXCEPTION: u8 = 16;
|
||||||
|
pub const ALIGNMENT_CHECK: u8 = 17;
|
||||||
|
pub const MACHINE_CHECK: u8 = 18;
|
||||||
|
pub const SIMD_FLOATING_POINT_EXCEPTION: u8 = 19;
|
||||||
|
pub const VIRTUALIZATION_EXCEPTION: u8 = 20;
|
||||||
|
pub const CONTROL_PROTECTION_EXCEPTION: u8 = 21;
|
||||||
|
|
||||||
|
pub const HYPERVISOR_EXCEPTION: u8 = 28;
|
||||||
|
pub const VMM_COMMUNICATION_EXCEPTION: u8 = 29;
|
||||||
|
pub const SECURITY_EXCEPTION: u8 = 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entry {
|
||||||
|
pub unsafe fn new<T: ToAddress>(handler: T, selector: u16, options: EntryOptions) -> Self {
|
||||||
|
let handler_addr = handler.to_address();
|
||||||
|
Self {
|
||||||
|
offset_low: handler_addr as u16,
|
||||||
|
selector,
|
||||||
|
options,
|
||||||
|
offset_middle: (handler_addr >> 16) as u16,
|
||||||
|
offset_high: (handler_addr >> 32) as u32,
|
||||||
|
reserved: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn new_default_interrupt<const IDX: u8>() -> Self {
|
||||||
|
let cs = unsafe { super::instructions::get_cs() };
|
||||||
|
let mut options = EntryOptions::empty_interrupt_gate();
|
||||||
|
options.set_present(true);
|
||||||
|
let handler = match IDX {
|
||||||
|
8 | 10 | 11 | 12 | 13 | 14 | 17 | 21 | 29 | 30 => {
|
||||||
|
interrupt_trampoline_with_err::<IDX> as *const () as u64
|
||||||
|
}
|
||||||
|
_ => interrupt_trampoline_no_err::<IDX> as *const () as u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
match IDX {
|
||||||
|
ExceptionVector::DOUBLE_FAULT => {
|
||||||
|
options.set_interrupt_stack_table_index(super::gdt::DF_STACK);
|
||||||
|
}
|
||||||
|
ExceptionVector::NON_MASKABLE_INTERRUPT => {
|
||||||
|
options.set_interrupt_stack_table_index(super::gdt::NMI_STACK);
|
||||||
|
}
|
||||||
|
ExceptionVector::MACHINE_CHECK => {
|
||||||
|
options.set_interrupt_stack_table_index(super::gdt::MC_STACK);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { Self::new(handler, cs, options) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn missing_interrupt() -> Self {
|
||||||
|
Self {
|
||||||
|
offset_low: 0,
|
||||||
|
selector: 0,
|
||||||
|
options: EntryOptions::empty_interrupt_gate(),
|
||||||
|
offset_middle: 0,
|
||||||
|
offset_high: 0,
|
||||||
|
reserved: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handler_address(&self) -> u64 {
|
||||||
|
let low = self.offset_low as u64;
|
||||||
|
let middle = self.offset_middle as u64;
|
||||||
|
let high = self.offset_high as u64;
|
||||||
|
(high << 32) | (middle << 16) | low
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_handler<T>(&mut self, handler: T)
|
||||||
|
where
|
||||||
|
T: ToAddress,
|
||||||
|
{
|
||||||
|
let handler_addr = handler.to_address();
|
||||||
|
self.offset_low = handler_addr as u16;
|
||||||
|
self.offset_middle = (handler_addr >> 16) as u16;
|
||||||
|
self.offset_high = (handler_addr >> 32) as u32;
|
||||||
|
|
||||||
|
self.options = EntryOptions::empty_interrupt_gate();
|
||||||
|
unsafe {
|
||||||
|
self.selector = super::instructions::get_cs();
|
||||||
|
}
|
||||||
|
self.options.set_present(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ToAddress {
|
||||||
|
fn to_address(&self) -> u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAddress for u64 {
|
||||||
|
fn to_address(&self) -> u64 {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ToAddress for *const T {
|
||||||
|
fn to_address(&self) -> u64 {
|
||||||
|
*self as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ToAddress for *mut T {
|
||||||
|
fn to_address(&self) -> u64 {
|
||||||
|
*self as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ToAddress for NonNull<T> {
|
||||||
|
fn to_address(&self) -> u64 {
|
||||||
|
self.as_ptr() as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ToAddress for &T {
|
||||||
|
fn to_address(&self) -> u64 {
|
||||||
|
*self as *const T as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ToAddress for &mut T {
|
||||||
|
fn to_address(&self) -> u64 {
|
||||||
|
*self as *const T as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct EntryOptions(u16);
|
||||||
|
|
||||||
|
impl Debug for EntryOptions {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("EntryOptions")
|
||||||
|
.field("present", &self.present())
|
||||||
|
.field("privilege_level", &self.privilege_level())
|
||||||
|
.field(
|
||||||
|
"interrupt_stack_table_index",
|
||||||
|
&self.interrupt_stack_table_index(),
|
||||||
|
)
|
||||||
|
.field("kind", &self.kind())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum EntryType {
|
||||||
|
#[default]
|
||||||
|
InterruptGate = 0b1110,
|
||||||
|
TrapGate = 0b1111,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EntryType {
|
||||||
|
pub fn into_u8(self) -> u8 {
|
||||||
|
self as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// The caller must ensure that `value` is either `0b1110` or `0b1111`, or
|
||||||
|
/// the result of calling `into_u8` on an `EntryType`.
|
||||||
|
pub unsafe fn from_u8_unchecked(value: u8) -> Self {
|
||||||
|
unsafe { core::mem::transmute(value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EntryOptions {
|
||||||
|
pub const fn empty_interrupt_gate() -> Self {
|
||||||
|
Self(0b1110_0000_0000)
|
||||||
|
}
|
||||||
|
pub const fn empty_trap_gate() -> Self {
|
||||||
|
Self(0b1111_0000_0000)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_present(mut self, present: bool) -> Self {
|
||||||
|
self.set_present(present);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_privilege_level(mut self, level: u8) -> Self {
|
||||||
|
self.set_privilege_level(level);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_interrupt_stack_table_index(mut self, index: u8) -> Self {
|
||||||
|
self.set_interrupt_stack_table_index(index);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_kind(mut self, kind: EntryType) -> Self {
|
||||||
|
self.set_kind(kind);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn present(&self) -> bool {
|
||||||
|
self.0.get_bit(15)
|
||||||
|
}
|
||||||
|
pub fn set_present(&mut self, present: bool) {
|
||||||
|
self.0.set_bit(15, present);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn privilege_level(&self) -> u8 {
|
||||||
|
self.0.get_bits(13..15) as u8
|
||||||
|
}
|
||||||
|
pub fn set_privilege_level(&mut self, level: u8) {
|
||||||
|
self.0.set_bits(13..15, level as u16);
|
||||||
|
}
|
||||||
|
pub fn interrupt_stack_table_index(&self) -> Option<u8> {
|
||||||
|
let index = self.0.get_bits(0..3) as u8;
|
||||||
|
if index == 0 { None } else { Some(index - 1) }
|
||||||
|
}
|
||||||
|
pub fn set_interrupt_stack_table_index(&mut self, index: u8) {
|
||||||
|
self.0.set_bits(0..3, index as u16 + 1);
|
||||||
|
}
|
||||||
|
pub fn kind(&self) -> EntryType {
|
||||||
|
unsafe { EntryType::from_u8_unchecked(self.0.get_bits(8..12) as u8) }
|
||||||
|
}
|
||||||
|
pub fn set_kind(&mut self, kind: EntryType) {
|
||||||
|
self.0.set_bits(8..12, kind.into_u8() as u16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct InterruptStackFrameInner {
|
||||||
|
pub instruction_pointer: u64,
|
||||||
|
pub code_segment: u16,
|
||||||
|
_reserved: [u16; 3],
|
||||||
|
pub flags: RFlags,
|
||||||
|
pub stack_pointer: u64,
|
||||||
|
pub stack_segment: u16,
|
||||||
|
_reserved2: [u16; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct InterruptStackFrame(InterruptStackFrameInner);
|
||||||
|
|
||||||
|
impl Debug for InterruptStackFrame {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("InterruptStackFrame")
|
||||||
|
.field(
|
||||||
|
"instruction_pointer",
|
||||||
|
&format_args!("{:#x}", self.instruction_pointer),
|
||||||
|
)
|
||||||
|
.field("code_segment", &format_args!("{:#x}", self.code_segment))
|
||||||
|
.field("flags", &self.flags)
|
||||||
|
.field("stack_pointer", &format_args!("{:#x}", self.stack_pointer))
|
||||||
|
.field("stack_segment", &format_args!("{:#x}", self.stack_segment))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for InterruptStackFrame {
|
||||||
|
type Target = InterruptStackFrameInner;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct PageFaultErrorCode: u64 {
|
||||||
|
const PRESENT = 1 << 0;
|
||||||
|
const WRITE = 1 << 1;
|
||||||
|
const USER = 1 << 2;
|
||||||
|
const RESERVED = 1 << 3;
|
||||||
|
const INSTRUCTION_FETCH = 1 << 4;
|
||||||
|
const PROTECTION_KEY = 1 << 5;
|
||||||
|
const SGX = 1 << 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[repr(C, packed(2))]
|
||||||
|
pub struct IdtRegister<'idt> {
|
||||||
|
pub limit: u16,
|
||||||
|
pub base: u64,
|
||||||
|
_pd: PhantomData<&'idt ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct InterruptDescriptorTable {
|
||||||
|
pub divide_by_zero: Entry,
|
||||||
|
pub debug: Entry,
|
||||||
|
pub non_maskable_interrupt: Entry,
|
||||||
|
pub breakpoint: Entry,
|
||||||
|
pub overflow: Entry,
|
||||||
|
pub bound_range_exceeded: Entry,
|
||||||
|
pub invalid_opcode: Entry,
|
||||||
|
pub device_not_available: Entry,
|
||||||
|
pub double_fault: Entry,
|
||||||
|
pub coprocessor_segment_overrun: Entry,
|
||||||
|
pub invalid_tss: Entry,
|
||||||
|
pub segment_not_present: Entry,
|
||||||
|
pub stack_segment_fault: Entry,
|
||||||
|
pub general_protection_fault: Entry,
|
||||||
|
pub page_fault: Entry,
|
||||||
|
_reserved1: Entry,
|
||||||
|
pub x87_floating_point: Entry,
|
||||||
|
pub alignment_check: Entry,
|
||||||
|
pub machine_check: Entry,
|
||||||
|
pub simd_floating_point: Entry,
|
||||||
|
pub virtualization: Entry,
|
||||||
|
pub cp_protection: Entry,
|
||||||
|
_reserved2: [Entry; 6],
|
||||||
|
pub hypervisor: Entry,
|
||||||
|
pub vmm_communication_exception: Entry,
|
||||||
|
pub security_exception: Entry,
|
||||||
|
_reserved3: Entry,
|
||||||
|
pub interrupts: [Entry; 256 - 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
const impl Default for InterruptDescriptorTable {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptDescriptorTable {
|
||||||
|
pub const fn new_empty() -> Self {
|
||||||
|
Self {
|
||||||
|
divide_by_zero: Entry::missing_interrupt(),
|
||||||
|
debug: Entry::missing_interrupt(),
|
||||||
|
non_maskable_interrupt: Entry::missing_interrupt(),
|
||||||
|
breakpoint: Entry::missing_interrupt(),
|
||||||
|
overflow: Entry::missing_interrupt(),
|
||||||
|
bound_range_exceeded: Entry::missing_interrupt(),
|
||||||
|
invalid_opcode: Entry::missing_interrupt(),
|
||||||
|
device_not_available: Entry::missing_interrupt(),
|
||||||
|
double_fault: Entry::missing_interrupt(),
|
||||||
|
coprocessor_segment_overrun: Entry::missing_interrupt(),
|
||||||
|
invalid_tss: Entry::missing_interrupt(),
|
||||||
|
segment_not_present: Entry::missing_interrupt(),
|
||||||
|
stack_segment_fault: Entry::missing_interrupt(),
|
||||||
|
general_protection_fault: Entry::missing_interrupt(),
|
||||||
|
page_fault: Entry::missing_interrupt(),
|
||||||
|
_reserved1: Entry::missing_interrupt(),
|
||||||
|
x87_floating_point: Entry::missing_interrupt(),
|
||||||
|
alignment_check: Entry::missing_interrupt(),
|
||||||
|
machine_check: Entry::missing_interrupt(),
|
||||||
|
simd_floating_point: Entry::missing_interrupt(),
|
||||||
|
virtualization: Entry::missing_interrupt(),
|
||||||
|
cp_protection: Entry::missing_interrupt(),
|
||||||
|
_reserved2: [Entry::missing_interrupt(); 6],
|
||||||
|
hypervisor: Entry::missing_interrupt(),
|
||||||
|
vmm_communication_exception: Entry::missing_interrupt(),
|
||||||
|
security_exception: Entry::missing_interrupt(),
|
||||||
|
_reserved3: Entry::missing_interrupt(),
|
||||||
|
interrupts: [Entry::missing_interrupt(); 256 - 32],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_default() -> Self {
|
||||||
|
let mut idt = Self::new_empty();
|
||||||
|
let slice = unsafe { idt.as_mut_type_erased() };
|
||||||
|
seq_macro::seq!(
|
||||||
|
N in 0..=255 {
|
||||||
|
slice[N] = unsafe { Entry::new_default_interrupt::<N>() };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
idt
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn register(&self) -> IdtRegister<'_> {
|
||||||
|
IdtRegister {
|
||||||
|
limit: (core::mem::size_of::<Self>() - 1) as u16,
|
||||||
|
base: self as *const _ as u64,
|
||||||
|
_pd: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(&'static self) {
|
||||||
|
unsafe { Self::load_unsafe(self) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn load_unsafe(&self) {
|
||||||
|
unsafe {
|
||||||
|
super::instructions::lidt(&self.register());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_type_erased(&self) -> &[Entry; 256] {
|
||||||
|
unsafe { core::mem::transmute::<&Self, &[Entry; 256]>(self) }
|
||||||
|
}
|
||||||
|
pub unsafe fn as_mut_type_erased(&mut self) -> &mut [Entry; 256] {
|
||||||
|
unsafe { core::mem::transmute::<&mut Self, &mut [Entry; 256]>(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(naked)]
|
||||||
|
extern "C" fn interrupt_trampoline_with_err<const IDX: u8>() {
|
||||||
|
naked_asm!(
|
||||||
|
"push {idx}",
|
||||||
|
"jmp {dispatcher}",
|
||||||
|
idx = const { IDX },
|
||||||
|
dispatcher = sym interrupt_dispatcher,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(naked)]
|
||||||
|
extern "C" fn interrupt_trampoline_no_err<const IDX: u8>() {
|
||||||
|
naked_asm!(
|
||||||
|
"push 0", // push a dummy error code of 0
|
||||||
|
"push {idx}",
|
||||||
|
"jmp {dispatcher}",
|
||||||
|
idx = const { IDX },
|
||||||
|
dispatcher = sym interrupt_dispatcher,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(naked)]
|
||||||
|
extern "C" fn interrupt_dispatcher() {
|
||||||
|
naked_asm!(
|
||||||
|
"push rax",
|
||||||
|
"push rcx",
|
||||||
|
"push rdx",
|
||||||
|
"push rbx",
|
||||||
|
"push rbp",
|
||||||
|
"push rsi",
|
||||||
|
"push rdi",
|
||||||
|
"push r8",
|
||||||
|
"push r9",
|
||||||
|
"push r10",
|
||||||
|
"push r11",
|
||||||
|
"push r12",
|
||||||
|
"push r13",
|
||||||
|
"push r14",
|
||||||
|
"push r15",
|
||||||
|
"lea rdi, [rsp + {registers_size} + 16]", // interrupt frame
|
||||||
|
"mov rdx, [rsp + {registers_size} + 8]", // error code
|
||||||
|
"mov rsi, [rsp + {registers_size}]", // interrupt index
|
||||||
|
"mov rcx, rsp", // pass the current stack pointer as the registers pointer
|
||||||
|
"call {global_handler}",
|
||||||
|
"pop r15",
|
||||||
|
"pop r14",
|
||||||
|
"pop r13",
|
||||||
|
"pop r12",
|
||||||
|
"pop r11",
|
||||||
|
"pop r10",
|
||||||
|
"pop r9",
|
||||||
|
"pop r8",
|
||||||
|
"pop rdi",
|
||||||
|
"pop rsi",
|
||||||
|
"pop rbp",
|
||||||
|
"pop rbx",
|
||||||
|
"pop rdx",
|
||||||
|
"pop rcx",
|
||||||
|
"pop rax",
|
||||||
|
"add rsp, 16", // pop the interrupt index and error code
|
||||||
|
"iretq",
|
||||||
|
registers_size = const { core::mem::size_of::<Registers>() },
|
||||||
|
global_handler = sym global_interrupt_handler,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn global_interrupt_handler(
|
||||||
|
frame: &InterruptStackFrame,
|
||||||
|
index: u8,
|
||||||
|
error_code: u64,
|
||||||
|
registers: &Registers,
|
||||||
|
) {
|
||||||
|
serial_println!(
|
||||||
|
"Interrupt {} occurred! Error code: {:#x}, Frame: {:#?}, Registers: {:#?}",
|
||||||
|
index,
|
||||||
|
error_code,
|
||||||
|
frame,
|
||||||
|
registers
|
||||||
|
);
|
||||||
|
|
||||||
|
match index {
|
||||||
|
ExceptionVector::BREAKPOINT => {
|
||||||
|
serial_println!("Breakpoint interrupt handled successfully.");
|
||||||
|
}
|
||||||
|
ExceptionVector::PAGE_FAULT => {
|
||||||
|
serial_println!("Page fault occurred! Error code: {:#x}", error_code);
|
||||||
|
halt_loop()
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::super::instructions;
|
||||||
|
use super::*;
|
||||||
|
#[test_case]
|
||||||
|
fn test_interrupt_handler_trait() {
|
||||||
|
use crate::sync::LazyLock;
|
||||||
|
static IDT: LazyLock<InterruptDescriptorTable> =
|
||||||
|
crate::sync::LazyLock::new(InterruptDescriptorTable::new_default);
|
||||||
|
|
||||||
|
unsafe { IDT.load_unsafe() };
|
||||||
|
|
||||||
|
instructions::int3(); // Trigger a breakpoint interrupt (interrupt 3)
|
||||||
|
|
||||||
|
serial_println!("Breakpoint interrupt handled successfully.");
|
||||||
|
}
|
||||||
|
}
|
||||||
188
kernel/src/x86_64/instructions.rs
Normal file
188
kernel/src/x86_64/instructions.rs
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
#[inline]
|
||||||
|
pub fn hlt() {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!("hlt", options(nomem, nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn write_u8(port: u16, value: u8) {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"out dx, al",
|
||||||
|
in("dx") port,
|
||||||
|
in("al") value,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn write_u16(port: u16, value: u16) {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"out dx, ax",
|
||||||
|
in("dx") port,
|
||||||
|
in("ax") value,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn write_u32(port: u16, value: u32) {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"out dx, eax",
|
||||||
|
in("dx") port,
|
||||||
|
in("eax") value,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn read_u8(port: u16) -> u8 {
|
||||||
|
let value: u8;
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"in al, dx",
|
||||||
|
in("dx") port,
|
||||||
|
out("al") value,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn read_u16(port: u16) -> u16 {
|
||||||
|
let value: u16;
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"in ax, dx",
|
||||||
|
in("dx") port,
|
||||||
|
out("ax") value,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn read_u32(port: u16) -> u32 {
|
||||||
|
let value: u32;
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"in eax, dx",
|
||||||
|
in("dx") port,
|
||||||
|
out("eax") value,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn lidt(idt: &super::idt::IdtRegister) {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"lidt [{}]",
|
||||||
|
in(reg) idt,
|
||||||
|
options(readonly, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn int3() {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!("int3", options(nomem, nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! read_segment {
|
||||||
|
($segment:literal) => {
|
||||||
|
{
|
||||||
|
let value: u16;
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
concat!("mov {0:x}, ", $segment),
|
||||||
|
out(reg) value,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn get_cs() -> u16 {
|
||||||
|
read_segment!("cs")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod msr {
|
||||||
|
use core::arch::asm;
|
||||||
|
|
||||||
|
pub const MSR_STAR: u32 = 0xC000_0081;
|
||||||
|
pub const MSR_LSTAR: u32 = 0xC000_0082;
|
||||||
|
pub const MSR_CSTAR: u32 = 0xC000_0083;
|
||||||
|
pub const MSR_SFMASK: u32 = 0xC000_0084;
|
||||||
|
pub const MSR_FS_BASE: u32 = 0xC000_0100;
|
||||||
|
pub const MSR_GS_BASE: u32 = 0xC000_0101;
|
||||||
|
pub const MSR_KERNEL_GS_BASE: u32 = 0xC000_0102;
|
||||||
|
pub const MSR_EFER: u32 = 0xC000_0080;
|
||||||
|
|
||||||
|
/// Reads the value of the specified Model-Specific Register (MSR).
|
||||||
|
/// # Safety
|
||||||
|
/// This operation is inherently unsafe.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn read_msr(msr: u32) -> u64 {
|
||||||
|
unsafe {
|
||||||
|
let eax: u32;
|
||||||
|
let edx: u32;
|
||||||
|
asm!(
|
||||||
|
"rdmsr",
|
||||||
|
in("ecx") msr,
|
||||||
|
out("eax") eax,
|
||||||
|
out("edx") edx,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
((edx as u64) << 32) | (eax as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the value to the specified Model-Specific Register (MSR).
|
||||||
|
/// # Safety
|
||||||
|
/// This operation is inherently unsafe.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn write_msr(msr: u32, value: u64) {
|
||||||
|
unsafe {
|
||||||
|
let eax = value as u32;
|
||||||
|
let edx = (value >> 32) as u32;
|
||||||
|
asm!(
|
||||||
|
"wrmsr",
|
||||||
|
in("ecx") msr,
|
||||||
|
in("eax") eax,
|
||||||
|
in("edx") edx,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn rdtsc() -> u64 {
|
||||||
|
let low: u32;
|
||||||
|
let high: u32;
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"rdtsc",
|
||||||
|
out("eax") low,
|
||||||
|
out("edx") high,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
((high as u64) << 32) | (low as u64)
|
||||||
|
}
|
||||||
241
kernel/src/x86_64/paging.rs
Normal file
241
kernel/src/x86_64/paging.rs
Normal file
|
|
@ -0,0 +1,241 @@
|
||||||
|
use core::{
|
||||||
|
borrow::Borrow,
|
||||||
|
fmt::Debug,
|
||||||
|
hint::unlikely,
|
||||||
|
ops::{Deref, Index},
|
||||||
|
};
|
||||||
|
|
||||||
|
use bit_field::BitField;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
memory::{PhyAddr, VirtAddr, VirtAddrTranslationExt},
|
||||||
|
serial_println,
|
||||||
|
x86_64::{VirtAddrExt, registers::Cr4},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[repr(C, align(4096))]
|
||||||
|
pub struct PageTable {
|
||||||
|
entries: [PageTableEntry; 512],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageTable {
|
||||||
|
pub fn get(&self, index: u16) -> Option<PageTableEntry> {
|
||||||
|
if index < 512 {
|
||||||
|
let entry = self.entries[index as usize];
|
||||||
|
|
||||||
|
if entry.present() { Some(entry) } else { None }
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_unchecked(&self, index: u16) -> PageTableEntry {
|
||||||
|
assert!(index < 512, "Page table index out of bounds");
|
||||||
|
self.entries[index as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<u16> for PageTable {
|
||||||
|
type Output = PageTableEntry;
|
||||||
|
|
||||||
|
fn index(&self, index: u16) -> &Self::Output {
|
||||||
|
assert!(index < 512, "Page table index out of bounds");
|
||||||
|
&self.entries[index as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct PageTableEntry(PageTableEntryFlags);
|
||||||
|
|
||||||
|
impl Debug for PageTableEntry {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("PageTableEntry")
|
||||||
|
.field(
|
||||||
|
"flags",
|
||||||
|
&PageTableEntryFlags::from_bits_truncate(self.as_raw()),
|
||||||
|
)
|
||||||
|
.field("phy", &self.phy())
|
||||||
|
.field("pk", &self.pk())
|
||||||
|
.field("pat_index", &self.pat_index())
|
||||||
|
.field("free_bits", &format_args!("{:#b}", self.free_bits()))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<PageTableEntryFlags> for PageTableEntry {
|
||||||
|
fn as_ref(&self) -> &PageTableEntryFlags {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Borrow<PageTableEntryFlags> for PageTableEntry {
|
||||||
|
fn borrow(&self) -> &PageTableEntryFlags {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for PageTableEntry {
|
||||||
|
type Target = PageTableEntryFlags;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct PageTableEntryFlags: u64 {
|
||||||
|
const PRESENT = 1 << 0;
|
||||||
|
const WRITABLE = 1 << 1;
|
||||||
|
const USER_ACCESSIBLE = 1 << 2;
|
||||||
|
const WRITE_THROUGH = 1 << 3; // PWT
|
||||||
|
const NO_CACHE = 1 << 4; // PCD
|
||||||
|
const ACCESSED = 1 << 5;
|
||||||
|
const DIRTY = 1 << 6;
|
||||||
|
const HUGE_PAGE = 1 << 7;
|
||||||
|
const GLOBAL = 1 << 8;
|
||||||
|
const NO_EXECUTE = 1 << 63;
|
||||||
|
const PAT_4K = 1 << 7;
|
||||||
|
const PAT_HUGE = 1 << 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageTableEntry {
|
||||||
|
pub fn from_raw(bits: u64) -> Self {
|
||||||
|
Self(PageTableEntryFlags::from_bits_retain(bits))
|
||||||
|
}
|
||||||
|
pub fn as_raw(&self) -> u64 {
|
||||||
|
self.0.bits()
|
||||||
|
}
|
||||||
|
pub fn as_mut_raw(&mut self) -> &mut u64 {
|
||||||
|
self.0.0.bits_mut()
|
||||||
|
}
|
||||||
|
pub fn present(&self) -> bool {
|
||||||
|
self.contains(PageTableEntryFlags::PRESENT)
|
||||||
|
}
|
||||||
|
pub fn phy(&self) -> PhyAddr {
|
||||||
|
PhyAddr(self.as_raw().get_bits(12..52) << 12)
|
||||||
|
}
|
||||||
|
pub fn try_as_page_table(&self) -> Option<&PageTable> {
|
||||||
|
if !self.contains(PageTableEntryFlags::PRESENT) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if self.contains(PageTableEntryFlags::HUGE_PAGE) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
Some(
|
||||||
|
self.phy()
|
||||||
|
.into_hhdm_virt()
|
||||||
|
.as_ptr::<PageTable>()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_unchecked(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub unsafe fn as_page_table(&self) -> &PageTable {
|
||||||
|
// entry must be present
|
||||||
|
assert!(self.contains(PageTableEntryFlags::PRESENT));
|
||||||
|
// if the entry is a huge page, it does not point to a deeper page table.
|
||||||
|
assert!(!self.contains(PageTableEntryFlags::HUGE_PAGE));
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.phy()
|
||||||
|
.into_hhdm_virt()
|
||||||
|
.as_ptr::<PageTable>()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_unchecked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn pk(&self) -> u8 {
|
||||||
|
self.as_raw().get_bits(59..63) as u8
|
||||||
|
}
|
||||||
|
pub fn pat_index(&self) -> u8 {
|
||||||
|
let pat = if self.contains(PageTableEntryFlags::HUGE_PAGE) {
|
||||||
|
self.as_raw().get_bit(12) as u8
|
||||||
|
} else {
|
||||||
|
self.as_raw().get_bit(7) as u8
|
||||||
|
};
|
||||||
|
|
||||||
|
self.as_raw().get_bits(3..=4) as u8 | (pat << 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the free bits in the page table entry, which are bits 9-11 and
|
||||||
|
/// 52-58, combined into a single 10-bit value.
|
||||||
|
pub fn free_bits(&self) -> u16 {
|
||||||
|
let low = self.bits().get_bits(9..12) as u16;
|
||||||
|
let high = self.bits().get_bits(52..59) as u16;
|
||||||
|
low | (high << 3)
|
||||||
|
}
|
||||||
|
pub fn set_free_bits(&mut self, value: u16) {
|
||||||
|
let low = (value & 0b111) as u64;
|
||||||
|
let high = (value >> 3) as u64;
|
||||||
|
self.as_mut_raw().set_bits(9..12, low);
|
||||||
|
self.as_mut_raw().set_bits(52..59, high);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtAddrTranslationExt for VirtAddr {
|
||||||
|
fn into_phy_addr(self) -> Option<PhyAddr> {
|
||||||
|
get_physical_addr(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_physical_addr(virt: VirtAddr) -> Option<PhyAddr> {
|
||||||
|
let cr3 = crate::x86_64::registers::Cr3::read();
|
||||||
|
let cr4 = crate::x86_64::registers::Cr4::read();
|
||||||
|
serial_println!("cr3: {:?}", cr3);
|
||||||
|
serial_println!("cr4: {:?}", cr4);
|
||||||
|
|
||||||
|
let frame = cr3.phy();
|
||||||
|
let page_table = unsafe {
|
||||||
|
frame
|
||||||
|
.into_hhdm_virt()
|
||||||
|
.as_ptr::<PageTable>()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_unchecked()
|
||||||
|
};
|
||||||
|
|
||||||
|
let pml4_entry = if unlikely(cr4.contains(Cr4::LA57)) {
|
||||||
|
let l5_entry = page_table[virt.page_table_index::<{ VirtAddr::PML5 }>()];
|
||||||
|
|
||||||
|
l5_entry
|
||||||
|
.try_as_page_table()?
|
||||||
|
.get_unchecked(virt.page_table_index::<{ VirtAddr::PML4 }>())
|
||||||
|
} else {
|
||||||
|
page_table[virt.page_table_index::<{ VirtAddr::PML4 }>()]
|
||||||
|
};
|
||||||
|
|
||||||
|
serial_println!("pml4_entry: {:?}", pml4_entry);
|
||||||
|
|
||||||
|
let pdpt_entry = pml4_entry.try_as_page_table()?[virt.page_table_index::<{ VirtAddr::PDPT }>()];
|
||||||
|
serial_println!("pdpt_entry: {:?}", pdpt_entry);
|
||||||
|
|
||||||
|
if pdpt_entry.contains(PageTableEntryFlags::HUGE_PAGE) {
|
||||||
|
const PHY_MASK_1G: u64 = !((1 << 30) - 1);
|
||||||
|
let phys_addr = (pdpt_entry.phy().0 & PHY_MASK_1G) + virt.offset_1g() as u64;
|
||||||
|
return Some(PhyAddr(phys_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
let pd_entry = pdpt_entry.try_as_page_table()?[virt.page_table_index::<{ VirtAddr::PD }>()];
|
||||||
|
serial_println!("pd_entry: {:?}", pd_entry);
|
||||||
|
|
||||||
|
if pd_entry.contains(PageTableEntryFlags::HUGE_PAGE) {
|
||||||
|
const PHY_MASK_2M: u64 = !((1 << 21) - 1);
|
||||||
|
let phys_addr = (pd_entry.phy().0 & PHY_MASK_2M) + virt.offset_2m() as u64;
|
||||||
|
return Some(PhyAddr(phys_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
let pt_entry = pd_entry.try_as_page_table()?[virt.page_table_index::<{ VirtAddr::PT }>()];
|
||||||
|
serial_println!("pt_entry: {:?}", pt_entry);
|
||||||
|
|
||||||
|
if !pt_entry.contains(PageTableEntryFlags::PRESENT) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let phy_addr = pt_entry.phy().into_hhdm_virt().0 + virt.offset_4k() as u64;
|
||||||
|
|
||||||
|
Some(PhyAddr(phy_addr))
|
||||||
|
}
|
||||||
227
kernel/src/x86_64/registers.rs
Normal file
227
kernel/src/x86_64/registers.rs
Normal file
|
|
@ -0,0 +1,227 @@
|
||||||
|
use core::{arch::asm, fmt::Debug, ops::Index};
|
||||||
|
|
||||||
|
use bit_field::BitField;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
use crate::memory::PhyAddr;
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Cr0Flags: u64 {
|
||||||
|
const PROTECTED_MODE_ENABLE = 1 << 0;
|
||||||
|
const MONITOR_COPROCESSOR = 1 << 1;
|
||||||
|
const EMULATE_COPROCESSOR = 1 << 2;
|
||||||
|
const TASK_SWITCHED = 1 << 3;
|
||||||
|
const EXTENSION_TYPE = 1 << 4;
|
||||||
|
const NUMERIC_ERROR = 1 << 5;
|
||||||
|
const WRITE_PROTECT = 1 << 16;
|
||||||
|
const ALIGNMENT_MASK = 1 << 18;
|
||||||
|
const NOT_WRITE_THROUGH = 1 << 29;
|
||||||
|
const CACHE_DISABLE = 1 << 30;
|
||||||
|
const PAGING = 1 << 31;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cr0Flags {
|
||||||
|
pub fn read() -> Self {
|
||||||
|
let value: u64;
|
||||||
|
unsafe {
|
||||||
|
asm!("mov {}, cr0", out(reg) value, options(nomem, nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
Self::from_bits_truncate(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn write(&self) {
|
||||||
|
unsafe {
|
||||||
|
asm!("mov cr0, {}", in(reg) self.bits(), options(nomem, nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Cr4: u64 {
|
||||||
|
const VIRTUAL_8086_MODE_EXTENSIONS = 1 << 0;
|
||||||
|
const PROTECTED_MODE_VIRTUAL_INTERRUPTS = 1 << 1;
|
||||||
|
const TIME_STAMP_DISABLE = 1 << 2;
|
||||||
|
const DEBUGGING_EXTENSIONS = 1 << 3;
|
||||||
|
const PAGE_SIZE_EXTENSIONS = 1 << 4;
|
||||||
|
const PHYSICAL_ADDRESS_EXTENSION = 1 << 5;
|
||||||
|
const MACHINE_CHECK = 1 << 6;
|
||||||
|
const PAGE_GLOBAL = 1 << 7;
|
||||||
|
const PERFORMANCE_MONITOR_COUNTER = 1 << 8;
|
||||||
|
const OSFXSR_SUPPORT = 1 << 9;
|
||||||
|
const OSXMMEXCPT_SUPPORT = 1 << 10;
|
||||||
|
const USER_MODE_INSTRUCTION_PREVENTION = 1 << 11;
|
||||||
|
const LA57 = 1 << 12;
|
||||||
|
const VMX = 1 << 13;
|
||||||
|
const SMX = 1 << 14;
|
||||||
|
const FSGSBASE = 1 << 16;
|
||||||
|
const PCID = 1 << 17;
|
||||||
|
const OSXSAVE = 1 << 18;
|
||||||
|
const SMEP = 1 << 20;
|
||||||
|
const SMAP = 1 << 21;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cr4 {
|
||||||
|
pub fn read() -> Self {
|
||||||
|
let value: u64;
|
||||||
|
unsafe {
|
||||||
|
asm!("mov {}, cr4", out(reg) value, options(nomem, nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
Self::from_bits_truncate(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn write(&self) {
|
||||||
|
unsafe {
|
||||||
|
asm!("mov cr4, {}", in(reg) self.bits(), options(nomem, nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct IA32EferFlags: u64 {
|
||||||
|
const SYSTEM_CALL_EXTENSIONS = 1 << 0;
|
||||||
|
const LONG_MODE_ENABLE = 1 << 8;
|
||||||
|
const LONG_MODE_ACTIVE = 1 << 10;
|
||||||
|
const NO_EXECUTE_ENABLE = 1 << 11;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IA32EferFlags {
|
||||||
|
pub fn read() -> Self {
|
||||||
|
use super::instructions::msr::{MSR_EFER, read_msr};
|
||||||
|
let bits = unsafe { read_msr(MSR_EFER) };
|
||||||
|
|
||||||
|
Self::from_bits_truncate(bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn write(&self) {
|
||||||
|
use super::instructions::msr::{MSR_EFER, write_msr};
|
||||||
|
unsafe { write_msr(MSR_EFER, self.bits()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IA32Pat(u64);
|
||||||
|
|
||||||
|
const impl Default for IA32Pat {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::from_entries([
|
||||||
|
IA32PatEntry::WRITE_BACK,
|
||||||
|
IA32PatEntry::WRITE_THROUGH,
|
||||||
|
IA32PatEntry::UNCACHEABLE,
|
||||||
|
IA32PatEntry::UNCACHED,
|
||||||
|
IA32PatEntry::WRITE_BACK,
|
||||||
|
IA32PatEntry::WRITE_THROUGH,
|
||||||
|
IA32PatEntry::UNCACHEABLE,
|
||||||
|
IA32PatEntry::UNCACHED,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IA32Pat {
|
||||||
|
pub const fn from_entries(entries: [IA32PatEntry; 8]) -> Self {
|
||||||
|
let [
|
||||||
|
IA32PatEntry(e0),
|
||||||
|
IA32PatEntry(e1),
|
||||||
|
IA32PatEntry(e2),
|
||||||
|
IA32PatEntry(e3),
|
||||||
|
IA32PatEntry(e4),
|
||||||
|
IA32PatEntry(e5),
|
||||||
|
IA32PatEntry(e6),
|
||||||
|
IA32PatEntry(e7),
|
||||||
|
] = entries;
|
||||||
|
|
||||||
|
let val = e0 as u64
|
||||||
|
| ((e1 as u64) << 8)
|
||||||
|
| ((e2 as u64) << 16)
|
||||||
|
| ((e3 as u64) << 24)
|
||||||
|
| ((e4 as u64) << 32)
|
||||||
|
| ((e5 as u64) << 40)
|
||||||
|
| ((e6 as u64) << 48)
|
||||||
|
| ((e7 as u64) << 56);
|
||||||
|
|
||||||
|
Self(val)
|
||||||
|
}
|
||||||
|
pub fn get(&self, index: u8) -> IA32PatEntry {
|
||||||
|
assert!(index < 8, "Index out of bounds for IA32Pat");
|
||||||
|
let entry = (self.0 >> (index * 8)) & 0xF;
|
||||||
|
|
||||||
|
IA32PatEntry(entry as u8)
|
||||||
|
}
|
||||||
|
pub fn set(&mut self, index: u8, entry: IA32PatEntry) {
|
||||||
|
assert!(index < 8, "Index out of bounds for IA32Pat");
|
||||||
|
let mask = !(0xF << (index * 8));
|
||||||
|
self.0 = (self.0 & mask) | ((entry.0 as u64) << (index * 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IA32PatEntry(u8);
|
||||||
|
|
||||||
|
impl IA32PatEntry {
|
||||||
|
pub const UNCACHEABLE: Self = Self(0);
|
||||||
|
pub const WRITE_COMBINING: Self = Self(1);
|
||||||
|
pub const WRITE_THROUGH: Self = Self(4);
|
||||||
|
pub const WRITE_PROTECTED: Self = Self(5);
|
||||||
|
pub const WRITE_BACK: Self = Self(6);
|
||||||
|
pub const UNCACHED: Self = Self(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cr3(u64);
|
||||||
|
|
||||||
|
impl Debug for Cr3 {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("Cr3")
|
||||||
|
.field("phy", &self.phy())
|
||||||
|
.field("flags", &self.flags())
|
||||||
|
.field("free_bits", &format_args!("{:#x}", self.free_bits()))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Cr3Flags: u64 {
|
||||||
|
const PAGE_LEVEL_WRITE_THROUGH = 1 << 3;
|
||||||
|
const PAGE_LEVEL_CACHE_DISABLE = 1 << 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Cr3 {
|
||||||
|
pub fn read() -> Self {
|
||||||
|
let value: u64;
|
||||||
|
unsafe {
|
||||||
|
asm!("mov {}, cr3", out(reg) value, options(nomem, nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
pub unsafe fn write(&self) {
|
||||||
|
unsafe {
|
||||||
|
asm!("mov cr3, {}", in(reg) self.0, options(nomem, nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flags(&self) -> Cr3Flags {
|
||||||
|
Cr3Flags::from_bits_truncate(self.0)
|
||||||
|
}
|
||||||
|
pub fn set_flags(&mut self, flags: Cr3Flags) {
|
||||||
|
const MASK: u64 = Cr3Flags::all().bits();
|
||||||
|
self.0 = (self.0 & !MASK) | flags.bits();
|
||||||
|
}
|
||||||
|
pub fn phy(&self) -> PhyAddr {
|
||||||
|
PhyAddr(self.0.get_bits(12..52) << 12)
|
||||||
|
}
|
||||||
|
pub fn free_bits(&self) -> u16 {
|
||||||
|
let low = (self.0 & 0x7) as u16;
|
||||||
|
let high = self.0.get_bits(5..12) as u16;
|
||||||
|
low | (high << 3)
|
||||||
|
}
|
||||||
|
pub fn set_free_bits(&mut self, value: u16) {
|
||||||
|
let low = (value & 0x7) as u64;
|
||||||
|
let high = (value >> 3) as u64;
|
||||||
|
self.0.set_bits(0..3, low);
|
||||||
|
self.0.set_bits(5..12, high);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
kernel/tests/simple.rs
Normal file
13
kernel/tests/simple.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[unsafe(export_name = "_start")]
|
||||||
|
pub extern "C" fn main() -> ! {
|
||||||
|
kernel::serial_println!("Hello, world!");
|
||||||
|
kernel::testing::exit_qemu(kernel::testing::QemuExitCode::Success)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic_thunk(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
kernel::testing::test_panic_handler(info)
|
||||||
|
}
|
||||||
67
kernel/tests/stack_overflow.rs
Normal file
67
kernel/tests/stack_overflow.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
#![feature(abi_x86_interrupt)]
|
||||||
|
|
||||||
|
use core::{cell::UnsafeCell, mem::offset_of};
|
||||||
|
|
||||||
|
use kernel::{
|
||||||
|
sync::LazyLock,
|
||||||
|
x86_64::{
|
||||||
|
gdt::{DF_STACK, GlobalDescriptorTable, RING0},
|
||||||
|
idt::{self, Entry, InterruptDescriptorTable},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static GDT: LazyLock<GlobalDescriptorTable> = LazyLock::new(GlobalDescriptorTable::new);
|
||||||
|
|
||||||
|
#[unsafe(export_name = "_start")]
|
||||||
|
pub extern "C" fn main() -> ! {
|
||||||
|
kernel::serial_println!("Hello, world!");
|
||||||
|
|
||||||
|
GDT.load();
|
||||||
|
kernel::serial_println!("[ok] GDT loaded");
|
||||||
|
|
||||||
|
extern "x86-interrupt" fn double_fault_handler(
|
||||||
|
_stack_frame: &mut idt::InterruptStackFrame,
|
||||||
|
_error_code: u64,
|
||||||
|
) -> ! {
|
||||||
|
kernel::serial_println!("[ok] Double fault handler called");
|
||||||
|
kernel::testing::exit_qemu(kernel::testing::QemuExitCode::Success)
|
||||||
|
}
|
||||||
|
|
||||||
|
static IDT: LazyLock<InterruptDescriptorTable> = LazyLock::new(|| {
|
||||||
|
let mut idt = InterruptDescriptorTable::new_default();
|
||||||
|
idt.double_fault = unsafe {
|
||||||
|
Entry::new(
|
||||||
|
double_fault_handler as *const (),
|
||||||
|
offset_of!(GlobalDescriptorTable, kernel_code) as u16,
|
||||||
|
idt::EntryOptions::empty_interrupt_gate()
|
||||||
|
.with_present(true)
|
||||||
|
.with_privilege_level(RING0)
|
||||||
|
.with_interrupt_stack_table_index(DF_STACK),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
idt
|
||||||
|
});
|
||||||
|
|
||||||
|
IDT.load();
|
||||||
|
|
||||||
|
stack_overflow();
|
||||||
|
|
||||||
|
panic!("Triggering a stack overflow to test double fault handling");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unconditional_recursion)]
|
||||||
|
fn stack_overflow() {
|
||||||
|
stack_overflow(); // for each recursion, the return address is pushed
|
||||||
|
unsafe {
|
||||||
|
let cell = UnsafeCell::new(0);
|
||||||
|
cell.get().write_volatile(0);
|
||||||
|
}; // prevent tail recursion optimizations
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic_thunk(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
kernel::testing::test_panic_handler(info)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue