Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 27 additions & 34 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@
rustc = toolchains.stable.rust;
};

manifests = [
"Cargo.toml"
"src/tests/rust_guests/dummyguest/Cargo.toml"
"src/tests/rust_guests/simpleguest/Cargo.toml"
"src/tests/rust_guests/witguest/Cargo.toml"
];
manifestDeps = builtins.map (manifest:
let lockPath = builtins.replaceStrings [ "toml" ] [ "lock" ] manifest; in
let lockFile = ./${lockPath}; in
rust-platform.importCargoLock { inherit lockFile; }) manifests;
# when building a guest with cargo-hyperlight, or when
# building a miri sysroot for the main workspace, we need to
# include any crates.io dependencies of the standard library
Expand All @@ -118,20 +128,9 @@
) toolchains;
stdlibDeps = builtins.map (lockFile:
rust-platform.importCargoLock { inherit lockFile; }) stdlibLocks;
withStdlibLock = lockFile:
pkgs.symlinkJoin {
name = "cargo-deps";
paths = stdlibDeps ++ [
(rust-platform.importCargoLock {
inherit lockFile;
})
];
};
deps = {
"Cargo.toml" = withStdlibLock ./Cargo.lock;
"src/tests/rust_guests/dummyguest/Cargo.toml" = withStdlibLock ./src/tests/rust_guests/dummyguest/Cargo.lock;
"src/tests/rust_guests/simpleguest/Cargo.toml" = withStdlibLock ./src/tests/rust_guests/simpleguest/Cargo.lock;
"src/tests/rust_guests/witguest/Cargo.toml" = withStdlibLock ./src/tests/rust_guests/witguest/Cargo.lock;
deps = pkgs.symlinkJoin {
name = "cargo-deps";
paths = stdlibDeps ++ manifestDeps;
};

# Script snippet, used in the cargo/rustc wrappers below,
Expand All @@ -144,26 +143,10 @@
# like `cargo clippy` and `cargo hyperlight` (see
# https://github.com/rust-lang/cargo/issues/11031).
materialiseDeps = let
sortedNames = lib.lists.reverseList (builtins.attrNames deps);
sortedManifests = lib.lists.sort (p: q: p > q) manifests;
matchClause = path: '' */${path}) root="''${manifest%${path}}" ;;'';
matchClauses = lib.strings.concatStringsSep "\n"
(builtins.map matchClause sortedNames);
makeClause = manifest: vendor: let
dir = builtins.dirOf manifest;
gitExclude = builtins.toString (/. + "${dir}/.cargo");
in ''
mkdir -p $root/${dir}/.cargo
cat >$root/${dir}/.cargo/config.toml <<EOF
[source.crates-io]
replace-with = "vendored-sources"

