Skip to content

Commit 92f6a82

Browse files
committed
Merge branch 'master' into next
2 parents 3847259 + 8922d8d commit 92f6a82

File tree

7 files changed

+156
-38
lines changed

7 files changed

+156
-38
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ license = "MIT/Apache-2.0"
2222
name = "x86_64"
2323
readme = "README.md"
2424
repository = "https://github.com/rust-osdev/x86_64"
25-
version = "0.14.8"
25+
version = "0.14.9"
2626
edition = "2018"
2727
rust-version = "1.59" # Needed to support inline asm and default const generics
2828

Changelog.md

+32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
# Unreleased
22

3+
# 0.14.9 - 2022-03-31
4+
5+
## New Features
6+
7+
- Address in `VirtAddrNotValid` and `PhysAddrNotValid` is now public ([#340](https://github.com/rust-osdev/x86_64/pull/340)).
8+
- This field now contains the whole invalid address ([#347](https://github.com/rust-osdev/x86_64/pull/347)).
9+
- Remove all uses of external assembly ([#343](https://github.com/rust-osdev/x86_64/pull/343))
10+
- `external_asm` and `inline_asm` features are deprecated and now have no effect.
11+
- `instructions` feature (on by default) now requires Rust 1.59
12+
- Specific MSRV now noted in `README` ([#355](https://github.com/rust-osdev/x86_64/pull/355))
13+
- Implement `core::iter::Step` for `VirtAddr` and `Page` ([#342](https://github.com/rust-osdev/x86_64/pull/342))
14+
- This trait is only available on nightly.
15+
- Gated behind `step_trait` feature flag
16+
- Add `UCet` and `SCet` registers ([#349](https://github.com/rust-osdev/x86_64/pull/349))
17+
- Use [`rustversion`](https://crates.io/crates/rustversion) to mark certian functions `const fn` on Rust 1.61 ([#353](https://github.com/rust-osdev/x86_64/pull/353))
18+
- `Entry::handler_addr()` is now public ([#354](https://github.com/rust-osdev/x86_64/pull/354))
19+
- Increase packed structure alignment ([#362](https://github.com/rust-osdev/x86_64/pull/362))
20+
- Make more address methods `const fn` ([#369](https://github.com/rust-osdev/x86_64/pull/369))
21+
- `VirtAddr::as_ptr()`
22+
- `VirtAddr::as_mut_ptr()`
23+
- `PhysAddr::new()`
24+
- `PhysAddr::try_new()`
25+
26+
## Bug fixes and Documentation
27+
28+
- Fixed overflow bug in PageRangeInclusive ([#351](https://github.com/rust-osdev/x86_64/pull/351))
29+
- Remove stabilized `const_fn_fn_ptr_basics` and `const_fn_trait_bound` features ([#352](https://github.com/rust-osdev/x86_64/pull/352))
30+
- Don't set `nomem` in `load_tss` ([#358](https://github.com/rust-osdev/x86_64/pull/358))
31+
- Correctly initialize TSS's IOPB to be empty ([#357](https://github.com/rust-osdev/x86_64/pull/357))
32+
- Improve `GlobalDescriptorTable::add_entry` error handling ([#361](https://github.com/rust-osdev/x86_64/pull/361))
33+
- Update `tss_segment` documentation ([#366](https://github.com/rust-osdev/x86_64/pull/366))
34+
335
# 0.14.8 – 2022-02-03
436

537
- Add `Cr2::read_raw` ([#334](https://github.com/rust-osdev/x86_64/pull/334))

src/addr.rs

+14-13
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,14 @@ impl VirtAddr {
138138
/// Converts the address to a raw pointer.
139139
#[cfg(target_pointer_width = "64")]
140140
#[inline]
141-
pub fn as_ptr<T>(self) -> *const T {
141+
pub const fn as_ptr<T>(self) -> *const T {
142142
self.as_u64() as *const T
143143
}
144144

145145
/// Converts the address to a mutable raw pointer.
146146
#[cfg(target_pointer_width = "64")]
147147
#[inline]
148-
pub fn as_mut_ptr<T>(self) -> *mut T {
148+
pub const fn as_mut_ptr<T>(self) -> *mut T {
149149
self.as_ptr::<T>() as *mut T
150150
}
151151

@@ -386,13 +386,12 @@ impl PhysAddr {
386386
///
387387
/// This function panics if a bit in the range 52 to 64 is set.
388388
#[inline]
389-
pub fn new(addr: u64) -> PhysAddr {
390-
assert_eq!(
391-
addr.get_bits(52..64),
392-
0,
393-
"physical addresses must not have any bits in the range 52 to 64 set"
394-
);
395-
PhysAddr(addr)
389+
pub const fn new(addr: u64) -> Self {
390+
// TODO: Replace with .ok().expect(msg) when that works on stable.
391+
match Self::try_new(addr) {
392+
Ok(p) => p,
393+
Err(_) => panic!("physical addresses must not have any bits in the range 52 to 64 set"),
394+
}
396395
}
397396

398397
/// Creates a new physical address, throwing bits 52..64 away.
@@ -415,10 +414,12 @@ impl PhysAddr {
415414
///
416415
/// Fails if any bits in the range 52 to 64 are set.
417416
#[inline]
418-
pub fn try_new(addr: u64) -> Result<PhysAddr, PhysAddrNotValid> {
419-
match addr.get_bits(52..64) {
420-
0 => Ok(PhysAddr(addr)), // address is valid
421-
_ => Err(PhysAddrNotValid(addr)),
417+
pub const fn try_new(addr: u64) -> Result<Self, PhysAddrNotValid> {
418+
let p = Self::new_truncate(addr);
419+
if p.0 == addr {
420+
Ok(p)
421+
} else {
422+
Err(PhysAddrNotValid(addr))
422423
}
423424
}
424425

src/instructions/tables.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,20 @@ pub fn sidt() -> DescriptorTablePointer {
7070

7171
/// Load the task state register using the `ltr` instruction.
7272
///
73-
/// Loading the task state register changes the type of the entry
74-
/// in the GDT from `Available 64-bit TSS` to `Busy 64-bit TSS`.
73+
/// Note that loading a TSS segment selector marks the corresponding TSS
74+
/// Descriptor in the GDT as "busy", preventing it from being loaded again
75+
/// (either on this CPU or another CPU). TSS structures (including Descriptors
76+
/// and Selectors) should generally be per-CPU. See
77+
/// [`tss_segment`](crate::structures::gdt::Descriptor::tss_segment)
78+
/// for more information.
79+
///
80+
/// Calling `load_tss` with a busy TSS selector results in a `#GP` exception.
7581
///
7682
/// ## Safety
7783
///
7884
/// This function is unsafe because the caller must ensure that the given
79-
/// `SegmentSelector` points to a valid TSS entry in the GDT and that loading
80-
/// this TSS is safe.
85+
/// `SegmentSelector` points to a valid TSS entry in the GDT and that the
86+
/// corresponding data in the TSS is valid.
8187
#[inline]
8288
pub unsafe fn load_tss(sel: SegmentSelector) {
8389
unsafe {

src/structures/gdt.rs

+79-18
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use crate::registers::segmentation::{Segment, CS, SS};
5050
#[derive(Debug, Clone)]
5151
pub struct GlobalDescriptorTable<const MAX: usize = 8> {
5252
table: [u64; MAX],
53-
next_free: usize,
53+
len: usize,
5454
}
5555

5656
impl GlobalDescriptorTable {
@@ -69,7 +69,7 @@ impl<const MAX: usize> GlobalDescriptorTable<MAX> {
6969
assert!(MAX <= (1 << 13), "A GDT can only have at most 2^13 entries");
7070
Self {
7171
table: [0; MAX],
72-
next_free: 1,
72+
len: 1,
7373
}
7474
}
7575

@@ -81,40 +81,48 @@ impl<const MAX: usize> GlobalDescriptorTable<MAX> {
8181
/// * Panics if the provided slice has more than `MAX` entries
8282
#[inline]
8383
pub const unsafe fn from_raw_slice(slice: &[u64]) -> Self {
84-
let next_free = slice.len();
84+
let len = slice.len();
8585
let mut table = [0; MAX];
8686
let mut idx = 0;
8787

8888
assert!(
89-
next_free <= MAX,
89+
len <= MAX,
9090
"cannot initialize GDT with slice exceeding the maximum length"
9191
);
9292

93-
while idx != next_free {
93+
while idx < len {
9494
table[idx] = slice[idx];
9595
idx += 1;
9696
}
9797

98-
Self { table, next_free }
98+
Self { table, len }
9999
}
100100

101101
/// Get a reference to the internal table.
102102
///
103103
/// The resulting slice may contain system descriptors, which span two `u64`s.
104104
#[inline]
105105
pub fn as_raw_slice(&self) -> &[u64] {
106-
&self.table[..self.next_free]
106+
&self.table[..self.len]
107107
}
108108

109109
/// Adds the given segment descriptor to the GDT, returning the segment selector.
110110
///
111-
/// Panics if the GDT has no free entries left.
111+
/// Panics if the GDT doesn't have enough free entries to hold the Descriptor.
112112
#[inline]
113113
#[cfg_attr(feature = "const_fn", rustversion::attr(all(), const))]
114114
pub fn add_entry(&mut self, entry: Descriptor) -> SegmentSelector {
115115
let index = match entry {
116-
Descriptor::UserSegment(value) => self.push(value),
116+
Descriptor::UserSegment(value) => {
117+
if self.len > self.table.len().saturating_sub(1) {
118+
panic!("GDT full")
119+
}
120+
self.push(value)
121+
}
117122
Descriptor::SystemSegment(value_low, value_high) => {
123+
if self.len > self.table.len().saturating_sub(2) {
124+
panic!("GDT requires two free spaces to hold a SystemSegment")
125+
}
118126
let index = self.push(value_low);
119127
self.push(value_high);
120128
index
@@ -170,14 +178,10 @@ impl<const MAX: usize> GlobalDescriptorTable<MAX> {
170178
#[inline]
171179
#[cfg_attr(feature = "const_fn", rustversion::attr(all(), const))]
172180
fn push(&mut self, value: u64) -> usize {
173-
if self.next_free < self.table.len() {
174-
let index = self.next_free;
175-
self.table[index] = value;
176-
self.next_free += 1;
177-
index
178-
} else {
179-
panic!("GDT full");
180-
}
181+
let index = self.len;
182+
self.table[index] = value;
183+
self.len += 1;
184+
index
181185
}
182186

183187
/// Creates the descriptor pointer for this table. This pointer can only be
@@ -189,7 +193,7 @@ impl<const MAX: usize> GlobalDescriptorTable<MAX> {
189193
base: crate::VirtAddr::new(self.table.as_ptr() as u64),
190194
// 0 < self.next_free <= MAX <= 2^13, so the limit calculation
191195
// will not underflow or overflow.
192-
limit: (self.next_free * size_of::<u64>() - 1) as u16,
196+
limit: (self.len * size_of::<u64>() - 1) as u16,
193197
}
194198
}
195199
}
@@ -323,6 +327,13 @@ impl Descriptor {
323327
}
324328

325329
/// Creates a TSS system descriptor for the given TSS.
330+
///
331+
/// While it is possible to create multiple Descriptors that point to the
332+
/// same TSS, this generally isn't recommended, as the TSS usually contains
333+
/// per-CPU information such as the RSP and IST pointers. Instead, there
334+
/// should be exactly one TSS and one corresponding TSS Descriptor per CPU.
335+
/// Then, each of these descriptors should be placed in a GDT (which can
336+
/// either be global or per-CPU).
326337
#[inline]
327338
pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor {
328339
use self::DescriptorFlags as Flags;
@@ -349,6 +360,7 @@ impl Descriptor {
349360
#[cfg(test)]
350361
mod tests {
351362
use super::DescriptorFlags as Flags;
363+
use super::*;
352364

353365
#[test]
354366
#[rustfmt::skip]
@@ -362,4 +374,53 @@ mod tests {
362374
assert_eq!(Flags::USER_CODE32.bits(), 0x00cffb000000ffff);
363375
assert_eq!(Flags::USER_DATA.bits(), 0x00cff3000000ffff);
364376
}
377+
378+
// Makes a GDT that has two free slots
379+
fn make_six_entry_gdt() -> GlobalDescriptorTable {
380+
let mut gdt = GlobalDescriptorTable::new();
381+
gdt.add_entry(Descriptor::kernel_code_segment());
382+
gdt.add_entry(Descriptor::kernel_data_segment());
383+
gdt.add_entry(Descriptor::UserSegment(DescriptorFlags::USER_CODE32.bits()));
384+
gdt.add_entry(Descriptor::user_data_segment());
385+
gdt.add_entry(Descriptor::user_code_segment());
386+
assert_eq!(gdt.len, 6);
387+
gdt
388+
}
389+
390+
static TSS: TaskStateSegment = TaskStateSegment::new();
391+
392+
fn make_full_gdt() -> GlobalDescriptorTable {
393+
let mut gdt = make_six_entry_gdt();
394+
gdt.add_entry(Descriptor::tss_segment(&TSS));
395+
assert_eq!(gdt.len, 8);
396+
gdt
397+
}
398+
399+
#[test]
400+
pub fn push_max_segments() {
401+
// Make sure we don't panic with user segments
402+
let mut gdt = make_six_entry_gdt();
403+
gdt.add_entry(Descriptor::user_data_segment());
404+
assert_eq!(gdt.len, 7);
405+
gdt.add_entry(Descriptor::user_data_segment());
406+
assert_eq!(gdt.len, 8);
407+
// Make sure we don't panic with system segments
408+
let _ = make_full_gdt();
409+
}
410+
411+
#[test]
412+
#[should_panic]
413+
pub fn panic_user_segment() {
414+
let mut gdt = make_full_gdt();
415+
gdt.add_entry(Descriptor::user_data_segment());
416+
}
417+
418+
#[test]
419+
#[should_panic]
420+
pub fn panic_system_segment() {
421+
let mut gdt = make_six_entry_gdt();
422+
gdt.add_entry(Descriptor::user_data_segment());
423+
// We have one free slot, but the GDT requires two
424+
gdt.add_entry(Descriptor::tss_segment(&TSS));
425+
}
365426
}

src/structures/mod.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,28 @@ pub mod tss;
1313
/// A struct describing a pointer to a descriptor table (GDT / IDT).
1414
/// This is in a format suitable for giving to 'lgdt' or 'lidt'.
1515
#[derive(Debug, Clone, Copy)]
16-
#[repr(C, packed)]
16+
#[repr(C, packed(2))]
1717
pub struct DescriptorTablePointer {
1818
/// Size of the DT.
1919
pub limit: u16,
2020
/// Pointer to the memory region containing the DT.
2121
pub base: VirtAddr,
2222
}
23+
24+
#[cfg(test)]
25+
mod tests {
26+
use super::*;
27+
use std::mem::size_of;
28+
29+
#[test]
30+
pub fn check_descriptor_pointer_size() {
31+
// Per the SDM, a descriptor pointer has to be 2+8=10 bytes
32+
assert_eq!(size_of::<DescriptorTablePointer>(), 10);
33+
// Make sure that we can reference a pointer's limit
34+
let p = DescriptorTablePointer {
35+
limit: 5,
36+
base: VirtAddr::zero(),
37+
};
38+
let _: &u16 = &p.limit;
39+
}
40+
}

src/structures/tss.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use core::mem::size_of;
88
/// but is used for finding kernel level stack
99
/// if interrupts arrive while in kernel mode.
1010
#[derive(Debug, Clone, Copy)]
11-
#[repr(C, packed)]
11+
#[repr(C, packed(4))]
1212
pub struct TaskStateSegment {
1313
reserved_1: u32,
1414
/// The full 64-bit canonical forms of the stack pointers (RSP) for privilege levels 0-2.

0 commit comments

Comments
 (0)