Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a skeleton UEFI app #2605

Merged
merged 14 commits into from
Mar 21, 2022
2 changes: 1 addition & 1 deletion .devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// - https://code.visualstudio.com/docs/remote/devcontainerjson-reference
{
// Do not modify manually. This value is automatically updated by ./scripts/docker_build .
"image": "sha256:ae9a0f15ff37574d5584cebaaa563393369dac4ae35b1c5c9e7e991750021301",
"image": "sha256:d2ac0f6ad41502fdfe336b8074fbf1e4eff462fdb39b164f1cfba419f5eb3a60",
"extensions": [
"bazelbuild.vscode-bazel",
"bodil.prettier-toml",
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ members = [
"remote_attestation/rust",
"xtask",
]
exclude = ["oak_functions/loader/fuzz"]
exclude = ["experimental/uefi", "oak_functions/loader/fuzz"]
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ RUN apt-get --yes update \
python3 \
python3-six \
python3-distutils \
qemu-system-x86 \
shellcheck \
software-properties-common \
vim \
Expand All @@ -59,10 +60,11 @@ RUN echo "deb [arch=amd64] https://download.docker.com/linux/debian buster stabl
&& apt-get clean \
&& rm --recursive --force /var/lib/apt/lists/*

# Use a later version of clang-format from buster-backports.
# Use a later version of clang-format and OVMF (UEFI firmware) from buster-backports.
RUN echo 'deb http://deb.debian.org/debian buster-backports main' > /etc/apt/sources.list.d/backports.list \
&& apt-get --yes update \
&& apt-get install --no-install-recommends --yes clang-format-8 \
&& apt-get install --no-install-recommends --yes --target-release buster-backports ovmf \
&& apt-get clean \
&& rm --recursive --force /var/lib/apt/lists/* \
&& ln --symbolic --force clang-format-8 /usr/bin/clang-format
Expand Down
9 changes: 9 additions & 0 deletions experimental/uefi/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[build]
target = "x86_64-unknown-uefi"

[target.x86_64-unknown-uefi]
runner = "qemu-system-x86_64 -nodefaults -nographic -bios /usr/share/OVMF/OVMF_CODE.fd -serial stdio -machine q35 -device isa-debug-exit,iobase=0xf4,iosize=0x04 -kernel"

[unstable]
build-std = ["core"]
build-std-features = ["compiler-builtins-mem"]
123 changes: 123 additions & 0 deletions experimental/uefi/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions experimental/uefi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "uefi-simple"
version = "0.1.0"
authors = ["Andri Saar <[email protected]>"]
edition = "2021"
license = "Apache-2.0"

[dependencies]
uefi = "*"
uefi-services = "*"

[dev-dependencies]
uefi-services = { version = "*", features = ["qemu"] }
76 changes: 76 additions & 0 deletions experimental/uefi/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// Copyright 2022 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#![no_main]
#![no_std]
#![feature(abi_efiapi)]
#![feature(custom_test_frameworks)]
// As we're in a `no_std` environment, testing requires special handling. This
// approach was inspired by https://os.phil-opp.com/testing/.
#![test_runner(crate::test_runner)]
#![reexport_test_harness_main = "test_main"]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does the test_main string come from? Is it important that it stays like that, or can it be changed? Please add some details

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume it can be changed; I've added a link to the blog post this code draws upon heavily for the testing infrastructure.


use uefi::{prelude::*, table::runtime::ResetType, ResultExt};

// The main entry point of the UEFI application.
//
// The choice of name (`_start`) is entirely arbitrary; what matters is that
// there's exactly one function with the `#[entry]` attribute in the
// dependency graph.
#[entry]
fn _start(_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be named _start or is it just a convention? Either way, could you link to some reference material?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a convention. I've added some comments.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks! This stuff is pretty new for everyone, including people familiar with "vanilla" Rust, so expect to have to over communicate / document things :)

Comment on lines +33 to +34
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine some of this will end up being part of some generic harness library, while some other parts will be Oak-specific. Not in this PR, but it may be worth start thinking about that split going forward.

uefi_services::init(&mut system_table).unwrap_success();

// As we're not relying on the normal Rust test harness, we need to call
// the tests ourselves if necessary.
let status = if cfg!(test) {
#[cfg(test)]
test_main();
Status::SUCCESS
} else {
main(_handle, &mut system_table)
};

// After we're done running our code, we also tell the UEFI runtime to shut
// down the machine, otherwise we'd go back to the UEFI shell.
system_table
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some comment here to explain what's happening (and why)?

.runtime_services()
.reset(ResetType::Shutdown, status, None);
}

fn main(_handle: Handle, system_table: &mut SystemTable<Boot>) -> Status {
use core::fmt::Write;

let status = writeln!(system_table.stdout(), "Hello World!");

status
.map(|_| Status::SUCCESS)
.unwrap_or(Status::DEVICE_ERROR)
}

#[cfg(test)]
fn test_runner(tests: &[&dyn Fn()]) {
for test in tests {
test();
}
}

// Simple silly test just to prove that the test infrastructure works.
#[test_case]
fn test_simple() {
let x = 1;
assert_eq!(x, 1);
}
4 changes: 2 additions & 2 deletions scripts/common
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ readonly DOCKER_IMAGE_NAME='gcr.io/oak-ci/oak:latest'
# from a registry first.

# Do not modify manually. This value is automatically updated by ./scripts/docker_build .
readonly DOCKER_IMAGE_ID='sha256:ae9a0f15ff37574d5584cebaaa563393369dac4ae35b1c5c9e7e991750021301'
readonly DOCKER_IMAGE_ID='sha256:d2ac0f6ad41502fdfe336b8074fbf1e4eff462fdb39b164f1cfba419f5eb3a60'

# Do not modify manually. This value is automatically updated by ./scripts/docker_push .
readonly DOCKER_IMAGE_REPO_DIGEST='gcr.io/oak-ci/oak@sha256:aafe171f215dda322b45fc93a1b96ae0a270624a9402769bf17f11da12dd7aba'
readonly DOCKER_IMAGE_REPO_DIGEST='gcr.io/oak-ci/oak@sha256:4572fcc1f3f738f7db9faff26909a51afa4781bc5397356b3e8aaa8efb847f68'

readonly SERVER_DOCKER_IMAGE_NAME='gcr.io/oak-ci/oak-server'

Expand Down
4 changes: 4 additions & 0 deletions xtask/src/diffs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ impl ModifiedContent {
.unwrap()
.contains(&file_name.to_string())
}

pub fn contains_path(&self, file_name: &Path) -> bool {
self.contains(file_name.to_str().unwrap())
}
}

/// Get all the files that have been modified in the given `scope`. If the scope is `Scope::All`
Expand Down
30 changes: 19 additions & 11 deletions xtask/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,37 +681,45 @@ fn run_cargo_test(opt: &RunTestsOpt, all_affected_crates: &ModifiedContent) -> S
steps: crate_manifest_files()
// Exclude `fuzz` crates, as there are no tests and binaries should not be executed.
.filter(|path| !is_fuzzing_toml_file(path))
.map(to_string)
.filter(|path| all_affected_crates.contains(path))
.filter(|path| all_affected_crates.contains_path(path))
.map(|entry| {
// Run `cargo test` in the directory of the crate, not the top-level directory.
// This is needed as otherwise any crate-specific `.cargo/config.toml` files would
// be ignored. If a crate does not have a config file, Cargo will just backtrack
// up the tree and pick up the `.cargo/config.toml` file from the root directory.
let test_run_step = |name| Step::Single {
name,
command: Cmd::new(
command: Cmd::new_in_dir(
andrisaar marked this conversation as resolved.
Show resolved Hide resolved
"cargo",
&[
"test",
"--all-features",
&format!("--manifest-path={}", &entry),
&format!(
"--manifest-path={}",
entry.file_name().unwrap().to_str().unwrap()
),
],
entry.parent().unwrap(),
),
};
let target_path = &entry.replace("Cargo.toml", "target");

// If `cleanup` is enabled, add a cleanup step to remove the generated files. Do
// this only if `target_path` is a non-empty, valid target path.
if opt.cleanup && target_path.ends_with("/target") {
// If `cleanup` is enabled, add a cleanup step to remove the generated files.
if opt.cleanup {
Step::Multiple {
name: entry.clone(),
name: entry.to_str().unwrap().to_string(),
steps: vec![
test_run_step("run".to_string()),
Step::Single {
name: "cleanup".to_string(),
command: Cmd::new("rm", &["-rf", target_path]),
command: Cmd::new(
"rm",
&["-rf", entry.with_file_name("target").to_str().unwrap()],
),
},
],
}
} else {
test_run_step(entry.clone())
test_run_step(entry.to_str().unwrap().to_string())
}
})
.collect(),
Expand Down