diff --git a/src/Enclave.edl b/src/Enclave.edl index 58c3aabf..366de52c 100644 --- a/src/Enclave.edl +++ b/src/Enclave.edl @@ -6,6 +6,7 @@ enclave { from "sgx_tprotected_fs.edl" import *; from "sgx_net.edl" import *; from "sgx_occlum_utils.edl" import *; + from "sgx_vdso_time_ocalls.edl" import *; include "sgx_quote.h" include "occlum_edl_types.h" @@ -148,9 +149,6 @@ enclave { int occlum_ocall_thread_getcpuclock([out] struct timespec* ts) propagate_errno; - void occlum_ocall_gettimeofday([out] struct timeval* tv); - void occlum_ocall_clock_gettime(clockid_t clockid, [out] struct timespec* ts); - void occlum_ocall_clock_getres(clockid_t clockid, [out] struct timespec* res); void occlum_ocall_rdtsc([out] uint32_t* low, [out] uint32_t* high); void occlum_ocall_get_timerslack([out] int *timer_slack); diff --git a/src/libos/Cargo.toml b/src/libos/Cargo.toml index c4488c42..a4cb7e8b 100644 --- a/src/libos/Cargo.toml +++ b/src/libos/Cargo.toml @@ -25,7 +25,8 @@ rcore-fs-devfs = { path = "../../deps/sefs/rcore-fs-devfs" } resolv-conf = { path = "../../deps/resolv-conf" } serde = { path = "../../deps/serde-sgx/serde", features = ["derive"] } serde_json = { path = "../../deps/serde-json-sgx" } -errno = { path = "./crates/errno", features = ["occlum"] } +errno = { path = "crates/errno", features = ["occlum"] } +vdso-time = { path = "crates/vdso-time", default-features = false, features = ["sgx"] } memoffset = "0.6.1" scroll = { version = "0.11.0", default-features = false } itertools = { version = "0.10.0", default-features = false, features = ["use_alloc"] } diff --git a/src/libos/Makefile b/src/libos/Makefile index 82fb32bd..6ecffbd6 100644 --- a/src/libos/Makefile +++ b/src/libos/Makefile @@ -162,7 +162,11 @@ $(OBJ_DIR)/libos/$(SRC_OBJ)/Enclave_t.o: $(OBJ_DIR)/libos/$(SRC_OBJ)/Enclave_t.c @echo "CC <= $@" $(OBJ_DIR)/libos/$(SRC_OBJ)/Enclave_t.c: $(SGX_EDGER8R) ../Enclave.edl - @cd $(OBJ_DIR)/libos/$(SRC_OBJ) && $(SGX_EDGER8R) $(SGX_EDGER8R_MODE) --trusted $(CUR_DIR)/../Enclave.edl --search-path $(SGX_SDK)/include --search-path $(RUST_SGX_SDK_DIR)/edl + @cd $(OBJ_DIR)/libos/$(SRC_OBJ) && \ + $(SGX_EDGER8R) $(SGX_EDGER8R_MODE) --trusted $(CUR_DIR)/../Enclave.edl \ + --search-path $(SGX_SDK)/include \ + --search-path $(RUST_SGX_SDK_DIR)/edl \ + --search-path $(CRATES_DIR)/vdso-time/ocalls @echo "GEN <= $@" $(C_OBJS):$(OBJ_DIR)/libos/$(SRC_OBJ)/%.o: src/%.c diff --git a/src/libos/crates/vdso-time/.gitignore b/src/libos/crates/vdso-time/.gitignore new file mode 100644 index 00000000..50c83018 --- /dev/null +++ b/src/libos/crates/vdso-time/.gitignore @@ -0,0 +1,10 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk \ No newline at end of file diff --git a/src/libos/crates/vdso-time/Cargo.toml b/src/libos/crates/vdso-time/Cargo.toml new file mode 100644 index 00000000..678e3172 --- /dev/null +++ b/src/libos/crates/vdso-time/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "vdso-time" +version = "0.1.0" +authors = ["Shuocheng Wang "] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["std"] +std = ["libc"] +sgx = ["sgx_types", "sgx_tstd", "sgx_libc", "sgx_trts"] + +[dependencies] +cfg-if = "1.0" +errno = { path = "../errno" } +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +libc = { version = "0.2", optional = true } +log = "0.4" +sgx_types = { path = "../../../../deps/rust-sgx-sdk/sgx_types", optional = true } +sgx_tstd = { path = "../../../../deps/rust-sgx-sdk/sgx_tstd", optional = true, features = ["backtrace"] } +sgx_libc = { path = "../../../../deps/rust-sgx-sdk/sgx_libc", optional = true } +sgx_trts = { path = "../../../../deps/rust-sgx-sdk/sgx_trts", optional = true } + +[dev-dependencies] +criterion = "0.3" +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +ctor = "0.1" + +[[bench]] +name = "bench" +harness = false diff --git a/src/libos/crates/vdso-time/README.md b/src/libos/crates/vdso-time/README.md new file mode 100644 index 00000000..6fef14be --- /dev/null +++ b/src/libos/crates/vdso-time/README.md @@ -0,0 +1,40 @@ +# vdso-time +A rust crate for getting time using vDSO. This crate can support host and SGX (based on Rust-SGX-SDK). + +## Getting Started +Add the following dependency to your Cargo manifest: + +``` +vdso-time = { path = "yourpath/vdso-time" } +``` + +If you want to use in SGX environment, add the following dependency to your Cargo manifest: + +``` +vdso-time = { path = "yourpath/vdso-time", default-features = false, features = ["sgx"] } +``` + +## API examples + +``` +use vdso_time::ClockId; + +let time = vdso_time::clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap(); +println!("vdso_time::clock_gettime: {:?}", time); + +let res = vdso_time::clock_getres(ClockId::CLOCK_MONOTONIC).unwrap(); +println!("vdso_time::clock_getres: {:?}", res); +``` + +``` +use vdso_time::{Vdso, ClockId}; + +let vdso = Vdso::new().unwrap(); + +let time = vdso.clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap(); +println!("vdso.clock_gettime: {:?}", time); + +let res = vdso.clock_getres(ClockId::CLOCK_MONOTONIC).unwrap(); +println!("vdso.clock_getres: {:?}", res); +} +``` \ No newline at end of file diff --git a/src/libos/crates/vdso-time/benches/bench.rs b/src/libos/crates/vdso-time/benches/bench.rs new file mode 100644 index 00000000..0628c981 --- /dev/null +++ b/src/libos/crates/vdso-time/benches/bench.rs @@ -0,0 +1,42 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use vdso_time::ClockId; + +fn libc_clock_gettime() -> libc::timespec { + let mut tp = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + unsafe { + libc::clock_gettime(ClockId::CLOCK_MONOTONIC as _, &mut tp as *mut _); + } + tp +} + +fn libc_clock_getres() -> libc::timespec { + let mut tp = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + unsafe { + libc::clock_getres(ClockId::CLOCK_MONOTONIC as _, &mut tp as *mut _); + } + tp +} + +fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("libc clock_gettime", |b| { + b.iter(|| black_box(libc_clock_gettime())) + }); + c.bench_function("vdso clock_gettime", |b| { + b.iter(|| black_box(vdso_time::clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap())) + }); + c.bench_function("libc clock_getres", |b| { + b.iter(|| black_box(libc_clock_getres())) + }); + c.bench_function("vdso clock_getres", |b| { + b.iter(|| black_box(vdso_time::clock_getres(ClockId::CLOCK_MONOTONIC).unwrap())) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/src/libos/crates/vdso-time/examples/bench.rs b/src/libos/crates/vdso-time/examples/bench.rs new file mode 100644 index 00000000..cd7c0805 --- /dev/null +++ b/src/libos/crates/vdso-time/examples/bench.rs @@ -0,0 +1,33 @@ +include!("common/bench.rs"); + +fn libc_clock_gettime() -> Duration { + let mut tp = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + unsafe { + libc::clock_gettime(ClockId::CLOCK_MONOTONIC as _, &mut tp as *mut _); + } + Duration::new(tp.tv_sec as u64, tp.tv_nsec as u32) +} + +fn libc_clock_getres() -> Duration { + let mut tp = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + unsafe { + libc::clock_getres(ClockId::CLOCK_MONOTONIC as _, &mut tp as *mut _); + } + Duration::new(tp.tv_sec as u64, tp.tv_nsec as u32) +} + +fn libc_benchmarks() { + benchmark("Libc clock_gettime()", libc_clock_gettime); + benchmark("Libc clock_getres()", libc_clock_getres); +} + +fn main() { + libc_benchmarks(); + vdso_benchmarks(); +} diff --git a/src/libos/crates/vdso-time/examples/common/bench.rs b/src/libos/crates/vdso-time/examples/common/bench.rs new file mode 100644 index 00000000..ab72c3fa --- /dev/null +++ b/src/libos/crates/vdso-time/examples/common/bench.rs @@ -0,0 +1,31 @@ +use std::time::Duration; +use vdso_time::{ClockId, clock_getres, clock_gettime}; + +/// from criterion crate: +/// A function that is opaque to the optimizer, used to prevent the compiler from +/// optimizing away computations in a benchmark. +/// +/// This variant is stable-compatible, but it may cause some performance overhead +/// or fail to prevent code from being eliminated. +fn black_box(dummy: T) -> T { + unsafe { + let ret = std::ptr::read_volatile(&dummy); + std::mem::forget(dummy); + ret + } +} + +fn benchmark(name: &str, func: impl Fn() -> Duration) { + let start = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap(); + let loops = 1000000; + for _ in 0..loops { + black_box(func()); + } + let end = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap(); + println!("[{}] avg_time: {:?} ns", name, (end - start).as_nanos() / loops); +} + +fn vdso_benchmarks() { + benchmark("vdso clock_gettime()", || clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap()); + benchmark("vdso clock_getres()", || clock_getres(ClockId::CLOCK_MONOTONIC).unwrap()); +} \ No newline at end of file diff --git a/src/libos/crates/vdso-time/examples/common/example.rs b/src/libos/crates/vdso-time/examples/common/example.rs new file mode 100644 index 00000000..b9d5f1e7 --- /dev/null +++ b/src/libos/crates/vdso-time/examples/common/example.rs @@ -0,0 +1,26 @@ +fn first_example() { + use vdso_time::ClockId; + + let time = vdso_time::clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap(); + println!("vdso_time::clock_gettime: {:?}", time); + + let res = vdso_time::clock_getres(ClockId::CLOCK_MONOTONIC).unwrap(); + println!("vdso_time::clock_getres: {:?}", res); +} + +fn second_example() { + use vdso_time::{Vdso, ClockId}; + + let vdso = Vdso::new().unwrap(); + + let time = vdso.clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap(); + println!("vdso.clock_gettime: {:?}", time); + + let res = vdso.clock_getres(ClockId::CLOCK_MONOTONIC).unwrap(); + println!("vdso.clock_getres: {:?}", res); +} + +fn example() { + first_example(); + second_example(); +} \ No newline at end of file diff --git a/src/libos/crates/vdso-time/examples/example.rs b/src/libos/crates/vdso-time/examples/example.rs new file mode 100644 index 00000000..34f97141 --- /dev/null +++ b/src/libos/crates/vdso-time/examples/example.rs @@ -0,0 +1,5 @@ +include!("common/example.rs"); + +fn main() { + example(); +} diff --git a/src/libos/crates/vdso-time/examples/sgx/.gitignore b/src/libos/crates/vdso-time/examples/sgx/.gitignore new file mode 100644 index 00000000..3718176d --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/.gitignore @@ -0,0 +1,11 @@ +Cargo.lock +Enclave_u.c +Enclave_u.h +Enclave_t.c +Enclave_t.h +app/target +enclave/target +bin/app +*.o +*.a +*.so diff --git a/src/libos/crates/vdso-time/examples/sgx/Makefile b/src/libos/crates/vdso-time/examples/sgx/Makefile new file mode 100644 index 00000000..87625bba --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/Makefile @@ -0,0 +1,167 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +######## SGX SDK Settings ######## + +SGX_SDK ?= /opt/sgxsdk +SGX_MODE ?= HW +SGX_ARCH ?= x64 + +include ../../../common.mk + +include $(RUST_SGX_SDK_DIR)/buildenv.mk + +OCALLS_DIR := ../../ocalls + +ifeq ($(shell getconf LONG_BIT), 32) + SGX_ARCH := x86 +else ifeq ($(findstring -m32, $(CXXFLAGS)), -m32) + SGX_ARCH := x86 +endif + +ifeq ($(SGX_ARCH), x86) + SGX_COMMON_CFLAGS := -m32 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x86/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x86/sgx_edger8r +else + SGX_COMMON_CFLAGS := -m64 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r +endif + +ifeq ($(SGX_DEBUG), 1) +ifeq ($(SGX_PRERELEASE), 1) +$(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!) +endif +endif + +ifeq ($(SGX_DEBUG), 1) + SGX_COMMON_CFLAGS += -O0 -g +else + SGX_COMMON_CFLAGS += -O2 +endif + +SGX_COMMON_CFLAGS += -fstack-protector + +######## CUSTOM Settings ######## + +CUSTOM_LIBRARY_PATH := ./lib +CUSTOM_BIN_PATH := ./bin +CUSTOM_EDL_PATH := $(RUST_SGX_SDK_DIR)/edl +CUSTOM_COMMON_PATH := $(RUST_SGX_SDK_DIR)/common + +######## EDL Settings ######## + +Enclave_EDL_Files := enclave/Enclave_t.c enclave/Enclave_t.h app/Enclave_u.c app/Enclave_u.h + +######## APP Settings ######## + +App_Rust_Flags := --release +App_SRC_Files := $(shell find app/ -type f -name '*.rs') $(shell find app/ -type f -name 'Cargo.toml') +App_Include_Paths := -I ./app -I./include -I$(SGX_SDK)/include -I$(CUSTOM_EDL_PATH) +App_C_Flags := $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes $(App_Include_Paths) + +App_Rust_Path := ./app/target/release +App_Enclave_u_Object := lib/libEnclave_u.a +App_Ocall_Object_Name := libvdso_time_ocalls.a +App_Ocall_Object := lib/$(App_Ocall_Object_Name) +App_Name := bin/app + +######## Enclave Settings ######## + +ifneq ($(SGX_MODE), HW) + Trts_Library_Name := sgx_trts_sim + Service_Library_Name := sgx_tservice_sim +else + Trts_Library_Name := sgx_trts + Service_Library_Name := sgx_tservice +endif +Crypto_Library_Name := sgx_tcrypto +KeyExchange_Library_Name := sgx_tkey_exchange +ProtectedFs_Library_Name := sgx_tprotected_fs + +RustEnclave_C_Files := $(wildcard ./enclave/*.c) +RustEnclave_C_Objects := $(RustEnclave_C_Files:.c=.o) +RustEnclave_Include_Paths := -I$(CUSTOM_COMMON_PATH)/inc -I$(CUSTOM_EDL_PATH) -I$(SGX_SDK)/include -I$(SGX_SDK)/include/tlibc -I$(SGX_SDK)/include/stlport -I$(SGX_SDK)/include/epid -I ./enclave -I./include + +RustEnclave_Link_Libs := -L$(CUSTOM_LIBRARY_PATH) -lenclave +RustEnclave_Compile_Flags := $(SGX_COMMON_CFLAGS) $(ENCLAVE_CFLAGS) $(RustEnclave_Include_Paths) +RustEnclave_Link_Flags := -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) \ + -Wl,--whole-archive -l$(Trts_Library_Name) -Wl,--no-whole-archive \ + -Wl,--start-group -lsgx_tstdc -l$(Service_Library_Name) -l$(Crypto_Library_Name) $(RustEnclave_Link_Libs) -Wl,--end-group \ + -Wl,--version-script=enclave/Enclave.lds \ + $(ENCLAVE_LDFLAGS) + +RustEnclave_Name := enclave/enclave.so +Signed_RustEnclave_Name := bin/enclave.signed.so + +.PHONY: all +all: $(App_Name) $(Signed_RustEnclave_Name) + +######## EDL Objects ######## + +$(Enclave_EDL_Files): $(SGX_EDGER8R) enclave/Enclave.edl + $(SGX_EDGER8R) --trusted enclave/Enclave.edl --search-path $(OCALLS_DIR) --search-path $(SGX_SDK)/include --search-path $(CUSTOM_EDL_PATH) --trusted-dir enclave + $(SGX_EDGER8R) --untrusted enclave/Enclave.edl --search-path $(OCALLS_DIR) --search-path $(SGX_SDK)/include --search-path $(CUSTOM_EDL_PATH) --untrusted-dir app + @echo "GEN => $(Enclave_EDL_Files)" + +######## App Objects ######## + +app/Enclave_u.o: $(Enclave_EDL_Files) + @$(CC) $(App_C_Flags) -c app/Enclave_u.c -o $@ + @echo "CC <= $<" + +$(App_Enclave_u_Object): app/Enclave_u.o + $(AR) rcsD $@ $^ + +$(App_Ocall_Object): + @$(MAKE) -C $(OCALLS_DIR) + @cp $(OCALLS_DIR)/$(App_Ocall_Object_Name) $@ + +$(App_Name): $(App_Ocall_Object) $(App_Enclave_u_Object) $(App_SRC_Files) + @cd app && SGX_SDK=$(SGX_SDK) cargo build $(App_Rust_Flags) + @echo "Cargo => $@" + mkdir -p bin + cp $(App_Rust_Path)/app ./bin + +######## Enclave Objects ######## + +enclave/Enclave_t.o: $(Enclave_EDL_Files) + @$(CC) $(RustEnclave_Compile_Flags) -c enclave/Enclave_t.c -o $@ + @echo "CC <= $<" + +$(RustEnclave_Name): enclave enclave/Enclave_t.o + @$(CXX) enclave/Enclave_t.o -o $@ $(RustEnclave_Link_Flags) + @echo "LINK => $@" + +$(Signed_RustEnclave_Name): $(RustEnclave_Name) + mkdir -p bin + @$(SGX_ENCLAVE_SIGNER) sign -key enclave/Enclave_private.pem -enclave $(RustEnclave_Name) -out $@ -config enclave/Enclave.config.xml + @echo "SIGN => $@" + +.PHONY: enclave +enclave: + $(MAKE) -C ./enclave/ + + +.PHONY: clean +clean: + @rm -f $(App_Name) $(RustEnclave_Name) $(Signed_RustEnclave_Name) enclave/*_t.* app/*_u.* lib/*.a + @cd enclave && cargo clean && rm -f Cargo.lock + @cd app && cargo clean && rm -f Cargo.lock diff --git a/src/libos/crates/vdso-time/examples/sgx/README.md b/src/libos/crates/vdso-time/examples/sgx/README.md new file mode 100644 index 00000000..b9cf4940 --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/README.md @@ -0,0 +1,11 @@ +## example for SGX +This is an example of using vdso-time in SGX. +This example combines vdso-time example of io_uring and hello-rust example of incubator-teaclave-sgx-sdk. +- ./app : untrusted code +- ./bin : executable program +- ./enclave : trusted code +- ./lib : library + +### run example in SGX +1. ```make``` +2. ```cd bin && ./app``` diff --git a/src/libos/crates/vdso-time/examples/sgx/app/Cargo.toml b/src/libos/crates/vdso-time/examples/sgx/app/Cargo.toml new file mode 100644 index 00000000..65017b31 --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/app/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "app" +version = "1.0.0" +build = "build.rs" + +[dependencies] +sgx_types = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_types" } +sgx_urts = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_urts" } + +[workspace] \ No newline at end of file diff --git a/src/libos/crates/vdso-time/examples/sgx/app/build.rs b/src/libos/crates/vdso-time/examples/sgx/app/build.rs new file mode 100644 index 00000000..4ce7f1ab --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/app/build.rs @@ -0,0 +1,34 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.. + +use std::env; + +fn main() { + let sdk_dir = env::var("SGX_SDK").unwrap_or_else(|_| "/opt/sgxsdk".to_string()); + let is_sim = env::var("SGX_MODE").unwrap_or_else(|_| "HW".to_string()); + + println!("cargo:rustc-link-search=native=../lib"); + println!("cargo:rustc-link-lib=static=Enclave_u"); + println!("cargo:rustc-link-lib=static=vdso_time_ocalls"); + + println!("cargo:rustc-link-search=native={}/lib64", sdk_dir); + match is_sim.as_ref() { + "SW" => println!("cargo:rustc-link-lib=dylib=sgx_urts_sim"), + "HW" => println!("cargo:rustc-link-lib=dylib=sgx_urts"), + _ => println!("cargo:rustc-link-lib=dylib=sgx_urts"), // Treat undefined as HW + } +} diff --git a/src/libos/crates/vdso-time/examples/sgx/app/rust-toolchain b/src/libos/crates/vdso-time/examples/sgx/app/rust-toolchain new file mode 100644 index 00000000..148ed93d --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/app/rust-toolchain @@ -0,0 +1 @@ +nightly-2020-10-25 diff --git a/src/libos/crates/vdso-time/examples/sgx/app/src/main.rs b/src/libos/crates/vdso-time/examples/sgx/app/src/main.rs new file mode 100644 index 00000000..d80cc877 --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/app/src/main.rs @@ -0,0 +1,78 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.. + +extern crate sgx_types; +extern crate sgx_urts; +use sgx_types::*; +use sgx_urts::SgxEnclave; + +static ENCLAVE_FILE: &'static str = "enclave.signed.so"; + +extern "C" { + fn run_sgx_example(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t; +} + +fn init_enclave() -> SgxResult { + let mut launch_token: sgx_launch_token_t = [0; 1024]; + let mut launch_token_updated: i32 = 0; + // call sgx_create_enclave to initialize an enclave instance + // Debug Support: set 2nd parameter to 1 + let debug = 1; + let mut misc_attr = sgx_misc_attribute_t { + secs_attr: sgx_attributes_t { flags: 0, xfrm: 0 }, + misc_select: 0, + }; + SgxEnclave::create( + ENCLAVE_FILE, + debug, + &mut launch_token, + &mut launch_token_updated, + &mut misc_attr, + ) +} + +fn main() { + let enclave = match init_enclave() { + Ok(r) => { + println!("[+] Init Enclave Successful {}!", r.geteid()); + r + } + Err(x) => { + println!("[-] Init Enclave Failed {}!", x.as_str()); + return; + } + }; + + let mut retval = sgx_status_t::SGX_SUCCESS; + let result = unsafe { run_sgx_example(enclave.geteid(), &mut retval) }; + match result { + sgx_status_t::SGX_SUCCESS => {} + _ => { + println!("[-] ECALL Enclave Failed {}!", result.as_str()); + return; + } + } + match retval { + sgx_status_t::SGX_SUCCESS => {} + _ => { + println!("[-] ECALL Returned Error {}!", retval.as_str()); + return; + } + } + println!("[+] run_sgx_example success..."); + enclave.destroy(); +} diff --git a/src/libos/crates/vdso-time/examples/sgx/bin/readme.txt b/src/libos/crates/vdso-time/examples/sgx/bin/readme.txt new file mode 100644 index 00000000..c5e82d74 --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/bin/readme.txt @@ -0,0 +1 @@ +bin \ No newline at end of file diff --git a/src/libos/crates/vdso-time/examples/sgx/enclave/Cargo.toml b/src/libos/crates/vdso-time/examples/sgx/enclave/Cargo.toml new file mode 100644 index 00000000..89f91f2c --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/enclave/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "Helloworldsampleenclave" +version = "1.0.0" + +[lib] +name = "helloworldsampleenclave" +crate-type = ["staticlib"] + +[features] +default = [] + +[dependencies] +vdso-time = { path = "../../../../vdso-time", default-features = false, features = ["sgx"] } +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } + +[target.'cfg(not(target_env = "sgx"))'.dependencies] +sgx_types = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_types" } +sgx_tstd = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_tstd", features = ["backtrace", "thread"] } +sgx_trts = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_trts" } +sgx_libc = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_libc" } + +[workspace] \ No newline at end of file diff --git a/src/libos/crates/vdso-time/examples/sgx/enclave/Enclave.config.xml b/src/libos/crates/vdso-time/examples/sgx/enclave/Enclave.config.xml new file mode 100644 index 00000000..5293132b --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/enclave/Enclave.config.xml @@ -0,0 +1,13 @@ + + + 0 + 0 + 0x40000 + 0x400000 + 1 + 1 + 0 + 0 + 0 + 0xFFFFFFFF + diff --git a/src/libos/crates/vdso-time/examples/sgx/enclave/Enclave.edl b/src/libos/crates/vdso-time/examples/sgx/enclave/Enclave.edl new file mode 100644 index 00000000..243eb4e2 --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/enclave/Enclave.edl @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +enclave { + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; + from "sgx_net.edl" import *; + from "sgx_thread.edl" import *; + + from "sgx_vdso_time_ocalls.edl" import *; + + trusted { + /* define ECALLs here. */ + public sgx_status_t run_sgx_example(); + }; + + untrusted { + /* define OCALLs here. */ + }; +}; diff --git a/src/libos/crates/vdso-time/examples/sgx/enclave/Enclave.lds b/src/libos/crates/vdso-time/examples/sgx/enclave/Enclave.lds new file mode 100644 index 00000000..e3d9d0ee --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/enclave/Enclave.lds @@ -0,0 +1,9 @@ +enclave.so +{ + global: + g_global_data_sim; + g_global_data; + enclave_entry; + local: + *; +}; diff --git a/src/libos/crates/vdso-time/examples/sgx/enclave/Enclave_private.pem b/src/libos/crates/vdso-time/examples/sgx/enclave/Enclave_private.pem new file mode 100644 index 00000000..529d07be --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/enclave/Enclave_private.pem @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG4gIBAAKCAYEAroOogvsj/fZDZY8XFdkl6dJmky0lRvnWMmpeH41Bla6U1qLZ +AmZuyIF+mQC/cgojIsrBMzBxb1kKqzATF4+XwPwgKz7fmiddmHyYz2WDJfAjIveJ +ZjdMjM4+EytGlkkJ52T8V8ds0/L2qKexJ+NBLxkeQLfV8n1mIk7zX7jguwbCG1Pr +nEMdJ3Sew20vnje+RsngAzdPChoJpVsWi/K7cettX/tbnre1DL02GXc5qJoQYk7b +3zkmhz31TgFrd9VVtmUGyFXAysuSAb3EN+5VnHGr0xKkeg8utErea2FNtNIgua8H +ONfm9Eiyaav1SVKzPHlyqLtcdxH3I8Wg7yqMsaprZ1n5A1v/levxnL8+It02KseD +5HqV4rf/cImSlCt3lpRg8U5E1pyFQ2IVEC/XTDMiI3c+AR+w2jSRB3Bwn9zJtFlW +KHG3m1xGI4ck+Lci1JvWWLXQagQSPtZTsubxTQNx1gsgZhgv1JHVZMdbVlAbbRMC +1nSuJNl7KPAS/VfzAgEDAoIBgHRXxaynbVP5gkO0ug6Qw/E27wzIw4SmjsxG6Wpe +K7kfDeRskKxESdsA/xCrKkwGwhcx1iIgS5+Qscd1Yg+1D9X9asd/P7waPmWoZd+Z +AhlKwhdPsO7PiF3e1AzHhGQwsUTt/Y/aSI1MpHBvy2/s1h9mFCslOUxTmWw0oj/Q +ldIEgWeNR72CE2+jFIJIyml6ftnb6qzPiga8Bm48ubKh0kvySOqnkmnPzgh+JBD6 +JnBmtZbfPT97bwTT+N6rnPqOOApvfHPf15kWI8yDbprG1l4OCUaIUH1AszxLd826 +5IPM+8gINLRDP1MA6azECPjTyHXhtnSIBZCyWSVkc05vYmNXYUNiXWMajcxW9M02 +wKzFELO8NCEAkaTPxwo4SCyIjUxiK1LbQ9h8PSy4c1+gGP4LAMR8xqP4QKg6zdu9 +osUGG/xRe/uufgTBFkcjqBHtK5L5VI0jeNIUAgW/6iNbYXjBMJ0GfauLs+g1VsOm +WfdgXzsb9DYdMa0OXXHypmV4GwKBwQDUwQj8RKJ6c8cT4vcWCoJvJF00+RFL+P3i +Gx2DLERxRrDa8AVGfqaCjsR+3vLgG8V/py+z+dxZYSqeB80Qeo6PDITcRKoeAYh9 +xlT3LJOS+k1cJcEmlbbO2IjLkTmzSwa80fWexKu8/Xv6vv15gpqYl1ngYoqJM3pd +vzmTIOi7MKSZ0WmEQavrZj8zK4endE3v0eAEeQ55j1GImbypSf7Idh7wOXtjZ7WD +Dg6yWDrri+AP/L3gClMj8wsAxMV4ZR8CgcEA0fzDHkFa6raVOxWnObmRoDhAtE0a +cjUj976NM5yyfdf2MrKy4/RhdTiPZ6b08/lBC/+xRfV3xKVGzacm6QjqjZrUpgHC +0LKiZaMtccCJjLtPwQd0jGQEnKfMFaPsnhOc5y8qVkCzVOSthY5qhz0XNotHHFmJ +gffVgB0iqrMTvSL7IA2yqqpOqNRlhaYhNl8TiFP3gIeMtVa9rZy31JPgT2uJ+kfo +gV7sdTPEjPWZd7OshGxWpT6QfVDj/T9T7L6tAoHBAI3WBf2DFvxNL2KXT2QHAZ9t +k3imC4f7U+wSE6zILaDZyzygA4RUbwG0gv8/TJVn2P/Eynf76DuWHGlaiLWnCbSz +Az2DHBQBBaku409zDQym3j1ugMRjzzSQWzJg0SIyBH3hTmnYcn3+Uqcp/lEBvGW6 +O+rsXFt3pukqJmIV8HzLGGaLm62BHUeZf3dyWm+i3p/hQAL7Xvu04QW70xuGqdr5 +afV7p5eaeQIJXyGQJ0eylV/90+qxjMKiB1XYg6WYvwKBwQCL/ddpgOdHJGN8uRom +e7Zq0Csi3hGheMKlKbN3vcxT5U7MdyHtTZZOJbTvxKNNUNYH/8uD+PqDGNneb29G +BfGzvI3EASyLIcGZF3OhKwZd0jUrWk2y7Vhob91jwp2+t73vdMbkKyI4mHOuXvGv +fg95si9oO7EBT+Oqvhccd2J+F1IVXncccYnF4u5ZGWt5lLewN/pVr7MjjykeaHqN +t+rfnQam2psA6fL4zS2zTmZPzR2tnY8Y1GBTi0Ko1OKd1HMCgcAb5cB/7/AQlhP9 +yQa04PLH9ygQkKKptZp7dy5WcWRx0K/hAHRoi2aw1wZqfm7VBNu2SLcs90kCCCxp +6C5sfJi6b8NpNbIPC+sc9wsFr7pGo9SFzQ78UlcWYK2Gu2FxlMjonhka5hvo4zvg +WxlpXKEkaFt3gLd92m/dMqBrHfafH7VwOJY2zT3WIpjwuk0ZzmRg5p0pG/svVQEH +NZmwRwlopysbR69B/n1nefJ84UO50fLh5s5Zr3gBRwbWNZyzhXk= +-----END RSA PRIVATE KEY----- diff --git a/src/libos/crates/vdso-time/examples/sgx/enclave/Makefile b/src/libos/crates/vdso-time/examples/sgx/enclave/Makefile new file mode 100644 index 00000000..e269c002 --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/enclave/Makefile @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +Rust_Enclave_Name := libenclave.a +Rust_Enclave_Files := $(wildcard src/*.rs) +Rust_Target_Path := $(CURDIR)/../../../../../../deps/rust-sgx-sdk/xargo + +ifeq ($(MITIGATION-CVE-2020-0551), LOAD) +export MITIGATION_CVE_2020_0551=LOAD +else ifeq ($(MITIGATION-CVE-2020-0551), CF) +export MITIGATION_CVE_2020_0551=CF +endif + +.PHONY: all + +all: $(Rust_Enclave_Name) + +$(Rust_Enclave_Name): $(Rust_Enclave_Files) +ifeq ($(XARGO_SGX), 1) + RUST_TARGET_PATH=$(Rust_Target_Path) xargo build --target x86_64-unknown-linux-sgx --release + cp ./target/x86_64-unknown-linux-sgx/release/libhelloworldsampleenclave.a ../lib/libenclave.a +else + cargo build --release + cp ./target/release/libhelloworldsampleenclave.a ../lib/libenclave.a +endif \ No newline at end of file diff --git a/src/libos/crates/vdso-time/examples/sgx/enclave/rust-toolchain b/src/libos/crates/vdso-time/examples/sgx/enclave/rust-toolchain new file mode 100644 index 00000000..148ed93d --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/enclave/rust-toolchain @@ -0,0 +1 @@ +nightly-2020-10-25 diff --git a/src/libos/crates/vdso-time/examples/sgx/enclave/src/lib.rs b/src/libos/crates/vdso-time/examples/sgx/enclave/src/lib.rs new file mode 100644 index 00000000..88168fc8 --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/enclave/src/lib.rs @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.. + +#![crate_name = "helloworldsampleenclave"] +#![crate_type = "staticlib"] +#![cfg_attr(not(target_env = "sgx"), no_std)] +#![cfg_attr(target_env = "sgx", feature(rustc_private))] + +extern crate sgx_trts; +extern crate sgx_types; +#[cfg(not(target_env = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; +extern crate sgx_libc as libc; + +extern crate vdso_time; +extern crate lazy_static; + +use sgx_types::*; +use std::prelude::v1::*; + +include!("../../../common/example.rs"); +include!("../../../common/bench.rs"); + +#[no_mangle] +pub extern "C" fn run_sgx_example() -> sgx_status_t { + // std::backtrace::enable_backtrace("enclave.signed.so", std::backtrace::PrintFormat::Full); + println!("[ECALL] run_sgx_example"); + + example(); + + vdso_benchmarks(); + + sgx_status_t::SGX_SUCCESS +} diff --git a/src/libos/crates/vdso-time/examples/sgx/enclave/x86_64-unknown-linux-sgx.json b/src/libos/crates/vdso-time/examples/sgx/enclave/x86_64-unknown-linux-sgx.json new file mode 100644 index 00000000..10d37a74 --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/enclave/x86_64-unknown-linux-sgx.json @@ -0,0 +1,31 @@ +{ + "arch": "x86_64", + "cpu": "x86-64", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "dynamic-linking": true, + "env": "sgx", + "exe-allocation-crate": "alloc_system", + "executables": true, + "has-elf-tls": true, + "has-rpath": true, + "linker-flavor": "gcc", + "linker-is-gnu": true, + "llvm-target": "x86_64-unknown-linux-gnu", + "max-atomic-width": 64, + "os": "linux", + "position-independent-executables": true, + "pre-link-args": { + "gcc": [ + "-Wl,--as-needed", + "-Wl,-z,noexecstack", + "-m64" + ] + }, + "relro-level": "full", + "stack-probes": true, + "target-c-int-width": "32", + "target-endian": "little", + "target-family": "unix", + "target-pointer-width": "64", + "vendor": "mesalock" +} diff --git a/src/libos/crates/vdso-time/examples/sgx/lib/readme.txt b/src/libos/crates/vdso-time/examples/sgx/lib/readme.txt new file mode 100644 index 00000000..7951405f --- /dev/null +++ b/src/libos/crates/vdso-time/examples/sgx/lib/readme.txt @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/src/libos/crates/vdso-time/ocalls/.gitignore b/src/libos/crates/vdso-time/ocalls/.gitignore new file mode 100644 index 00000000..d3399f6c --- /dev/null +++ b/src/libos/crates/vdso-time/ocalls/.gitignore @@ -0,0 +1,3 @@ +*.o +*.a +*.so diff --git a/src/libos/crates/vdso-time/ocalls/Makefile b/src/libos/crates/vdso-time/ocalls/Makefile new file mode 100644 index 00000000..e95d3a4a --- /dev/null +++ b/src/libos/crates/vdso-time/ocalls/Makefile @@ -0,0 +1,12 @@ +all: libs + +libs: libvdso_time_ocalls.a + +libvdso_time_ocalls.a: vdso_time_ocalls.o + ar rcs $@ $^ + +vdso_time_ocalls.o: vdso-time-ocalls.c + gcc -O3 -c -o $@ $< + +clean: + rm -f *.o *.a \ No newline at end of file diff --git a/src/libos/crates/vdso-time/ocalls/sgx_vdso_time_ocalls.edl b/src/libos/crates/vdso-time/ocalls/sgx_vdso_time_ocalls.edl new file mode 100644 index 00000000..2daed5eb --- /dev/null +++ b/src/libos/crates/vdso-time/ocalls/sgx_vdso_time_ocalls.edl @@ -0,0 +1,14 @@ +enclave { + include "time.h" + + untrusted { + int vdso_ocall_get_vdso_info( + [out] unsigned long* vdso_addr, + [out, size = release_len] char* release, + int release_len + ); + + int vdso_ocall_clock_gettime(int clockid, [out] struct timespec* ts); + int vdso_ocall_clock_getres(int clockid, [out] struct timespec* res); + }; +}; diff --git a/src/libos/crates/vdso-time/ocalls/vdso-time-ocalls.c b/src/libos/crates/vdso-time/ocalls/vdso-time-ocalls.c new file mode 100644 index 00000000..da8c5e1e --- /dev/null +++ b/src/libos/crates/vdso-time/ocalls/vdso-time-ocalls.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +int vdso_ocall_get_vdso_info( + unsigned long *vdso_addr, + char *release, + int release_len) { + // If AT_SYSINFO_EHDR isn't found, getauxval will return 0. + *vdso_addr = getauxval(AT_SYSINFO_EHDR); + + struct utsname buf; + int ret = uname(&buf); + // uname should always succeed here, since uname only fails when buf is not invalid. + if (ret != 0) { return -1; } + + strncpy(release, buf.release, release_len); + release[release_len - 1] = '\0'; + + return 0; +} + +int vdso_ocall_clock_gettime(int clockid, struct timespec *tp) { + return clock_gettime(clockid, tp); +} + +int vdso_ocall_clock_getres(int clockid, struct timespec *res) { + return clock_getres(clockid, res); +} diff --git a/src/libos/crates/vdso-time/src/lib.rs b/src/libos/crates/vdso-time/src/lib.rs new file mode 100644 index 00000000..9737f2f6 --- /dev/null +++ b/src/libos/crates/vdso-time/src/lib.rs @@ -0,0 +1,622 @@ +#![cfg_attr(feature = "sgx", no_std)] + +#[cfg(feature = "sgx")] +extern crate sgx_types; +#[cfg(feature = "sgx")] +#[macro_use] +extern crate sgx_tstd as std; +#[cfg(feature = "sgx")] +extern crate sgx_libc as libc; +#[cfg(feature = "sgx")] +extern crate sgx_trts; + +mod sys; + +use errno::prelude::*; +use lazy_static::lazy_static; +use log::trace; +use std::convert::TryFrom; +use std::time::Duration; +use std::{hint, str}; +use sys::*; + +pub const NANOS_PER_SEC: u32 = 1_000_000_000; +pub const NANOS_PER_MILLI: u32 = 1_000_000; +pub const NANOS_PER_MICRO: u32 = 1_000; +pub const MILLIS_PER_SEC: u64 = 1_000; +pub const MICROS_PER_SEC: u64 = 1_000_000; + +/// Clocks supported by the linux kernel, corresponding to clockid_t in Linux. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[allow(non_camel_case_types)] +pub enum ClockId { + CLOCK_REALTIME = 0, + CLOCK_MONOTONIC = 1, + // vDSO doesn't support CLOCK_PROCESS_CPUTIME_ID. + CLOCK_PROCESS_CPUTIME_ID = 2, + // vDSO doesn't support CLOCK_THREAD_CPUTIME_ID. + CLOCK_THREAD_CPUTIME_ID = 3, + CLOCK_MONOTONIC_RAW = 4, + CLOCK_REALTIME_COARSE = 5, + CLOCK_MONOTONIC_COARSE = 6, + CLOCK_BOOTTIME = 7, +} + +impl TryFrom for ClockId { + type Error = Error; + + fn try_from(clockid: i32) -> Result { + Ok(match clockid { + 0 => ClockId::CLOCK_REALTIME, + 1 => ClockId::CLOCK_MONOTONIC, + 2 => ClockId::CLOCK_PROCESS_CPUTIME_ID, + 3 => ClockId::CLOCK_THREAD_CPUTIME_ID, + 4 => ClockId::CLOCK_MONOTONIC_RAW, + 5 => ClockId::CLOCK_REALTIME_COARSE, + 6 => ClockId::CLOCK_MONOTONIC_COARSE, + 7 => ClockId::CLOCK_BOOTTIME, + _ => return_errno!(EINVAL, "Unsupported clockid"), + }) + } +} + +/// An abstraction of Linux vDSO provides the clock and time interface through Linux vDSO. +pub struct Vdso { + vdso_data_ptr: VdsoDataPtr, + // hres resolution for clock_getres + hres_resolution: Option, + // coarse resolution for clock_getres + coarse_resolution: Option, +} + +impl Vdso { + /// Try to create a new Vdso by libc or SGX OCALL. + /// + /// # Examples + /// + /// ``` + /// use vdso_time::Vdso; + /// let vdso = Vdso::new().unwrap(); + /// ``` + pub fn new() -> Result { + let vdso_data_ptr = Self::get_vdso_data_ptr_from_host()?; + let hres_resolution = clock_getres_slow(ClockId::CLOCK_MONOTONIC).ok(); + let coarse_resolution = clock_getres_slow(ClockId::CLOCK_MONOTONIC_COARSE).ok(); + let vdso = Self { + vdso_data_ptr, + hres_resolution, + coarse_resolution, + }; + vdso.check_accuracy()?; + Ok(vdso) + } + + #[cfg(feature = "sgx")] + fn get_vdso_data_ptr_from_host() -> Result { + extern "C" { + fn vdso_ocall_get_vdso_info( + ret: *mut libc::c_int, + vdso_addr: *mut libc::c_ulong, + release: *mut libc::c_char, + release_len: libc::c_int, + ) -> sgx_types::sgx_status_t; + } + + let mut vdso_addr: libc::c_ulong = 0; + let mut release = [0 as libc::c_char; 65]; + let mut ret: libc::c_int = 0; + unsafe { + vdso_ocall_get_vdso_info( + &mut ret as *mut _, + &mut vdso_addr as *mut _, + release.as_mut_ptr(), + release.len() as _, + ); + } + if ret != 0 { + return_errno!(EINVAL, "Vdso vdso_ocall_get_vdso_info() failed") + } + + Self::match_kernel_version(vdso_addr, &release) + } + + #[cfg(not(feature = "sgx"))] + fn get_vdso_data_ptr_from_host() -> Result { + const AT_SYSINFO_EHDR: u64 = 33; + let vdso_addr = unsafe { libc::getauxval(AT_SYSINFO_EHDR) }; + + let mut utsname: libc::utsname = unsafe { std::mem::zeroed() }; + let ret = unsafe { libc::uname(&mut utsname as *mut _) }; + if ret != 0 { + return_errno!(EINVAL, "Vdso get utsname failed"); + } + let release = utsname.release; + + Self::match_kernel_version(vdso_addr, &release) + } + + fn check_vdso_addr(vdso_addr: &u64) -> Result<()> { + let vdso_addr = *vdso_addr; + if vdso_addr == 0 { + return_errno!(EFAULT, "Vdso vdso_addr is 0") + } + const VDSO_DATA_MAX_SIZE: u64 = 4 * PAGE_SIZE; + if vdso_addr < VDSO_DATA_MAX_SIZE { + return_errno!(EFAULT, "Vdso vdso_addr is less than vdso data size"); + } + + #[cfg(feature = "sgx")] + if !sgx_trts::trts::rsgx_raw_is_outside_enclave( + (vdso_addr - VDSO_DATA_MAX_SIZE) as *const u8, + VDSO_DATA_MAX_SIZE as _, + ) { + return_errno!(EFAULT, "Vdso vdso_addr we got is not outside enclave") + } + + Ok(()) + } + + fn match_kernel_version(vdso_addr: u64, release: &[libc::c_char]) -> Result { + Self::check_vdso_addr(&vdso_addr)?; + + // release, e.g., "5.9.6-050906-generic" + let release = unsafe { &*(release as *const [i8] as *const [u8]) }; + let release = str::from_utf8(release); + if release.is_err() { + return_errno!(EINVAL, "Vdso get kernel release failed") + } + let mut release = release.unwrap().split(&['-', '.', ' '][..]); + let version_big: u8 = release + .next() + .ok_or(errno!(EINVAL, "Vdso get kernel big version failed"))? + .parse()?; + let version_little: u8 = release + .next() + .ok_or(errno!(EINVAL, "Vdso get kernel little version failed"))? + .parse()?; + + Ok(match (version_big, version_little) { + (4, 0..=4) | (4, 7..=11) => VdsoDataPtr::V4_0(vdso_data_v4_0::vdsodata_ptr(vdso_addr)), + (4, 5..=6) | (4, 12..=19) => VdsoDataPtr::V4_5(vdso_data_v4_5::vdsodata_ptr(vdso_addr)), + (5, 0..=2) => VdsoDataPtr::V5_0(vdso_data_v5_0::vdsodata_ptr(vdso_addr)), + (5, 3..=5) => VdsoDataPtr::V5_3(vdso_data_v5_3::vdsodata_ptr(vdso_addr)), + (5, 6..=8) => VdsoDataPtr::V5_6(vdso_data_v5_6::vdsodata_ptr(vdso_addr)), + (5, 9..=19) | (6, 0..=2) => VdsoDataPtr::V5_9(vdso_data_v5_9::vdsodata_ptr(vdso_addr)), + (_, _) => return_errno!(EINVAL, "Vdso match kernel release failed"), + }) + } + + /// Compare the results of Linux syscall and vdso to check whether vdso can support the clockid correctly. + fn check_accuracy(&self) -> Result<()> { + let vdso_supported_clockids = [ + ClockId::CLOCK_REALTIME, + ClockId::CLOCK_MONOTONIC, + ClockId::CLOCK_MONOTONIC_RAW, + ClockId::CLOCK_REALTIME_COARSE, + ClockId::CLOCK_MONOTONIC_COARSE, + ClockId::CLOCK_BOOTTIME, + ]; + const MAX_INACCURACY: Duration = Duration::from_millis(1); + const MAX_RETRY_NUM: u32 = 3; + for &clockid in vdso_supported_clockids.iter() { + for retry_num in 0..MAX_RETRY_NUM { + let time = match self.do_clock_gettime(clockid) { + Ok(time) => time, + Err(_) => break, + }; + + let host_time = match clock_gettime_slow(clockid) { + Ok(host_time) => host_time, + Err(_) => break, + }; + + let estimated_inaccuracy = match host_time.checked_sub(time) { + Some(diff) => diff, + None => return_errno!(EOPNOTSUPP, "Vdso can not provide valid time"), + }; + if estimated_inaccuracy > MAX_INACCURACY { + if retry_num == MAX_RETRY_NUM - 1 { + return_errno!(EOPNOTSUPP, "Vdso reached max retry number"); + } + continue; + } + break; + } + } + Ok(()) + } + + /// Try to get time according to ClockId. + /// Firstly try to get time through vDSO, if failed, then try fallback. + /// + /// # Examples + /// + /// ``` + /// use vdso_time::{Vdso, ClockId}; + /// let vdso = Vdso::new().unwrap(); + /// let time = vdso.clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap(); + /// println!("{:?}", time); + /// ``` + pub fn clock_gettime(&self, clockid: ClockId) -> Result { + self.do_clock_gettime(clockid) + .or_else(|_| clock_gettime_slow(clockid)) + } + + /// Try to get time resolution according to ClockId. + /// Firstly try to return resolution inside self, if failed, then try fallback. + /// + /// # Examples + /// + /// ``` + /// use vdso_time::{Vdso, ClockId}; + /// let vdso = Vdso::new().unwrap(); + /// let res = vdso.clock_getres(ClockId::CLOCK_MONOTONIC).unwrap(); + /// println!("{:?}", res); + /// ``` + pub fn clock_getres(&self, clockid: ClockId) -> Result { + self.do_clock_getres(clockid) + .or_else(|_| clock_getres_slow(clockid)) + } + + fn do_clock_gettime(&self, clockid: ClockId) -> Result { + match clockid { + ClockId::CLOCK_REALTIME | ClockId::CLOCK_MONOTONIC | ClockId::CLOCK_BOOTTIME => { + self.do_hres(ClockSource::CS_HRES_COARSE, clockid) + } + ClockId::CLOCK_MONOTONIC_RAW => self.do_hres(ClockSource::CS_RAW, clockid), + ClockId::CLOCK_REALTIME_COARSE | ClockId::CLOCK_MONOTONIC_COARSE => { + self.do_coarse(ClockSource::CS_HRES_COARSE, clockid) + } + // TODO: support CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID. + _ => return_errno!(EINVAL, "Unsupported clockid in do_clock_gettime()"), + } + } + + fn do_clock_getres(&self, clockid: ClockId) -> Result { + match clockid { + ClockId::CLOCK_REALTIME + | ClockId::CLOCK_MONOTONIC + | ClockId::CLOCK_BOOTTIME + | ClockId::CLOCK_MONOTONIC_RAW => self + .hres_resolution + .ok_or(errno!(EOPNOTSUPP, "hres_resolution is none")), + ClockId::CLOCK_REALTIME_COARSE | ClockId::CLOCK_MONOTONIC_COARSE => self + .coarse_resolution + .ok_or(errno!(EOPNOTSUPP, "coarse_resolution is none")), + // TODO: support CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID. + _ => return_errno!(EINVAL, "Unsupported clockid in do_clock_getres()"), + } + } + + fn vdso_data(&self, cs: ClockSource) -> &'static dyn VdsoData { + match self.vdso_data_ptr { + VdsoDataPtr::V4_0(ptr) => unsafe { &*(ptr) }, + VdsoDataPtr::V4_5(ptr) => unsafe { &*(ptr) }, + VdsoDataPtr::V5_0(ptr) => unsafe { &*(ptr) }, + VdsoDataPtr::V5_3(ptr) => unsafe { &*(ptr.add(cs as _)) }, + VdsoDataPtr::V5_6(ptr) => unsafe { &*(ptr.add(cs as _)) }, + VdsoDataPtr::V5_9(ptr) => unsafe { &*(ptr.add(cs as _)) }, + } + } + + fn do_hres(&self, cs: ClockSource, clockid: ClockId) -> Result { + let vdso_data = self.vdso_data(cs); + loop { + let seq = vdso_data.seq(); + // if seq is odd, it might means that a concurrent update is in progress. + // Hence, we do some instructions to spin waiting for seq to become even again. + if seq & 1 != 0 { + hint::spin_loop(); + continue; + } + + // Make sure that all prior load-from-memory instructions have completed locally, + // and no later instruction begins execution until LFENCE completes. + // We want to make sure the execution order as followning: + // seq -> [cycles, cycle_last, mult, shift, sec, secs] -> seq + // This LFENCE can ensure that the first seq is before [cycles, cycle_last, mult, shift, sec, secs] + lfence(); + + // Get hardware counter according to vdso_data's clock_mode. + let cycles = Self::get_hw_counter(vdso_data)?; + + let cycle_last = vdso_data.cycle_last(); + let mult = vdso_data.mult(); + let shift = vdso_data.shift(); + let secs = vdso_data.sec(clockid as _)?; + let mut nanos = vdso_data.nsec(clockid as _)?; + + if !Self::vdso_read_retry(vdso_data, seq) { + // On x86 arch, the TSC can be slightly off across sockets, + // which might cause cycles < cycle_last. Since they are u64 type, + // cycles - cycle_last will panic in this case. + // Hence we need to verify that cycles is greater than cycle_last. + // If not then just use cycle_last, which is the base time of the + // current conversion period. + // And the vdso mask is always u64_MAX on x86, we don't need use mask. + if cycles > cycle_last { + nanos += (cycles - cycle_last) * mult as u64 + } + nanos = nanos >> shift; + + return Ok(Duration::new(secs, nanos as u32)); + } + } + } + + fn do_coarse(&self, cs: ClockSource, clockid: ClockId) -> Result { + let vdso_data = self.vdso_data(cs); + loop { + let seq = vdso_data.seq(); + // see comments in do_hres + if seq & 1 != 0 { + hint::spin_loop(); + continue; + } + + // see comments in do_hres + lfence(); + + let secs = vdso_data.sec(clockid as _)?; + let nanos = vdso_data.nsec(clockid as _)?; + + if !Self::vdso_read_retry(vdso_data, seq) { + return Ok(Duration::new(secs, nanos as u32)); + } + } + } + + fn vdso_read_retry(vdso_data: &dyn VdsoData, old_seq: u32) -> bool { + // Make sure that all prior load-from-memory instructions have completed locally, + // and no later instruction begins execution until LFENCE completes + lfence(); + + old_seq != vdso_data.seq() + } + + fn get_hw_counter(vdso_data: &dyn VdsoData) -> Result { + let clock_mode = vdso_data.clock_mode(); + if clock_mode == VdsoClockMode::VDSO_CLOCKMODE_TSC as i32 { + return Ok(rdtsc_ordered()); + } else if clock_mode == VdsoClockMode::VDSO_CLOCKMODE_PVCLOCK as i32 { + // TODO: support pvclock + return_errno!( + EOPNOTSUPP, + "VDSO_CLOCKMODE_PVCLOCK support is not implemented" + ); + } else if clock_mode == VdsoClockMode::VDSO_CLOCKMODE_HVCLOCK as i32 { + // TODO: support hvclock + return_errno!( + EOPNOTSUPP, + "VDSO_CLOCKMODE_HVCLOCK support is not implemented" + ); + } else if clock_mode == VdsoClockMode::VDSO_CLOCKMODE_TIMENS as i32 { + // TODO: support timens + return_errno!( + EOPNOTSUPP, + "VDSO_CLOCKMODE_TIMENS support is not implemented" + ); + } else if clock_mode == VdsoClockMode::VDSO_CLOCKMODE_NONE as i32 { + // In x86 Linux, the clock_mode will never be VDSO_CLOCKMODE_NONE. + return_errno!(EINVAL, "The clock_mode must not be VDSO_CLOCKMODE_NONE"); + } + return_errno!(EINVAL, "Unsupported clock_mode"); + } +} + +unsafe impl Sync for Vdso {} +unsafe impl Send for Vdso {} + +lazy_static! { + static ref VDSO: Option = Vdso::new().map_or_else( + |e| { + trace!("{}", e); + None + }, + |v| Some(v) + ); +} + +/// Try to get time according to ClockId. +/// Firstly try to get time through vDSO, if failed, then try fallback. +/// +/// # Examples +/// +/// ``` +/// use vdso_time::ClockId; +/// +/// let time = vdso_time::clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap(); +/// println!("{:?}", time); +/// ``` +pub fn clock_gettime(clockid: ClockId) -> Result { + if VDSO.is_none() { + clock_gettime_slow(clockid) + } else { + VDSO.as_ref().unwrap().clock_gettime(clockid) + } +} + +/// Try to get time resolution according to ClockId. +/// Firstly try to get time through vDSO, if failed, then try fallback. +/// +/// # Examples +/// +/// ``` +/// use vdso_time::ClockId; +/// +/// let time = vdso_time::clock_getres(ClockId::CLOCK_MONOTONIC).unwrap(); +/// println!("{:?}", time); +/// ``` +pub fn clock_getres(clockid: ClockId) -> Result { + if VDSO.is_none() { + clock_getres_slow(clockid) + } else { + VDSO.as_ref().unwrap().clock_getres(clockid) + } +} + +fn clock_gettime_slow(clockid: ClockId) -> Result { + let mut ts = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + + cfg_if::cfg_if! { + if #[cfg(feature = "sgx")] { + extern "C" { + fn vdso_ocall_clock_gettime( + ret: *mut libc::c_int, + clockid: libc::c_int, + ts: *mut libc::timespec, + ) -> sgx_types::sgx_status_t; + } + let mut ret: libc::c_int = 0; + unsafe { + vdso_ocall_clock_gettime(&mut ret as *mut _, clockid as _, &mut ts as *mut _); + } + } else { + let ret = unsafe { libc::clock_gettime(clockid as _, &mut ts as *mut _) }; + } + } + + if ret == 0 { + Ok(Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32)) + } else { + return_errno!(EINVAL, "clock_gettime_slow failed") + } +} + +fn clock_getres_slow(clockid: ClockId) -> Result { + let mut res = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + + cfg_if::cfg_if! { + if #[cfg(feature = "sgx")] { + extern "C" { + fn vdso_ocall_clock_getres( + ret: *mut libc::c_int, + clockid: libc::c_int, + res: *mut libc::timespec, + ) -> sgx_types::sgx_status_t; + } + let mut ret: libc::c_int = 0; + unsafe { + vdso_ocall_clock_getres(&mut ret as *mut _, clockid as _, &mut res as *mut _); + } + } else { + let ret = unsafe { libc::clock_getres(clockid as _, &mut res as *mut _) }; + } + } + + if ret == 0 { + Ok(Duration::new(res.tv_sec as u64, res.tv_nsec as u32)) + } else { + return_errno!(EINVAL, "clock_getres_slow failed") + } +} + +// All unit tests +#[cfg(test)] +#[allow(deprecated)] +mod tests { + use super::*; + use std::thread; + + const LOOPS: usize = 3; + const SLEEP_DURATION: u64 = 10; + const HRES_MAX_DIFF_NANOS: u64 = 50_000; + const COARSE_MAX_DIFF_NANOS: u64 = 4_000_000; + + #[test] + fn test_clock_gettime() { + test_single_clock_gettime(ClockId::CLOCK_REALTIME_COARSE, COARSE_MAX_DIFF_NANOS); + test_single_clock_gettime(ClockId::CLOCK_MONOTONIC_COARSE, COARSE_MAX_DIFF_NANOS); + test_single_clock_gettime(ClockId::CLOCK_REALTIME, HRES_MAX_DIFF_NANOS); + test_single_clock_gettime(ClockId::CLOCK_MONOTONIC, HRES_MAX_DIFF_NANOS); + test_single_clock_gettime(ClockId::CLOCK_BOOTTIME, HRES_MAX_DIFF_NANOS); + test_single_clock_gettime(ClockId::CLOCK_MONOTONIC_RAW, HRES_MAX_DIFF_NANOS); + } + + fn test_single_clock_gettime(clockid: ClockId, max_diff_nanos: u64) { + for _ in 0..LOOPS { + let mut libc_tp = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + unsafe { libc::clock_gettime(clockid as _, &mut libc_tp as *mut _) }; + let libc_time = Duration::new(libc_tp.tv_sec as u64, libc_tp.tv_nsec as u32); + + let vdso_time = clock_gettime(clockid).unwrap(); + + assert!(vdso_time - libc_time <= Duration::from_nanos(max_diff_nanos)); + + thread::sleep(Duration::from_millis(SLEEP_DURATION)); + } + } + + #[test] + fn test_clock_getres() { + test_single_clock_getres(ClockId::CLOCK_REALTIME_COARSE); + test_single_clock_getres(ClockId::CLOCK_MONOTONIC_COARSE); + test_single_clock_getres(ClockId::CLOCK_REALTIME); + test_single_clock_getres(ClockId::CLOCK_MONOTONIC); + test_single_clock_getres(ClockId::CLOCK_BOOTTIME); + test_single_clock_getres(ClockId::CLOCK_MONOTONIC_RAW); + } + + fn test_single_clock_getres(clockid: ClockId) { + for _ in 0..LOOPS { + let mut libc_tp = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + unsafe { libc::clock_getres(clockid as _, &mut libc_tp as *mut _) }; + + let res = clock_getres(clockid).unwrap(); + + assert_eq!(res.as_secs(), libc_tp.tv_sec as u64); + assert_eq!(res.subsec_nanos(), libc_tp.tv_nsec as u32); + } + } + + #[test] + fn test_monotonic() { + let mut last_now = Duration::new(0, 0); + for _ in 0..1_000_000 { + let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap(); + assert!(now >= last_now); + last_now = now; + } + } + + mod logger { + use log::{Level, LevelFilter, Metadata, Record}; + + #[ctor::ctor] + fn auto_init() { + log::set_logger(&LOGGER) + .map(|()| log::set_max_level(LevelFilter::Trace)) + .expect("failed to init the logger"); + } + + static LOGGER: SimpleLogger = SimpleLogger; + + struct SimpleLogger; + + impl log::Log for SimpleLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= Level::Trace + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + println!("[{}] {}", record.level(), record.args()); + } + } + + fn flush(&self) {} + } + } +} diff --git a/src/libos/crates/vdso-time/src/sys.rs b/src/libos/crates/vdso-time/src/sys.rs new file mode 100644 index 00000000..69450acd --- /dev/null +++ b/src/libos/crates/vdso-time/src/sys.rs @@ -0,0 +1,595 @@ +use super::*; +use std::sync::atomic::{AtomicU32, Ordering}; + +pub const PAGE_SIZE: u64 = 4096; + +pub const CLOCK_TAI: usize = 11; +pub const VDSO_BASES: usize = CLOCK_TAI + 1; + +#[cfg(not(any(arget_arch = "x86", target_arch = "x86_64")))] +compile_error!("Only support x86 or x86_64 architecture now."); + +/// Reads the current value of the processor’s time-stamp counter. +/// +/// The processor monotonically increments the time-stamp counter MSR every clock cycle +/// and resets it to 0 whenever the processor is reset. +/// +/// The RDTSC instruction is not a serializing instruction. It does not necessarily +/// wait until all previous instructions have been executed before reading the counter. +/// Similarly, subsequent instructions may begin execution before the read operation is performed. +pub fn rdtsc() -> u64 { + cfg_if::cfg_if! { + if #[cfg(target_arch = "x86_64")] { + unsafe { core::arch::x86_64::_rdtsc() as u64 } + } else if #[cfg(target_arch = "x86")] { + unsafe { core::arch::x86::_rdtsc() as u64 } + } + } +} + +/// Reads the current value of the processor’s time-stamp counter. +/// +/// The processor monotonically increments the time-stamp counter MSR every clock cycle +/// and resets it to 0 whenever the processor is reset. +/// The RDTSCP instruction waits until all previous instructions have been executed before +/// reading the counter. However, subsequent instructions may begin execution before +/// the read operation is performed. +#[allow(dead_code)] +pub fn rdtscp() -> u64 { + cfg_if::cfg_if! { + if #[cfg(target_arch = "x86_64")] { + let mut aux: u32 = 0; + unsafe { core::arch::x86_64::__rdtscp(&mut aux) as u64 } + } else if #[cfg(target_arch = "x86")] { + let mut aux: u32 = 0; + unsafe { core::arch::x86::__rdtscp(&mut aux) as u64 } + } + } +} + +/// Performs a serializing operation on all load-from-memory instructions +/// that were issued prior to this instruction. +/// +/// Guarantees that every load instruction that precedes, in program order, +/// is globally visible before any load instruction which follows the fence in program order. +pub fn lfence() { + cfg_if::cfg_if! { + if #[cfg(target_arch = "x86_64")] { + unsafe { core::arch::x86_64::_mm_lfence() } + } else if #[cfg(target_arch = "x86")] { + unsafe { core::arch::x86::_mm_lfence() } + } + } +} + +/// Read the current TSC in program order. +/// +/// The RDTSC instruction might not be ordered relative to memory access. +/// But an RDTSC immediately after an appropriate barrier appears to be ordered as a normal load. +/// Hence, we could use a barrier before RDTSC to get ordered TSC. +/// +/// We also can just use RDTSCP, which is also ordered. +pub fn rdtsc_ordered() -> u64 { + lfence(); + rdtsc() +} + +/// The timers is divided in 3 sets (HRES, COARSE, RAW) since Linux v5.3. +/// CS_HRES_COARSE refers to the first two and CS_RAW to the third. +#[derive(Debug, Copy, Clone)] +#[allow(non_camel_case_types)] +pub enum ClockSource { + CS_HRES_COARSE = 0, + CS_RAW = 1, +} + +#[derive(Debug, Copy, Clone)] +#[allow(non_camel_case_types)] +pub enum VdsoClockMode { + VDSO_CLOCKMODE_NONE = 0, + VDSO_CLOCKMODE_TSC = 1, + VDSO_CLOCKMODE_PVCLOCK = 2, + VDSO_CLOCKMODE_HVCLOCK = 3, + VDSO_CLOCKMODE_TIMENS = i32::MAX as isize, +} + +// Struct VdsoDataPtr must impl this trait to unify vdso_data interface of different linux verisons. +pub trait VdsoData { + fn sec(&self, clockid: ClockId) -> Result; + fn nsec(&self, clockid: ClockId) -> Result; + fn seq(&self) -> u32; + fn clock_mode(&self) -> i32; + fn cycle_last(&self) -> u64; + fn mask(&self) -> u64; + fn mult(&self) -> u32; + fn shift(&self) -> u32; + fn tz_minuteswest(&self) -> i32; + fn tz_dsttime(&self) -> i32; + + fn vdsodata_ptr(vdso_addr: u64) -> *const Self + where + Self: Sized; +} + +pub enum VdsoDataPtr { + // === Linux 4.0 - 4.4, 4.7 - 4.11 === + V4_0(*const vdso_data_v4_0), + // === Linux 4.5 - 4.6, 4.12 - 4.19 === + V4_5(*const vdso_data_v4_5), + // === Linux 5.0 - 5.2 === + V5_0(*const vdso_data_v5_0), + // === Linux 5.3 - 5.5 === + V5_3(*const vdso_data_v5_3), + // === Linux 5.6 - 5.8 === + V5_6(*const vdso_data_v5_6), + // === Linux 5.9 - 6.2 === + V5_9(*const vdso_data_v5_9), +} + +// === Linux 4.0 - 4.4, 4.7 - 4.11 === +// struct vsyscall_gtod_data + +#[repr(C)] +pub struct vdso_data_v4_0 { + pub seq: AtomicU32, + + pub vclock_mode: i32, + pub cycle_last: u64, + pub mask: u64, + pub mult: u32, + pub shift: u32, + + pub wall_time_snsec: u64, + pub wall_time_sec: u64, + pub monotonic_time_sec: u64, + pub monotonic_time_snsec: u64, + pub wall_time_coarse_sec: u64, + pub wall_time_coarse_nsec: u64, + pub monotonic_time_coarse_sec: u64, + pub monotonic_time_coarse_nsec: u64, + + pub tz_minuteswest: i32, + pub tz_dsttime: i32, +} + +impl VdsoData for vdso_data_v4_0 { + fn vdsodata_ptr(vdso_addr: u64) -> *const Self { + (vdso_addr - 2 * PAGE_SIZE + 128) as *const Self + } + + fn sec(&self, clockid: ClockId) -> Result { + match clockid { + ClockId::CLOCK_REALTIME => Ok(self.wall_time_sec), + ClockId::CLOCK_MONOTONIC => Ok(self.monotonic_time_sec), + ClockId::CLOCK_REALTIME_COARSE => Ok(self.wall_time_coarse_sec), + ClockId::CLOCK_MONOTONIC_COARSE => Ok(self.monotonic_time_coarse_sec), + _ => return_errno!(EINVAL, "Unsupported clockid in sec()"), + } + } + + fn nsec(&self, clockid: ClockId) -> Result { + match clockid { + ClockId::CLOCK_REALTIME => Ok(self.wall_time_snsec), + ClockId::CLOCK_MONOTONIC => Ok(self.monotonic_time_snsec), + ClockId::CLOCK_REALTIME_COARSE => Ok(self.wall_time_coarse_nsec), + ClockId::CLOCK_MONOTONIC_COARSE => Ok(self.monotonic_time_coarse_nsec), + _ => return_errno!(EINVAL, "Unsupported clockid in nsec()"), + } + } + + fn seq(&self) -> u32 { + self.seq.load(Ordering::Relaxed) + } + + fn clock_mode(&self) -> i32 { + self.vclock_mode + } + + fn cycle_last(&self) -> u64 { + self.cycle_last + } + + fn mask(&self) -> u64 { + self.mask + } + + fn mult(&self) -> u32 { + self.mult + } + + fn shift(&self) -> u32 { + self.shift + } + + fn tz_minuteswest(&self) -> i32 { + self.tz_minuteswest + } + + fn tz_dsttime(&self) -> i32 { + self.tz_dsttime + } +} + +// === Linux 4.5 - 4.6, 4.12 - 4.19 === +// struct vsyscall_gtod_data + +#[repr(C)] +pub struct vdso_data_v4_5 { + pub seq: AtomicU32, + + pub vclock_mode: i32, + pub cycle_last: u64, + pub mask: u64, + pub mult: u32, + pub shift: u32, + + pub wall_time_snsec: u64, + pub wall_time_sec: u64, + pub monotonic_time_sec: u64, + pub monotonic_time_snsec: u64, + pub wall_time_coarse_sec: u64, + pub wall_time_coarse_nsec: u64, + pub monotonic_time_coarse_sec: u64, + pub monotonic_time_coarse_nsec: u64, + + pub tz_minuteswest: i32, + pub tz_dsttime: i32, +} + +impl VdsoData for vdso_data_v4_5 { + fn vdsodata_ptr(vdso_addr: u64) -> *const Self { + (vdso_addr - 3 * PAGE_SIZE + 128) as *const Self + } + + fn sec(&self, clockid: ClockId) -> Result { + match clockid { + ClockId::CLOCK_REALTIME => Ok(self.wall_time_sec), + ClockId::CLOCK_MONOTONIC => Ok(self.monotonic_time_sec), + ClockId::CLOCK_REALTIME_COARSE => Ok(self.wall_time_coarse_sec), + ClockId::CLOCK_MONOTONIC_COARSE => Ok(self.monotonic_time_coarse_sec), + _ => return_errno!(EINVAL, "Unsupported clockid in sec()"), + } + } + + fn nsec(&self, clockid: ClockId) -> Result { + match clockid { + ClockId::CLOCK_REALTIME => Ok(self.wall_time_snsec), + ClockId::CLOCK_MONOTONIC => Ok(self.monotonic_time_snsec), + ClockId::CLOCK_REALTIME_COARSE => Ok(self.wall_time_coarse_nsec), + ClockId::CLOCK_MONOTONIC_COARSE => Ok(self.monotonic_time_coarse_nsec), + _ => return_errno!(EINVAL, "Unsupported clockid in nsec()"), + } + } + + fn seq(&self) -> u32 { + self.seq.load(Ordering::Relaxed) + } + + fn clock_mode(&self) -> i32 { + self.vclock_mode + } + + fn cycle_last(&self) -> u64 { + self.cycle_last + } + + fn mask(&self) -> u64 { + self.mask + } + + fn mult(&self) -> u32 { + self.mult + } + + fn shift(&self) -> u32 { + self.shift + } + + fn tz_minuteswest(&self) -> i32 { + self.tz_minuteswest + } + + fn tz_dsttime(&self) -> i32 { + self.tz_dsttime + } +} + +// === Linux 5.0 - 5.2 === +// struct vsyscall_gtod_data + +#[repr(C)] +pub struct vdso_data_v5_0 { + pub seq: AtomicU32, + + pub vclock_mode: i32, + pub cycle_last: u64, + pub mask: u64, + pub mult: u32, + pub shift: u32, + + pub basetime: [vgtod_ts; VDSO_BASES], + + pub tz_minuteswest: i32, + pub tz_dsttime: i32, +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct vgtod_ts { + pub sec: u64, + pub nsec: u64, +} + +impl VdsoData for vdso_data_v5_0 { + fn vdsodata_ptr(vdso_addr: u64) -> *const Self { + (vdso_addr - 3 * PAGE_SIZE + 128) as *const Self + } + + fn sec(&self, clockid: ClockId) -> Result { + Ok(self.basetime[clockid as usize].sec) + } + + fn nsec(&self, clockid: ClockId) -> Result { + Ok(self.basetime[clockid as usize].nsec) + } + + fn seq(&self) -> u32 { + self.seq.load(Ordering::Relaxed) + } + + fn clock_mode(&self) -> i32 { + self.vclock_mode + } + + fn cycle_last(&self) -> u64 { + self.cycle_last + } + + fn mask(&self) -> u64 { + self.mask + } + + fn mult(&self) -> u32 { + self.mult + } + + fn shift(&self) -> u32 { + self.shift + } + + fn tz_minuteswest(&self) -> i32 { + self.tz_minuteswest + } + + fn tz_dsttime(&self) -> i32 { + self.tz_dsttime + } +} + +// === Linux 5.3 - 5.5 === +// struct vdso_data + +#[repr(C)] +pub struct vdso_data_v5_3 { + pub seq: AtomicU32, + + pub clock_mode: i32, + pub cycle_last: u64, + pub mask: u64, + pub mult: u32, + pub shift: u32, + + pub basetime: [vdso_timestamp; VDSO_BASES], + + pub tz_minuteswest: i32, + pub tz_dsttime: i32, + pub hrtimer_res: u32, + pub __unused: u32, +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct vdso_timestamp { + pub sec: u64, + pub nsec: u64, +} + +impl VdsoData for vdso_data_v5_3 { + fn vdsodata_ptr(vdso_addr: u64) -> *const Self { + (vdso_addr - 3 * PAGE_SIZE + 128) as *const Self + } + + fn sec(&self, clockid: ClockId) -> Result { + Ok(self.basetime[clockid as usize].sec) + } + + fn nsec(&self, clockid: ClockId) -> Result { + Ok(self.basetime[clockid as usize].nsec) + } + + fn seq(&self) -> u32 { + self.seq.load(Ordering::Relaxed) + } + + fn clock_mode(&self) -> i32 { + self.clock_mode + } + + fn cycle_last(&self) -> u64 { + self.cycle_last + } + + fn mask(&self) -> u64 { + self.mask + } + + fn mult(&self) -> u32 { + self.mult + } + + fn shift(&self) -> u32 { + self.shift + } + + fn tz_minuteswest(&self) -> i32 { + self.tz_minuteswest + } + + fn tz_dsttime(&self) -> i32 { + self.tz_dsttime + } +} + +// === Linux 5.6 - 5.8 === +// struct vdso_data + +#[repr(C)] +pub struct vdso_data_v5_6 { + pub seq: AtomicU32, + + pub clock_mode: i32, + pub cycle_last: u64, + pub mask: u64, + pub mult: u32, + pub shift: u32, + + pub union_1: vdso_data_v5_6_union_1, + + pub tz_minuteswest: i32, + pub tz_dsttime: i32, + pub hrtimer_res: u32, + pub __unused: u32, +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct timens_offset { + pub sec: i64, + pub nsec: u64, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub union vdso_data_v5_6_union_1 { + pub basetime: [vdso_timestamp; VDSO_BASES], + pub offset: [timens_offset; VDSO_BASES], +} + +impl VdsoData for vdso_data_v5_6 { + fn vdsodata_ptr(vdso_addr: u64) -> *const Self { + (vdso_addr - 4 * PAGE_SIZE + 128) as *const Self + } + + fn sec(&self, clockid: ClockId) -> Result { + unsafe { Ok(self.union_1.basetime[clockid as usize].sec) } + } + + fn nsec(&self, clockid: ClockId) -> Result { + unsafe { Ok(self.union_1.basetime[clockid as usize].nsec) } + } + + fn seq(&self) -> u32 { + self.seq.load(Ordering::Relaxed) + } + + fn clock_mode(&self) -> i32 { + self.clock_mode + } + + fn cycle_last(&self) -> u64 { + self.cycle_last + } + + fn mask(&self) -> u64 { + self.mask + } + + fn mult(&self) -> u32 { + self.mult + } + + fn shift(&self) -> u32 { + self.shift + } + + fn tz_minuteswest(&self) -> i32 { + self.tz_minuteswest + } + + fn tz_dsttime(&self) -> i32 { + self.tz_dsttime + } +} + +// === Linux 5.9 - 5.19, 6.0 - 6.2 === +// struct vdso_data + +#[repr(C)] +pub struct vdso_data_v5_9 { + pub seq: AtomicU32, + + pub clock_mode: i32, + pub cycle_last: u64, + pub mask: u64, + pub mult: u32, + pub shift: u32, + + pub union_1: vdso_data_v5_6_union_1, + + pub tz_minuteswest: i32, + pub tz_dsttime: i32, + pub hrtimer_res: u32, + pub __unused: u32, + + pub arch_data: arch_vdso_data, +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct arch_vdso_data {} + +impl VdsoData for vdso_data_v5_9 { + fn vdsodata_ptr(vdso_addr: u64) -> *const Self { + (vdso_addr - 4 * PAGE_SIZE + 128) as *const Self + } + + fn sec(&self, clockid: ClockId) -> Result { + unsafe { Ok(self.union_1.basetime[clockid as usize].sec) } + } + + fn nsec(&self, clockid: ClockId) -> Result { + unsafe { Ok(self.union_1.basetime[clockid as usize].nsec) } + } + + fn seq(&self) -> u32 { + self.seq.load(Ordering::Relaxed) + } + + fn clock_mode(&self) -> i32 { + self.clock_mode + } + + fn cycle_last(&self) -> u64 { + self.cycle_last + } + + fn mask(&self) -> u64 { + self.mask + } + + fn mult(&self) -> u32 { + self.mult + } + + fn shift(&self) -> u32 { + self.shift + } + + fn tz_minuteswest(&self) -> i32 { + self.tz_minuteswest + } + + fn tz_dsttime(&self) -> i32 { + self.tz_dsttime + } +} diff --git a/src/libos/src/error/mod.rs b/src/libos/src/error/mod.rs index 5be585f1..96342a50 100644 --- a/src/libos/src/error/mod.rs +++ b/src/libos/src/error/mod.rs @@ -1,3 +1 @@ -use super::*; - pub use errno::{Errno::*, *}; diff --git a/src/libos/src/fs/syscalls.rs b/src/libos/src/fs/syscalls.rs index 35b495de..45a89834 100644 --- a/src/libos/src/fs/syscalls.rs +++ b/src/libos/src/fs/syscalls.rs @@ -6,7 +6,7 @@ use super::file_ops::{ }; use super::fs_ops; use super::fs_ops::{MountFlags, MountOptions, UmountFlags}; -use super::time::{clockid_t, itimerspec_t, timespec_t, timeval_t, ClockID}; +use super::time::{clockid_t, itimerspec_t, timespec_t, timeval_t, ClockId}; use super::timer_file::{TimerCreationFlags, TimerSetFlags}; use super::*; use crate::config::{user_rootfs_config, ConfigApp, ConfigMountFsType}; @@ -42,9 +42,9 @@ pub fn do_eventfd2(init_val: u32, flags: i32) -> Result { pub fn do_timerfd_create(clockid: clockid_t, flags: i32) -> Result { debug!("timerfd: clockid {}, flags {} ", clockid, flags); - let clockid = ClockID::from_raw(clockid)?; + let clockid = ClockId::try_from(clockid)?; match clockid { - ClockID::CLOCK_REALTIME | ClockID::CLOCK_MONOTONIC => {} + ClockId::CLOCK_REALTIME | ClockId::CLOCK_MONOTONIC => {} _ => { return_errno!(EINVAL, "invalid clockid"); } diff --git a/src/libos/src/fs/timer_file.rs b/src/libos/src/fs/timer_file.rs index 638ced52..a5042b14 100644 --- a/src/libos/src/fs/timer_file.rs +++ b/src/libos/src/fs/timer_file.rs @@ -1,6 +1,6 @@ use super::*; -use crate::time::{clockid_t, itimerspec_t, timespec_t, ClockID}; +use crate::time::{clockid_t, itimerspec_t, timespec_t, ClockId}; use atomic::{Atomic, Ordering}; use std::time::Duration; @@ -13,7 +13,7 @@ pub struct TimerFile { } impl TimerFile { - pub fn new(clockid: ClockID, flags: TimerCreationFlags) -> Result { + pub fn new(clockid: ClockId, flags: TimerCreationFlags) -> Result { let raw_host_fd = try_libc!({ let mut ret: i32 = 0; let status = occlum_ocall_timerfd_create(&mut ret, clockid as clockid_t, flags.bits()); diff --git a/src/libos/src/lib.rs b/src/libos/src/lib.rs index dfc8c8bd..54239950 100644 --- a/src/libos/src/lib.rs +++ b/src/libos/src/lib.rs @@ -66,6 +66,7 @@ extern crate intrusive_collections; extern crate itertools; extern crate modular_bitfield; extern crate resolv_conf; +extern crate vdso_time; use sgx_trts::libc; use sgx_types::*; diff --git a/src/libos/src/process/do_exec.rs b/src/libos/src/process/do_exec.rs index 6dc7f0f2..4de7c616 100644 --- a/src/libos/src/process/do_exec.rs +++ b/src/libos/src/process/do_exec.rs @@ -102,12 +102,12 @@ pub fn do_exec( // Blocking wait until there is only one thread in the calling process fn wait_for_other_threads_to_exit(current_ref: &ThreadRef) { use super::do_futex::{self, FutexTimeout}; - use crate::time::{timespec_t, ClockID}; + use crate::time::{timespec_t, ClockId}; use core::time::Duration; // Set timeout to 50ms let timeout = FutexTimeout::new( - ClockID::CLOCK_MONOTONIC, + ClockId::CLOCK_MONOTONIC, timespec_t::from(Duration::from_millis(50)), false, ); diff --git a/src/libos/src/process/do_futex.rs b/src/libos/src/process/do_futex.rs index d3bb64dc..0b802684 100644 --- a/src/libos/src/process/do_futex.rs +++ b/src/libos/src/process/do_futex.rs @@ -4,7 +4,7 @@ use std::intrinsics::atomic_load_seqcst; use std::sync::atomic::{AtomicBool, Ordering}; use crate::prelude::*; -use crate::time::{timespec_t, ClockID}; +use crate::time::{timespec_t, ClockId}; /// `FutexOp`, `FutexFlags`, and `futex_op_and_flags_from_u32` are helper types and /// functions for handling the versatile commands and arguments of futex system @@ -75,13 +75,13 @@ const FUTEX_BITSET_MATCH_ANY: u32 = 0xFFFF_FFFF; #[derive(Debug, Copy, Clone)] pub struct FutexTimeout { - clock_id: ClockID, + clock_id: ClockId, ts: timespec_t, absolute_time: bool, } impl FutexTimeout { - pub fn new(clock_id: ClockID, ts: timespec_t, absolute_time: bool) -> Self { + pub fn new(clock_id: ClockId, ts: timespec_t, absolute_time: bool) -> Self { Self { clock_id, ts, @@ -89,7 +89,7 @@ impl FutexTimeout { } } - pub fn clock_id(&self) -> &ClockID { + pub fn clock_id(&self) -> &ClockId { &self.clock_id } @@ -502,7 +502,7 @@ fn wait_event_timeout(thread: *const c_void, timeout: &Option) -> .as_ref() .map(|timeout| { let clockbit = match timeout.clock_id() { - ClockID::CLOCK_REALTIME => FutexFlags::FUTEX_CLOCK_REALTIME.bits() as i32, + ClockId::CLOCK_REALTIME => FutexFlags::FUTEX_CLOCK_REALTIME.bits() as i32, _ => 0, }; ( diff --git a/src/libos/src/process/syscalls.rs b/src/libos/src/process/syscalls.rs index 563c8a69..7d56fbd4 100644 --- a/src/libos/src/process/syscalls.rs +++ b/src/libos/src/process/syscalls.rs @@ -11,7 +11,7 @@ use super::process::ProcessFilter; use super::spawn_attribute::{clone_spawn_atrributes_safely, posix_spawnattr_t, SpawnAttr}; use crate::prelude::*; use crate::syscall::CpuContext; -use crate::time::{timespec_t, ClockID}; +use crate::time::{timespec_t, ClockId}; use crate::util::mem_util::from_user::*; use std::ptr::NonNull; @@ -290,9 +290,9 @@ pub fn do_futex( "FUTEX_CLOCK_REALTIME with this futex operation is not supported" ); } - ClockID::CLOCK_REALTIME + ClockId::CLOCK_REALTIME } else { - ClockID::CLOCK_MONOTONIC + ClockId::CLOCK_MONOTONIC }; // From futex man page: diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 6bbd379d..c5bec80a 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -932,7 +932,7 @@ fn do_gettimeofday(tv_u: *mut timeval_t) -> Result { fn do_clock_gettime(clockid: clockid_t, ts_u: *mut timespec_t) -> Result { check_mut_ptr(ts_u)?; - let clockid = time::ClockID::from_raw(clockid)?; + let clockid = time::ClockId::try_from(clockid)?; let ts = time::do_clock_gettime(clockid)?; unsafe { *ts_u = ts; @@ -941,7 +941,7 @@ fn do_clock_gettime(clockid: clockid_t, ts_u: *mut timespec_t) -> Result } fn do_time(tloc_u: *mut time_t) -> Result { - let ts = time::do_clock_gettime(time::ClockID::CLOCK_REALTIME)?; + let ts = time::do_clock_gettime(time::ClockId::CLOCK_REALTIME)?; if !tloc_u.is_null() { check_mut_ptr(tloc_u)?; unsafe { @@ -952,7 +952,7 @@ fn do_time(tloc_u: *mut time_t) -> Result { } fn do_clock_getres(clockid: clockid_t, res_u: *mut timespec_t) -> Result { - let clockid = time::ClockID::from_raw(clockid)?; + let clockid = time::ClockId::try_from(clockid)?; if res_u.is_null() { return Ok(0); } @@ -981,7 +981,7 @@ fn do_clock_nanosleep( } else { None }; - let clockid = time::ClockID::from_raw(clockid)?; + let clockid = time::ClockId::try_from(clockid)?; time::do_clock_nanosleep(clockid, flags, &req, rem)?; Ok(0) } diff --git a/src/libos/src/time/mod.rs b/src/libos/src/time/mod.rs index d6dfac4e..8bcbb794 100644 --- a/src/libos/src/time/mod.rs +++ b/src/libos/src/time/mod.rs @@ -14,6 +14,7 @@ pub mod up_time; pub use profiler::ThreadProfiler; pub use timer_slack::TIMERSLACK; +pub use vdso_time::ClockId; #[allow(non_camel_case_types)] pub type time_t = i64; @@ -74,15 +75,9 @@ impl From for timeval_t { } pub fn do_gettimeofday() -> timeval_t { - extern "C" { - fn occlum_ocall_gettimeofday(tv: *mut timeval_t) -> sgx_status_t; - } - - let mut tv: timeval_t = Default::default(); - unsafe { - occlum_ocall_gettimeofday(&mut tv as *mut timeval_t); - } - tv.validate().expect("ocall returned invalid timeval_t"); + let tv = timeval_t::from(vdso_time::clock_gettime(ClockId::CLOCK_REALTIME).unwrap()); + tv.validate() + .expect("gettimeofday returned invalid timeval_t"); tv } @@ -149,58 +144,15 @@ impl timespec_t { #[allow(non_camel_case_types)] pub type clockid_t = i32; -#[derive(Debug, Copy, Clone)] -#[allow(non_camel_case_types)] -pub enum ClockID { - CLOCK_REALTIME = 0, - CLOCK_MONOTONIC = 1, - CLOCK_PROCESS_CPUTIME_ID = 2, - CLOCK_THREAD_CPUTIME_ID = 3, - CLOCK_MONOTONIC_RAW = 4, - CLOCK_REALTIME_COARSE = 5, - CLOCK_MONOTONIC_COARSE = 6, - CLOCK_BOOTTIME = 7, -} - -impl ClockID { - #[deny(unreachable_patterns)] - pub fn from_raw(clockid: clockid_t) -> Result { - Ok(match clockid as i32 { - 0 => ClockID::CLOCK_REALTIME, - 1 => ClockID::CLOCK_MONOTONIC, - 2 => ClockID::CLOCK_PROCESS_CPUTIME_ID, - 3 => ClockID::CLOCK_THREAD_CPUTIME_ID, - 4 => ClockID::CLOCK_MONOTONIC_RAW, - 5 => ClockID::CLOCK_REALTIME_COARSE, - 6 => ClockID::CLOCK_MONOTONIC_COARSE, - 7 => ClockID::CLOCK_BOOTTIME, - _ => return_errno!(EINVAL, "invalid command"), - }) - } -} - -pub fn do_clock_gettime(clockid: ClockID) -> Result { - extern "C" { - fn occlum_ocall_clock_gettime(clockid: clockid_t, tp: *mut timespec_t) -> sgx_status_t; - } - - let mut tv: timespec_t = Default::default(); - unsafe { - occlum_ocall_clock_gettime(clockid as clockid_t, &mut tv as *mut timespec_t); - } - tv.validate().expect("ocall returned invalid timespec"); +pub fn do_clock_gettime(clockid: ClockId) -> Result { + let tv = timespec_t::from(vdso_time::clock_gettime(clockid).unwrap()); + tv.validate() + .expect("clock_gettime returned invalid timespec"); Ok(tv) } -pub fn do_clock_getres(clockid: ClockID) -> Result { - extern "C" { - fn occlum_ocall_clock_getres(clockid: clockid_t, res: *mut timespec_t) -> sgx_status_t; - } - - let mut res: timespec_t = Default::default(); - unsafe { - occlum_ocall_clock_getres(clockid as clockid_t, &mut res as *mut timespec_t); - } +pub fn do_clock_getres(clockid: ClockId) -> Result { + let res = timespec_t::from(vdso_time::clock_getres(clockid).unwrap()); let validate_resolution = |res: ×pec_t| -> Result<()> { // The resolution can be ranged from 1 nanosecond to a few milliseconds if res.sec == 0 && res.nsec > 0 && res.nsec < 1_000_000_000 { @@ -210,14 +162,14 @@ pub fn do_clock_getres(clockid: ClockID) -> Result { } }; // do sanity check - validate_resolution(&res).expect("ocall returned invalid resolution"); + validate_resolution(&res).expect("clock_getres returned invalid resolution"); Ok(res) } const TIMER_ABSTIME: i32 = 0x01; pub fn do_clock_nanosleep( - clockid: ClockID, + clockid: ClockId, flags: i32, req: ×pec_t, rem: Option<&mut timespec_t>, @@ -235,11 +187,11 @@ pub fn do_clock_nanosleep( let mut ret = 0; let mut u_rem: timespec_t = timespec_t { sec: 0, nsec: 0 }; match clockid { - ClockID::CLOCK_REALTIME - | ClockID::CLOCK_MONOTONIC - | ClockID::CLOCK_BOOTTIME - | ClockID::CLOCK_PROCESS_CPUTIME_ID => {} - ClockID::CLOCK_THREAD_CPUTIME_ID => { + ClockId::CLOCK_REALTIME + | ClockId::CLOCK_MONOTONIC + | ClockId::CLOCK_BOOTTIME + | ClockId::CLOCK_PROCESS_CPUTIME_ID => {} + ClockId::CLOCK_THREAD_CPUTIME_ID => { return_errno!(EINVAL, "CLOCK_THREAD_CPUTIME_ID is not a permitted value"); } _ => { @@ -269,7 +221,7 @@ pub fn do_nanosleep(req: ×pec_t, rem: Option<&mut timespec_t>) -> Result<() // the CLOCK_REALTIME clock. However, Linux measures the time using // the CLOCK_MONOTONIC clock. // Here we follow the POSIX.1 - let clock_id = ClockID::CLOCK_REALTIME; + let clock_id = ClockId::CLOCK_REALTIME; return do_clock_nanosleep(clock_id, 0, req, rem); } diff --git a/src/libos/src/time/up_time.rs b/src/libos/src/time/up_time.rs index 2078c0e5..9a1b83a8 100644 --- a/src/libos/src/time/up_time.rs +++ b/src/libos/src/time/up_time.rs @@ -1,11 +1,11 @@ -use super::{do_clock_gettime, ClockID}; +use super::{do_clock_gettime, ClockId}; use std::time::Duration; lazy_static! { - static ref BOOT_TIME_STAMP: Duration = do_clock_gettime(ClockID::CLOCK_MONOTONIC_RAW) + static ref BOOT_TIME_STAMP: Duration = do_clock_gettime(ClockId::CLOCK_MONOTONIC_RAW) .unwrap() .as_duration(); - static ref BOOT_TIME_STAMP_SINCE_EPOCH: Duration = do_clock_gettime(ClockID::CLOCK_REALTIME) + static ref BOOT_TIME_STAMP_SINCE_EPOCH: Duration = do_clock_gettime(ClockId::CLOCK_REALTIME) .unwrap() .as_duration(); } @@ -20,7 +20,7 @@ pub fn boot_time_since_epoch() -> Duration { } pub fn get() -> Option { - do_clock_gettime(ClockID::CLOCK_MONOTONIC_RAW) + do_clock_gettime(ClockId::CLOCK_MONOTONIC_RAW) .unwrap() .as_duration() .checked_sub(*BOOT_TIME_STAMP) diff --git a/src/pal/Makefile b/src/pal/Makefile index 203d2c60..5e33ba67 100644 --- a/src/pal/Makefile +++ b/src/pal/Makefile @@ -21,6 +21,8 @@ C_SRCS := $(sort $(wildcard src/*.c src/*/*.c)) CXX_SRCS := $(sort $(wildcard src/*.cpp src/*/*.cpp)) C_OBJS := $(addprefix $(OBJ_DIR)/pal/,$(C_SRCS:.c=.o)) CXX_OBJS := $(addprefix $(OBJ_DIR)/pal/,$(CXX_SRCS:.cpp=.o)) +VDSO_SRCS := $(CRATES_DIR)/vdso-time/ocalls/vdso-time-ocalls.c +VDSO_OBJS := $(addprefix $(OBJ_DIR)/pal/external_ocalls,$(VDSO_SRCS:.c=.o)) # Object files for simulation mode are stored in pal/src_sim ifeq ($(SGX_MODE), SIM) @@ -52,7 +54,7 @@ LINK_FLAGS += -lsgx_quote_ex_sim endif endif -ALL_BUILD_SUBDIRS := $(sort $(patsubst %/,%,$(dir $(LIBOCCLUM_PAL_SO_REAL) $(EDL_C_OBJS) $(C_OBJS) $(CXX_OBJS)))) +ALL_BUILD_SUBDIRS := $(sort $(patsubst %/,%,$(dir $(LIBOCCLUM_PAL_SO_REAL) $(EDL_C_OBJS) $(C_OBJS) $(CXX_OBJS) $(VDSO_OBJS)))) .PHONY: all format format-check clean @@ -61,7 +63,7 @@ all: $(ALL_BUILD_SUBDIRS) $(LIBOCCLUM_PAL_SO_REAL) $(ALL_BUILD_SUBDIRS): @mkdir -p $@ -$(LIBOCCLUM_PAL_SO_REAL): $(LIBSGX_USTDC_A) $(EDL_C_OBJS) $(C_OBJS) $(CXX_OBJS) +$(LIBOCCLUM_PAL_SO_REAL): $(LIBSGX_USTDC_A) $(EDL_C_OBJS) $(C_OBJS) $(CXX_OBJS) $(VDSO_OBJS) @$(CXX) $^ -o $@ $(LINK_FLAGS) -Wl,-soname=$(LIBOCCLUM_PAL_SONAME) @# Create symbolic files because occlum run and exec will need it when linking. @cd $(BUILD_DIR)/lib && ln -sf $(notdir $(LIBOCCLUM_PAL_SO_REAL)) $(notdir $(LIBOCCLUM_PAL_SONAME)) && \ @@ -76,7 +78,8 @@ $(OBJ_DIR)/pal/$(SRC_OBJ)/Enclave_u.c: $(SGX_EDGER8R) ../Enclave.edl @cd $(OBJ_DIR)/pal/$(SRC_OBJ) && \ $(SGX_EDGER8R) $(SGX_EDGER8R_MODE) --untrusted $(CUR_DIR)/../Enclave.edl \ --search-path $(SGX_SDK)/include \ - --search-path $(RUST_SGX_SDK_DIR)/edl/ + --search-path $(RUST_SGX_SDK_DIR)/edl/ \ + --search-path $(CRATES_DIR)/vdso-time/ocalls @echo "GEN <= $@" $(OBJ_DIR)/pal/$(SRC_OBJ)/%.o: src/%.c @@ -87,6 +90,10 @@ $(OBJ_DIR)/pal/$(SRC_OBJ)/%.o: src/%.cpp @$(CXX) $(CXX_FLAGS) -c $< -o $@ @echo "CXX <= $@" +$(VDSO_OBJS): $(VDSO_SRCS) + @$(CC) $(C_FLAGS) -c $< -o $@ + @echo "CC <= $@" + $(LIBSGX_USTDC_A): @$(MAKE) --no-print-directory -C $(RUST_SGX_SDK_DIR)/sgx_ustdc/ > /dev/null @cp $(RUST_SGX_SDK_DIR)/sgx_ustdc/libsgx_ustdc.a $(LIBSGX_USTDC_A) diff --git a/src/pal/src/ocalls/time.c b/src/pal/src/ocalls/time.c index 7bfcf737..3d25471f 100644 --- a/src/pal/src/ocalls/time.c +++ b/src/pal/src/ocalls/time.c @@ -4,18 +4,6 @@ #include #include "ocalls.h" -void occlum_ocall_gettimeofday(struct timeval *tv) { - gettimeofday(tv, NULL); -} - -void occlum_ocall_clock_gettime(int clockid, struct timespec *tp) { - clock_gettime(clockid, tp); -} - -void occlum_ocall_clock_getres(int clockid, struct timespec *res) { - clock_getres(clockid, res); -} - int occlum_ocall_clock_nanosleep(clockid_t clockid, int flags, const struct timespec *req, struct timespec *rem) { return clock_nanosleep(clockid, flags, req, rem); diff --git a/src/sgxenv.mk b/src/sgxenv.mk index afa17797..033f6b4d 100644 --- a/src/sgxenv.mk +++ b/src/sgxenv.mk @@ -2,6 +2,7 @@ MAIN_MAKEFILE := $(firstword $(MAKEFILE_LIST)) INCLUDE_MAKEFILE := $(lastword $(MAKEFILE_LIST)) CUR_DIR := $(shell dirname $(realpath $(MAIN_MAKEFILE))) PROJECT_DIR := $(realpath $(CUR_DIR)/../../) +CRATES_DIR := $(realpath $(CUR_DIR)/../libos/crates) SHELL := /bin/bash