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

Add UEFI target support #1406

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
52 changes: 52 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -453,3 +453,55 @@ jobs:
directory: ./target/${{ matrix.target }}/debug/coverage/reports
fail_ci_if_error: true
verbose: true

# The x86_64-unknown-uefi targets doesn't have std crate.
# It can only be compiled under `no_std`.
test_uefi:
# Don't run duplicate `push` jobs for the repo owner's PRs.
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository

runs-on: ${{ matrix.host_os }}

strategy:
matrix:
features:
- # Default
target:
- x86_64-unknown-uefi

mode:
- # debug
- --release

# Only nightly support no std build.
rust_channel:
- nightly

host_os:
- windows-latest
- ubuntu-18.04

steps:
- if: ${{ contains(matrix.host_os, 'ubuntu') }}
run: sudo apt-get update -y

- uses: briansmith/actions-checkout@v2
with:
persist-credentials: false

- if: ${{ !contains(matrix.host_os, 'windows') }}
run: mk/install-build-tools.sh --target=${{ matrix.target }} ${{ matrix.features }}

- if: ${{ contains(matrix.host_os, 'windows') }}
run: ./mk/install-build-tools.ps1

- uses: briansmith/toolchain@v1
with:
toolchain: nightly
override: true
components: rust-src

# Currently, there is no 'rust-std' for target 'x86_64-unknown-uefi'.
Copy link
Owner

Choose a reason for hiding this comment

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

Is this still the case? Or can we simplify all this now?

