Compare commits
10 Commits
ded96d761d
...
ffe6a750b0
Author | SHA1 | Date | |
---|---|---|---|
|
ffe6a750b0 | ||
|
f8e8d400d3 | ||
|
060d2ec1b3 | ||
|
3b90df67dc | ||
|
d3e84d2269 | ||
|
501abda5ca | ||
|
5b704984e3 | ||
|
4027258ec5 | ||
|
0c1c0621e6 | ||
|
f58652da96 |
@ -12,12 +12,6 @@ on:
|
||||
description: 'Whether it is a release image or test image (must choose from <Y/N/T>)'
|
||||
required: true
|
||||
default: 'N'
|
||||
# If rc image works well, directly tag with the release version
|
||||
# source image: "reuse_image" specified here
|
||||
# target iamge: Get from the current source code
|
||||
reuse_image:
|
||||
description: 'Set reuse image name, e.g.: 0.30.0-rc (When release is set to <Y>, admin can choose to reuse an old rc image as the new release image)'
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
generate-anolis-image:
|
||||
@ -44,6 +38,16 @@ jobs:
|
||||
# ${GITHUB_REF##*/} == branch-name
|
||||
run: echo "OCCLUM_BRANCH=$(echo ${GITHUB_REF##*/})" >> $GITHUB_ENV;
|
||||
|
||||
- name: Get image tag name for release image
|
||||
if: github.event.inputs.release == 'Y'
|
||||
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}" >> $GITHUB_ENV
|
||||
- name: Get image tage name for dev image
|
||||
if: github.event.inputs.release == 'N'
|
||||
run: echo "IMAGE_TAG=latest-dev-${GITHUB_SHA:0:7}" >> $GITHUB_ENV
|
||||
- name: Get image tage name for test image
|
||||
if: github.event.inputs.release == 'T'
|
||||
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}-test" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
@ -58,19 +62,7 @@ jobs:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Get image tag name for release image
|
||||
if: github.event.inputs.release == 'Y'
|
||||
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}" >> $GITHUB_ENV
|
||||
- name: Get image tage name for dev image
|
||||
if: github.event.inputs.release == 'N'
|
||||
run: echo "IMAGE_TAG=latest-dev-${GITHUB_SHA:0:7}" >> $GITHUB_ENV
|
||||
- name: Get image tage name for test image
|
||||
if: github.event.inputs.release == 'T'
|
||||
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}-test" >> $GITHUB_ENV
|
||||
|
||||
# Rebuild new image
|
||||
- name: Build and push
|
||||
if: "${{ github.event.inputs.reuse_image == '' }}"
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
@ -80,15 +72,6 @@ jobs:
|
||||
push: true
|
||||
tags: occlum/occlum:${{ env.IMAGE_TAG }}-anolis8.8
|
||||
|
||||
# Reuse old image
|
||||
- name: Reuse old image
|
||||
if: "${{ github.event.inputs.reuse_image != '' }}"
|
||||
run: |
|
||||
echo "REUSE_VERSION=${{ github.event.inputs.reuse_image }}" >> $GITHUB_ENV;
|
||||
docker pull occlum/occlum:${{ env.REUSE_VERSION }}-anolis8.8
|
||||
docker tag occlum/occlum:${{ env.REUSE_VERSION }}-anolis8.8 occlum/occlum:${{ env.IMAGE_TAG }}-anolis8.8
|
||||
docker push occlum/occlum:${{ env.IMAGE_TAG }}-anolis8.8
|
||||
|
||||
|
||||
generate-ubuntu20-image:
|
||||
runs-on: ubuntu-20.04
|
||||
@ -113,6 +96,16 @@ jobs:
|
||||
# ${GITHUB_REF##*/} == branch-name
|
||||
run: echo "OCCLUM_BRANCH=$(echo ${GITHUB_REF##*/})" >> $GITHUB_ENV;
|
||||
|
||||
- name: Get image tag name for release image
|
||||
if: github.event.inputs.release == 'Y'
|
||||
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}" >> $GITHUB_ENV
|
||||
- name: Get image tage name for dev image
|
||||
if: github.event.inputs.release == 'N'
|
||||
run: echo "IMAGE_TAG=latest-dev-${GITHUB_SHA:0:7}" >> $GITHUB_ENV
|
||||
- name: Get image tage name for test image
|
||||
if: github.event.inputs.release == 'T'
|
||||
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}-test" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
@ -127,19 +120,7 @@ jobs:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Get image tag name for release image
|
||||
if: github.event.inputs.release == 'Y'
|
||||
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}" >> $GITHUB_ENV
|
||||
- name: Get image tage name for dev image
|
||||
if: github.event.inputs.release == 'N'
|
||||
run: echo "IMAGE_TAG=latest-dev-${GITHUB_SHA:0:7}" >> $GITHUB_ENV
|
||||
- name: Get image tage name for test image
|
||||
if: github.event.inputs.release == 'T'
|
||||
run: echo "IMAGE_TAG=${{ env.RELEASE_VERSION }}-test" >> $GITHUB_ENV
|
||||
|
||||
# Rebuild new image
|
||||
- name: Build and push
|
||||
if: "${{ github.event.inputs.reuse_image == '' }}"
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
@ -148,12 +129,3 @@ jobs:
|
||||
build-args: OCCLUM_BRANCH=${{ env.OCCLUM_BRANCH }}
|
||||
push: true
|
||||
tags: occlum/occlum:${{ env.IMAGE_TAG }}-ubuntu20.04
|
||||
|
||||
# Reuse old image
|
||||
- name: Reuse old image
|
||||
if: "${{ github.event.inputs.reuse_image != '' }}"
|
||||
run: |
|
||||
echo "REUSE_VERSION=${{ github.event.inputs.reuse_image }}" >> $GITHUB_ENV;
|
||||
docker pull occlum/occlum:${{ env.REUSE_VERSION }}-ubuntu20.04
|
||||
docker tag occlum/occlum:${{ env.REUSE_VERSION }}-ubuntu20.04 occlum/occlum:${{ env.IMAGE_TAG }}-ubuntu20.04
|
||||
docker push occlum/occlum:${{ env.IMAGE_TAG }}-ubuntu20.04
|
||||
|
9
.github/workflows/hw_mode_test.yml
vendored
9
.github/workflows/hw_mode_test.yml
vendored
@ -54,6 +54,15 @@ jobs:
|
||||
container-name: ${{ github.job }}
|
||||
build-envs: 'OCCLUM_RELEASE_BUILD=1'
|
||||
|
||||
# Udpate the test json file
|
||||
# When there comes new features, the configuration should be enabled accordingly
|
||||
- name: Configure Occlum features
|
||||
run: |
|
||||
if [[ "${{ matrix.self_runner[2] }}" == "EDMM" ]]; then
|
||||
docker exec ${{ env.CONTAINER_NAME }} bash -c "jq '.feature.enable_posix_shm = true | .feature.enable_edmm = true' /root/occlum/test/Occlum.json > /tmp.json && mv /tmp.json /root/occlum/test/Occlum.json"
|
||||
fi;
|
||||
shell: bash
|
||||
|
||||
- name: Integration test
|
||||
run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum; OCCLUM_LOG_LEVEL=trace make test"
|
||||
|
||||
|
6
.github/workflows/main.yml
vendored
6
.github/workflows/main.yml
vendored
@ -68,6 +68,12 @@ jobs:
|
||||
if: ${{ failure() }}
|
||||
run: docker exec ${{ github.job }} bash -c "cat /root/occlum/build/test/.fail"
|
||||
|
||||
- name: Integration test with optional Occlum features
|
||||
run: |
|
||||
docker exec ${{ github.job }} bash -c 'source /opt/intel/sgxsdk/environment; cd /root/occlum; make clean && LIBOS_FEATURES="kernel_heap_monitor" make install'
|
||||
docker exec ${{ github.job }} bash -c "cd /root/occlum; SGX_MODE=SIM make test-glibc"
|
||||
shell: bash
|
||||
|
||||
# Make_test_on_centos:
|
||||
# runs-on: ubuntu-18.04
|
||||
|
||||
|
2
deps/rust-sgx-sdk
vendored
2
deps/rust-sgx-sdk
vendored
@ -1 +1 @@
|
||||
Subproject commit e28e25ce718cc21b232a1bf37bb6446535d7ea00
|
||||
Subproject commit 81384ce4d10c67eea5e1ba4ea332087940c1836b
|
@ -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);
|
||||
|
||||
|
2
src/exec/Cargo.lock
generated
2
src/exec/Cargo.lock
generated
@ -542,7 +542,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "occlum_exec"
|
||||
version = "0.30.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "occlum_exec"
|
||||
version = "0.30.0"
|
||||
version = "0.30.1"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
2
src/libos/Cargo.lock
generated
2
src/libos/Cargo.lock
generated
@ -4,7 +4,7 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "Occlum"
|
||||
version = "0.30.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"aligned",
|
||||
"atomic",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "Occlum"
|
||||
version = "0.30.0"
|
||||
version = "0.30.1"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
@ -25,6 +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"] }
|
||||
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"] }
|
||||
@ -48,6 +50,7 @@ dcap = [] # DCAP support. The compilation relies on DCAP package.
|
||||
cov = ["sgx_cov"] # Enable coverage colletcion.
|
||||
hyper_mode = [] # For running in hyper mode.
|
||||
pku = [] # PKU Support
|
||||
kernel_heap_monitor = []# Kernel heap usage tracking. With overhead.
|
||||
|
||||
[target.'cfg(not(target_env = "sgx"))'.dependencies]
|
||||
sgx_types = { path = "../../deps/rust-sgx-sdk/sgx_types" }
|
||||
|
@ -45,7 +45,7 @@ LIBOS_LOG ?= error
|
||||
|
||||
LIBOS_SONAME := libocclum-libos.so.$(MAJOR_VER_NUM)
|
||||
|
||||
LIBOS_FEATURES :=
|
||||
LIBOS_FEATURES := $(LIBOS_FEATURES)
|
||||
|
||||
ifeq ($(SGX_MODE), HW)
|
||||
LIBOS_CORE_LIB_NAME := occlum-libos-core
|
||||
@ -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
|
||||
|
265
src/libos/crates/errno/Cargo.lock
generated
Normal file
265
src/libos/crates/errno/Cargo.lock
generated
Normal file
@ -0,0 +1,265 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"rcore-fs",
|
||||
"serde_json",
|
||||
"sgx_tstd 1.1.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown_tstd"
|
||||
version = "0.12.0"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.5"
|
||||
dependencies = [
|
||||
"sgx_tstd 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "rcore-fs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.104"
|
||||
dependencies = [
|
||||
"sgx_tstd 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.40"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"sgx_tstd 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_alloc"
|
||||
version = "1.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
|
||||
[[package]]
|
||||
name = "sgx_alloc"
|
||||
version = "1.1.6"
|
||||
|
||||
[[package]]
|
||||
name = "sgx_backtrace_sys"
|
||||
version = "1.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"sgx_build_helper 0.1.0",
|
||||
"sgx_libc 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_backtrace_sys"
|
||||
version = "1.1.6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"sgx_build_helper 1.1.6",
|
||||
"sgx_libc 1.1.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_build_helper"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
|
||||
[[package]]
|
||||
name = "sgx_build_helper"
|
||||
version = "1.1.6"
|
||||
|
||||
[[package]]
|
||||
name = "sgx_demangle"
|
||||
version = "1.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
|
||||
[[package]]
|
||||
name = "sgx_demangle"
|
||||
version = "1.1.6"
|
||||
|
||||
[[package]]
|
||||
name = "sgx_libc"
|
||||
version = "1.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
dependencies = [
|
||||
"sgx_types 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_libc"
|
||||
version = "1.1.6"
|
||||
dependencies = [
|
||||
"sgx_types 1.1.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_tcrypto"
|
||||
version = "1.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
dependencies = [
|
||||
"sgx_types 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_tprotected_fs"
|
||||
version = "1.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
dependencies = [
|
||||
"sgx_trts 1.1.0",
|
||||
"sgx_types 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_tprotected_fs"
|
||||
version = "1.1.6"
|
||||
dependencies = [
|
||||
"sgx_trts 1.1.6",
|
||||
"sgx_types 1.1.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_trts"
|
||||
version = "1.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
dependencies = [
|
||||
"sgx_libc 1.1.0",
|
||||
"sgx_types 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_trts"
|
||||
version = "1.1.6"
|
||||
dependencies = [
|
||||
"sgx_libc 1.1.6",
|
||||
"sgx_types 1.1.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_tse"
|
||||
version = "1.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
dependencies = [
|
||||
"sgx_types 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_tseal"
|
||||
version = "1.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
dependencies = [
|
||||
"sgx_tcrypto",
|
||||
"sgx_trts 1.1.0",
|
||||
"sgx_tse",
|
||||
"sgx_types 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_tstd"
|
||||
version = "1.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
dependencies = [
|
||||
"sgx_alloc 1.1.0",
|
||||
"sgx_backtrace_sys 1.1.0",
|
||||
"sgx_demangle 1.1.0",
|
||||
"sgx_libc 1.1.0",
|
||||
"sgx_tprotected_fs 1.1.0",
|
||||
"sgx_trts 1.1.0",
|
||||
"sgx_tseal",
|
||||
"sgx_types 1.1.0",
|
||||
"sgx_unwind 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_tstd"
|
||||
version = "1.1.6"
|
||||
dependencies = [
|
||||
"hashbrown_tstd",
|
||||
"sgx_alloc 1.1.6",
|
||||
"sgx_backtrace_sys 1.1.6",
|
||||
"sgx_demangle 1.1.6",
|
||||
"sgx_libc 1.1.6",
|
||||
"sgx_tprotected_fs 1.1.6",
|
||||
"sgx_trts 1.1.6",
|
||||
"sgx_types 1.1.6",
|
||||
"sgx_unwind 1.1.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_types"
|
||||
version = "1.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
|
||||
[[package]]
|
||||
name = "sgx_types"
|
||||
version = "1.1.6"
|
||||
|
||||
[[package]]
|
||||
name = "sgx_unwind"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.0#71a88b647bb76a16cbc5c3e29403e2afb67f82fd"
|
||||
dependencies = [
|
||||
"sgx_build_helper 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sgx_unwind"
|
||||
version = "1.1.6"
|
||||
dependencies = [
|
||||
"sgx_build_helper 1.1.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
19
src/libos/crates/errno/Cargo.toml
Normal file
19
src/libos/crates/errno/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "errno"
|
||||
version = "0.1.0"
|
||||
authors = ["Tate, Hongliang Tian <tate.thl@antgroup.com>"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = []
|
||||
std = []
|
||||
occlum = ["sgx", "serde_json", "rcore-fs"]
|
||||
sgx = ["sgx_tstd"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
serde_json = { path = "../../../../deps/serde-json-sgx", optional = true }
|
||||
sgx_tstd = { path = "../../../../deps/rust-sgx-sdk/sgx_tstd", optional = true }
|
||||
rcore-fs = { path = "../../../../deps/sefs/rcore-fs", optional = true }
|
@ -1,4 +1,9 @@
|
||||
use super::*;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt;
|
||||
|
||||
use super::Error;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ErrorBacktrace<'a> {
|
||||
@ -15,7 +20,7 @@ impl<'a> ErrorBacktrace<'a> {
|
||||
|
||||
impl<'a> fmt::Display for ErrorBacktrace<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let error_strings: Vec<String> = self.clone().map(|e| e.to_string()).collect();
|
||||
let error_strings: Vec<String> = self.clone().map(|e| alloc::format!("{}", e)).collect();
|
||||
let error_backtrace = error_strings.join("\n Caused by ");
|
||||
write!(f, "{}", error_backtrace)
|
||||
}
|
||||
@ -48,18 +53,3 @@ impl Error {
|
||||
ErrorBacktrace::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ResultExt<T> {
|
||||
fn cause_err<F>(self, f: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(&Error) -> Error;
|
||||
}
|
||||
|
||||
impl<T> ResultExt<T> for Result<T> {
|
||||
fn cause_err<F>(self, f: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(&Error) -> Error,
|
||||
{
|
||||
self.map_err(|old_e| old_e.cause_err(f))
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use super::*;
|
||||
use core::fmt;
|
||||
|
||||
/// POSIX errno
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
@ -46,7 +46,6 @@ pub enum Errno {
|
||||
ENOSYS = 38,
|
||||
ENOTEMPTY = 39,
|
||||
ELOOP = 40,
|
||||
EWOULDBLOCK = 41,
|
||||
ENOMSG = 42,
|
||||
EIDRM = 43,
|
||||
ECHRNG = 44,
|
||||
@ -145,6 +144,9 @@ const ERRNO_MIN: u32 = Errno::EPERM as u32;
|
||||
const ERRNO_MAX: u32 = Errno::EHWPOISON as u32;
|
||||
|
||||
impl Errno {
|
||||
// EWOULDBLOCK was used on BSD/Sun variants of Unix, and EAGAIN was the AT&T System V error code.
|
||||
// Here we keep same with linux, define EWOULDBLOCK with EAGAIN.
|
||||
pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
|
||||
pub(crate) fn as_str(&self) -> &'static str {
|
||||
use self::Errno::*;
|
||||
match *self {
|
@ -1,4 +1,7 @@
|
||||
use super::*;
|
||||
use alloc::boxed::Box;
|
||||
use core::fmt;
|
||||
|
||||
use super::{Errno, ToErrno};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
@ -10,7 +13,7 @@ pub struct Error {
|
||||
#[derive(Debug)]
|
||||
enum Error__ {
|
||||
Embedded((Errno, &'static str)),
|
||||
Boxed(Box<dyn ToErrno + 'static>),
|
||||
Boxed(Box<dyn ToErrno + Send + 'static>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -30,7 +33,7 @@ impl Error {
|
||||
|
||||
pub fn boxed<T>(inner: T, location: Option<ErrorLocation>) -> Error
|
||||
where
|
||||
T: ToErrno + 'static,
|
||||
T: ToErrno + Send + 'static,
|
||||
{
|
||||
Error {
|
||||
inner: Error__::Boxed(Box::new(inner)),
|
||||
@ -64,23 +67,6 @@ impl ErrorLocation {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
self.errno().as_str()
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&dyn std::error::Error> {
|
||||
self.cause.as_ref().map(|e| e as &dyn std::error::Error)
|
||||
}
|
||||
/*
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
self.cause
|
||||
.as_ref()
|
||||
.map(|e| e as &(dyn std::error::Error + 'static))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.inner)?;
|
||||
@ -105,3 +91,25 @@ impl fmt::Display for ErrorLocation {
|
||||
write!(f, "[line = {}, file = {}]", self.line, self.file)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "sgx", test, doctest))]
|
||||
mod if_std {
|
||||
use super::*;
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
self.errno().as_str()
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&dyn std::error::Error> {
|
||||
self.cause.as_ref().map(|e| e as &dyn std::error::Error)
|
||||
}
|
||||
/*
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
self.cause
|
||||
.as_ref()
|
||||
.map(|e| e as &(dyn std::error::Error + 'static))
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
204
src/libos/crates/errno/src/lib.rs
Normal file
204
src/libos/crates/errno/src/lib.rs
Normal file
@ -0,0 +1,204 @@
|
||||
//! User-friendly error handling with build-in support for POSIX errno.
|
||||
//!
|
||||
//! This crate extends Rust's standard error handling with the abilities of
|
||||
//! reporting error locations, providing backtrace information, and unifying
|
||||
//! all types of errors with POSIX errno.
|
||||
//!
|
||||
//! # Motivation
|
||||
//!
|
||||
//! While the built-in error handling mechanism of Rust is undoubtedly superior
|
||||
//! than that of a traditional system programming language (e.g., C/C++), it
|
||||
//! is _not perfect_.
|
||||
//!
|
||||
//! First, trait `std::error::Error` does not provide any means to
|
||||
//! record the location of the source code that triggers an error, leading to
|
||||
//! a slow process of diagnosing errors or bugs.
|
||||
//!
|
||||
//! Second, while the `Error` trait (which has a `cause` method)
|
||||
//! supports backtrace in theory, it is inconvenient---in practice---to implement
|
||||
//! backtrace. This is because the users still need to manually write the concrete
|
||||
//! implementation that stores the cause for every error struct.
|
||||
//!
|
||||
//! Third, one challenging aspect of error handling in Rust is
|
||||
//! dealing with the various types of errors. The standard library
|
||||
//! defines errors like `std::io::Error`, `std::fmt::Error`, `std::str::Utf8Error`, etc.
|
||||
//! Not to mention the error types defined by third-party libraries.
|
||||
//! To make it even worse, we, as OS writers, have to convert all these errors
|
||||
//! into POSIX errno eventually.
|
||||
//!
|
||||
//! To cope with the issues above, this crate extends Rust's standard error
|
||||
//! handling mechanism. Specifically, it aims at the following design goals:
|
||||
//!
|
||||
//! * **Fast diagnose** (e.g., reporting the backtrace and the code location of an error).
|
||||
//! * **First-class POSIX errno** (e.g., every error has an errno).
|
||||
//! * **Zero-overhead abstraction** (e.g., no heap allocation unless absolutely necesary).
|
||||
//! * **Ergonomic grammar** (e.g., use macros to avoid writing code manually).
|
||||
//! * **Compatibility with `no_std`**.
|
||||
//!
|
||||
//! # How to Use
|
||||
//!
|
||||
//! ## Basic Usage
|
||||
//!
|
||||
//! The simplest usage involves just one macro---`errno!`.
|
||||
//! See the sample code below:
|
||||
//! ```rust
|
||||
//! use errno::prelude::*;
|
||||
//!
|
||||
//! fn return_err() -> Result<()> {
|
||||
//! Err(errno!(EINVAL, "the root error"))
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! if let Err(e) = return_err() {
|
||||
//! println!("{}", e);
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//! which prints something like
|
||||
//! ```text
|
||||
//! EINVAL (#22, Invalid argument): the root error [line = 45, file = src/lib.rs]
|
||||
//! ```
|
||||
//! Note that the specific line and file of source code that generates the error
|
||||
//! is printed. This facilitates diagnosing errors.
|
||||
//!
|
||||
//! ## Backtrace
|
||||
//!
|
||||
//! A more interesting usage is to print the backtrace of an error. To create
|
||||
//! the chains of errors, `std::result::Result` is extended with a new method
|
||||
//! named `cause_err`. If the result is `Ok`, the method does nothing; otherwise,
|
||||
//! this method executes a user-given closure to output a new error whose cause
|
||||
//! is the error contained in the result. The method consumes the current result
|
||||
//! and generates a new result that contains the new error. The two errors are
|
||||
//! chained. More calls to `cause_err` form deeper backtraces.
|
||||
//!
|
||||
//! See the sample code below:
|
||||
//! ```rust
|
||||
//! use errno::prelude::*;
|
||||
//!
|
||||
//! fn return_err() -> Result<()> {
|
||||
//! Err(errno!(EINVAL, "the root error"))
|
||||
//! }
|
||||
//!
|
||||
//! fn cause_err() -> Result<()> {
|
||||
//! return_err()
|
||||
//! .cause_err(|_e| errno!(EIO, "another error"))
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! if let Err(e) = cause_err() {
|
||||
//! println!("{}", e.backtrace());
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//! which prints something like
|
||||
//! ```text
|
||||
//! EIO (#5, I/O error): another error [line = 71, file = src/lib.rs]
|
||||
//! Caused by EINVAL (#22, Invalid argument): the root error [line = 68, file = src/lib.rs]
|
||||
//! ```
|
||||
//!
|
||||
|
||||
#![feature(allocator_api)]
|
||||
// Use no_std and alloc crate except when given std feature or during test.
|
||||
#![cfg_attr(not(any(feature = "std", test, doctest)), no_std)]
|
||||
extern crate alloc;
|
||||
// Use Rust SGX SDK's std when given SGX feature.
|
||||
#[cfg(feature = "sgx")]
|
||||
extern crate sgx_tstd as std;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
mod backtrace;
|
||||
mod errno;
|
||||
mod error;
|
||||
pub mod prelude;
|
||||
mod result;
|
||||
mod to_errno;
|
||||
|
||||
pub use self::backtrace::ErrorBacktrace;
|
||||
pub use self::errno::*;
|
||||
pub use self::errno::Errno::*;
|
||||
pub use self::error::{Error, ErrorLocation};
|
||||
pub use self::result::{Result, ResultExt};
|
||||
pub use self::to_errno::ToErrno;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! errno {
|
||||
($errno_expr: expr, $error_msg: expr) => {{
|
||||
let inner_error = {
|
||||
let errno: Errno = $errno_expr;
|
||||
let msg: &'static str = $error_msg;
|
||||
(errno, msg)
|
||||
};
|
||||
let error =
|
||||
$crate::Error::embedded(inner_error, Some($crate::ErrorLocation::new(file!(), line!())));
|
||||
error
|
||||
}};
|
||||
($error_expr: expr) => {{
|
||||
let inner_error = $error_expr;
|
||||
let error = $crate::Error::boxed(inner_error, Some($crate::ErrorLocation::new(file!(), line!())));
|
||||
error
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! return_errno {
|
||||
($errno_expr: expr, $error_msg: expr) => {{
|
||||
return Err(errno!($errno_expr, $error_msg));
|
||||
}};
|
||||
($error_expr: expr) => {{
|
||||
return Err(errno!($error_expr));
|
||||
}};
|
||||
}
|
||||
|
||||
// return Err(errno) if libc return -1
|
||||
#[macro_export]
|
||||
macro_rules! try_libc {
|
||||
($ret: expr) => {{
|
||||
let ret = unsafe { $ret };
|
||||
if ret < 0 {
|
||||
let errno = unsafe { libc::errno() };
|
||||
return_errno!(Errno::from(errno as u32), "libc error");
|
||||
}
|
||||
ret
|
||||
}};
|
||||
}
|
||||
|
||||
// return Err(errno) if libc return -1
|
||||
// raise SIGPIPE if errno == EPIPE
|
||||
#[macro_export]
|
||||
macro_rules! try_libc_may_epipe {
|
||||
($ret: expr) => {{
|
||||
let ret = unsafe { $ret };
|
||||
if ret < 0 {
|
||||
let errno = unsafe { libc::errno() };
|
||||
if errno == Errno::EPIPE as i32 {
|
||||
crate::signal::do_tkill(current!().tid(), crate::signal::SIGPIPE.as_u8() as i32);
|
||||
}
|
||||
return_errno!(Errno::from(errno as u32), "libc error");
|
||||
}
|
||||
ret
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn convert_std_io_error() -> Result<()> {
|
||||
use std::io::{BufWriter, Write};
|
||||
let mut buf_writer = BufWriter::new(Vec::<u8>::new());
|
||||
// std::io::Error can be converted crate::Error implicitly
|
||||
buf_writer.write("foo".as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_std_ffi_nul_error() -> Result<()> {
|
||||
use std::ffi::CString;
|
||||
// std::ffi::NulError can be converted crate::Error implicitly
|
||||
let _ = CString::new(b"foo".to_vec())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
3
src/libos/crates/errno/src/prelude.rs
Normal file
3
src/libos/crates/errno/src/prelude.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub use crate::{
|
||||
errno, return_errno, Errno, Errno::*, Error, ErrorLocation, Result, ResultExt, ToErrno,
|
||||
};
|
34
src/libos/crates/errno/src/result.rs
Normal file
34
src/libos/crates/errno/src/result.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use super::{Errno, Error};
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
/// Extending `Result` with extra functionalities.
|
||||
pub trait ResultExt<T> {
|
||||
fn cause_err<F>(self, f: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(&Error) -> Error;
|
||||
|
||||
fn errno(&self) -> Option<Errno>;
|
||||
|
||||
fn has_errno(&self, errno: Errno) -> bool;
|
||||
}
|
||||
|
||||
impl<T> ResultExt<T> for Result<T> {
|
||||
fn cause_err<F>(self, f: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(&Error) -> Error,
|
||||
{
|
||||
self.map_err(|old_e| old_e.cause_err(f))
|
||||
}
|
||||
|
||||
fn errno(&self) -> Option<Errno> {
|
||||
match self {
|
||||
Ok(_) => None,
|
||||
Err(e) => Some(e.errno()),
|
||||
}
|
||||
}
|
||||
|
||||
fn has_errno(&self, errno: Errno) -> bool {
|
||||
self.errno() == Some(errno)
|
||||
}
|
||||
}
|
138
src/libos/crates/errno/src/to_errno.rs
Normal file
138
src/libos/crates/errno/src/to_errno.rs
Normal file
@ -0,0 +1,138 @@
|
||||
use core::fmt;
|
||||
|
||||
use super::{Errno, Error};
|
||||
|
||||
pub trait ToErrno: fmt::Display + fmt::Debug {
|
||||
fn errno(&self) -> Errno;
|
||||
}
|
||||
|
||||
impl<T> From<T> for Error
|
||||
where
|
||||
T: ToErrno + Send + 'static,
|
||||
{
|
||||
fn from(t: T) -> Error {
|
||||
Error::boxed(t, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for Errno {
|
||||
fn errno(&self) -> Errno {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for core::alloc::AllocError {
|
||||
fn errno(&self) -> Errno {
|
||||
Errno::ENOMEM
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for core::alloc::LayoutError {
|
||||
fn errno(&self) -> Errno {
|
||||
Errno::EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for core::num::ParseIntError {
|
||||
fn errno(&self) -> Errno {
|
||||
Errno::EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "sgx", test, doctest))]
|
||||
mod if_std {
|
||||
use super::*;
|
||||
|
||||
impl From<std::io::ErrorKind> for Errno {
|
||||
fn from(kind: std::io::ErrorKind) -> Errno {
|
||||
use std::io::ErrorKind::*;
|
||||
use Errno::*;
|
||||
match kind {
|
||||
NotFound => ENOENT,
|
||||
PermissionDenied => EPERM,
|
||||
ConnectionRefused => ECONNREFUSED,
|
||||
ConnectionReset => ECONNRESET,
|
||||
ConnectionAborted => ECONNABORTED,
|
||||
NotConnected => ENOTCONN,
|
||||
AddrInUse => EADDRINUSE,
|
||||
AddrNotAvailable => EADDRNOTAVAIL,
|
||||
BrokenPipe => EPIPE,
|
||||
AlreadyExists => EEXIST,
|
||||
WouldBlock => Errno::EWOULDBLOCK,
|
||||
InvalidInput => EINVAL,
|
||||
InvalidData => EBADMSG, /* TODO: correct? */
|
||||
TimedOut => ETIMEDOUT,
|
||||
Interrupted => EINTR,
|
||||
WriteZero => EINVAL,
|
||||
UnexpectedEof => EIO,
|
||||
Other => EIO,
|
||||
_ => EIO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for std::io::Error {
|
||||
fn errno(&self) -> Errno {
|
||||
Errno::from(self.kind())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for std::ffi::NulError {
|
||||
fn errno(&self) -> Errno {
|
||||
Errno::EINVAL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "occlum")]
|
||||
mod if_occlum {
|
||||
use rcore_fs::dev::DevError;
|
||||
use rcore_fs::vfs::FsError;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl ToErrno for serde_json::Error {
|
||||
fn errno(&self) -> Errno {
|
||||
Errno::EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for FsError {
|
||||
fn errno(&self) -> Errno {
|
||||
use Errno::*;
|
||||
match *self {
|
||||
FsError::NotSupported => ENOSYS,
|
||||
FsError::NotFile => EISDIR,
|
||||
FsError::IsDir => EISDIR,
|
||||
FsError::NotDir => ENOTDIR,
|
||||
FsError::EntryNotFound => ENOENT,
|
||||
FsError::EntryExist => EEXIST,
|
||||
FsError::NotSameFs => EXDEV,
|
||||
FsError::InvalidParam => EINVAL,
|
||||
FsError::NoDeviceSpace => ENOMEM,
|
||||
FsError::DirRemoved => ENOENT,
|
||||
FsError::DirNotEmpty => ENOTEMPTY,
|
||||
FsError::WrongFs => EINVAL,
|
||||
FsError::DeviceError(_err) => EIO,
|
||||
FsError::SymLoop => ELOOP,
|
||||
FsError::NoDevice => ENXIO,
|
||||
FsError::IOCTLError => EINVAL,
|
||||
FsError::Again => EAGAIN,
|
||||
FsError::Busy => EBUSY,
|
||||
FsError::WrProtected => EROFS,
|
||||
FsError::NoIntegrity => EIO,
|
||||
FsError::PermError => EPERM,
|
||||
FsError::NameTooLong => ENAMETOOLONG,
|
||||
FsError::FileTooBig => EFBIG,
|
||||
FsError::OpNotSupported => EOPNOTSUPP,
|
||||
FsError::NotMountPoint => EINVAL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for DevError {
|
||||
fn from(e: Error) -> Self {
|
||||
DevError(e.errno() as i32)
|
||||
}
|
||||
}
|
||||
}
|
10
src/libos/crates/vdso-time/.gitignore
vendored
Normal file
10
src/libos/crates/vdso-time/.gitignore
vendored
Normal file
@ -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
|
32
src/libos/crates/vdso-time/Cargo.toml
Normal file
32
src/libos/crates/vdso-time/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "vdso-time"
|
||||
version = "0.1.0"
|
||||
authors = ["Shuocheng Wang <shuocheng.wsc@antgroup.com>"]
|
||||
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
|
40
src/libos/crates/vdso-time/README.md
Normal file
40
src/libos/crates/vdso-time/README.md
Normal file
@ -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);
|
||||
}
|
||||
```
|
42
src/libos/crates/vdso-time/benches/bench.rs
Normal file
42
src/libos/crates/vdso-time/benches/bench.rs
Normal file
@ -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);
|
33
src/libos/crates/vdso-time/examples/bench.rs
Normal file
33
src/libos/crates/vdso-time/examples/bench.rs
Normal file
@ -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();
|
||||
}
|
31
src/libos/crates/vdso-time/examples/common/bench.rs
Normal file
31
src/libos/crates/vdso-time/examples/common/bench.rs
Normal file
@ -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<T>(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());
|
||||
}
|
26
src/libos/crates/vdso-time/examples/common/example.rs
Normal file
26
src/libos/crates/vdso-time/examples/common/example.rs
Normal file
@ -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();
|
||||
}
|
5
src/libos/crates/vdso-time/examples/example.rs
Normal file
5
src/libos/crates/vdso-time/examples/example.rs
Normal file
@ -0,0 +1,5 @@
|
||||
include!("common/example.rs");
|
||||
|
||||
fn main() {
|
||||
example();
|
||||
}
|
11
src/libos/crates/vdso-time/examples/sgx/.gitignore
vendored
Normal file
11
src/libos/crates/vdso-time/examples/sgx/.gitignore
vendored
Normal file
@ -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
|
167
src/libos/crates/vdso-time/examples/sgx/Makefile
Normal file
167
src/libos/crates/vdso-time/examples/sgx/Makefile
Normal file
@ -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
|
11
src/libos/crates/vdso-time/examples/sgx/README.md
Normal file
11
src/libos/crates/vdso-time/examples/sgx/README.md
Normal file
@ -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```
|
10
src/libos/crates/vdso-time/examples/sgx/app/Cargo.toml
Normal file
10
src/libos/crates/vdso-time/examples/sgx/app/Cargo.toml
Normal file
@ -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]
|
34
src/libos/crates/vdso-time/examples/sgx/app/build.rs
Normal file
34
src/libos/crates/vdso-time/examples/sgx/app/build.rs
Normal file
@ -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
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
nightly-2020-10-25
|
78
src/libos/crates/vdso-time/examples/sgx/app/src/main.rs
Normal file
78
src/libos/crates/vdso-time/examples/sgx/app/src/main.rs
Normal file
@ -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<SgxEnclave> {
|
||||
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();
|
||||
}
|
1
src/libos/crates/vdso-time/examples/sgx/bin/readme.txt
Normal file
1
src/libos/crates/vdso-time/examples/sgx/bin/readme.txt
Normal file
@ -0,0 +1 @@
|
||||
bin
|
22
src/libos/crates/vdso-time/examples/sgx/enclave/Cargo.toml
Normal file
22
src/libos/crates/vdso-time/examples/sgx/enclave/Cargo.toml
Normal file
@ -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]
|
@ -0,0 +1,13 @@
|
||||
<!-- Please refer to User's Guide for the explanation of each field -->
|
||||
<EnclaveConfiguration>
|
||||
<ProdID>0</ProdID>
|
||||
<ISVSVN>0</ISVSVN>
|
||||
<StackMaxSize>0x40000</StackMaxSize>
|
||||
<HeapMaxSize>0x400000</HeapMaxSize>
|
||||
<TCSNum>1</TCSNum>
|
||||
<TCSMaxNum>1</TCSMaxNum>
|
||||
<TCSPolicy>0</TCSPolicy>
|
||||
<DisableDebug>0</DisableDebug>
|
||||
<MiscSelect>0</MiscSelect>
|
||||
<MiscMask>0xFFFFFFFF</MiscMask>
|
||||
</EnclaveConfiguration>
|
36
src/libos/crates/vdso-time/examples/sgx/enclave/Enclave.edl
Normal file
36
src/libos/crates/vdso-time/examples/sgx/enclave/Enclave.edl
Normal file
@ -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. */
|
||||
};
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
enclave.so
|
||||
{
|
||||
global:
|
||||
g_global_data_sim;
|
||||
g_global_data;
|
||||
enclave_entry;
|
||||
local:
|
||||
*;
|
||||
};
|
@ -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-----
|
38
src/libos/crates/vdso-time/examples/sgx/enclave/Makefile
Normal file
38
src/libos/crates/vdso-time/examples/sgx/enclave/Makefile
Normal file
@ -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
|
@ -0,0 +1 @@
|
||||
nightly-2020-10-25
|
49
src/libos/crates/vdso-time/examples/sgx/enclave/src/lib.rs
Normal file
49
src/libos/crates/vdso-time/examples/sgx/enclave/src/lib.rs
Normal file
@ -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
|
||||
}
|
@ -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"
|
||||
}
|
1
src/libos/crates/vdso-time/examples/sgx/lib/readme.txt
Normal file
1
src/libos/crates/vdso-time/examples/sgx/lib/readme.txt
Normal file
@ -0,0 +1 @@
|
||||
lib
|
3
src/libos/crates/vdso-time/ocalls/.gitignore
vendored
Normal file
3
src/libos/crates/vdso-time/ocalls/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
12
src/libos/crates/vdso-time/ocalls/Makefile
Normal file
12
src/libos/crates/vdso-time/ocalls/Makefile
Normal file
@ -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
|
14
src/libos/crates/vdso-time/ocalls/sgx_vdso_time_ocalls.edl
Normal file
14
src/libos/crates/vdso-time/ocalls/sgx_vdso_time_ocalls.edl
Normal file
@ -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);
|
||||
};
|
||||
};
|
30
src/libos/crates/vdso-time/ocalls/vdso-time-ocalls.c
Normal file
30
src/libos/crates/vdso-time/ocalls/vdso-time-ocalls.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
616
src/libos/crates/vdso-time/src/lib.rs
Normal file
616
src/libos/crates/vdso-time/src/lib.rs
Normal file
@ -0,0 +1,616 @@
|
||||
#![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<i32> for ClockId {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(clockid: i32) -> Result<Self> {
|
||||
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<Duration>,
|
||||
// coarse resolution for clock_getres
|
||||
coarse_resolution: Option<Duration>,
|
||||
}
|
||||
|
||||
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<Self> {
|
||||
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<VdsoDataPtr> {
|
||||
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<VdsoDataPtr> {
|
||||
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<VdsoDataPtr> {
|
||||
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<Duration> {
|
||||
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<Duration> {
|
||||
self.do_clock_getres(clockid)
|
||||
.or_else(|_| clock_getres_slow(clockid))
|
||||
}
|
||||
|
||||
fn do_clock_gettime(&self, clockid: ClockId) -> Result<Duration> {
|
||||
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<Duration> {
|
||||
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<Duration> {
|
||||
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<Duration> {
|
||||
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<u64> {
|
||||
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> = Vdso::new().ok();
|
||||
}
|
||||
|
||||
/// 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<Duration> {
|
||||
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<Duration> {
|
||||
if VDSO.is_none() {
|
||||
clock_getres_slow(clockid)
|
||||
} else {
|
||||
VDSO.as_ref().unwrap().clock_getres(clockid)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clock_gettime_slow(clockid: ClockId) -> Result<Duration> {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clock_getres_slow(clockid: ClockId) -> Result<Duration> {
|
||||
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) {}
|
||||
}
|
||||
}
|
||||
}
|
595
src/libos/crates/vdso-time/src/sys.rs
Normal file
595
src/libos/crates/vdso-time/src/sys.rs
Normal file
@ -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<u64>;
|
||||
fn nsec(&self, clockid: ClockId) -> Result<u64>;
|
||||
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<u64> {
|
||||
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<u64> {
|
||||
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<u64> {
|
||||
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<u64> {
|
||||
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<u64> {
|
||||
Ok(self.basetime[clockid as usize].sec)
|
||||
}
|
||||
|
||||
fn nsec(&self, clockid: ClockId) -> Result<u64> {
|
||||
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<u64> {
|
||||
Ok(self.basetime[clockid as usize].sec)
|
||||
}
|
||||
|
||||
fn nsec(&self, clockid: ClockId) -> Result<u64> {
|
||||
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<u64> {
|
||||
unsafe { Ok(self.union_1.basetime[clockid as usize].sec) }
|
||||
}
|
||||
|
||||
fn nsec(&self, clockid: ClockId) -> Result<u64> {
|
||||
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<u64> {
|
||||
unsafe { Ok(self.union_1.basetime[clockid as usize].sec) }
|
||||
}
|
||||
|
||||
fn nsec(&self, clockid: ClockId) -> Result<u64> {
|
||||
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
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ use crate::interrupt;
|
||||
use crate::process::idle_reap_zombie_children;
|
||||
use crate::process::{ProcessFilter, SpawnAttr};
|
||||
use crate::signal::SigNum;
|
||||
use crate::time::up_time::init;
|
||||
use crate::time::init;
|
||||
use crate::util::host_file_util::{host_file_buffer, parse_host_file, write_host_file, HostFile};
|
||||
use crate::util::log::LevelFilter;
|
||||
use crate::util::mem_util::from_untrusted::*;
|
||||
@ -96,8 +96,8 @@ pub extern "C" fn occlum_ecall_init(
|
||||
|
||||
interrupt::init();
|
||||
|
||||
// Init boot up time stamp here.
|
||||
time::up_time::init();
|
||||
// Init vdso and boot up time stamp here.
|
||||
time::init();
|
||||
|
||||
vm::init_user_space();
|
||||
|
||||
|
@ -1,69 +1 @@
|
||||
use super::*;
|
||||
use std::fmt;
|
||||
|
||||
mod backtrace;
|
||||
mod errno;
|
||||
mod error;
|
||||
mod to_errno;
|
||||
|
||||
pub use self::backtrace::{ErrorBacktrace, ResultExt};
|
||||
pub use self::errno::Errno;
|
||||
pub use self::errno::Errno::*;
|
||||
pub use self::error::{Error, ErrorLocation};
|
||||
pub use self::to_errno::ToErrno;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
macro_rules! errno {
|
||||
($errno_expr: expr, $error_msg: expr) => {{
|
||||
let inner_error = {
|
||||
let errno: Errno = $errno_expr;
|
||||
let msg: &'static str = $error_msg;
|
||||
(errno, msg)
|
||||
};
|
||||
let error = Error::embedded(inner_error, Some(ErrorLocation::new(file!(), line!())));
|
||||
error
|
||||
}};
|
||||
($error_expr: expr) => {{
|
||||
let inner_error = $error_expr;
|
||||
let error = Error::boxed(inner_error, Some(ErrorLocation::new(file!(), line!())));
|
||||
error
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! return_errno {
|
||||
($errno_expr: expr, $error_msg: expr) => {{
|
||||
return Err(errno!($errno_expr, $error_msg));
|
||||
}};
|
||||
($error_expr: expr) => {{
|
||||
return Err(errno!($error_expr));
|
||||
}};
|
||||
}
|
||||
|
||||
// return Err(errno) if libc return -1
|
||||
macro_rules! try_libc {
|
||||
($ret: expr) => {{
|
||||
let ret = unsafe { $ret };
|
||||
if ret < 0 {
|
||||
let errno = unsafe { libc::errno() };
|
||||
return_errno!(Errno::from(errno as u32), "libc error");
|
||||
}
|
||||
ret
|
||||
}};
|
||||
}
|
||||
|
||||
// return Err(errno) if libc return -1
|
||||
// raise SIGPIPE if errno == EPIPE
|
||||
macro_rules! try_libc_may_epipe {
|
||||
($ret: expr) => {{
|
||||
let ret = unsafe { $ret };
|
||||
if ret < 0 {
|
||||
let errno = unsafe { libc::errno() };
|
||||
if errno == Errno::EPIPE as i32 {
|
||||
crate::signal::do_tkill(current!().tid(), crate::signal::SIGPIPE.as_u8() as i32);
|
||||
}
|
||||
return_errno!(Errno::from(errno as u32), "libc error");
|
||||
}
|
||||
ret
|
||||
}};
|
||||
}
|
||||
pub use errno::{Errno::*, *};
|
||||
|
@ -1,116 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
pub trait ToErrno: fmt::Display + fmt::Debug {
|
||||
fn errno(&self) -> Errno;
|
||||
}
|
||||
|
||||
impl ToErrno for Errno {
|
||||
fn errno(&self) -> Errno {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Error
|
||||
where
|
||||
T: ToErrno + 'static,
|
||||
{
|
||||
fn from(t: T) -> Error {
|
||||
Error::boxed(t, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::ErrorKind> for Errno {
|
||||
fn from(kind: std::io::ErrorKind) -> Errno {
|
||||
use std::io::ErrorKind::*;
|
||||
match kind {
|
||||
NotFound => ENOENT,
|
||||
PermissionDenied => EPERM,
|
||||
ConnectionRefused => ECONNREFUSED,
|
||||
ConnectionReset => ECONNRESET,
|
||||
ConnectionAborted => ECONNABORTED,
|
||||
NotConnected => ENOTCONN,
|
||||
AddrInUse => EADDRINUSE,
|
||||
AddrNotAvailable => EADDRNOTAVAIL,
|
||||
BrokenPipe => EPIPE,
|
||||
AlreadyExists => EEXIST,
|
||||
WouldBlock => EWOULDBLOCK,
|
||||
InvalidInput => EINVAL,
|
||||
InvalidData => EBADMSG, /* TODO: correct? */
|
||||
TimedOut => ETIMEDOUT,
|
||||
Interrupted => EINTR,
|
||||
WriteZero => EINVAL,
|
||||
UnexpectedEof => EIO,
|
||||
Other => EIO,
|
||||
_ => EIO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for std::io::Error {
|
||||
fn errno(&self) -> Errno {
|
||||
Errno::from(self.kind())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for std::ffi::NulError {
|
||||
fn errno(&self) -> Errno {
|
||||
EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for std::num::ParseIntError {
|
||||
fn errno(&self) -> Errno {
|
||||
EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for serde_json::Error {
|
||||
fn errno(&self) -> Errno {
|
||||
EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for rcore_fs::vfs::FsError {
|
||||
fn errno(&self) -> Errno {
|
||||
use rcore_fs::vfs::FsError;
|
||||
match *self {
|
||||
FsError::NotSupported => ENOSYS,
|
||||
FsError::NotFile => EISDIR,
|
||||
FsError::IsDir => EISDIR,
|
||||
FsError::NotDir => ENOTDIR,
|
||||
FsError::EntryNotFound => ENOENT,
|
||||
FsError::EntryExist => EEXIST,
|
||||
FsError::NotSameFs => EXDEV,
|
||||
FsError::InvalidParam => EINVAL,
|
||||
FsError::NoDeviceSpace => ENOMEM,
|
||||
FsError::DirRemoved => ENOENT,
|
||||
FsError::DirNotEmpty => ENOTEMPTY,
|
||||
FsError::WrongFs => EINVAL,
|
||||
FsError::DeviceError(err) => EIO,
|
||||
FsError::SymLoop => ELOOP,
|
||||
FsError::NoDevice => ENXIO,
|
||||
FsError::IOCTLError => EINVAL,
|
||||
FsError::Again => EAGAIN,
|
||||
FsError::Busy => EBUSY,
|
||||
FsError::WrProtected => EROFS,
|
||||
FsError::NoIntegrity => EIO,
|
||||
FsError::PermError => EPERM,
|
||||
FsError::NameTooLong => ENAMETOOLONG,
|
||||
FsError::FileTooBig => EFBIG,
|
||||
FsError::OpNotSupported => EOPNOTSUPP,
|
||||
FsError::NotMountPoint => EINVAL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for std::alloc::AllocError {
|
||||
fn errno(&self) -> Errno {
|
||||
ENOMEM
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for std::alloc::LayoutError {
|
||||
fn errno(&self) -> Errno {
|
||||
EINVAL
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ struct CpuIdInput {
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
|
||||
struct CpuIdResult {
|
||||
pub struct CpuIdResult {
|
||||
eax: u32,
|
||||
ebx: u32,
|
||||
ecx: u32,
|
||||
@ -218,6 +218,15 @@ impl CpuId {
|
||||
};
|
||||
cpuid_result
|
||||
}
|
||||
|
||||
pub fn support_sgx2(&self) -> bool {
|
||||
const SGX_CPUID: u32 = 0x12;
|
||||
let cpuid = self.get_cpuid_info(SGX_CPUID, 0);
|
||||
|
||||
// The 0th bit set to 1 in `cpuid.eax` indicates that the SGX feature is enabled.
|
||||
// The 1st bit set to 1 in `cpuid.eax` indicates that the SGX2 feature is enabled.
|
||||
(cpuid.eax & 0b11) == 0b11
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
@ -247,6 +256,14 @@ fn get_cpuid_info_via_ocall(cpuid_input: CpuIdInput) -> CpuIdResult {
|
||||
cpuid_result
|
||||
}
|
||||
|
||||
pub fn is_cpu_support_sgx2() -> bool {
|
||||
CPUID.support_sgx2()
|
||||
}
|
||||
|
||||
pub fn get_cpuid_info(leaf: u32, subleaf: u32) -> CpuIdResult {
|
||||
CPUID.get_cpuid_info(leaf, subleaf)
|
||||
}
|
||||
|
||||
pub fn setup_cpuid_info() {
|
||||
// Make lazy_static to be executed at runtime in order to be initialized
|
||||
let max_basic_leaf = CPUID.get_max_basic_leaf();
|
||||
|
@ -11,6 +11,8 @@ use crate::vm::{enclave_page_fault_handler, is_page_committed, VMRange, USER_SPA
|
||||
use sgx_types::*;
|
||||
use sgx_types::{sgx_exception_type_t, sgx_exception_vector_t};
|
||||
|
||||
pub use self::cpuid::{get_cpuid_info, is_cpu_support_sgx2};
|
||||
|
||||
const ENCLU: u32 = 0xd7010f;
|
||||
const EACCEPT: u32 = 0x5;
|
||||
const EACCEPTCOPY: u32 = 0x7;
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use crate::util::kernel_alloc::KernelAlloc;
|
||||
use crate::vm::USER_SPACE_VM_MANAGER;
|
||||
|
||||
pub struct MemInfoINode;
|
||||
@ -15,13 +16,26 @@ impl ProcINode for MemInfoINode {
|
||||
fn generate_data_in_bytes(&self) -> vfs::Result<Vec<u8>> {
|
||||
let total_ram = USER_SPACE_VM_MANAGER.get_total_size();
|
||||
let free_ram = USER_SPACE_VM_MANAGER.get_precise_free_size();
|
||||
let kernel_heap_total = KernelAlloc::get_kernel_heap_config();
|
||||
let kernel_heap_peak_used = KernelAlloc::get_kernel_heap_peak_used();
|
||||
let kernel_heap_in_use = if let Some(bytes) = KernelAlloc::get_kernel_mem_size() {
|
||||
format!("{} kB", bytes / KB)
|
||||
} else {
|
||||
"Feature not enabled".to_string()
|
||||
};
|
||||
Ok(format!(
|
||||
"MemTotal: {} kB\n\
|
||||
MemFree: {} kB\n\
|
||||
MemAvailable: {} kB\n",
|
||||
MemAvailable: {} kB\n\
|
||||
KernelHeapTotal: {} kB\n\
|
||||
KernelHeapPeakUsed: {} kB\n\
|
||||
KernelHeapInUse: {}\n",
|
||||
total_ram / KB,
|
||||
free_ram / KB,
|
||||
free_ram / KB,
|
||||
kernel_heap_total / KB,
|
||||
kernel_heap_peak_used / KB,
|
||||
kernel_heap_in_use,
|
||||
)
|
||||
.into_bytes())
|
||||
}
|
||||
|
@ -321,10 +321,3 @@ impl File for LockedFile {
|
||||
Ok(SefsMac(file.get_mac().unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for DevError {
|
||||
fn from(e: Error) -> Self {
|
||||
error!("SGX protected file I/O error: {}", e.backtrace());
|
||||
DevError(e.errno() as i32)
|
||||
}
|
||||
}
|
||||
|
@ -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<isize> {
|
||||
pub fn do_timerfd_create(clockid: clockid_t, flags: i32) -> Result<isize> {
|
||||
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");
|
||||
}
|
||||
|
@ -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<Self> {
|
||||
pub fn new(clockid: ClockId, flags: TimerCreationFlags) -> Result<Self> {
|
||||
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());
|
||||
|
@ -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::*;
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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<FutexTimeout>) ->
|
||||
.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,
|
||||
};
|
||||
(
|
||||
|
@ -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:
|
||||
|
@ -932,7 +932,7 @@ fn do_gettimeofday(tv_u: *mut timeval_t) -> Result<isize> {
|
||||
|
||||
fn do_clock_gettime(clockid: clockid_t, ts_u: *mut timespec_t) -> Result<isize> {
|
||||
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<isize>
|
||||
}
|
||||
|
||||
fn do_time(tloc_u: *mut time_t) -> Result<isize> {
|
||||
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<isize> {
|
||||
}
|
||||
|
||||
fn do_clock_getres(clockid: clockid_t, res_u: *mut timespec_t) -> Result<isize> {
|
||||
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)
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
use self::timer_slack::*;
|
||||
use super::*;
|
||||
use crate::exception::is_cpu_support_sgx2;
|
||||
use core::convert::TryFrom;
|
||||
use process::pid_t;
|
||||
use rcore_fs::dev::TimeProvider;
|
||||
use rcore_fs::vfs::Timespec;
|
||||
use sgx_trts::enclave::{rsgx_get_enclave_mode, EnclaveMode};
|
||||
use spin::Once;
|
||||
use std::time::Duration;
|
||||
use std::{fmt, u64};
|
||||
use syscall::SyscallNum;
|
||||
@ -14,6 +17,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;
|
||||
@ -27,6 +31,26 @@ pub type clock_t = i64;
|
||||
/// Clock ticks per second
|
||||
pub const SC_CLK_TCK: u64 = 100;
|
||||
|
||||
static IS_ENABLE_VDSO: Once<bool> = Once::new();
|
||||
|
||||
pub fn init() {
|
||||
init_vdso();
|
||||
up_time::init();
|
||||
}
|
||||
|
||||
fn init_vdso() {
|
||||
IS_ENABLE_VDSO.call_once(|| match rsgx_get_enclave_mode() {
|
||||
EnclaveMode::Hw if is_cpu_support_sgx2() => true,
|
||||
EnclaveMode::Sim => true,
|
||||
_ => false,
|
||||
});
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_enable_vdso() -> bool {
|
||||
IS_ENABLE_VDSO.get().map_or(false, |is_enable| *is_enable)
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
@ -74,15 +98,16 @@ impl From<Duration> for timeval_t {
|
||||
}
|
||||
|
||||
pub fn do_gettimeofday() -> timeval_t {
|
||||
extern "C" {
|
||||
fn occlum_ocall_gettimeofday(tv: *mut timeval_t) -> sgx_status_t;
|
||||
}
|
||||
let duration = if is_enable_vdso() {
|
||||
vdso_time::clock_gettime(ClockId::CLOCK_REALTIME).unwrap()
|
||||
} else {
|
||||
// SGX1 Hardware doesn't support rdtsc instruction
|
||||
vdso_time::clock_gettime_slow(ClockId::CLOCK_REALTIME).unwrap()
|
||||
};
|
||||
|
||||
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(duration);
|
||||
tv.validate()
|
||||
.expect("gettimeofday returned invalid timeval_t");
|
||||
tv
|
||||
}
|
||||
|
||||
@ -149,58 +174,29 @@ 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,
|
||||
}
|
||||
pub fn do_clock_gettime(clockid: ClockId) -> Result<timespec_t> {
|
||||
let duration = if is_enable_vdso() {
|
||||
vdso_time::clock_gettime(clockid).unwrap()
|
||||
} else {
|
||||
// SGX1 Hardware doesn't support rdtsc instruction
|
||||
vdso_time::clock_gettime_slow(clockid).unwrap()
|
||||
};
|
||||
|
||||
impl ClockID {
|
||||
#[deny(unreachable_patterns)]
|
||||
pub fn from_raw(clockid: clockid_t) -> Result<ClockID> {
|
||||
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<timespec_t> {
|
||||
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");
|
||||
let tv = timespec_t::from(duration);
|
||||
tv.validate()
|
||||
.expect("clock_gettime returned invalid timespec");
|
||||
Ok(tv)
|
||||
}
|
||||
|
||||
pub fn do_clock_getres(clockid: ClockID) -> Result<timespec_t> {
|
||||
extern "C" {
|
||||
fn occlum_ocall_clock_getres(clockid: clockid_t, res: *mut timespec_t) -> sgx_status_t;
|
||||
}
|
||||
pub fn do_clock_getres(clockid: ClockId) -> Result<timespec_t> {
|
||||
let duration = if is_enable_vdso() {
|
||||
vdso_time::clock_getres(clockid).unwrap()
|
||||
} else {
|
||||
// SGX1 Hardware doesn't support rdtsc instruction
|
||||
vdso_time::clock_getres_slow(clockid).unwrap()
|
||||
};
|
||||
|
||||
let mut res: timespec_t = Default::default();
|
||||
unsafe {
|
||||
occlum_ocall_clock_getres(clockid as clockid_t, &mut res as *mut timespec_t);
|
||||
}
|
||||
let res = timespec_t::from(duration);
|
||||
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 +206,14 @@ pub fn do_clock_getres(clockid: ClockID) -> Result<timespec_t> {
|
||||
}
|
||||
};
|
||||
// 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 +231,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 +265,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);
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
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();
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
pub(super) fn init() {
|
||||
*BOOT_TIME_STAMP;
|
||||
*BOOT_TIME_STAMP_SINCE_EPOCH;
|
||||
}
|
||||
@ -20,7 +20,7 @@ pub fn boot_time_since_epoch() -> Duration {
|
||||
}
|
||||
|
||||
pub fn get() -> Option<Duration> {
|
||||
do_clock_gettime(ClockID::CLOCK_MONOTONIC_RAW)
|
||||
do_clock_gettime(ClockId::CLOCK_MONOTONIC_RAW)
|
||||
.unwrap()
|
||||
.as_duration()
|
||||
.checked_sub(*BOOT_TIME_STAMP)
|
||||
|
81
src/libos/src/util/kernel_alloc.rs
Normal file
81
src/libos/src/util/kernel_alloc.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use std::alloc::{GlobalAlloc, Layout, System};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
// This file provides "KernelAlloc", a wrapper for sgx_std "System" allocator, is used as
|
||||
// the global allocator for Occlum kernel. Currently, this can provides the ability to
|
||||
// monitor the kernel heap usage.
|
||||
|
||||
pub struct KernelAlloc {
|
||||
size: AtomicUsize,
|
||||
}
|
||||
|
||||
impl KernelAlloc {
|
||||
pub fn get_kernel_mem_size() -> Option<usize> {
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "kernel_heap_monitor")] {
|
||||
Some(ALLOC.size.load(Ordering::Relaxed))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_kernel_heap_config() -> usize {
|
||||
std::enclave::get_heap_size()
|
||||
}
|
||||
|
||||
pub fn get_kernel_heap_peak_used() -> usize {
|
||||
sgx_trts::enclave::rsgx_get_peak_heap_used()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl GlobalAlloc for KernelAlloc {
|
||||
#[inline]
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
let ptr = System.alloc(layout);
|
||||
if !ptr.is_null() {
|
||||
self.size.fetch_add(layout.size(), Ordering::Relaxed);
|
||||
}
|
||||
|
||||
ptr
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
|
||||
let ptr = System.alloc_zeroed(layout);
|
||||
if !ptr.is_null() {
|
||||
self.size.fetch_add(layout.size(), Ordering::Relaxed);
|
||||
}
|
||||
|
||||
ptr
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
System.dealloc(ptr, layout);
|
||||
self.size.fetch_sub(layout.size(), Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
|
||||
let ptr = System.realloc(ptr, layout, new_size);
|
||||
if !ptr.is_null() {
|
||||
let old_size = layout.size();
|
||||
if new_size > old_size {
|
||||
// grow
|
||||
self.size.fetch_add(new_size - old_size, Ordering::Relaxed);
|
||||
} else if new_size < old_size {
|
||||
// shrink
|
||||
self.size.fetch_sub(old_size - new_size, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
ptr
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kernel_heap_monitor")]
|
||||
#[global_allocator]
|
||||
static ALLOC: KernelAlloc = KernelAlloc {
|
||||
size: AtomicUsize::new(0),
|
||||
};
|
@ -3,6 +3,7 @@ use super::*;
|
||||
pub mod dirty;
|
||||
pub mod host_file_util;
|
||||
pub mod hosts_parser_util;
|
||||
pub mod kernel_alloc;
|
||||
pub mod log;
|
||||
pub mod mem_util;
|
||||
pub mod mpx_util;
|
||||
|
@ -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)
|
||||
|
@ -3,10 +3,10 @@
|
||||
#ifndef _OCCLUM_VERSION_H_
|
||||
#define _OCCLUM_VERSION_H_
|
||||
|
||||
// Version = 0.30.0
|
||||
// Version = 0.30.1
|
||||
#define OCCLUM_MAJOR_VERSION 0
|
||||
#define OCCLUM_MINOR_VERSION 30
|
||||
#define OCCLUM_PATCH_VERSION 0
|
||||
#define OCCLUM_PATCH_VERSION 1
|
||||
|
||||
#define STRINGIZE_PRE(X) #X
|
||||
#define STRINGIZE(X) STRINGIZE_PRE(X)
|
||||
|
@ -4,18 +4,6 @@
|
||||
#include <sys/prctl.h>
|
||||
#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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -2,9 +2,12 @@
|
||||
SRC_DIR=/tmp/glibc/glibc
|
||||
BUILD_DIR=/tmp/glibc/glibc_build
|
||||
INSTALL_DIR=/opt/occlum/glibc
|
||||
GLIBC_BRANCH=${1:-"occlum-glibc-2.31"}
|
||||
|
||||
# GCC 9/10 introduces many new checkings and will cause the build to fail.
|
||||
if [ "$(gcc -dumpversion)" = "9" -o "$(gcc -dumpversion)" = "10" ]; then
|
||||
if [ "$(gcc -dumpversion)" = "9" ] || \
|
||||
[ "$(gcc -dumpversion)" = "10" ] || \
|
||||
[ "$(gcc -dumpversion)" = "11" ]; then
|
||||
EXTRA_CFLAGS=-fcommon
|
||||
EXTRA_CONFIG_OPTION="--disable-werror"
|
||||
fi
|
||||
@ -20,7 +23,7 @@ rm -rf ${INSTALL_DIR}
|
||||
mkdir -p ${SRC_DIR}
|
||||
cd ${SRC_DIR}
|
||||
# Download glibc
|
||||
git clone -b occlum-glibc-2.31 https://github.com/occlum/glibc .
|
||||
git clone -b ${GLIBC_BRANCH} https://github.com/occlum/glibc .
|
||||
|
||||
mkdir -p ${BUILD_DIR}
|
||||
cd ${BUILD_DIR}
|
||||
|
Loading…
Reference in New Issue
Block a user