Skip to content

Commit ac73d8e

Browse files
committed
feat(vmem): fold space_aware_map dispatch
Fixes: #1409 Signed-off-by: Tomasz Andrzejak <andreiltd@gmail.com>
1 parent 5805bb2 commit ac73d8e

5 files changed

Lines changed: 109 additions & 73 deletions

File tree

src/hyperlight_common/src/arch/aarch64/vmem.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,18 @@ pub unsafe fn walk_va_spaces<Op: TableReadOps>(
6060
::alloc::vec::Vec::new()
6161
}
6262

63-
/// Stub — see [`crate::vmem::space_aware_map`].
63+
/// aarch64 never emits `AnotherSpace` entries, so [`SpaceReferenceMapping`]
64+
/// is [`crate::vmem::Void`] making the `AnotherSpace` match arm statically unreachable.
65+
pub type SpaceReferenceMapping = crate::vmem::Void;
66+
67+
/// Statically unreachable: amd64 never emits `AnotherSpace`.
6468
#[allow(clippy::missing_safety_doc)]
65-
pub unsafe fn space_aware_map<Op: TableOps>(
69+
pub(super) unsafe fn link_shared_table<Op: TableOps>(
6670
_op: &Op,
67-
_ref_map: crate::vmem::SpaceReferenceMapping,
71+
impossible: crate::vmem::Void,
6872
_built_roots: &::alloc::collections::BTreeMap<crate::vmem::SpaceId, Op::TableAddr>,
6973
) {
74+
match impossible {}
7075
}
7176

7277
pub trait TableMovability<Op: TableReadOps + ?Sized, TableMoveInfo> {}

src/hyperlight_common/src/arch/amd64/vmem.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -412,15 +412,18 @@ pub unsafe fn walk_va_spaces<Op: TableReadOps>(
412412
out
413413
}
414414

415-
/// See [`walk_va_spaces`]: amd64 never emits `AnotherSpace`, so this
416-
/// is unreachable in practice. It silently no-ops (rather than
417-
/// panicking) to keep the architecture-independent re-export usable.
415+
/// aarch64 never emits `AnotherSpace` entries, so [`SpaceReferenceMapping`]
416+
/// is [`crate::vmem::Void`] making the `AnotherSpace` match arm statically unreachable.
417+
pub type SpaceReferenceMapping = crate::vmem::Void;
418+
419+
/// Statically unreachable: amd64 never emits `AnotherSpace`.
418420
#[allow(clippy::missing_safety_doc)]
419-
pub unsafe fn space_aware_map<Op: TableOps>(
421+
pub(super) unsafe fn link_shared_table<Op: TableOps>(
420422
_op: &Op,
421-
_ref_map: crate::vmem::SpaceReferenceMapping,
423+
impossible: crate::vmem::Void,
422424
_built_roots: &::alloc::collections::BTreeMap<crate::vmem::SpaceId, Op::TableAddr>,
423425
) {
426+
match impossible {}
424427
}
425428

426429
#[allow(clippy::missing_safety_doc)]

src/hyperlight_common/src/arch/i686/vmem.rs

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ limitations under the License.
2323
2424
use crate::vmem::{
2525
BasicMapping, CowMapping, MapRequest, MapResponse, Mapping, MappingKind, SpaceAwareMapping,
26-
SpaceId, SpaceReferenceMapping, TableMovabilityBase, TableOps, TableReadOps, UpdateParent,
27-
UpdateParentNone, modify_ptes, write_entry_updating,
26+
SpaceId, TableMovabilityBase, TableOps, TableReadOps, UpdateParent, UpdateParentNone,
27+
modify_ptes, write_entry_updating,
2828
};
2929

3030
pub const PAGE_SIZE: usize = 4096;
@@ -44,6 +44,40 @@ const PAGE_AVL_COW: u64 = 1 << 9;
4444

4545
const VA_BITS: usize = 32;
4646

47+
/// The level of the page-table hierarchy at which sharing occurs.
48+
///
49+
/// On i686 the only shareable level is the leaf page table: the table a PDE points at.
50+
#[derive(Debug, Clone, Copy)]
51+
pub enum SharedTableLevel {
52+
/// A leaf page table
53+
Pt,
54+
}
55+
56+
/// A reference from one address space to an intermediate page table
57+
/// that lives in a different space. Produced by [`walk_va_spaces`]
58+
/// when the walker encounters an intermediate table whose physical
59+
/// address was already seen via an earlier root.
60+
///
61+
/// On i686 the only shareable level is [`SharedTableLevel::Pt`] (one
62+
/// level below the root PD).
63+
///
64+
/// `our_va` and `their_va` may be any virtual address inside the
65+
/// aliased region but callers must not assume they are region-aligned.
66+
#[derive(Debug, Clone, Copy)]
67+
pub struct SpaceReferenceMapping {
68+
/// The level at which the alias starts.
69+
pub level: SharedTableLevel,
70+
/// The "owning" space is the first root that visited this
71+
/// intermediate PA during [`walk_va_spaces`].
72+
pub space: SpaceId,
73+
/// Any VA inside the aliased sub-tree in OUR space.
74+
pub our_va: u64,
75+
/// Any VA inside the aliased sub-tree in the owning space.
76+
/// Usually equal to `our_va` (kernel mappings at the same VA
77+
/// across processes) but the design permits different VAs.
78+
pub their_va: u64,
79+
}
80+
4781
pub trait TableMovability<Op: TableReadOps + ?Sized, TableMoveInfo> {
4882
type RootUpdateParent: UpdateParent<Op, TableMoveInfo = TableMoveInfo>;
4983
fn root_update_parent() -> Self::RootUpdateParent;
@@ -213,17 +247,16 @@ pub unsafe fn map<Op: TableOps>(op: &Op, mapping: Mapping) {
213247
// Multi-space walk / link (shared intermediate tables)
214248
//==================================================================================================
215249

216-
/// i686 has two levels (PD -> PT). The only sharable thing is a PT,
217-
/// at depth 1 (one level below the root PD).
218-
const SHARED_TABLE_DEPTH: usize = 1;
219-
220250
/// Walk multiple root PDs together, detecting PDEs that point at the
221251
/// same PT PA across roots (i.e. aliased PTs — the standard
222252
/// "kernel-half shared" trick on x86 without KPTI). The first root to
223253
/// visit a given PT PA becomes the "owner"; later roots that alias it
224-
/// receive `AnotherSpace(SpaceReferenceMapping { depth: 1, .. })`
254+
/// receive `AnotherSpace(SpaceReferenceMapping { level: SharedTableLevel::Pt, .. })`
225255
/// entries.
226256
///
257+
/// i686 has two levels (PD -> PT). The only sharable thing is a PT,
258+
/// one level below the root PD ([`SharedTableLevel::Pt`]).
259+
///
227260
/// Generic over `TableAddr` so it works with both the in-guest
228261
/// implementation (`TableAddr = u32`, backed by raw pointers) and the
229262
/// host-side snapshot buffer (`TableAddr = u64`, byte offsets).
@@ -273,7 +306,7 @@ pub unsafe fn walk_va_spaces<Op: TableReadOps>(
273306
if let Some(&(owner, their_va)) = seen_pts.get(&pt_pa) {
274307
if owner != root_id {
275308
mappings.push(SpaceAwareMapping::AnotherSpace(SpaceReferenceMapping {
276-
depth: SHARED_TABLE_DEPTH,
309+
level: SharedTableLevel::Pt,
277310
space: owner,
278311
our_va: r.vmin,
279312
their_va,
@@ -335,24 +368,18 @@ pub unsafe fn walk_va_spaces<Op: TableReadOps>(
335368
/// PDE slot, and write that PA into our root's PDE slot for
336369
/// `our_va`. The owner's rebuilt root is found via `built_roots`.
337370
///
338-
/// On i686 `ref_map.depth` must be 1 (PT-level sharing). Other depths
339-
/// are rejected defensively.
340-
///
341371
/// # Safety
342372
/// Same invariants as [`map`]: caller owns the concurrency story and
343373
/// must invalidate the TLB if the page tables are live.
344374
#[allow(clippy::missing_safety_doc)]
345-
pub unsafe fn space_aware_map<Op: TableOps>(
375+
pub(super) unsafe fn link_shared_table<Op: TableOps>(
346376
op: &Op,
347377
ref_map: SpaceReferenceMapping,
348378
built_roots: &::alloc::collections::BTreeMap<SpaceId, Op::TableAddr>,
349379
) {
350-
assert!(
351-
ref_map.depth == SHARED_TABLE_DEPTH,
352-
"i686 only supports depth={} sharing; got depth={}",
353-
SHARED_TABLE_DEPTH,
354-
ref_map.depth
355-
);
380+
match ref_map.level {
381+
SharedTableLevel::Pt => {}
382+
}
356383

357384
// Their rebuilt root — must have been populated earlier in the
358385
// rebuild loop (walk_va_spaces guarantees topological order).

src/hyperlight_common/src/vmem.rs

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,10 @@ pub trait TableReadOps {
289289

290290
/// Our own version of ! until it is stable. Used to avoid needing to
291291
/// implement [`TableOps::update_root`] for ops that never need
292-
/// to move a table.
292+
/// to move a table. Also used as the `SpaceReferenceMapping` type on
293+
/// architectures that never emit `AnotherSpace` entries, making the
294+
/// corresponding match arm statically unreachable.
295+
#[derive(Debug, Clone, Copy)]
293296
pub enum Void {}
294297

295298
/// A marker struct, used by an implementation of [`TableOps`] to
@@ -464,34 +467,11 @@ pub use arch::virt_to_phys;
464467
pub type SpaceId = u64;
465468

466469
/// A reference from one address space to an intermediate page table
467-
/// that lives in a different space. Produced by [`walk_va_spaces`] when
468-
/// the walker encounters an intermediate table (at some `depth` below
469-
/// the root) whose physical address was already seen via an earlier
470-
/// root — i.e. the two spaces alias that sub-tree.
471-
///
472-
/// Semantics: the level-`depth` block in **our** space that contains
473-
/// VAs starting at `our_va` is aliased to the level-`depth` block in
474-
/// `space` that contains VAs starting at `their_va`. Everything below
475-
/// that sub-tree — PDEs, PTEs, leaf mappings — is shared wholesale.
476-
///
477-
/// `depth` is counted from the root:
478-
/// - `depth = 1` on i686: the shared thing is a leaf PT (the thing a
479-
/// PDE points to).
480-
/// - `depth = 1, 2, 3` on amd64: PDPT, PD, or PT respectively.
481-
#[derive(Debug, Clone, Copy)]
482-
pub struct SpaceReferenceMapping {
483-
/// Depth from the root at which the alias starts (1-based).
484-
pub depth: usize,
485-
/// The "owning" space — the first root that visited this
486-
/// intermediate PA during [`walk_va_spaces`].
487-
pub space: SpaceId,
488-
/// Start VA of the aliased sub-tree in OUR space.
489-
pub our_va: u64,
490-
/// Start VA of the aliased sub-tree in the owning space. Usually
491-
/// equal to `our_va` (kernel mappings at the same VA across
492-
/// processes) but the design permits different VAs.
493-
pub their_va: u64,
494-
}
470+
/// that lives in a different space. Defined per-arch: on
471+
/// architectures that never emit `AnotherSpace` entries (amd64,
472+
/// aarch64) it is [`Void`], making the `AnotherSpace` variant of
473+
/// [`SpaceAwareMapping`] statically unreachable.
474+
pub use arch::SpaceReferenceMapping;
495475

496476
/// Either a normal leaf mapping in the current space, or a reference
497477
/// to an intermediate table in another space. The compaction loop in
@@ -501,19 +481,21 @@ pub struct SpaceReferenceMapping {
501481
/// backing page is compacted into the new snapshot blob, the PTE is
502482
/// written, and intermediate tables are allocated on demand.
503483
/// - `AnotherSpace(r)` is rebuilt by *linking*: the entry in our
504-
/// rebuilt root at depth `r.depth - 1` for `r.our_va` is made to
505-
/// point at whatever table the owning space ended up with at
506-
/// `r.their_va`. See [`space_aware_map`].
484+
/// rebuilt root for `r.our_va` is made to point at whatever table
485+
/// the owning space ended up with at `r.their_va`.
486+
/// See [`space_aware_map`].
487+
///
488+
/// On architectures where `SpaceReferenceMapping` is [`Void`], the
489+
/// `AnotherSpace` variant is statically unreachable so match arms
490+
/// for it should use `match impossible {}`.
507491
#[derive(Debug)]
508492
pub enum SpaceAwareMapping {
509493
ThisSpace(Mapping),
510494
AnotherSpace(SpaceReferenceMapping),
511495
}
512496

513-
/// Counterpart of [`walk_va_spaces`]'s `AnotherSpace` entries on the
514-
/// write side: installs a link in `op`'s root PT tree at `ref_map.our_va`
515-
/// that points at whatever intermediate table the owning space ended
516-
/// up with at `ref_map.their_va` (in `built_roots[ref_map.space]`).
497+
/// Install a [`SpaceAwareMapping`] into the page tables managed by
498+
/// `op`.
517499
///
518500
/// Callers must ensure that `built_roots` contains populated page
519501
/// tables for any other space referenced by the mapping.
@@ -522,7 +504,19 @@ pub enum SpaceAwareMapping {
522504
/// Same invariants as [`map`]: the caller owns the concurrency story
523505
/// around the page tables being written, and must invalidate TLBs
524506
/// afterwards if they were live.
525-
pub use arch::space_aware_map;
507+
pub unsafe fn space_aware_map<Op: TableOps>(
508+
op: &Op,
509+
sam: SpaceAwareMapping,
510+
built_roots: &::alloc::collections::BTreeMap<SpaceId, Op::TableAddr>,
511+
) {
512+
match sam {
513+
SpaceAwareMapping::ThisSpace(mapping) => unsafe { arch::map(op, mapping) },
514+
SpaceAwareMapping::AnotherSpace(ref_map) => unsafe {
515+
arch::link_shared_table(op, ref_map, built_roots)
516+
},
517+
}
518+
}
519+
526520
/// Walk multiple page-table roots together, emitting either a normal
527521
/// leaf mapping (`ThisSpace`) or a reference to an alias that was
528522
/// already seen via an earlier root (`AnotherSpace`).

src/hyperlight_host/src/sandbox/snapshot.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -552,18 +552,25 @@ impl Snapshot {
552552
kind,
553553
user_accessible: mapping.user_accessible,
554554
};
555-
unsafe { vmem::map(&pt_buf, compacted) };
556-
}
557-
SpaceAwareMapping::AnotherSpace(ref_map) => {
558-
// Link to the owning space's already-
559-
// rebuilt intermediate table — this
560-
// is what preserves Nanvix's
561-
// kernel-half-shared invariant across
562-
// process PDs after relocation.
555+
563556
unsafe {
564-
vmem::space_aware_map(&pt_buf, ref_map, &built_roots);
565-
}
557+
vmem::space_aware_map(
558+
&pt_buf,
559+
SpaceAwareMapping::ThisSpace(compacted),
560+
&built_roots,
561+
)
562+
};
566563
}
564+
// Preserve Nanvix's kernel-half-shared
565+
// invariant by linking to the owning
566+
// space's rebuilt intermediate table.
567+
SpaceAwareMapping::AnotherSpace(ref_map) => unsafe {
568+
vmem::space_aware_map(
569+
&pt_buf,
570+
SpaceAwareMapping::AnotherSpace(ref_map),
571+
&built_roots,
572+
)
573+
},
567574
}
568575
}
569576
}

0 commit comments

Comments
 (0)