# Build-only approach for now.
# TODO: no std tests
- run: mk/cargo.sh build -Zbuild-std="core,alloc" --target=${{ matrix.target }}
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ include = [
"crypto/fipsmodule/modes/asm/ghash-x86.pl",
"crypto/fipsmodule/modes/asm/ghash-x86_64.pl",
"crypto/fipsmodule/modes/asm/ghashv8-armx.pl",
"crypto/fipsmodule/rand/asm/rdrand-x86_64.pl",
Copy link
Owner

Choose a reason for hiding this comment

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

This can go away since we'd use getrandom.

"crypto/fipsmodule/sha/asm/sha256-armv4.pl",
"crypto/fipsmodule/sha/asm/sha512-armv4.pl",
"crypto/fipsmodule/sha/asm/sha512-armv8.pl",
Expand Down
36 changes: 30 additions & 6 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const RING_SRCS: &[(&[&str], &str)] = &[
(&[X86_64], "crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl"),
(&[X86_64], "crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl"),
(&[X86_64], "crypto/fipsmodule/modes/asm/ghash-x86_64.pl"),
(&[X86_64], "crypto/fipsmodule/rand/asm/rdrand-x86_64.pl"),
Copy link
Owner

Choose a reason for hiding this comment

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

Ditto.

(&[X86_64], "crypto/poly1305/poly1305_vec.c"),
(&[X86_64], SHA512_X86_64),
(&[X86_64], "crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl"),
Expand Down Expand Up @@ -222,7 +223,7 @@ const ASM_TARGETS: &[AsmTarget] = &[
preassemble: true,
},
AsmTarget {
oss: &[WINDOWS],
oss: &[WINDOWS, UEFI],
Copy link
Owner

Choose a reason for hiding this comment

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

This deserves a comment about why UEFI uses Windows ASM stuff unlike everything else.

arch: "x86_64",
perlasm_format: "nasm",
asm_extension: "asm",
Expand Down Expand Up @@ -279,6 +280,8 @@ const MACOS_ABI: &[&str] = &["ios", "macos"];

const WINDOWS: &str = "windows";

const UEFI: &str = "uefi";

/// Read an environment variable and tell Cargo that we depend on it.
///
/// This needs to be used for any environment variable that isn't a standard
Expand Down Expand Up @@ -381,8 +384,9 @@ fn pregenerate_asm_main() {
perlasm(&perlasm_src_dsts, asm_target);

if asm_target.preassemble {
// Preassembly is currently only done for Windows targets.
assert_eq!(&asm_target.oss, &[WINDOWS]);
// Preassembly is currently done for Windows and UEFI targets.
Copy link
Owner

Choose a reason for hiding this comment

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

let's just remove the comment since there's now a preassemble field that makes this clear, and it duplicates the assertion below.

assert!(asm_target.oss.contains(&WINDOWS) || asm_target.oss.contains(&UEFI));

let os = WINDOWS;

let srcs = asm_srcs(perlasm_src_dsts);
Expand Down Expand Up @@ -460,7 +464,7 @@ fn build_c_code(

// For Windows we also pregenerate the object files for non-Git builds so
// the user doesn't need to install the assembler.
if use_pregenerated && target.os == WINDOWS {
if use_pregenerated && supports_preassembly(&target.arch, &target.os) {
Copy link
Owner

Choose a reason for hiding this comment

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

Change this to look at the preassemble field.

asm_srcs = asm_srcs
.iter()
.map(|src| obj_path(&pregenerated, src.as_path()))
Expand Down Expand Up @@ -554,7 +558,7 @@ fn compile(p: &Path, target: &Target, include_dir: &Path, out_dir: &Path) -> Str
p.to_str().expect("Invalid path").into()
} else {
let out_file = obj_path(out_dir, p);
let cmd = if target.os != WINDOWS || ext != "asm" {
let cmd = if !supports_preassembly(&target.arch, &target.os) || ext != "asm" {
Copy link
Owner

Choose a reason for hiding this comment

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

Ditto.

cc(p, ext, target, include_dir, &out_file)
} else {
nasm(p, &target.arch, include_dir, &out_file)
Expand Down Expand Up @@ -602,6 +606,7 @@ fn cc(file: &Path, ext: &str, target: &Target, include_dir: &Path, out_file: &Pa
&& target.os != "redox"
&& target.os != "windows"
&& target.arch != "wasm32"
&& target.os != UEFI
{
let _ = c.flag("-fstack-protector");
}
Expand All @@ -627,17 +632,25 @@ fn cc(file: &Path, ext: &str, target: &Target, include_dir: &Path, out_file: &Pa
}
}

// UEFI is a baremental freestanding environment without stdlib.
let freestanding = target.os == UEFI;
Copy link
Owner

Choose a reason for hiding this comment

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

Is this still true? I heard there is a libstd now.


// Allow cross-compiling without a target sysroot for these targets.
//
// poly1305_vec.c requires <emmintrin.h> which requires <stdlib.h>.
if (target.arch == "wasm32" && target.os == "unknown")
|| (target.os == "linux" && target.is_musl && target.arch != "x86_64")
|| freestanding
{
if let Ok(compiler) = c.try_get_compiler() {
// TODO: Expand this to non-clang compilers in 0.17.0 if practical.
if compiler.is_like_clang() {
let _ = c.flag("-nostdlibinc");
let _ = c.define("RING_CORE_NOSTDLIBINC", "1");

if freestanding {
let _ = c.flag("-ffreestanding");
}
}
}
}
Expand Down Expand Up @@ -680,7 +693,10 @@ fn nasm(file: &Path, arch: &str, include_dir: &Path, out_file: &Path) -> Command
std::path::MAIN_SEPARATOR,
)));

let mut c = Command::new("./target/tools/windows/nasm/nasm");
let mut c = Command::new(&get_command(
"NASM_EXECUTABLE",
"./target/tools/windows/nasm/nasm",
));
Copy link
Owner

Choose a reason for hiding this comment

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

Please move this to a separate PR.

let _ = c
.arg("-o")
.arg(out_file.to_str().expect("Invalid path"))
Expand Down Expand Up @@ -917,6 +933,8 @@ fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String {
"CRYPTO_poly1305_init_neon",
"CRYPTO_poly1305_update",
"CRYPTO_poly1305_update_neon",
"CRYPTO_rdrand",
"CRYPTO_rdrand_multiple8_buf",
"ChaCha20_ctr32",
"LIMBS_add_mod",
"LIMBS_are_even",
Expand Down Expand Up @@ -1027,3 +1045,9 @@ fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String {

out
}

fn supports_preassembly(arch: &str, os: &str) -> bool {
ASM_TARGETS.iter().any(|asm_target| {
asm_target.preassemble && asm_target.arch == arch && asm_target.oss.contains(&os)
})
}
87 changes: 87 additions & 0 deletions crypto/fipsmodule/rand/asm/rdrand-x86_64.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env perl

# Copyright (c) 2015, Google Inc.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */

use strict;

my $flavour = shift;
my $output = shift;
if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }

my $win64 = 0;
$win64 = 1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);

$0 =~ m/(.*[\/\\])[^\/\\]+$/;
my $dir = $1;
my $xlate;
( $xlate="${dir}../../../perlasm/x86_64-xlate.pl" and -f $xlate) or
die "can't locate x86_64-xlate.pl";

open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
*STDOUT=*OUT;

my ($out, $len, $tmp1, $tmp2) = $win64 ? ("%rcx", "%rdx", "%r8", "%r9")
: ("%rdi", "%rsi", "%rdx", "%rcx");

print<<___;
.text

# CRYPTO_rdrand writes eight bytes of random data from the hardware RNG to
# |out|. It returns one on success or zero on hardware failure.
# int CRYPTO_rdrand(uint8_t out[8]);
.globl CRYPTO_rdrand
.type CRYPTO_rdrand,\@abi-omnipotent
.align 16
CRYPTO_rdrand:
.cfi_startproc
xorq %rax, %rax
rdrand $tmp1
# An add-with-carry of zero effectively sets %rax to the carry flag.
adcq %rax, %rax
movq $tmp1, 0($out)
retq
.cfi_endproc
.size CRYPTO_rdrand,.-CRYPTO_rdrand

# CRYPTO_rdrand_multiple8_buf fills |len| bytes at |buf| with random data from
# the hardware RNG. The |len| argument must be a multiple of eight. It returns
# one on success and zero on hardware failure.
# int CRYPTO_rdrand_multiple8_buf(uint8_t *buf, size_t len);
.globl CRYPTO_rdrand_multiple8_buf
.type CRYPTO_rdrand_multiple8_buf,\@abi-omnipotent
.align 16
CRYPTO_rdrand_multiple8_buf:
.cfi_startproc
test $len, $len
jz .Lout
movq \$8, $tmp1
.Lloop:
rdrand $tmp2
jnc .Lerr
movq $tmp2, 0($out)
addq $tmp1, $out
subq $tmp1, $len
jnz .Lloop
.Lout:
movq \$1, %rax
retq
.Lerr:
xorq %rax, %rax
retq
.cfi_endproc
.size CRYPTO_rdrand_multiple8_buf,.-CRYPTO_rdrand_multiple8_buf
___

close STDOUT or die "error closing STDOUT: $!"; # flush
5 changes: 5 additions & 0 deletions mk/cargo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ case $target in
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUSTFLAGS="$rustflags_self_contained"
fi
;;
x86_64-unknown-uefi)
export CC_x86_64_unknown_uefi=clang-$llvm_version
export AR_x86_64_unknown_uefi=llvm-ar-$llvm_version
export NASM_EXECUTABLE=nasm
;;
wasm32-unknown-unknown)
# The first two are only needed for when the "wasm_c" feature is enabled.
export CC_wasm32_unknown_unknown=clang-$llvm_version
Expand Down
5 changes: 5 additions & 0 deletions mk/install-build-tools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ case $target in
--target=i686-unknown-linux-musl|--target=x86_64-unknown-linux-musl)
use_clang=1
;;
--target=x86_64-unknown-uefi)
use_clang=1
install_packages \
nasm
;;
--target=wasm32-unknown-unknown)
# The version of wasm-bindgen-cli must match the wasm-bindgen version.
wasm_bindgen_version=$(cargo metadata --format-version 1 | jq -r '.packages | map(select( .name == "wasm-bindgen")) | map(.version) | .[0]')
Expand Down
70 changes: 70 additions & 0 deletions src/rand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ impl<T> RandomlyConstructable for T where T: self::sealed::RandomlyConstructable
/// random number generation.
///
/// [`getrandom`]: http://man7.org/linux/man-pages/man2/getrandom.2.html
///
/// On UEFI, `fill` is implemented using `CRYPTO_rdrand`
/// & `CRYPTO_rdrand_multiple8_buf` which provided by BoringSSL.
///
#[derive(Clone, Debug)]
pub struct SystemRandom(());

Expand Down Expand Up @@ -196,6 +200,9 @@ use self::darwin::fill as fill_impl;
#[cfg(any(target_os = "fuchsia"))]
use self::fuchsia::fill as fill_impl;

#[cfg(any(target_os = "uefi"))]
use self::uefi::fill as fill_impl;

#[cfg(any(target_os = "android", target_os = "linux"))]
mod sysrand_chunk {
use crate::{c, error};
Expand Down Expand Up @@ -419,3 +426,66 @@ mod fuchsia {
fn zx_cprng_draw(buffer: *mut u8, length: usize);
}
}

#[cfg(any(target_os = "uefi"))]
mod uefi {
use crate::error;

pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
fill_impl(dest)
}

#[cfg(not(any(target_arch = "x86_64")))]
fn fill_impl(dest: &mut [u8]) -> Result<(), error::Unspecified> {
Err(error::Unspecified)
}

#[cfg(any(target_arch = "x86_64"))]
fn fill_impl(dest: &mut [u8]) -> Result<(), error::Unspecified> {
fn is_avaiable() -> bool {
// TODO(xiaoyuxlu): use cpu::intel::RDRAND.avaiable when cpu.rs updated
// https://github.com/briansmith/ring/pull/1406#discussion_r720394928
// Current implementation may cause problem on AMD cpu. REF:
// https://github.com/nagisa/rust_rdrand/blob/f2fdd528a6103c946a2e9d0961c0592498b36493/src/lib.rs#L161
prefixed_extern! {
static mut OPENSSL_ia32cap_P: [u32; 4];
}
const FLAG: u32 = 1 << 30;
unsafe { OPENSSL_ia32cap_P[1] & FLAG == FLAG }
}

// We must make sure current cpu support `rdrand`
if !is_avaiable() {
return Err(error::Unspecified);
}

use crate::c;
prefixed_extern! {
fn CRYPTO_rdrand_multiple8_buf(buffer: *mut u8, length: c::size_t) -> c::int;
}
prefixed_extern! {
fn CRYPTO_rdrand(dest: *mut u8) -> c::int;
}

let len = dest.len();
let len_multiple8 = len & (!7usize);
let remainder = len - len_multiple8;

let mut res = 1;
if res != 0 && len_multiple8 != 0 {
res = unsafe { CRYPTO_rdrand_multiple8_buf(dest.as_mut_ptr(), len_multiple8) };
}
if res != 0 && remainder != 0 {
let mut rand_buf = [0u8; 8];
res = unsafe { CRYPTO_rdrand(rand_buf.as_mut_ptr()) };
if res != 0 {
dest[len_multiple8..].copy_from_slice(&rand_buf[..remainder]);
}
}
if res == 1 {
Ok(())
} else {
Err(error::Unspecified)
}
}
}