[source.vendored-sources]
directory = "${vendor}"
EOF
printf "# vendor dependency configuration generated by nix\n%s\n" "${gitExclude}" >> $root/.git/info/exclude
'';
makeClauses = lib.strings.concatStringsSep "\n"
(lib.mapAttrsToList makeClause deps);
(builtins.map matchClause sortedManifests);
in ''
base_cargo() {
PATH="$base/bin:$PATH" "$base/bin/cargo" "$@"
Expand All @@ -174,8 +157,18 @@
${matchClauses}
esac
if [ -f ''${root}/flake.nix ]; then

mkdir -p $root/$.cargo
cat >$root/.cargo/config.toml <<EOF
[source.crates-io]
replace-with = "vendored-sources"

[source.vendored-sources]
directory = "${deps}"
EOF

sed -i '/# vendor dependency configuration generated by nix/{N;d;}' $root/.git/info/exclude
${makeClauses}
printf "# vendor dependency configuration generated by nix\n%s\n" "/.cargo" >> $root/.git/info/exclude
fi

# libgit2-sys copies a vendored git2 into the target/
Expand Down Expand Up @@ -238,7 +231,7 @@
pname = "hyperlight";
version = "0.0.0";
src = lib.cleanSource ./.;
cargoDeps = deps."Cargo.toml";
cargoDeps = deps;

nativeBuildInputs = [
azure-cli
Expand Down
1 change: 1 addition & 0 deletions fuzz/fuzz_targets/host_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ fuzz_target!(
let mut cfg = SandboxConfiguration::default();
cfg.set_output_data_size(64 * 1024); // 64 KB output buffer
cfg.set_input_data_size(64 * 1024); // 64 KB input buffer
cfg.set_scratch_size(512 * 1024); // large scratch region to contain those buffers, any data copies, etc.
let u_sbox = UninitializedSandbox::new(
GuestBinary::FilePath(simple_guest_for_fuzzing_as_string().expect("Guest Binary Missing")),
Some(cfg)
Expand Down
6 changes: 4 additions & 2 deletions src/hyperlight_common/src/arch/amd64/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub const MAX_GPA: usize = 0x0000_000f_ffff_ffff;
/// - A page for the smallest possible non-exception stack
/// - (up to) 3 pages for mapping that
/// - Two pages for the exception stack and metadata
pub fn min_scratch_size() -> usize {
12 * crate::vmem::PAGE_SIZE
/// - A page-aligned amount of memory for I/O buffers (for now)
pub fn min_scratch_size(input_data_size: usize, output_data_size: usize) -> usize {
(input_data_size + output_data_size).next_multiple_of(crate::vmem::PAGE_SIZE)
+ 12 * crate::vmem::PAGE_SIZE
}
145 changes: 123 additions & 22 deletions src/hyperlight_common/src/arch/amd64/vmem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ limitations under the License.
//! allocating intermediate tables as needed and setting appropriate flags on leaf PTEs

use crate::vmem::{
BasicMapping, Mapping, MappingKind, TableMovabilityBase, TableOps, TableReadOps, Void,
BasicMapping, CowMapping, Mapping, MappingKind, TableMovabilityBase, TableOps, TableReadOps,
Void,
};

// Paging Flags
Expand Down Expand Up @@ -58,12 +59,17 @@ const PAGE_NX: u64 = 1 << 63;
/// This masks out the lower 12 flag bits AND the upper bits including NX (bit 63)
const PTE_ADDR_MASK: u64 = 0x000F_FFFF_FFFF_F000;
const PAGE_USER_ACCESS_DISABLED: u64 = 0 << 2; // U/S bit not set - supervisor mode only (no code runs in user mode for now)
const PAGE_DIRTY_CLEAR: u64 = 0 << 6; // D - dirty bit cleared (set by CPU when written)
const PAGE_ACCESSED_CLEAR: u64 = 0 << 5; // A - accessed bit cleared (set by CPU when accessed)
const PAGE_DIRTY_SET: u64 = 1 << 6; // D - dirty bit
const PAGE_ACCESSED_SET: u64 = 1 << 5; // A - accessed bit
const PAGE_CACHE_ENABLED: u64 = 0 << 4; // PCD - page cache disable bit not set (caching enabled)
const PAGE_WRITE_BACK: u64 = 0 << 3; // PWT - page write-through bit not set (write-back caching)
const PAGE_PAT_WB: u64 = 0 << 7; // PAT - page attribute table index bit (0 for write-back memory when PCD=0, PWT=0)

// We use various patterns of the available-for-software-use bits to
// represent certain special mappings.
const PTE_AVL_MASK: u64 = 0x0000_0000_0000_0E00;
const PAGE_AVL_COW: u64 = 1 << 9;

/// Returns PAGE_RW if writable is true, 0 otherwise
#[inline(always)]
const fn page_rw_flag(writable: bool) -> u64 {
Expand Down Expand Up @@ -101,7 +107,7 @@ fn bits<const HIGH_BIT: u8, const LOW_BIT: u8>(x: u64) -> u64 {
#[allow(clippy::precedence)]
fn pte_for_table<Op: TableOps>(table_addr: Op::TableAddr) -> u64 {
Op::to_phys(table_addr) |
PAGE_ACCESSED_CLEAR | // accessed bit cleared (will be set by CPU when page is accessed - but we dont use the access bit for anything at present)
PAGE_ACCESSED_SET | // prevent the CPU writing to the access flag
PAGE_CACHE_ENABLED | // leave caching enabled
PAGE_WRITE_BACK | // use write-back caching
PAGE_USER_ACCESS_DISABLED |// dont allow user access (no code runs in user mode for now)
Expand Down Expand Up @@ -417,7 +423,7 @@ unsafe fn map_page<
r: MapResponse<Op, P>,
) {
let pte = match &mapping.kind {
MappingKind::BasicMapping(bm) =>
MappingKind::Basic(bm) =>
// TODO: Support not readable
// NOTE: On x86-64, there is no separate "readable" bit in the page table entry.
// This means that pages cannot be made write-only or execute-only without also being readable.
Expand All @@ -429,14 +435,27 @@ unsafe fn map_page<
(mapping.phys_base + (r.vmin - mapping.virt_base)) |
page_nx_flag(bm.executable) | // NX - no execute unless allowed
PAGE_PAT_WB | // PAT index bit for write-back memory
PAGE_DIRTY_CLEAR | // dirty bit (set by CPU when written)
PAGE_ACCESSED_CLEAR | // accessed bit cleared (will be set by CPU when page is accessed - but we dont use the access bit for anything at present)
PAGE_DIRTY_SET | // prevent the CPU writing to the dirty bit
PAGE_ACCESSED_SET | // prevent the CPU writing to the access flag
PAGE_CACHE_ENABLED | // leave caching enabled
PAGE_WRITE_BACK | // use write-back caching
PAGE_USER_ACCESS_DISABLED | // dont allow user access (no code runs in user mode for now)
page_rw_flag(bm.writable) | // R/W - set if writable
PAGE_PRESENT // P - this entry is present
}
MappingKind::Cow(cm) => {
(mapping.phys_base + (r.vmin - mapping.virt_base)) |
page_nx_flag(cm.executable) | // NX - no execute unless allowed
PAGE_AVL_COW |
PAGE_PAT_WB | // PAT index bit for write-back memory
PAGE_DIRTY_SET | // prevent the CPU writing to the dirty bit
PAGE_ACCESSED_SET | // prevent the CPU writing to the access flag
PAGE_CACHE_ENABLED | // leave caching enabled
PAGE_WRITE_BACK | // use write-back caching
PAGE_USER_ACCESS_DISABLED | // dont allow user access (no code runs in user mode for now)
0 | // R/W - Cow page is never writable
PAGE_PRESENT // P - this entry is present
}
};
unsafe {
write_entry_updating(op, r.update_parent, r.entry_ptr, pte);
Expand Down Expand Up @@ -517,7 +536,7 @@ pub unsafe fn virt_to_phys<'a, Op: TableReadOps + 'a>(
op: impl core::convert::AsRef<Op> + Copy + 'a,
address: u64,
len: u64,
) -> impl Iterator<Item = (VirtAddr, PhysAddr, BasicMapping)> + 'a {
) -> impl Iterator<Item = Mapping> + 'a {
// Undo sign-extension, and mask off any sub-page bits
let vmin = (address & ((1u64 << VA_BITS) - 1)) & !(PAGE_SIZE as u64 - 1);
let vmax = core::cmp::min(vmin + len, 1u64 << VA_BITS);
Expand All @@ -540,12 +559,27 @@ pub unsafe fn virt_to_phys<'a, Op: TableReadOps + 'a>(
let sgn_bit = r.vmin >> (VA_BITS - 1);
let sgn_bits = 0u64.wrapping_sub(sgn_bit) << VA_BITS;
let virt_addr = sgn_bits | r.vmin;
let perms = BasicMapping {
readable: true,
writable: (pte & PAGE_RW) != 0,
executable: (pte & PAGE_NX) == 0,

let executable = (pte & PAGE_NX) == 0;
let avl = pte & PTE_AVL_MASK;
let kind = if avl == PAGE_AVL_COW {
MappingKind::Cow(CowMapping {
readable: true,
executable,
})
} else {
MappingKind::Basic(BasicMapping {
readable: true,
writable: (pte & PAGE_RW) != 0,
executable,
})
};
Some((virt_addr, phys_addr, perms))
Some(Mapping {
phys_base: phys_addr,
virt_base: virt_addr,
len: PAGE_SIZE as u64,
kind,
})
})
}

Expand Down Expand Up @@ -721,7 +755,7 @@ mod tests {
phys_base: 0x1000,
virt_base: 0x1000,
len: PAGE_SIZE as u64,
kind: MappingKind::BasicMapping(BasicMapping {
kind: MappingKind::Basic(BasicMapping {
readable: true,
writable: true,
executable: false,
Expand Down Expand Up @@ -754,7 +788,7 @@ mod tests {
phys_base: 0x2000,
virt_base: 0x2000,
len: PAGE_SIZE as u64,
kind: MappingKind::BasicMapping(BasicMapping {
kind: MappingKind::Basic(BasicMapping {
readable: true,
writable: false,
executable: true,
Expand All @@ -777,7 +811,7 @@ mod tests {
phys_base: 0x10000,
virt_base: 0x10000,
len: 4 * PAGE_SIZE as u64, // 4 pages = 16KB
kind: MappingKind::BasicMapping(BasicMapping {
kind: MappingKind::Basic(BasicMapping {
readable: true,
writable: true,
executable: false,
Expand Down Expand Up @@ -810,7 +844,7 @@ mod tests {
phys_base: 0x1000,
virt_base: 0x1000,
len: PAGE_SIZE as u64,
kind: MappingKind::BasicMapping(BasicMapping {
kind: MappingKind::Basic(BasicMapping {
readable: true,
writable: true,
executable: false,
Expand All @@ -824,7 +858,7 @@ mod tests {
phys_base: 0x5000,
virt_base: 0x5000,
len: PAGE_SIZE as u64,
kind: MappingKind::BasicMapping(BasicMapping {
kind: MappingKind::Basic(BasicMapping {
readable: true,
writable: true,
executable: false,
Expand All @@ -849,7 +883,7 @@ mod tests {
phys_base: 0x1000,
virt_base: 0x1000,
len: PAGE_SIZE as u64,
kind: MappingKind::BasicMapping(BasicMapping {
kind: MappingKind::Basic(BasicMapping {
readable: true,
writable: true,
executable: false,
Expand All @@ -860,8 +894,75 @@ mod tests {

let result = unsafe { virt_to_phys(&ops, 0x1000, 1).next() };
assert!(result.is_some(), "Should find mapped address");
let pte = result.unwrap();
assert_eq!(pte.1, 0x1000);
let mapping = result.unwrap();
assert_eq!(mapping.phys_base, 0x1000);
}

#[test]
fn test_virt_to_phys_unaligned_virt() {
let ops = MockTableOps::new();
let mapping = Mapping {
phys_base: 0x1000,
virt_base: 0x1000,
len: PAGE_SIZE as u64,
kind: MappingKind::Basic(BasicMapping {
readable: true,
writable: true,
executable: false,
}),
};

unsafe { map(&ops, mapping) };

let result = unsafe { virt_to_phys(&ops, 0x1234, 1).next() };
assert!(result.is_some(), "Should find mapped address");
let mapping = result.unwrap();
assert_eq!(mapping.phys_base, 0x1000);
}

#[test]
fn test_virt_to_phys_perms() {
let test = |kind| {
let ops = MockTableOps::new();
let mapping = Mapping {
phys_base: 0x1000,
virt_base: 0x1000,
len: PAGE_SIZE as u64,
kind,
};
unsafe { map(&ops, mapping) };
let result = unsafe { virt_to_phys(&ops, 0x1000, 1).next() };
let mapping = result.unwrap();
assert_eq!(mapping.kind, kind);
};
test(MappingKind::Basic(BasicMapping {
readable: true,
writable: false,
executable: false,
}));
test(MappingKind::Basic(BasicMapping {
readable: true,
writable: false,
executable: true,
}));
test(MappingKind::Basic(BasicMapping {
readable: true,
writable: true,
executable: false,
}));
test(MappingKind::Basic(BasicMapping {
readable: true,
writable: true,
executable: true,
}));
test(MappingKind::Cow(CowMapping {
readable: true,
executable: false,
}));
test(MappingKind::Cow(CowMapping {
readable: true,
executable: true,
}));
}

#[test]
Expand All @@ -880,7 +981,7 @@ mod tests {
phys_base: 0x1000,
virt_base: 0x1000,
len: PAGE_SIZE as u64,
kind: MappingKind::BasicMapping(BasicMapping {
kind: MappingKind::Basic(BasicMapping {
readable: true,
writable: true,
executable: false,
Expand Down
7 changes: 2 additions & 5 deletions src/hyperlight_common/src/arch/i686/vmem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ limitations under the License.
// This file is just dummy definitions at the moment, in order to
// allow compiling the guest for real mode boot scenarios.

use crate::vmem::{BasicMapping, Mapping, TableOps, TableReadOps, Void};
use crate::vmem::{Mapping, TableOps, TableReadOps, Void};

pub const PAGE_SIZE: usize = 4096;
pub const PAGE_TABLE_SIZE: usize = 4096;
Expand All @@ -31,10 +31,7 @@ pub unsafe fn map<Op: TableOps>(_op: &Op, _mapping: Mapping) {
}

#[allow(clippy::missing_safety_doc)]
pub unsafe fn virt_to_phys<Op: TableOps>(
_op: &Op,
_address: u64,
) -> impl Iterator<Item = (VirtAddr, PhysAddr, BasicMapping)> {
pub unsafe fn virt_to_phys<Op: TableOps>(_op: &Op, _address: u64) -> impl Iterator<Item = Mapping> {
panic!(
"vmem::virt_to_phys: i686 guests do not support booting the full hyperlight guest kernel"
);
Expand Down
Loading
Loading