[libos] Implement sub-crates including uring_callback/keyable-arc/object-id/untrusted-alloc etc
This commit is contained in:
parent
44eb5ca3fe
commit
fd6e1bae45
@ -34,9 +34,16 @@ ctor = "0.1"
|
|||||||
regex = { git = "https://github.com/mesalock-linux/regex-sgx", default-features = false, features = ["std", "unicode", "mesalock_sgx"] }
|
regex = { git = "https://github.com/mesalock-linux/regex-sgx", default-features = false, features = ["std", "unicode", "mesalock_sgx"] }
|
||||||
goblin = { version = "0.5.4", default-features = false, features = ["elf64", "elf32", "endian_fd"] }
|
goblin = { version = "0.5.4", default-features = false, features = ["elf64", "elf32", "endian_fd"] }
|
||||||
intrusive-collections = "0.9"
|
intrusive-collections = "0.9"
|
||||||
spin = "0.7"
|
|
||||||
modular-bitfield = "0.11.2"
|
modular-bitfield = "0.11.2"
|
||||||
|
|
||||||
|
sgx-untrusted-alloc = { path = "./crates/sgx-untrusted-alloc", features = ["sgx"]}
|
||||||
|
io-uring-callback = { path = "./crates/io-uring-callback", features = ["sgx"]}
|
||||||
|
num_enum = { version = "0.5", default-features = false }
|
||||||
|
keyable-arc = { path = "./crates/keyable-arc" }
|
||||||
|
downcast-rs = { version = "1.2.0", default-features = false }
|
||||||
|
spin = "0.7"
|
||||||
|
byteorder = { version = "1.3.2", default-features = false }
|
||||||
|
|
||||||
[patch.'https://github.com/apache/teaclave-sgx-sdk.git']
|
[patch.'https://github.com/apache/teaclave-sgx-sdk.git']
|
||||||
sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd" }
|
sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd" }
|
||||||
|
|
||||||
@ -54,7 +61,7 @@ kernel_heap_monitor = []# Kernel heap usage tracking. With overhead.
|
|||||||
|
|
||||||
[target.'cfg(not(target_env = "sgx"))'.dependencies]
|
[target.'cfg(not(target_env = "sgx"))'.dependencies]
|
||||||
sgx_types = { path = "../../deps/rust-sgx-sdk/sgx_types" }
|
sgx_types = { path = "../../deps/rust-sgx-sdk/sgx_types" }
|
||||||
sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd", features = ["backtrace"] }
|
sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd", features = ["backtrace", "thread"] }
|
||||||
sgx_trts = { path = "../../deps/rust-sgx-sdk/sgx_trts" }
|
sgx_trts = { path = "../../deps/rust-sgx-sdk/sgx_trts" }
|
||||||
sgx_tse = { path = "../../deps/rust-sgx-sdk/sgx_tse" }
|
sgx_tse = { path = "../../deps/rust-sgx-sdk/sgx_tse" }
|
||||||
sgx_tcrypto = { path = "../../deps/rust-sgx-sdk/sgx_tcrypto" }
|
sgx_tcrypto = { path = "../../deps/rust-sgx-sdk/sgx_tcrypto" }
|
||||||
|
@ -64,7 +64,8 @@ LIBOS_CORE_A := $(OBJ_DIR)/libos/lib/lib$(LIBOS_CORE_LIB_NAME).a
|
|||||||
LIBOS_CORE_RS_A := $(OBJ_DIR)/libos/lib/libocclum_libos_core_rs.a
|
LIBOS_CORE_RS_A := $(OBJ_DIR)/libos/lib/libocclum_libos_core_rs.a
|
||||||
|
|
||||||
# All source code
|
# All source code
|
||||||
RUST_SRCS := $(wildcard src/*.rs src/*/*.rs src/*/*/*.rs src/*/*/*/*.rs src/*/*/*/*/*.rs)
|
RUST_SRCS := $(wildcard src/*.rs src/*/*.rs src/*/*/*.rs src/*/*/*/*.rs src/*/*/*/*/*.rs \
|
||||||
|
crates/*/src/*.rs crates/*/src/*/*.rs crates/*/src/*/*/*.rs crates/*/src/*/*/*/*.rs)
|
||||||
RUST_TARGET_DIR := $(OBJ_DIR)/libos/cargo-target
|
RUST_TARGET_DIR := $(OBJ_DIR)/libos/cargo-target
|
||||||
RUST_OUT_DIR := $(OBJ_DIR)/libos/lib
|
RUST_OUT_DIR := $(OBJ_DIR)/libos/lib
|
||||||
EDL_C_SRCS := $(addprefix $(OBJ_DIR)/libos/,$(SRC_OBJ)/Enclave_t.c $(SRC_OBJ)/Enclave_t.h)
|
EDL_C_SRCS := $(addprefix $(OBJ_DIR)/libos/,$(SRC_OBJ)/Enclave_t.c $(SRC_OBJ)/Enclave_t.h)
|
||||||
@ -166,7 +167,8 @@ $(OBJ_DIR)/libos/$(SRC_OBJ)/Enclave_t.c: $(SGX_EDGER8R) ../Enclave.edl
|
|||||||
$(SGX_EDGER8R) $(SGX_EDGER8R_MODE) --trusted $(CUR_DIR)/../Enclave.edl \
|
$(SGX_EDGER8R) $(SGX_EDGER8R_MODE) --trusted $(CUR_DIR)/../Enclave.edl \
|
||||||
--search-path $(SGX_SDK)/include \
|
--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
|
--search-path $(CRATES_DIR)/vdso-time/ocalls \
|
||||||
|
--search-path $(PROJECT_DIR)/deps/io-uring/ocalls
|
||||||
@echo "GEN <= $@"
|
@echo "GEN <= $@"
|
||||||
|
|
||||||
$(C_OBJS):$(OBJ_DIR)/libos/$(SRC_OBJ)/%.o: src/%.c
|
$(C_OBJS):$(OBJ_DIR)/libos/$(SRC_OBJ)/%.o: src/%.c
|
||||||
@ -188,6 +190,7 @@ format-c: $(C_SRCS) $(CXX_SRCS)
|
|||||||
|
|
||||||
format-rust: $(RUST_SRCS)
|
format-rust: $(RUST_SRCS)
|
||||||
@$(call format-rust)
|
@$(call format-rust)
|
||||||
|
@cd crates && $(call format-rust)
|
||||||
|
|
||||||
format-check: format-check-c format-check-rust
|
format-check: format-check-c format-check-rust
|
||||||
|
|
||||||
@ -196,6 +199,7 @@ format-check-c: $(C_SRCS) $(CXX_SRCS)
|
|||||||
|
|
||||||
format-check-rust: $(RUST_SRCS)
|
format-check-rust: $(RUST_SRCS)
|
||||||
@$(call format-check-rust)
|
@$(call format-check-rust)
|
||||||
|
@cd crates && $(call format-check-rust)
|
||||||
|
|
||||||
COV_TARGET_DIR := $(RUST_TARGET_DIR)/debug/deps
|
COV_TARGET_DIR := $(RUST_TARGET_DIR)/debug/deps
|
||||||
DEPS_DIR := $(shell pwd)/../../deps
|
DEPS_DIR := $(shell pwd)/../../deps
|
||||||
|
24
src/libos/crates/Cargo.toml
Normal file
24
src/libos/crates/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[workspace]
|
||||||
|
|
||||||
|
members = [
|
||||||
|
"errno",
|
||||||
|
"io-uring-callback",
|
||||||
|
"keyable-arc",
|
||||||
|
"object-id",
|
||||||
|
"sgx-untrusted-alloc",
|
||||||
|
"vdso-time"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Default members can run on Linux; non-default members can only run inside SGX.
|
||||||
|
default-members = [
|
||||||
|
"errno",
|
||||||
|
"io-uring-callback",
|
||||||
|
"keyable-arc",
|
||||||
|
"object-id",
|
||||||
|
"sgx-untrusted-alloc",
|
||||||
|
"vdso-time"
|
||||||
|
]
|
||||||
|
|
||||||
|
exclude = [
|
||||||
|
"test",
|
||||||
|
]
|
7
src/libos/crates/common.mk
Normal file
7
src/libos/crates/common.mk
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
MAIN_MAKEFILE := $(firstword $(MAKEFILE_LIST))
|
||||||
|
INCLUDE_MAKEFILE := $(lastword $(MAKEFILE_LIST))
|
||||||
|
CURRENT_DIR := $(shell dirname $(realpath $(MAIN_MAKEFILE)))
|
||||||
|
ROOT_DIR := $(realpath $(shell dirname $(realpath $(INCLUDE_MAKEFILE)))/../../../)
|
||||||
|
RUST_SGX_SDK_DIR := $(ROOT_DIR)/deps/rust-sgx-sdk
|
||||||
|
LIBOS_DIR := $(ROOT_DIR)/src/libos
|
||||||
|
LIBOS_CRATES_DIR := $(LIBOS_DIR)/crates
|
@ -116,8 +116,8 @@ mod result;
|
|||||||
mod to_errno;
|
mod to_errno;
|
||||||
|
|
||||||
pub use self::backtrace::ErrorBacktrace;
|
pub use self::backtrace::ErrorBacktrace;
|
||||||
pub use self::errno::*;
|
|
||||||
pub use self::errno::Errno::*;
|
pub use self::errno::Errno::*;
|
||||||
|
pub use self::errno::*;
|
||||||
pub use self::error::{Error, ErrorLocation};
|
pub use self::error::{Error, ErrorLocation};
|
||||||
pub use self::result::{Result, ResultExt};
|
pub use self::result::{Result, ResultExt};
|
||||||
pub use self::to_errno::ToErrno;
|
pub use self::to_errno::ToErrno;
|
||||||
@ -130,13 +130,18 @@ macro_rules! errno {
|
|||||||
let msg: &'static str = $error_msg;
|
let msg: &'static str = $error_msg;
|
||||||
(errno, msg)
|
(errno, msg)
|
||||||
};
|
};
|
||||||
let error =
|
let error = $crate::Error::embedded(
|
||||||
$crate::Error::embedded(inner_error, Some($crate::ErrorLocation::new(file!(), line!())));
|
inner_error,
|
||||||
|
Some($crate::ErrorLocation::new(file!(), line!())),
|
||||||
|
);
|
||||||
error
|
error
|
||||||
}};
|
}};
|
||||||
($error_expr: expr) => {{
|
($error_expr: expr) => {{
|
||||||
let inner_error = $error_expr;
|
let inner_error = $error_expr;
|
||||||
let error = $crate::Error::boxed(inner_error, Some($crate::ErrorLocation::new(file!(), line!())));
|
let error = $crate::Error::boxed(
|
||||||
|
inner_error,
|
||||||
|
Some($crate::ErrorLocation::new(file!(), line!())),
|
||||||
|
);
|
||||||
error
|
error
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
400
src/libos/crates/io-uring-callback/Cargo.lock
generated
Normal file
400
src/libos/crates/io-uring-callback/Cargo.lock
generated
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel"
|
||||||
|
version = "0.3.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"pin-project-lite",
|
||||||
|
"pin-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown_tstd"
|
||||||
|
version = "0.12.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-uring"
|
||||||
|
version = "0.5.9"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"libc",
|
||||||
|
"sgx_libc",
|
||||||
|
"sgx_trts",
|
||||||
|
"sgx_tstd",
|
||||||
|
"sgx_types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-uring-callback"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"atomic",
|
||||||
|
"cfg-if",
|
||||||
|
"futures",
|
||||||
|
"io-uring",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"lock_api",
|
||||||
|
"log",
|
||||||
|
"sgx_libc",
|
||||||
|
"sgx_tstd",
|
||||||
|
"slab",
|
||||||
|
"spin 0.7.1",
|
||||||
|
"tempfile",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
dependencies = [
|
||||||
|
"spin 0.5.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.150"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.4.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.38.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.4.1",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sgx_alloc"
|
||||||
|
version = "1.1.5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sgx_backtrace_sys"
|
||||||
|
version = "1.1.5"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"sgx_build_helper",
|
||||||
|
"sgx_libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sgx_build_helper"
|
||||||
|
version = "1.1.5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sgx_demangle"
|
||||||
|
version = "1.1.5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sgx_libc"
|
||||||
|
version = "1.1.5"
|
||||||
|
dependencies = [
|
||||||
|
"sgx_types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sgx_tprotected_fs"
|
||||||
|
version = "1.1.5"
|
||||||
|
dependencies = [
|
||||||
|
"sgx_trts",
|
||||||
|
"sgx_types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sgx_trts"
|
||||||
|
version = "1.1.5"
|
||||||
|
dependencies = [
|
||||||
|
"sgx_libc",
|
||||||
|
"sgx_types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sgx_tstd"
|
||||||
|
version = "1.1.5"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown_tstd",
|
||||||
|
"sgx_alloc",
|
||||||
|
"sgx_backtrace_sys",
|
||||||
|
"sgx_demangle",
|
||||||
|
"sgx_libc",
|
||||||
|
"sgx_tprotected_fs",
|
||||||
|
"sgx_trts",
|
||||||
|
"sgx_types",
|
||||||
|
"sgx_unwind",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sgx_types"
|
||||||
|
version = "1.1.5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sgx_unwind"
|
||||||
|
version = "1.1.5"
|
||||||
|
dependencies = [
|
||||||
|
"sgx_build_helper",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"fastrand",
|
||||||
|
"redox_syscall",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
29
src/libos/crates/io-uring-callback/Cargo.toml
Normal file
29
src/libos/crates/io-uring-callback/Cargo.toml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[package]
|
||||||
|
name = "io-uring-callback"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Tate, Hongliang Tian <tate.thl@antfin.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["libc"]
|
||||||
|
sgx = ["sgx_tstd", "sgx_libc", "io-uring/sgx"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
atomic = "0.5.0"
|
||||||
|
cfg-if = "1.0.0"
|
||||||
|
lock_api = "=0.4.2"
|
||||||
|
log = "0.4"
|
||||||
|
futures = { version = "0.3", default-features = false, features = ["alloc"] }
|
||||||
|
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
|
||||||
|
slab = { version = "0.4.5", default-features = false }
|
||||||
|
libc = { version = "0.2", optional = true }
|
||||||
|
|
||||||
|
io-uring = { path = "../../../../deps/io-uring", features = ["unstable"] }
|
||||||
|
sgx_tstd = { path = "../../../../deps/rust-sgx-sdk/sgx_tstd", optional = true, features = ["backtrace"] }
|
||||||
|
sgx_libc = { path = "../../../../deps/rust-sgx-sdk/sgx_libc", optional = true }
|
||||||
|
spin = "0.7"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = "3"
|
15
src/libos/crates/io-uring-callback/README.md
Normal file
15
src/libos/crates/io-uring-callback/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# io-uring-callback
|
||||||
|
|
||||||
|
io-uring with callback interface.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
To use io-uring-callback, place the following line under the `[dependencies]` section in your `Cargo.toml`:
|
||||||
|
|
||||||
|
```
|
||||||
|
io-uring-callback = { path = "your_path/io-uring-callback" }
|
||||||
|
```
|
||||||
|
|
||||||
|
if use io-uring-callback in SGX (based on rust-sgx-sdk), place the following line under the `[dependencies]` section in your `Cargo.toml` and prepare incubator-teaclave-sgx-sdk envirenments according to io-uring-callback's `Cargo.toml`:
|
||||||
|
```
|
||||||
|
io-uring-callback = { path = "your_path/io-uring-callback", features = ["sgx"] }
|
||||||
|
```
|
11
src/libos/crates/io-uring-callback/examples/sgx/.gitignore
vendored
Normal file
11
src/libos/crates/io-uring-callback/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
|
161
src/libos/crates/io-uring-callback/examples/sgx/Makefile
Normal file
161
src/libos/crates/io-uring-callback/examples/sgx/Makefile
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# 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 := $(ROOT_DIR)/deps/io-uring/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_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_Name): $(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
|
14
src/libos/crates/io-uring-callback/examples/sgx/README.md
Normal file
14
src/libos/crates/io-uring-callback/examples/sgx/README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
## tcp_echo example for SGX
|
||||||
|
This is an example of using io-uring-callback in SGX.
|
||||||
|
This example combines tcp_echo example of io-uring-callback and hello-rust example of incubator-teaclave-sgx-sdk.
|
||||||
|
- ./app : untrusted code
|
||||||
|
- ./bin : executable program
|
||||||
|
- ./enclave : trusted code
|
||||||
|
- ./lib : library
|
||||||
|
|
||||||
|
### run tcp_echo example in SGX
|
||||||
|
1. Prepare environments.
|
||||||
|
- clone incubator-teaclave-sgx-sdk repo to ```../../../third_parties/```. And checkout incubator-teaclave-sgx-sdk to ```d94996``` commit.
|
||||||
|
- prepare environments for incubator-teaclave-sgx-sdk.
|
||||||
|
2. ```make```
|
||||||
|
3. ```cd bin && ./app```
|
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "app"
|
||||||
|
version = "1.0.0"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
sgx-io-uring-ocalls = { path = "../deps/io-uring/ocalls" }
|
||||||
|
sgx_types = { path = "../deps/rust-sgx-sdk/sgx_types" }
|
||||||
|
sgx_urts = { path = "../deps/rust-sgx-sdk/sgx_urts" }
|
||||||
|
|
||||||
|
[workspace]
|
33
src/libos/crates/io-uring-callback/examples/sgx/app/build.rs
Normal file
33
src/libos/crates/io-uring-callback/examples/sgx/app/build.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 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-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
|
@ -0,0 +1,81 @@
|
|||||||
|
// 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_io_uring_ocalls;
|
||||||
|
extern crate sgx_types;
|
||||||
|
extern crate sgx_urts;
|
||||||
|
use sgx_types::*;
|
||||||
|
use sgx_urts::SgxEnclave;
|
||||||
|
|
||||||
|
pub use sgx_io_uring_ocalls::*;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
bin
|
@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
name = "Helloworldsampleenclave"
|
||||||
|
version = "1.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "helloworldsampleenclave"
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
io-uring-callback = { path = "../../../../io-uring-callback", features = ["sgx"] }
|
||||||
|
io-uring = { path = "../../../../../../../deps/io-uring", features = ["sgx"] }
|
||||||
|
slab = { version = "0.4.5", default-features = false }
|
||||||
|
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" }
|
||||||
|
|
||||||
|
[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>
|
@ -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_io_uring_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-----
|
@ -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)/../../../../incubator-teaclave-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
|
@ -0,0 +1,334 @@
|
|||||||
|
// 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 io_uring;
|
||||||
|
extern crate io_uring_callback;
|
||||||
|
extern crate lazy_static;
|
||||||
|
extern crate slab;
|
||||||
|
|
||||||
|
use sgx_trts::libc;
|
||||||
|
use sgx_types::*;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
use std::prelude::v1::*;
|
||||||
|
use std::ptr;
|
||||||
|
use std::sync::SgxMutex as Mutex;
|
||||||
|
|
||||||
|
use io_uring::opcode::types;
|
||||||
|
use io_uring_callback::{Builder, Handle, IoUring};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use slab::Slab;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref TOKEN_QUEUE: Mutex<VecDeque<(Token, i32)>> = Mutex::new(VecDeque::new());
|
||||||
|
static ref HANDLE_SLAB: Mutex<slab::Slab<Handle>> = Mutex::new(slab::Slab::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum Token {
|
||||||
|
Accept,
|
||||||
|
Poll {
|
||||||
|
fd: RawFd,
|
||||||
|
},
|
||||||
|
Read {
|
||||||
|
fd: RawFd,
|
||||||
|
buf_index: usize,
|
||||||
|
},
|
||||||
|
Write {
|
||||||
|
fd: RawFd,
|
||||||
|
buf_index: usize,
|
||||||
|
offset: usize,
|
||||||
|
len: usize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AcceptCount {
|
||||||
|
fd: types::Fd,
|
||||||
|
count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AcceptCount {
|
||||||
|
fn new(fd: RawFd, count: usize) -> AcceptCount {
|
||||||
|
AcceptCount {
|
||||||
|
fd: types::Fd(fd),
|
||||||
|
count: count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_push_accept(&mut self, ring: &IoUring) {
|
||||||
|
while self.count > 0 {
|
||||||
|
let to_complete_token = Token::Accept;
|
||||||
|
let mut handle_slab = HANDLE_SLAB.lock().unwrap();
|
||||||
|
let slab_entry = handle_slab.vacant_entry();
|
||||||
|
let slab_key = slab_entry.key();
|
||||||
|
|
||||||
|
let complete_fn = move |retval: i32| {
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
queue.push_back((to_complete_token, retval));
|
||||||
|
|
||||||
|
HANDLE_SLAB.lock().unwrap().remove(slab_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle =
|
||||||
|
unsafe { ring.accept(self.fd, ptr::null_mut(), ptr::null_mut(), 0, complete_fn) };
|
||||||
|
|
||||||
|
slab_entry.insert(handle);
|
||||||
|
|
||||||
|
self.count -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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");
|
||||||
|
|
||||||
|
let ring = Builder::new()
|
||||||
|
.setup_sqpoll(Some(500/* ms */))
|
||||||
|
.build(256)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let socket_fd = {
|
||||||
|
let socket_fd = unsafe { libc::ocall::socket(libc::AF_INET, libc::SOCK_STREAM, 0) };
|
||||||
|
if socket_fd < 0 {
|
||||||
|
println!("[ECALL] create socket failed, ret: {}", socket_fd);
|
||||||
|
return sgx_status_t::SGX_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = unsafe {
|
||||||
|
let servaddr = libc::sockaddr_in {
|
||||||
|
sin_family: libc::AF_INET as u16,
|
||||||
|
sin_port: 3456_u16.to_be(),
|
||||||
|
sin_addr: libc::in_addr { s_addr: 0 },
|
||||||
|
sin_zero: [0; 8],
|
||||||
|
};
|
||||||
|
libc::ocall::bind(
|
||||||
|
socket_fd,
|
||||||
|
&servaddr as *const _ as *const libc::sockaddr,
|
||||||
|
core::mem::size_of::<libc::sockaddr_in>() as u32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if ret < 0 {
|
||||||
|
println!("[ECALL] bind failed, ret: {}", ret);
|
||||||
|
unsafe {
|
||||||
|
libc::ocall::close(socket_fd);
|
||||||
|
}
|
||||||
|
return sgx_status_t::SGX_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = unsafe { libc::ocall::listen(socket_fd, 10) };
|
||||||
|
if ret < 0 {
|
||||||
|
println!("[ECALL] listen failed, ret: {}", ret);
|
||||||
|
unsafe {
|
||||||
|
libc::ocall::close(socket_fd);
|
||||||
|
}
|
||||||
|
return sgx_status_t::SGX_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
socket_fd
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut bufpool = Vec::with_capacity(64);
|
||||||
|
let mut buf_alloc = Slab::with_capacity(64);
|
||||||
|
|
||||||
|
println!("[ECALL] listen 127.0.0.1:3456");
|
||||||
|
|
||||||
|
let mut accept = AcceptCount::new(socket_fd, 3);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
accept.try_push_accept(&ring);
|
||||||
|
|
||||||
|
ring.trigger_callbacks();
|
||||||
|
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
while !queue.is_empty() {
|
||||||
|
let (token, ret) = queue.pop_front().unwrap();
|
||||||
|
match token {
|
||||||
|
Token::Accept => {
|
||||||
|
println!("[ECALL] accept");
|
||||||
|
|
||||||
|
accept.count += 1;
|
||||||
|
|
||||||
|
let fd = ret;
|
||||||
|
|
||||||
|
let to_complete_token = Token::Poll { fd };
|
||||||
|
let mut handle_slab = HANDLE_SLAB.lock().unwrap();
|
||||||
|
let slab_entry = handle_slab.vacant_entry();
|
||||||
|
let slab_key = slab_entry.key();
|
||||||
|
|
||||||
|
let complete_fn = move |retval: i32| {
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
queue.push_back((to_complete_token, retval));
|
||||||
|
|
||||||
|
HANDLE_SLAB.lock().unwrap().remove(slab_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle =
|
||||||
|
unsafe { ring.poll(types::Fd(fd), libc::POLLIN as _, complete_fn) };
|
||||||
|
|
||||||
|
slab_entry.insert(handle);
|
||||||
|
}
|
||||||
|
Token::Poll { fd } => {
|
||||||
|
let (buf_index, buf) = match bufpool.pop() {
|
||||||
|
Some(buf_index) => (buf_index, &mut buf_alloc[buf_index]),
|
||||||
|
None => {
|
||||||
|
let buf = Box::new(unsafe {
|
||||||
|
std::slice::from_raw_parts_mut(
|
||||||
|
libc::ocall::malloc(2048) as *mut u8,
|
||||||
|
2048,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let buf_entry = buf_alloc.vacant_entry();
|
||||||
|
let buf_index = buf_entry.key();
|
||||||
|
(buf_index, buf_entry.insert(buf))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let to_complete_token = Token::Read { fd, buf_index };
|
||||||
|
let mut handle_slab = HANDLE_SLAB.lock().unwrap();
|
||||||
|
let slab_entry = handle_slab.vacant_entry();
|
||||||
|
let slab_key = slab_entry.key();
|
||||||
|
|
||||||
|
let complete_fn = move |retval: i32| {
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
queue.push_back((to_complete_token, retval));
|
||||||
|
|
||||||
|
HANDLE_SLAB.lock().unwrap().remove(slab_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle = unsafe {
|
||||||
|
ring.read(
|
||||||
|
types::Fd(fd),
|
||||||
|
buf.as_mut_ptr(),
|
||||||
|
buf.len() as _,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
complete_fn,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
slab_entry.insert(handle);
|
||||||
|
}
|
||||||
|
Token::Read { fd, buf_index } => {
|
||||||
|
if ret == 0 {
|
||||||
|
bufpool.push(buf_index);
|
||||||
|
|
||||||
|
println!("[ECALL] shutdown");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
libc::ocall::close(fd);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let len = ret as usize;
|
||||||
|
let buf = &buf_alloc[buf_index];
|
||||||
|
|
||||||
|
let to_complete_token = Token::Write {
|
||||||
|
fd,
|
||||||
|
buf_index,
|
||||||
|
len,
|
||||||
|
offset: 0,
|
||||||
|
};
|
||||||
|
let mut handle_slab = HANDLE_SLAB.lock().unwrap();
|
||||||
|
let slab_entry = handle_slab.vacant_entry();
|
||||||
|
let slab_key = slab_entry.key();
|
||||||
|
|
||||||
|
let complete_fn = move |retval: i32| {
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
queue.push_back((to_complete_token, retval));
|
||||||
|
|
||||||
|
HANDLE_SLAB.lock().unwrap().remove(slab_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle = unsafe {
|
||||||
|
ring.write(types::Fd(fd), buf.as_ptr(), len as _, 0, 0, complete_fn)
|
||||||
|
};
|
||||||
|
|
||||||
|
slab_entry.insert(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Token::Write {
|
||||||
|
fd,
|
||||||
|
buf_index,
|
||||||
|
offset,
|
||||||
|
len,
|
||||||
|
} => {
|
||||||
|
let write_len = ret as usize;
|
||||||
|
|
||||||
|
if offset + write_len >= len {
|
||||||
|
bufpool.push(buf_index);
|
||||||
|
|
||||||
|
let to_complete_token = Token::Poll { fd };
|
||||||
|
let mut handle_slab = HANDLE_SLAB.lock().unwrap();
|
||||||
|
let slab_entry = handle_slab.vacant_entry();
|
||||||
|
let slab_key = slab_entry.key();
|
||||||
|
|
||||||
|
let complete_fn = move |retval: i32| {
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
queue.push_back((to_complete_token, retval));
|
||||||
|
|
||||||
|
HANDLE_SLAB.lock().unwrap().remove(slab_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle =
|
||||||
|
unsafe { ring.poll_add(types::Fd(fd), libc::POLLIN as _, complete_fn) };
|
||||||
|
|
||||||
|
slab_entry.insert(handle);
|
||||||
|
} else {
|
||||||
|
let offset = offset + write_len;
|
||||||
|
let len = len - offset;
|
||||||
|
|
||||||
|
let buf = &buf_alloc[buf_index][offset..];
|
||||||
|
|
||||||
|
let to_complete_token = Token::Write {
|
||||||
|
fd,
|
||||||
|
buf_index,
|
||||||
|
offset,
|
||||||
|
len,
|
||||||
|
};
|
||||||
|
let mut handle_slab = HANDLE_SLAB.lock().unwrap();
|
||||||
|
let slab_entry = handle_slab.vacant_entry();
|
||||||
|
let slab_key = slab_entry.key();
|
||||||
|
|
||||||
|
let complete_fn = move |retval: i32| {
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
queue.push_back((to_complete_token, retval));
|
||||||
|
|
||||||
|
HANDLE_SLAB.lock().unwrap().remove(slab_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle = unsafe {
|
||||||
|
ring.write(types::Fd(fd), buf.as_ptr(), len as _, 0, 0, complete_fn)
|
||||||
|
};
|
||||||
|
|
||||||
|
slab_entry.insert(handle);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
src/libos/crates/io-uring-callback/examples/sgx/enclave/x86_64-unknown-linux-sgx.json
Normal file
31
src/libos/crates/io-uring-callback/examples/sgx/enclave/x86_64-unknown-linux-sgx.json
Normal file
@ -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"
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
lib
|
251
src/libos/crates/io-uring-callback/examples/tcp_echo.rs
Normal file
251
src/libos/crates/io-uring-callback/examples/tcp_echo.rs
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::net::TcpListener;
|
||||||
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
use std::ptr;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use io_uring::opcode::types;
|
||||||
|
use io_uring_callback::{Builder, IoHandle, IoUring};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref TOKEN_QUEUE: Mutex<VecDeque<(Token, i32)>> = Mutex::new(VecDeque::new());
|
||||||
|
static ref HANDLE_SLAB: Mutex<slab::Slab<IoHandle>> = Mutex::new(slab::Slab::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum Token {
|
||||||
|
Accept,
|
||||||
|
Poll {
|
||||||
|
fd: RawFd,
|
||||||
|
},
|
||||||
|
Read {
|
||||||
|
fd: RawFd,
|
||||||
|
buf_index: usize,
|
||||||
|
},
|
||||||
|
Write {
|
||||||
|
fd: RawFd,
|
||||||
|
buf_index: usize,
|
||||||
|
offset: usize,
|
||||||
|
len: usize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AcceptCount {
|
||||||
|
fd: types::Fd,
|
||||||
|
count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AcceptCount {
|
||||||
|
fn new(fd: RawFd, count: usize) -> AcceptCount {
|
||||||
|
AcceptCount {
|
||||||
|
fd: types::Fd(fd),
|
||||||
|
count: count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_push_accept(&mut self, ring: &IoUring) {
|
||||||
|
while self.count > 0 {
|
||||||
|
let to_complete_token = Token::Accept;
|
||||||
|
let mut handle_slab = HANDLE_SLAB.lock().unwrap();
|
||||||
|
let slab_entry = handle_slab.vacant_entry();
|
||||||
|
let slab_key = slab_entry.key();
|
||||||
|
|
||||||
|
let complete_fn = move |retval: i32| {
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
queue.push_back((to_complete_token, retval));
|
||||||
|
|
||||||
|
HANDLE_SLAB.lock().unwrap().remove(slab_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle =
|
||||||
|
unsafe { ring.accept(self.fd, ptr::null_mut(), ptr::null_mut(), 0, complete_fn) };
|
||||||
|
|
||||||
|
slab_entry.insert(handle);
|
||||||
|
|
||||||
|
self.count -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let ring = Builder::new()
|
||||||
|
.setup_sqpoll(Some(500 /* ms */))
|
||||||
|
.build(256)
|
||||||
|
.unwrap();
|
||||||
|
let listener = TcpListener::bind(("127.0.0.1", 3456)).unwrap();
|
||||||
|
|
||||||
|
let mut bufpool = Vec::with_capacity(64);
|
||||||
|
let mut buf_alloc = slab::Slab::with_capacity(64);
|
||||||
|
|
||||||
|
println!("listen {}", listener.local_addr().unwrap());
|
||||||
|
|
||||||
|
let mut accept = AcceptCount::new(listener.as_raw_fd(), 3);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
accept.try_push_accept(&ring);
|
||||||
|
|
||||||
|
ring.poll_completions(0, 100);
|
||||||
|
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
while !queue.is_empty() {
|
||||||
|
let (token, ret) = queue.pop_front().unwrap();
|
||||||
|
match token {
|
||||||
|
Token::Accept => {
|
||||||
|
println!("accept");
|
||||||
|
|
||||||
|
accept.count += 1;
|
||||||
|
|
||||||
|
let fd = ret;
|
||||||
|
|
||||||
|
let to_complete_token = Token::Poll { fd };
|
||||||
|
let mut handle_slab = HANDLE_SLAB.lock().unwrap();
|
||||||
|
let slab_entry = handle_slab.vacant_entry();
|
||||||
|
let slab_key = slab_entry.key();
|
||||||
|
|
||||||
|
let complete_fn = move |retval: i32| {
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
queue.push_back((to_complete_token, retval));
|
||||||
|
|
||||||
|
HANDLE_SLAB.lock().unwrap().remove(slab_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle =
|
||||||
|
unsafe { ring.poll(types::Fd(fd), libc::POLLIN as _, complete_fn) };
|
||||||
|
|
||||||
|
slab_entry.insert(handle);
|
||||||
|
}
|
||||||
|
Token::Poll { fd } => {
|
||||||
|
let (buf_index, buf) = match bufpool.pop() {
|
||||||
|
Some(buf_index) => (buf_index, &mut buf_alloc[buf_index]),
|
||||||
|
None => {
|
||||||
|
let buf = vec![0u8; 2048].into_boxed_slice();
|
||||||
|
let buf_entry = buf_alloc.vacant_entry();
|
||||||
|
let buf_index = buf_entry.key();
|
||||||
|
(buf_index, buf_entry.insert(buf))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let to_complete_token = Token::Read { fd, buf_index };
|
||||||
|
let mut handle_slab = HANDLE_SLAB.lock().unwrap();
|
||||||
|
let slab_entry = handle_slab.vacant_entry();
|
||||||
|
let slab_key = slab_entry.key();
|
||||||
|
|
||||||
|
let complete_fn = move |retval: i32| {
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
queue.push_back((to_complete_token, retval));
|
||||||
|
|
||||||
|
HANDLE_SLAB.lock().unwrap().remove(slab_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle = unsafe {
|
||||||
|
ring.read(
|
||||||
|
types::Fd(fd),
|
||||||
|
buf.as_mut_ptr(),
|
||||||
|
buf.len() as _,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
complete_fn,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
slab_entry.insert(handle);
|
||||||
|
}
|
||||||
|
Token::Read { fd, buf_index } => {
|
||||||
|
if ret == 0 {
|
||||||
|
bufpool.push(buf_index);
|
||||||
|
|
||||||
|
println!("shutdown");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
libc::close(fd);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let len = ret as usize;
|
||||||
|
let buf = &buf_alloc[buf_index];
|
||||||
|
|
||||||
|
let to_complete_token = Token::Write {
|
||||||
|
fd,
|
||||||
|
buf_index,
|
||||||
|
len,
|
||||||
|
offset: 0,
|
||||||
|
};
|
||||||
|
let mut handle_slab = HANDLE_SLAB.lock().unwrap();
|
||||||
|
let slab_entry = handle_slab.vacant_entry();
|
||||||
|
let slab_key = slab_entry.key();
|
||||||
|
|
||||||
|
let complete_fn = move |retval: i32| {
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
queue.push_back((to_complete_token, retval));
|
||||||
|
|
||||||
|
HANDLE_SLAB.lock().unwrap().remove(slab_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle = unsafe {
|
||||||
|
ring.write(types::Fd(fd), buf.as_ptr(), len as _, 0, 0, complete_fn)
|
||||||
|
};
|
||||||
|
|
||||||
|
slab_entry.insert(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Token::Write {
|
||||||
|
fd,
|
||||||
|
buf_index,
|
||||||
|
offset,
|
||||||
|
len,
|
||||||
|
} => {
|
||||||
|
let write_len = ret as usize;
|
||||||
|
|
||||||
|
if offset + write_len >= len {
|
||||||
|
bufpool.push(buf_index);
|
||||||
|
|
||||||
|
let to_complete_token = Token::Poll { fd };
|
||||||
|
let mut handle_slab = HANDLE_SLAB.lock().unwrap();
|
||||||
|
let slab_entry = handle_slab.vacant_entry();
|
||||||
|
let slab_key = slab_entry.key();
|
||||||
|
|
||||||
|
let complete_fn = move |retval: i32| {
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
queue.push_back((to_complete_token, retval));
|
||||||
|
|
||||||
|
HANDLE_SLAB.lock().unwrap().remove(slab_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle =
|
||||||
|
unsafe { ring.poll(types::Fd(fd), libc::POLLIN as _, complete_fn) };
|
||||||
|
|
||||||
|
slab_entry.insert(handle);
|
||||||
|
} else {
|
||||||
|
let offset = offset + write_len;
|
||||||
|
let len = len - offset;
|
||||||
|
|
||||||
|
let buf = &buf_alloc[buf_index][offset..];
|
||||||
|
|
||||||
|
let to_complete_token = Token::Write {
|
||||||
|
fd,
|
||||||
|
buf_index,
|
||||||
|
offset,
|
||||||
|
len,
|
||||||
|
};
|
||||||
|
let mut handle_slab = HANDLE_SLAB.lock().unwrap();
|
||||||
|
let slab_entry = handle_slab.vacant_entry();
|
||||||
|
let slab_key = slab_entry.key();
|
||||||
|
|
||||||
|
let complete_fn = move |retval: i32| {
|
||||||
|
let mut queue = TOKEN_QUEUE.lock().unwrap();
|
||||||
|
queue.push_back((to_complete_token, retval));
|
||||||
|
|
||||||
|
HANDLE_SLAB.lock().unwrap().remove(slab_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle = unsafe {
|
||||||
|
ring.write(types::Fd(fd), buf.as_ptr(), len as _, 0, 0, complete_fn)
|
||||||
|
};
|
||||||
|
|
||||||
|
slab_entry.insert(handle);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
src/libos/crates/io-uring-callback/rust-toolchain
Normal file
1
src/libos/crates/io-uring-callback/rust-toolchain
Normal file
@ -0,0 +1 @@
|
|||||||
|
nightly-2022-02-23
|
192
src/libos/crates/io-uring-callback/src/io_handle.rs
Normal file
192
src/libos/crates/io-uring-callback/src/io_handle.rs
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "sgx")] {
|
||||||
|
use std::prelude::v1::*;
|
||||||
|
use spin::Mutex as Mutex;
|
||||||
|
} else {
|
||||||
|
use std::sync::Mutex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The handle to an I/O request pushed to the submission queue of an io_uring instance.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct IoHandle(pub(crate) Arc<IoToken>);
|
||||||
|
|
||||||
|
/// The state of an I/O request represented by an [`IoHandle`].
|
||||||
|
/// If a request is in `Processed` or `Cancelled` state, means that the request is completed.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum IoState {
|
||||||
|
/// The I/O request has been submitted.
|
||||||
|
Submitted,
|
||||||
|
/// The I/O request has been processed by the kernel and returns a value.
|
||||||
|
Processed(i32),
|
||||||
|
/// The I/O request is being cancelled.
|
||||||
|
Cancelling,
|
||||||
|
/// The I/O request has been cancelled by the kernel.
|
||||||
|
Cancelled,
|
||||||
|
}
|
||||||
|
|
||||||
|
const CANCEL_RETVAL: i32 = -libc::ECANCELED;
|
||||||
|
|
||||||
|
impl IoHandle {
|
||||||
|
pub(crate) fn new(token: Arc<IoToken>) -> Self {
|
||||||
|
Self(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the state of the I/O request.
|
||||||
|
pub fn state(&self) -> IoState {
|
||||||
|
self.0.state()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the return value of the I/O request if it is completed.
|
||||||
|
pub fn retval(&self) -> Option<i32> {
|
||||||
|
self.0.retval()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Release a handle.
|
||||||
|
///
|
||||||
|
/// Normally, a handle is not alloed to be dropped before the I/O is completed.
|
||||||
|
/// This helps discover memory safety problems due to potential misuse by users.
|
||||||
|
///
|
||||||
|
/// But sometimes keeping handles can be pointless. This is when the `release`
|
||||||
|
/// method can help. The release method explictly states that a handle is
|
||||||
|
/// useless and then drop it.
|
||||||
|
pub fn release(self) {
|
||||||
|
// Safety. The representation is transparent.
|
||||||
|
let token = unsafe { std::mem::transmute::<Self, Arc<IoToken>>(self) };
|
||||||
|
drop(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unpin for IoHandle {}
|
||||||
|
|
||||||
|
impl Drop for IoHandle {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// The user cannot drop a handle if the request isn't completed.
|
||||||
|
assert!(matches!(
|
||||||
|
self.state(),
|
||||||
|
IoState::Processed(_) | IoState::Cancelled
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A token representing an on-going I/O request.
|
||||||
|
///
|
||||||
|
/// Tokens and handles are basically the same thing---an on-going I/O request. The main difference
|
||||||
|
/// is that handles are used by users, while tokens are used internally.
|
||||||
|
pub(crate) struct IoToken {
|
||||||
|
inner: Mutex<Inner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IoToken {
|
||||||
|
pub fn new(completion_callback: impl FnOnce(i32) + Send + 'static, token_key: u64) -> Self {
|
||||||
|
let inner = Mutex::new(Inner::new(completion_callback, token_key));
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state(&self) -> IoState {
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
inner.state()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn retval(&self) -> Option<i32> {
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
inner.retval()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn complete(&self, retval: i32) {
|
||||||
|
// let mut inner = self.inner.lock().unwrap();
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
let callback = inner.complete(retval);
|
||||||
|
// Must release the lock before invoking the callback function.
|
||||||
|
// This avoids any deadlock if the IoHandle is accessed inside the callback by
|
||||||
|
// user.
|
||||||
|
drop(inner);
|
||||||
|
|
||||||
|
(callback)(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change the state from submited to cancelling.
|
||||||
|
/// If transition succeeds, return the token_key for following cancel operation.
|
||||||
|
pub fn transit_to_cancelling(&self) -> Result<u64, ()> {
|
||||||
|
// let mut inner = self.inner.lock().unwrap();
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
inner.transit_to_cancelling()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for IoToken {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("IoToken")
|
||||||
|
.field("state", &self.state())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Inner {
|
||||||
|
state: IoState,
|
||||||
|
completion_callback: Option<Callback>,
|
||||||
|
token_key: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Callback = Box<dyn FnOnce(i32) + Send + 'static>;
|
||||||
|
|
||||||
|
impl Inner {
|
||||||
|
pub fn new(completion_callback: impl FnOnce(i32) + Send + 'static, token_key: u64) -> Self {
|
||||||
|
let state = IoState::Submitted;
|
||||||
|
let completion_callback = Some(Box::new(completion_callback) as _);
|
||||||
|
Self {
|
||||||
|
state,
|
||||||
|
completion_callback,
|
||||||
|
token_key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn complete(&mut self, retval: i32) -> Callback {
|
||||||
|
match self.state {
|
||||||
|
IoState::Submitted => {
|
||||||
|
self.state = IoState::Processed(retval);
|
||||||
|
}
|
||||||
|
IoState::Cancelling => {
|
||||||
|
if retval == CANCEL_RETVAL {
|
||||||
|
// case 1: The request was cancelled successfully.
|
||||||
|
self.state = IoState::Cancelled;
|
||||||
|
} else {
|
||||||
|
// case 2.1: The request was cancelled with error.
|
||||||
|
// case 2.2: The request was not actually canceled.
|
||||||
|
self.state = IoState::Processed(retval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!("cannot do complete twice");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.completion_callback.take().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transit_to_cancelling(&mut self) -> Result<u64, ()> {
|
||||||
|
match self.state {
|
||||||
|
IoState::Submitted => {
|
||||||
|
self.state = IoState::Cancelling;
|
||||||
|
return Ok(self.token_key);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn retval(&self) -> Option<i32> {
|
||||||
|
match self.state {
|
||||||
|
IoState::Processed(retval) => Some(retval),
|
||||||
|
IoState::Cancelled => Some(CANCEL_RETVAL),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state(&self) -> IoState {
|
||||||
|
self.state
|
||||||
|
}
|
||||||
|
}
|
812
src/libos/crates/io-uring-callback/src/lib.rs
Normal file
812
src/libos/crates/io-uring-callback/src/lib.rs
Normal file
@ -0,0 +1,812 @@
|
|||||||
|
//! A more user-friendly io_uring crate.
|
||||||
|
//!
|
||||||
|
//! # Overview
|
||||||
|
//!
|
||||||
|
//! While the original [io_uring crate](https://github.com/tokio-rs/io-uring) exposes io_uring's API in Rust, it has
|
||||||
|
//! one big shortcoming: users have to manually pop entries out of the completion queue and map those entries to
|
||||||
|
//! user requests. It makes the APIs cumbersome to use.
|
||||||
|
//!
|
||||||
|
//! This crate provides more user-friend APIs with the following features:
|
||||||
|
//!
|
||||||
|
//! * Callback-based. On the completion of an I/O request, the corresponding user-registered
|
||||||
|
//! callback will get invoked. No manual dispatching of I/O completions.
|
||||||
|
//!
|
||||||
|
//! * Async/await-ready. After submitting an I/O request, the user will get a handle that
|
||||||
|
//! represents the on-going I/O request. The user can await the handle (as it is a `Future`).
|
||||||
|
//!
|
||||||
|
//! * Polling-based I/O. Both I/O submissions and completions can be easily done in polling mode.
|
||||||
|
//!
|
||||||
|
//! # Usage
|
||||||
|
//!
|
||||||
|
//! Use [`Builder`] to create a new instance of [`IoUring`].
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use io_uring_callback::{Builder, IoUring};
|
||||||
|
//!
|
||||||
|
//! let io_uring: IoUring = Builder::new().build(256).unwrap();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! A number of I/O operations are supported, e.g., `read`, `write`, `fsync`, `sendmsg`,
|
||||||
|
//! `recvmsg`, etc. Requests for such I/O operations can be pushed into the submission
|
||||||
|
//! queue of the io_uring with the corresponding methods.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use io_uring_callback::{Builder};
|
||||||
|
//! use io_uring_callback::{Fd, RwFlags};
|
||||||
|
//!
|
||||||
|
//! # let io_uring = Builder::new().build(256).unwrap();
|
||||||
|
//! let fd = Fd(1); // use the stdout
|
||||||
|
//! let msg = "hello world\0";
|
||||||
|
//! let completion_callback = move |retval: i32| {
|
||||||
|
//! assert!(retval > 0);
|
||||||
|
//! };
|
||||||
|
//! let handle = unsafe {
|
||||||
|
//! io_uring.write(fd, msg.as_ptr(), msg.len() as u32, 0, RwFlags::default(), completion_callback)
|
||||||
|
//! };
|
||||||
|
//! # while handle.retval().is_none() {
|
||||||
|
//! # io_uring.wait_completions(1);
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! You have to two ways to get notified about the completion of I/O requests. The first
|
||||||
|
//! is through the registered callback function and the second is by `await`ing the handle
|
||||||
|
//! (which is a `Future`) obtained as a result of pushing I/O requests.
|
||||||
|
//!
|
||||||
|
//! After completing the I/O requests, Linux will push I/O responses into the completion queue of
|
||||||
|
//! the io_uring instance. You need _periodically_ poll completions from the queue:
|
||||||
|
//! ```no_run
|
||||||
|
//! # use io_uring_callback::{Builder};
|
||||||
|
//! # let io_uring = Builder::new().build(256).unwrap();
|
||||||
|
//! let min_complete = 1;
|
||||||
|
//! let polling_retries = 5000;
|
||||||
|
//! io_uring.poll_completions(min_complete, polling_retries);
|
||||||
|
//! ```
|
||||||
|
//! which will trigger registered callbacks and wake up handles.
|
||||||
|
//!
|
||||||
|
//! When the I/O request is completed (the request is processed or cancelled by the kernel),
|
||||||
|
//! `poll_completions` will trigger the user-registered callback.
|
||||||
|
//!
|
||||||
|
//! # I/O Handles
|
||||||
|
//!
|
||||||
|
//! After submitting an I/O request, the user will get as the return value
|
||||||
|
//! an instance of [`IoHandle`], which represents the submitted I/O requests.
|
||||||
|
//!
|
||||||
|
//! So why bother keeping I/O handles? The reasons are three-fold.
|
||||||
|
//!
|
||||||
|
//! - First, as a future, `IoHandle` allows you to await on it, which is quite
|
||||||
|
//! convenient if you happen to use io_uring with Rust's async/await.
|
||||||
|
//! - Second, `IoHandle` makes it possible to _cancel_ on-going I/O requests.
|
||||||
|
//! - Third, it makes the whole APIs less prone to memory safety issues. Recall that all I/O submitting
|
||||||
|
//! methods (e.g., `write`, `accept`, etc.) are _unsafe_ as there are no guarantee that
|
||||||
|
//! their arguments---like FDs or buffer pointers---are valid throughout the lifetime of
|
||||||
|
//! an I/O request. What if an user accidentally releases the in-use resources associated with
|
||||||
|
//! an on-going I/O request? I/O handles can detect such programming bugs as long as
|
||||||
|
//! the handles are also released along with other in-use I/O resources (which is most likely).
|
||||||
|
//! This is because when an `IoHandle` is dropped, we will panic if its state is neither
|
||||||
|
//! processed (`IoState::Processed`) or canceled (`IoState::Canceled`). That is, dropping
|
||||||
|
//! an `IoHandle` that is still in-use is forbidden.
|
||||||
|
//!
|
||||||
|
//! After pushing an I/O request into the submission queue, you will get an `IoHandle`.
|
||||||
|
//! With this handle, you can cancel the I/O request.
|
||||||
|
//! ```
|
||||||
|
//! # use io_uring_callback::Builder;
|
||||||
|
//! use io_uring_callback::{Timespec, TimeoutFlags};
|
||||||
|
//!
|
||||||
|
//! # let io_uring = Builder::new().build(256).unwrap();
|
||||||
|
//! let tp = Timespec { tv_sec: 1, tv_nsec: 0, };
|
||||||
|
//! let completion_callback = move |_retval: i32| {};
|
||||||
|
//! let handle = unsafe {
|
||||||
|
//! io_uring.timeout(&tp as *const _, 0, TimeoutFlags::empty(), completion_callback)
|
||||||
|
//! };
|
||||||
|
//! unsafe { io_uring.cancel(&handle); }
|
||||||
|
//! io_uring.wait_completions(1);
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
#![feature(get_mut_unchecked)]
|
||||||
|
#![cfg_attr(feature = "sgx", no_std)]
|
||||||
|
|
||||||
|
#[cfg(feature = "sgx")]
|
||||||
|
extern crate sgx_libc as libc;
|
||||||
|
#[cfg(feature = "sgx")]
|
||||||
|
extern crate sgx_tstd as std;
|
||||||
|
|
||||||
|
use core::sync::atomic::AtomicUsize;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::{collections::HashMap, io};
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "sgx")] {
|
||||||
|
use std::prelude::v1::*;
|
||||||
|
use spin::Mutex as Mutex;
|
||||||
|
} else {
|
||||||
|
use std::sync::Mutex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use atomic::Ordering;
|
||||||
|
use io_uring::opcode;
|
||||||
|
use io_uring::squeue::Entry as SqEntry;
|
||||||
|
use io_uring::types;
|
||||||
|
use slab::Slab;
|
||||||
|
use spin::RwLock;
|
||||||
|
use std::os::unix::prelude::RawFd;
|
||||||
|
|
||||||
|
use crate::io_handle::IoToken;
|
||||||
|
|
||||||
|
mod io_handle;
|
||||||
|
|
||||||
|
pub use crate::io_handle::{IoHandle, IoState};
|
||||||
|
pub use io_uring::types::{Fd, RwFlags, TimeoutFlags, Timespec};
|
||||||
|
pub type IoUringRef = Arc<IoUring>;
|
||||||
|
|
||||||
|
/// An io_uring instance.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// All I/O methods are based on the assumption that the resources (e.g., file descriptors, pointers, etc.)
|
||||||
|
/// given in their arguments are valid before the completion of the async I/O.
|
||||||
|
pub struct IoUring {
|
||||||
|
ring: io_uring::IoUring,
|
||||||
|
token_table: Mutex<Slab<Arc<IoToken>>>,
|
||||||
|
sq_lock: Mutex<()>, // For submission queue synchronization
|
||||||
|
fd_map: RwLock<HashMap<usize, AtomicUsize>>, // (key: fd, value: op num)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for IoUring {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// By the end of the life of the io_uring instance, its token table should have been emptied.
|
||||||
|
// This emptyness check prevents handles created by this io_uring become "dangling".
|
||||||
|
// That is, no user will ever hold a handle whose associated io_uring instance has
|
||||||
|
// been destroyed.
|
||||||
|
// let token_table = self.token_table.lock().unwrap();
|
||||||
|
let token_table = self.token_table.lock();
|
||||||
|
assert!(token_table.len() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IoUring {
|
||||||
|
/// The magic token_key for Cancel I/O request.
|
||||||
|
/// The magic token_key should be different from the token_table's keys.
|
||||||
|
const CANCEL_TOKEN_KEY: u64 = u64::MAX;
|
||||||
|
|
||||||
|
/// Constructor for internal uses.
|
||||||
|
///
|
||||||
|
/// Users should use `Builder` instead.
|
||||||
|
pub(crate) fn new(ring: io_uring::IoUring) -> Self {
|
||||||
|
let token_table = Mutex::new(Slab::new());
|
||||||
|
let sq_lock = Mutex::new(());
|
||||||
|
let fd_map = RwLock::new(HashMap::new());
|
||||||
|
Self {
|
||||||
|
ring,
|
||||||
|
token_table,
|
||||||
|
sq_lock,
|
||||||
|
fd_map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the raw io_uring instance for advanced usage.
|
||||||
|
pub fn raw(&self) -> &io_uring::IoUring {
|
||||||
|
&self.ring
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push an accept request into the submission queue of the io_uring.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// See the safety section of the `IoUring`.
|
||||||
|
pub unsafe fn accept(
|
||||||
|
&self,
|
||||||
|
fd: Fd,
|
||||||
|
addr: *mut libc::sockaddr,
|
||||||
|
addrlen: *mut libc::socklen_t,
|
||||||
|
flags: u32,
|
||||||
|
callback: impl FnOnce(i32) + Send + 'static,
|
||||||
|
) -> IoHandle {
|
||||||
|
let entry = opcode::Accept::new(fd, addr, addrlen)
|
||||||
|
.flags(flags as i32)
|
||||||
|
.build();
|
||||||
|
self.op_fetch_add(fd.0 as usize, 1);
|
||||||
|
self.push_entry(entry, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a connect request into the submission queue of the io_uring.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// See the safety section of the `IoUring`.
|
||||||
|
pub unsafe fn connect(
|
||||||
|
&self,
|
||||||
|
fd: Fd,
|
||||||
|
addr: *const libc::sockaddr,
|
||||||
|
addrlen: libc::socklen_t,
|
||||||
|
callback: impl FnOnce(i32) + Send + 'static,
|
||||||
|
) -> IoHandle {
|
||||||
|
let entry = opcode::Connect::new(fd, addr, addrlen).build();
|
||||||
|
self.op_fetch_add(fd.0 as usize, 1);
|
||||||
|
self.push_entry(entry, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a poll request into the submission queue of the io_uring.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// See the safety section of the `IoUring`.
|
||||||
|
pub unsafe fn poll(
|
||||||
|
&self,
|
||||||
|
fd: Fd,
|
||||||
|
flags: u32,
|
||||||
|
callback: impl FnOnce(i32) + Send + 'static,
|
||||||
|
) -> IoHandle {
|
||||||
|
let entry = opcode::PollAdd::new(fd, flags).build();
|
||||||
|
self.op_fetch_add(fd.0 as usize, 1);
|
||||||
|
self.push_entry(entry, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a read request into the submission queue of the io_uring.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// See the safety section of the `IoUring`.
|
||||||
|
pub unsafe fn read(
|
||||||
|
&self,
|
||||||
|
fd: Fd,
|
||||||
|
buf: *mut u8,
|
||||||
|
len: u32,
|
||||||
|
offset: libc::off_t,
|
||||||
|
flags: types::RwFlags,
|
||||||
|
callback: impl FnOnce(i32) + Send + 'static,
|
||||||
|
) -> IoHandle {
|
||||||
|
let entry = opcode::Read::new(fd, buf, len)
|
||||||
|
.offset(offset)
|
||||||
|
.rw_flags(flags)
|
||||||
|
.build();
|
||||||
|
self.op_fetch_add(fd.0 as usize, 1);
|
||||||
|
self.push_entry(entry, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a write request into the submission queue of the io_uring.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// See the safety section of the `IoUring`.
|
||||||
|
pub unsafe fn write(
|
||||||
|
&self,
|
||||||
|
fd: Fd,
|
||||||
|
buf: *const u8,
|
||||||
|
len: u32,
|
||||||
|
offset: libc::off_t,
|
||||||
|
flags: types::RwFlags,
|
||||||
|
callback: impl FnOnce(i32) + Send + 'static,
|
||||||
|
) -> IoHandle {
|
||||||
|
let entry = opcode::Write::new(fd, buf, len)
|
||||||
|
.offset(offset)
|
||||||
|
.rw_flags(flags)
|
||||||
|
.build();
|
||||||
|
self.op_fetch_add(fd.0 as usize, 1);
|
||||||
|
self.push_entry(entry, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a readv request into the submission queue of the io_uring.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// See the safety section of the `IoUring`.
|
||||||
|
pub unsafe fn readv(
|
||||||
|
&self,
|
||||||
|
fd: Fd,
|
||||||
|
iovec: *const libc::iovec,
|
||||||
|
len: u32,
|
||||||
|
offset: libc::off_t,
|
||||||
|
flags: types::RwFlags,
|
||||||
|
callback: impl FnOnce(i32) + Send + 'static,
|
||||||
|
) -> IoHandle {
|
||||||
|
let entry = opcode::Readv::new(fd, iovec, len)
|
||||||
|
.offset(offset)
|
||||||
|
.rw_flags(flags)
|
||||||
|
.build();
|
||||||
|
self.op_fetch_add(fd.0 as usize, 1);
|
||||||
|
self.push_entry(entry, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a writev request into the submission queue of the io_uring.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// See the safety section of the `IoUring`.
|
||||||
|
pub unsafe fn writev(
|
||||||
|
&self,
|
||||||
|
fd: Fd,
|
||||||
|
iovec: *const libc::iovec,
|
||||||
|
len: u32,
|
||||||
|
offset: libc::off_t,
|
||||||
|
flags: types::RwFlags,
|
||||||
|
callback: impl FnOnce(i32) + Send + 'static,
|
||||||
|
) -> IoHandle {
|
||||||
|
let entry = opcode::Writev::new(fd, iovec, len)
|
||||||
|
.offset(offset)
|
||||||
|
.rw_flags(flags)
|
||||||
|
.build();
|
||||||
|
self.op_fetch_add(fd.0 as usize, 1);
|
||||||
|
self.push_entry(entry, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a recvmsg request into the submission queue of the io_uring.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// See the safety section of the `IoUring`.
|
||||||
|
pub unsafe fn recvmsg(
|
||||||
|
&self,
|
||||||
|
fd: Fd,
|
||||||
|
msg: *mut libc::msghdr,
|
||||||
|
flags: u32,
|
||||||
|
callback: impl FnOnce(i32) + Send + 'static,
|
||||||
|
) -> IoHandle {
|
||||||
|
let entry = opcode::RecvMsg::new(fd, msg).flags(flags).build();
|
||||||
|
self.op_fetch_add(fd.0 as usize, 1);
|
||||||
|
self.push_entry(entry, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a sendmsg request into the submission queue of the io_uring.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// See the safety section of the `IoUring`.
|
||||||
|
pub unsafe fn sendmsg(
|
||||||
|
&self,
|
||||||
|
fd: Fd,
|
||||||
|
msg: *const libc::msghdr,
|
||||||
|
flags: u32,
|
||||||
|
callback: impl FnOnce(i32) + Send + 'static,
|
||||||
|
) -> IoHandle {
|
||||||
|
let entry = opcode::SendMsg::new(fd, msg).flags(flags).build();
|
||||||
|
self.op_fetch_add(fd.0 as usize, 1);
|
||||||
|
self.push_entry(entry, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a fsync request into the submission queue of the io_uring.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// See the safety section of the `IoUring`.
|
||||||
|
pub unsafe fn fsync(
|
||||||
|
&self,
|
||||||
|
fd: Fd,
|
||||||
|
datasync: bool,
|
||||||
|
callback: impl FnOnce(i32) + Send + 'static,
|
||||||
|
) -> IoHandle {
|
||||||
|
let entry = if datasync {
|
||||||
|
opcode::Fsync::new(fd)
|
||||||
|
.flags(types::FsyncFlags::DATASYNC)
|
||||||
|
.build()
|
||||||
|
} else {
|
||||||
|
opcode::Fsync::new(fd).build()
|
||||||
|
};
|
||||||
|
self.op_fetch_add(fd.0 as usize, 1);
|
||||||
|
self.push_entry(entry, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a timeout request into the submission queue of the io_uring.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// See the safety section of the `IoUring`.
|
||||||
|
pub unsafe fn timeout(
|
||||||
|
&self,
|
||||||
|
timespec: *const types::Timespec,
|
||||||
|
count: u32,
|
||||||
|
flags: types::TimeoutFlags,
|
||||||
|
callback: impl FnOnce(i32) + Send + 'static,
|
||||||
|
) -> IoHandle {
|
||||||
|
let entry = opcode::Timeout::new(timespec)
|
||||||
|
.count(count)
|
||||||
|
.flags(flags)
|
||||||
|
.build();
|
||||||
|
self.push_entry(entry, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Poll new I/O completions in the completions queue of io_uring
|
||||||
|
/// and return the number of I/O completions.
|
||||||
|
///
|
||||||
|
/// Upon receiving completed I/O, the corresponding user-registered callback functions
|
||||||
|
/// will get invoked and the `IoHandle` (as a `Future`) will become ready.
|
||||||
|
///
|
||||||
|
/// The method guarantees at least the specified number of entries are
|
||||||
|
/// popped from the completion queue. To do so, it starts by polling the
|
||||||
|
/// completion queue for at most the specified number of retries.
|
||||||
|
/// If the number of completion entries popped so far does not reach the
|
||||||
|
/// the specified minimum value, then the method shall block
|
||||||
|
/// until new completions arrive. After getting unblocked, the method
|
||||||
|
/// repeats polling.
|
||||||
|
///
|
||||||
|
/// If the user does not want to the method to block, set `min_complete`
|
||||||
|
/// to 0. If the user does not want to the method to busy polling, set
|
||||||
|
/// `polling_retries` to 0.
|
||||||
|
pub fn poll_completions(&self, min_complete: usize, polling_retries: usize) -> usize {
|
||||||
|
let mut cq = unsafe { self.ring.completion_shared() }; // Safety: Only polling thread is using the completion queue
|
||||||
|
let mut nr_complete = 0;
|
||||||
|
loop {
|
||||||
|
// Polling for at most a specified number of times
|
||||||
|
let mut nr_retries = 0;
|
||||||
|
while nr_retries <= polling_retries {
|
||||||
|
// completetion queue must be synchoronized when loop for next entry.
|
||||||
|
cq.sync();
|
||||||
|
if let Some(cqe) = cq.next() {
|
||||||
|
let retval = cqe.result();
|
||||||
|
let token_key = cqe.user_data();
|
||||||
|
|
||||||
|
if token_key != IoUring::CANCEL_TOKEN_KEY {
|
||||||
|
let io_token = {
|
||||||
|
let token_idx = token_key as usize;
|
||||||
|
let mut token_table = self.token_table.lock();
|
||||||
|
token_table.remove(token_idx)
|
||||||
|
};
|
||||||
|
|
||||||
|
io_token.complete(retval);
|
||||||
|
nr_complete += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nr_retries += 1;
|
||||||
|
std::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if nr_complete >= min_complete {
|
||||||
|
return nr_complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until at least one new completion entry arrives
|
||||||
|
let _ = self.ring.submit_and_wait(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for at least the specified number of I/O completions.
|
||||||
|
pub fn wait_completions(&self, min_complete: usize) -> usize {
|
||||||
|
self.poll_completions(min_complete, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn push(&self, entry: SqEntry) {
|
||||||
|
// Push the entry into the submission queue
|
||||||
|
// No other `SubmissionQueue`s may exist when calling submission_shared(). Thus must lock here.
|
||||||
|
// Since the loop below should be very quick, acquire lock here.
|
||||||
|
let sq_guard = self.sq_lock.lock();
|
||||||
|
loop {
|
||||||
|
if self.ring.submission_shared().push(&entry).is_err() {
|
||||||
|
if self.ring.enter(1, 1, 0, None).is_err() {
|
||||||
|
panic!("sq broken");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop(sq_guard);
|
||||||
|
|
||||||
|
// Make sure Linux is aware of the new submission
|
||||||
|
if let Err(e) = self.ring.submit() {
|
||||||
|
panic!("submit failed, error: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push a submission entry to io_uring and return a corresponding handle.
|
||||||
|
//
|
||||||
|
// Safety. All resources referenced by the entry must be valid before its completion.
|
||||||
|
unsafe fn push_entry(
|
||||||
|
&self,
|
||||||
|
mut entry: SqEntry,
|
||||||
|
callback: impl FnOnce(i32) + Send + 'static,
|
||||||
|
) -> IoHandle {
|
||||||
|
// Create the user-visible handle that is associated with the submission entry
|
||||||
|
let io_handle = {
|
||||||
|
// let mut token_table = self.token_table.lock().unwrap();
|
||||||
|
let mut token_table = self.token_table.lock();
|
||||||
|
let token_slot = token_table.vacant_entry();
|
||||||
|
let token_key = token_slot.key() as u64;
|
||||||
|
assert!(token_key != IoUring::CANCEL_TOKEN_KEY);
|
||||||
|
|
||||||
|
let token = Arc::new(IoToken::new(callback, token_key));
|
||||||
|
token_slot.insert(token.clone());
|
||||||
|
let handle = IoHandle::new(token);
|
||||||
|
|
||||||
|
// Associated entry with token, the latter of which is pointed to by handle.
|
||||||
|
entry = entry.user_data(token_key);
|
||||||
|
|
||||||
|
handle
|
||||||
|
};
|
||||||
|
|
||||||
|
self.push(entry);
|
||||||
|
|
||||||
|
io_handle
|
||||||
|
}
|
||||||
|
|
||||||
|
fn op_fetch_add(&self, fd: usize, val: usize) -> usize {
|
||||||
|
let fd_map = self.fd_map.upgradeable_read();
|
||||||
|
match fd_map.get(&fd) {
|
||||||
|
Some(ops_num) => ops_num.fetch_add(val, Ordering::Relaxed),
|
||||||
|
None => {
|
||||||
|
let mut fd_map = fd_map.upgrade();
|
||||||
|
fd_map.insert(fd, AtomicUsize::new(val));
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disattach_fd(&self, fd: usize) -> Option<AtomicUsize> {
|
||||||
|
let mut fd_map = self.fd_map.write();
|
||||||
|
fd_map.remove(&fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using the sum of the number of attached file descriptors (raw fd) as a measure of task load.
|
||||||
|
pub fn task_load(&self) -> usize {
|
||||||
|
let fd_map = self.fd_map.read();
|
||||||
|
fd_map
|
||||||
|
.values()
|
||||||
|
.fold(0, |acc, val| acc + val.load(Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of registered fd in this io_uring instance
|
||||||
|
pub fn registered_fds(&self) -> usize {
|
||||||
|
let fd_map = self.fd_map.read();
|
||||||
|
fd_map.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancel an ongoing I/O request.
|
||||||
|
///
|
||||||
|
/// # safety
|
||||||
|
///
|
||||||
|
/// The handle must be generated by this IoUring instance.
|
||||||
|
pub unsafe fn cancel(&self, handle: &IoHandle) {
|
||||||
|
let target_token_key = match handle.0.transit_to_cancelling() {
|
||||||
|
Ok(target_token_key) => target_token_key,
|
||||||
|
Err(_) => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut entry = opcode::AsyncCancel::new(target_token_key).build();
|
||||||
|
entry = entry.user_data(IoUring::CANCEL_TOKEN_KEY);
|
||||||
|
|
||||||
|
self.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A builder for `IoUring`.
|
||||||
|
pub struct Builder {
|
||||||
|
inner: io_uring::Builder,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Builder {
|
||||||
|
/// Creates a `IoUring` builder.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let inner = io_uring::IoUring::builder();
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When this flag is specified, a kernel thread is created to perform submission queue polling.
|
||||||
|
/// An io_uring instance configured in this way enables an application to issue I/O
|
||||||
|
/// without ever context switching into the kernel.
|
||||||
|
pub fn setup_sqpoll(&mut self, idle: u32) -> &mut Self {
|
||||||
|
self.inner.setup_sqpoll(idle);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_attach_wq(&mut self, fd: RawFd) -> &mut Self {
|
||||||
|
self.inner.setup_attach_wq(fd);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If this flag is specified,
|
||||||
|
/// then the poll thread will be bound to the cpu set in the value.
|
||||||
|
/// This flag is only meaningful when [Builder::setup_sqpoll] is enabled.
|
||||||
|
pub fn setup_sqpoll_cpu(&mut self, n: u32) -> &mut Self {
|
||||||
|
self.inner.setup_sqpoll_cpu(n);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create the completion queue with struct `io_uring_params.cq_entries` entries.
|
||||||
|
/// The value must be greater than entries, and may be rounded up to the next power-of-two.
|
||||||
|
pub fn setup_cqsize(&mut self, n: u32) -> &mut Self {
|
||||||
|
self.inner.setup_cqsize(n);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a [IoUring].
|
||||||
|
#[inline]
|
||||||
|
pub fn build(&self, entries: u32) -> io::Result<IoUring> {
|
||||||
|
let io_uring_inner = self.inner.build(entries)?;
|
||||||
|
let io_uring = IoUring::new(io_uring_inner);
|
||||||
|
Ok(io_uring)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{IoSlice, IoSliceMut, Write};
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_builder() {
|
||||||
|
let _io_uring = Builder::new().setup_sqpoll(1000).build(256).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new() {
|
||||||
|
let _io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_writev_readv() {
|
||||||
|
let io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap());
|
||||||
|
|
||||||
|
let fd = tempfile::tempfile().unwrap();
|
||||||
|
let fd = Fd(fd.as_raw_fd());
|
||||||
|
|
||||||
|
let text = b"1234";
|
||||||
|
let text2 = b"5678";
|
||||||
|
let mut output = vec![0; text.len()];
|
||||||
|
let mut output2 = vec![0; text2.len()];
|
||||||
|
|
||||||
|
let w_iovecs = vec![IoSlice::new(text), IoSlice::new(text2)];
|
||||||
|
let r_iovecs = vec![IoSliceMut::new(&mut output), IoSliceMut::new(&mut output2)];
|
||||||
|
|
||||||
|
let complete_fn = move |_retval: i32| {};
|
||||||
|
let handle = unsafe {
|
||||||
|
io_uring.writev(
|
||||||
|
fd,
|
||||||
|
w_iovecs.as_ptr().cast(),
|
||||||
|
w_iovecs.len() as _,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
complete_fn,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
io_uring.wait_completions(1);
|
||||||
|
let retval = handle.retval().unwrap();
|
||||||
|
assert_eq!(retval, (text.len() + text2.len()) as i32);
|
||||||
|
|
||||||
|
let complete_fn = move |_retval: i32| {};
|
||||||
|
let handle = unsafe {
|
||||||
|
io_uring.readv(
|
||||||
|
fd,
|
||||||
|
r_iovecs.as_ptr().cast(),
|
||||||
|
r_iovecs.len() as _,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
complete_fn,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
io_uring.wait_completions(1);
|
||||||
|
let retval = handle.retval().unwrap();
|
||||||
|
assert_eq!(retval, (text.len() + text2.len()) as i32);
|
||||||
|
assert_eq!(&output, text);
|
||||||
|
assert_eq!(&output2, text2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_poll() {
|
||||||
|
let mut fd = unsafe {
|
||||||
|
let fd = libc::eventfd(0, libc::EFD_CLOEXEC);
|
||||||
|
assert!(fd != -1);
|
||||||
|
File::from_raw_fd(fd)
|
||||||
|
};
|
||||||
|
|
||||||
|
let io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap());
|
||||||
|
|
||||||
|
let complete_fn = move |_retval: i32| {};
|
||||||
|
let handle = unsafe { io_uring.poll(Fd(fd.as_raw_fd()), libc::POLLIN as _, complete_fn) };
|
||||||
|
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
assert_eq!(io_uring.poll_completions(0, 10000), 0);
|
||||||
|
|
||||||
|
fd.write(&0x1u64.to_ne_bytes()).unwrap();
|
||||||
|
io_uring.wait_completions(1);
|
||||||
|
assert_eq!(handle.retval().unwrap(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cancel_poll() {
|
||||||
|
let mut fd = unsafe {
|
||||||
|
let fd = libc::eventfd(0, libc::EFD_CLOEXEC);
|
||||||
|
assert!(fd != -1);
|
||||||
|
File::from_raw_fd(fd)
|
||||||
|
};
|
||||||
|
|
||||||
|
let io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap());
|
||||||
|
|
||||||
|
let complete_fn = move |_retval: i32| {};
|
||||||
|
let poll_handle =
|
||||||
|
unsafe { io_uring.poll(Fd(fd.as_raw_fd()), libc::POLLIN as _, complete_fn) };
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
io_uring.cancel(&poll_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
|
||||||
|
fd.write(&0x1u64.to_ne_bytes()).unwrap();
|
||||||
|
io_uring.wait_completions(1);
|
||||||
|
|
||||||
|
assert_eq!(poll_handle.retval().unwrap(), -libc::ECANCELED);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cancel_poll_failed() {
|
||||||
|
let mut fd = unsafe {
|
||||||
|
let fd = libc::eventfd(0, libc::EFD_CLOEXEC);
|
||||||
|
assert!(fd != -1);
|
||||||
|
File::from_raw_fd(fd)
|
||||||
|
};
|
||||||
|
|
||||||
|
let io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap());
|
||||||
|
|
||||||
|
let complete_fn = move |_retval: i32| {};
|
||||||
|
let poll_handle =
|
||||||
|
unsafe { io_uring.poll(Fd(fd.as_raw_fd()), libc::POLLIN as _, complete_fn) };
|
||||||
|
|
||||||
|
fd.write(&0x1u64.to_ne_bytes()).unwrap();
|
||||||
|
io_uring.wait_completions(1);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
io_uring.cancel(&poll_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
assert_eq!(poll_handle.retval().unwrap(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_timeout() {
|
||||||
|
let io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap());
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let secs = 1;
|
||||||
|
let timespec = types::Timespec::new().sec(secs).nsec(0);
|
||||||
|
let complete_fn = move |_retval: i32| {};
|
||||||
|
|
||||||
|
let handle = unsafe {
|
||||||
|
io_uring.timeout(
|
||||||
|
×pec as *const _,
|
||||||
|
0,
|
||||||
|
types::TimeoutFlags::empty(),
|
||||||
|
complete_fn,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
io_uring.wait_completions(1);
|
||||||
|
|
||||||
|
assert_eq!(handle.retval().unwrap(), -libc::ETIME);
|
||||||
|
assert_eq!(start.elapsed().as_secs(), secs as u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cancel_timeout() {
|
||||||
|
let io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap());
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let secs = 1;
|
||||||
|
let timespec = types::Timespec::new().sec(secs).nsec(0);
|
||||||
|
|
||||||
|
let complete_fn = move |_retval: i32| {};
|
||||||
|
|
||||||
|
let handle = unsafe {
|
||||||
|
io_uring.timeout(
|
||||||
|
×pec as *const _,
|
||||||
|
0,
|
||||||
|
types::TimeoutFlags::empty(),
|
||||||
|
complete_fn,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
io_uring.cancel(&handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
io_uring.wait_completions(1);
|
||||||
|
|
||||||
|
assert_eq!(handle.retval().unwrap(), -libc::ECANCELED);
|
||||||
|
assert_eq!(start.elapsed().as_secs(), 0);
|
||||||
|
}
|
||||||
|
}
|
10
src/libos/crates/keyable-arc/Cargo.toml
Normal file
10
src/libos/crates/keyable-arc/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "keyable-arc"
|
||||||
|
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
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
348
src/libos/crates/keyable-arc/src/lib.rs
Normal file
348
src/libos/crates/keyable-arc/src/lib.rs
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
//! Same as the standard `Arc`, except that it can be used as the key type of a hash table.
|
||||||
|
//!
|
||||||
|
//! # Motivation
|
||||||
|
//!
|
||||||
|
//! A type `K` is _keyable_ if it can be used as the key type for a hash map. Specifically,
|
||||||
|
//! according to the document of `std::collections::HashMap`, the type `K` must satisfy
|
||||||
|
//! the following properties.
|
||||||
|
//!
|
||||||
|
//! 1. It implements the `Eq` and `Hash` traits.
|
||||||
|
//! 2. The two values of `k1` and `k2` of type `K` equal to each other,
|
||||||
|
//! if and only if their hash values equal to each other.
|
||||||
|
//! 3. The hashes of a value of `k` of type `K` cannot change while it
|
||||||
|
//! is in a map.
|
||||||
|
//!
|
||||||
|
//! Sometimes we want to use `Arc<T>` as the key type for a hash map but cannot do so
|
||||||
|
//! since `T` does not satisfy the properties above. For example, a lot of types
|
||||||
|
//! do not or cannot implemennt the `Eq` trait. This is when `KeyableArc<T>` can come
|
||||||
|
//! to your aid.
|
||||||
|
//!
|
||||||
|
//! # Overview
|
||||||
|
//!
|
||||||
|
//! For any type `T`, `KeyableArc<T>` satisfies all the properties to be keyable.
|
||||||
|
//! This can be achieved easily and efficiently as we can simply use the address
|
||||||
|
//! of the data (of `T` type) of a `KeyableArc<T>` object in the heap to determine the
|
||||||
|
//! equality and hash of the `KeyableArc<T>` object. As the address won't change for
|
||||||
|
//! an immutable `KeyableArc<T>` object, the hash and equality also stay the same.
|
||||||
|
//!
|
||||||
|
//! This crate is `#[no_std]` compatible, but requires the `alloc` crate.
|
||||||
|
//!
|
||||||
|
//! # Usage
|
||||||
|
//!
|
||||||
|
//! Here is a basic example to how that `KeyableArc<T>` is keyable even when `T`
|
||||||
|
//! is not.
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use std::collections::HashMap;
|
||||||
|
//! use std::sync::Arc;
|
||||||
|
//! use keyable_arc::KeyableArc;
|
||||||
|
//!
|
||||||
|
//! struct Dummy; // Does not implement Eq and Hash
|
||||||
|
//!
|
||||||
|
//! let map: HashMap<KeyableArc<Dummy>, String> = HashMap::new();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! `KeyableArc` is a reference counter-based smart pointer, just like `Arc`.
|
||||||
|
//! So you can use `KeyableArc` the same way you would use `Arc`.
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
|
||||||
|
//! use keyable_arc::KeyableArc;
|
||||||
|
//!
|
||||||
|
//! let key_arc0 = KeyableArc::new(AtomicU64::new(0));
|
||||||
|
//! let key_arc1 = key_arc0.clone();
|
||||||
|
//! assert!(key_arc0.load(Relaxed) == 0 && key_arc1.load(Relaxed) == 0);
|
||||||
|
//!
|
||||||
|
//! key_arc0.fetch_add(1, Relaxed);
|
||||||
|
//! assert!(key_arc0.load(Relaxed) == 1 && key_arc1.load(Relaxed) == 1);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Differences from `Arc<T>`
|
||||||
|
//!
|
||||||
|
//! Notice how `KeyableArc` differs from standard smart pointers in determining equality?
|
||||||
|
//! Two `KeyableArc` objects are considered different even when their data have the same
|
||||||
|
//! value.
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use keyable_arc::KeyableArc;
|
||||||
|
//!
|
||||||
|
//! let key_arc0 = KeyableArc::new(0);
|
||||||
|
//! let key_arc1 = key_arc0.clone();
|
||||||
|
//! assert!(key_arc0 == key_arc1);
|
||||||
|
//! assert!(*key_arc0 == *key_arc1);
|
||||||
|
//!
|
||||||
|
//! let key_arc1 = KeyableArc::new(0);
|
||||||
|
//! assert!(key_arc0 != key_arc1);
|
||||||
|
//! assert!(*key_arc0 == *key_arc1);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! `KeyableArc<T>` is simply a wrapper of `Arc<T>. So converting between them
|
||||||
|
//! through the `From` and `Into` traits is zero cost.
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use std::sync::Arc;
|
||||||
|
//! use keyable_arc::KeyableArc;
|
||||||
|
//!
|
||||||
|
//! let key_arc: KeyableArc<u32> = Arc::new(0).into();
|
||||||
|
//! let arc: Arc<u32> = KeyableArc::new(0).into();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # The weak version
|
||||||
|
//!
|
||||||
|
//! `KeyableWeak<T>` is the weak version of `KeyableArc<T>`, just like `Weak<T>` is
|
||||||
|
//! that of `Arc<T>`. And of course, `KeyableWeak<T>` is also _keyable_ for any
|
||||||
|
//! type `T`.
|
||||||
|
|
||||||
|
// TODO: Add `KeyableBox<T>` or other keyable versions of smart pointers.
|
||||||
|
// If this is needed in the future, this crate should be renamed to `keyable`.
|
||||||
|
|
||||||
|
// TODO: Add the missing methods offered by `Arc` or `Weak` but not their
|
||||||
|
// keyable counterparts.
|
||||||
|
|
||||||
|
#![cfg_attr(not(test), no_std)]
|
||||||
|
#![feature(coerce_unsized)]
|
||||||
|
#![feature(unsize)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use alloc::sync::{Arc, Weak};
|
||||||
|
use core::borrow::Borrow;
|
||||||
|
use core::convert::AsRef;
|
||||||
|
use core::fmt;
|
||||||
|
use core::hash::{Hash, Hasher};
|
||||||
|
use core::marker::Unsize;
|
||||||
|
use core::ops::{CoerceUnsized, Deref};
|
||||||
|
|
||||||
|
/// Same as the standard `Arc`, except that it can be used as the key type of a hash table.
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct KeyableArc<T: ?Sized>(Arc<T>);
|
||||||
|
|
||||||
|
impl<T> KeyableArc<T> {
|
||||||
|
/// Constructs a new instance of `KeyableArc<T>`.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(data: T) -> Self {
|
||||||
|
Self(Arc::new(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> KeyableArc<T> {
|
||||||
|
/// Returns a raw pointer to the object `T` pointed to by this `KeyableArc<T>`.
|
||||||
|
#[inline]
|
||||||
|
pub fn as_ptr(this: &Self) -> *const T {
|
||||||
|
Arc::as_ptr(&this.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `KeyableWeak` pointer to this allocation.
|
||||||
|
pub fn downgrade(this: &Self) -> KeyableWeak<T> {
|
||||||
|
Arc::downgrade(&this.0).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Deref for KeyableArc<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
&*self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> AsRef<T> for KeyableArc<T> {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &T {
|
||||||
|
&**self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Borrow<T> for KeyableArc<T> {
|
||||||
|
#[inline]
|
||||||
|
fn borrow(&self) -> &T {
|
||||||
|
&**self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> From<Arc<T>> for KeyableArc<T> {
|
||||||
|
#[inline]
|
||||||
|
fn from(arc: Arc<T>) -> Self {
|
||||||
|
Self(arc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Into<Arc<T>> for KeyableArc<T> {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> Arc<T> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> PartialEq for KeyableArc<T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
Arc::as_ptr(&self.0) == Arc::as_ptr(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Eq for KeyableArc<T> {}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Hash for KeyableArc<T> {
|
||||||
|
fn hash<H: Hasher>(&self, s: &mut H) {
|
||||||
|
Arc::as_ptr(&self.0).hash(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Clone for KeyableArc<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + fmt::Debug> fmt::Debug for KeyableArc<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&**self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// The weak version
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
/// The weak counterpart of `KeyableArc<T>`, similar to `Weak<T>`.
|
||||||
|
///
|
||||||
|
/// `KeyableWeak<T>` is also _keyable_ for any type `T` just like
|
||||||
|
/// `KeyableArc<T>`.
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct KeyableWeak<T: ?Sized>(Weak<T>);
|
||||||
|
|
||||||
|
impl<T> KeyableWeak<T> {
|
||||||
|
/// Constructs a new `KeyableWeak<T>`, without allocating any memory.
|
||||||
|
/// Calling `upgrade` on the return value always gives `None`.
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self(Weak::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a raw pointer to the object `T` pointed to by this `KeyableWeak<T>`.
|
||||||
|
///
|
||||||
|
/// The pointer is valid only if there are some strong references.
|
||||||
|
/// The pointer may be dangling, unaligned or even null otherwise.
|
||||||
|
#[inline]
|
||||||
|
pub fn as_ptr(&self) -> *const T {
|
||||||
|
self.0.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> KeyableWeak<T> {
|
||||||
|
/// Attempts to upgrade the Weak pointer to an Arc,
|
||||||
|
/// delaying dropping of the inner value if successful.
|
||||||
|
///
|
||||||
|
/// Returns None if the inner value has since been dropped.
|
||||||
|
#[inline]
|
||||||
|
pub fn upgrade(&self) -> Option<KeyableArc<T>> {
|
||||||
|
self.0.upgrade().map(|arc| arc.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the number of strong pointers pointing to this allocation.
|
||||||
|
#[inline]
|
||||||
|
pub fn strong_count(&self) -> usize {
|
||||||
|
self.0.strong_count()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the number of weak pointers pointing to this allocation.
|
||||||
|
#[inline]
|
||||||
|
pub fn weak_count(&self) -> usize {
|
||||||
|
self.0.weak_count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Hash for KeyableWeak<T> {
|
||||||
|
fn hash<H: Hasher>(&self, s: &mut H) {
|
||||||
|
self.0.as_ptr().hash(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> From<Weak<T>> for KeyableWeak<T> {
|
||||||
|
#[inline]
|
||||||
|
fn from(weak: Weak<T>) -> Self {
|
||||||
|
Self(weak)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Into<Weak<T>> for KeyableWeak<T> {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> Weak<T> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> PartialEq for KeyableWeak<T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0.as_ptr() == other.0.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> PartialOrd for KeyableWeak<T> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Eq for KeyableWeak<T> {}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Ord for KeyableWeak<T> {
|
||||||
|
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||||
|
self.0.as_ptr().cmp(&other.0.as_ptr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + fmt::Debug> fmt::Debug for KeyableWeak<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "(KeyableWeak)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabling type coercing, e.g., converting from `KeyableArc<T>` to `KeyableArc<dyn S>`,
|
||||||
|
// where `T` implements `S`.
|
||||||
|
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<KeyableArc<U>> for KeyableArc<T> {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn downgrade_and_upgrade() {
|
||||||
|
let arc = KeyableArc::new(1);
|
||||||
|
let weak = KeyableArc::downgrade(&arc);
|
||||||
|
assert!(arc.clone() == weak.upgrade().unwrap());
|
||||||
|
assert!(weak == KeyableArc::downgrade(&arc));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn debug_format() {
|
||||||
|
println!("{:?}", KeyableArc::new(1u32));
|
||||||
|
println!("{:?}", KeyableWeak::<u32>::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn use_as_key() {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
let mut map: HashMap<KeyableArc<u32>, u32> = HashMap::new();
|
||||||
|
let key = KeyableArc::new(1);
|
||||||
|
let val = 1;
|
||||||
|
map.insert(key.clone(), val);
|
||||||
|
assert!(map.get(&key) == Some(&val));
|
||||||
|
assert!(map.remove(&key) == Some(val));
|
||||||
|
assert!(map.keys().count() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn as_trait_object() {
|
||||||
|
trait DummyTrait {}
|
||||||
|
struct DummyStruct;
|
||||||
|
impl DummyTrait for DummyStruct {}
|
||||||
|
|
||||||
|
let arc_struct = KeyableArc::new(DummyStruct);
|
||||||
|
let arc_dyn0: KeyableArc<dyn DummyTrait> = arc_struct.clone();
|
||||||
|
let arc_dyn1: KeyableArc<dyn DummyTrait> = arc_struct.clone();
|
||||||
|
assert!(arc_dyn0 == arc_dyn1);
|
||||||
|
}
|
||||||
|
}
|
9
src/libos/crates/object-id/Cargo.toml
Normal file
9
src/libos/crates/object-id/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "object-id"
|
||||||
|
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
|
||||||
|
|
||||||
|
[dependencies]
|
62
src/libos/crates/object-id/src/lib.rs
Normal file
62
src/libos/crates/object-id/src/lib.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
//! Assign a unique and immutable ID.
|
||||||
|
//!
|
||||||
|
//! Some types do not have a natural implementation for `PartialEq` or `Hash`.
|
||||||
|
//! In such cases, it can be convenient to assign an unique ID for each instance
|
||||||
|
//! of such types and use the ID to implement `PartialEq` or `Hash`.
|
||||||
|
//!
|
||||||
|
//! An ID have a length of 64-bit.
|
||||||
|
|
||||||
|
#![cfg_attr(not(any(test, doctest)), no_std)]
|
||||||
|
|
||||||
|
use core::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
||||||
|
/// A unique id.
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct ObjectId(u64);
|
||||||
|
|
||||||
|
impl ObjectId {
|
||||||
|
/// Create a new unique ID.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
static NEXT_ID: AtomicU64 = AtomicU64::new(1);
|
||||||
|
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
|
|
||||||
|
// Make sure that we can detect the overflow of id even in face of
|
||||||
|
// (extremely) concurrent addition on NEXT_ID.
|
||||||
|
assert!(id <= u64::max_value() / 2);
|
||||||
|
|
||||||
|
Self(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a special "null" ID.
|
||||||
|
///
|
||||||
|
/// Note that no ID created by `ObjectId::new()` will be equivalent to the
|
||||||
|
/// null ID.
|
||||||
|
pub const fn null() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the ID value as `u64`.
|
||||||
|
pub const fn get(&self) -> u64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unique() {
|
||||||
|
let id0 = ObjectId::new();
|
||||||
|
let id1 = ObjectId::new();
|
||||||
|
assert!(id0 != id1);
|
||||||
|
assert!(id0.get() < id1.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_null() {
|
||||||
|
let id0 = ObjectId::new();
|
||||||
|
assert!(id0 != ObjectId::null());
|
||||||
|
}
|
||||||
|
}
|
2
src/libos/crates/sgx-untrusted-alloc/.gitignore
vendored
Normal file
2
src/libos/crates/sgx-untrusted-alloc/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
target/
|
||||||
|
Cargo.lock
|
22
src/libos/crates/sgx-untrusted-alloc/Cargo.toml
Normal file
22
src/libos/crates/sgx-untrusted-alloc/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "sgx-untrusted-alloc"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["libc"]
|
||||||
|
sgx = ["sgx_types", "sgx_tstd", "sgx_trts", "sgx_libc"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cfg-if = "1.0.0"
|
||||||
|
libc = { version = "0.2", optional = true }
|
||||||
|
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
|
||||||
|
intrusive-collections = "0.9"
|
||||||
|
log = "0.4"
|
||||||
|
spin = "0.7"
|
||||||
|
errno = { path = "../errno" }
|
||||||
|
|
||||||
|
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_trts = { path = "../../../../deps/rust-sgx-sdk/sgx_trts", optional = true }
|
||||||
|
sgx_libc = { path = "../../../../deps/rust-sgx-sdk/sgx_libc", optional = true }
|
10
src/libos/crates/sgx-untrusted-alloc/README.md
Normal file
10
src/libos/crates/sgx-untrusted-alloc/README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# sgx-untrusted-alloc
|
||||||
|
|
||||||
|
untrusted memory allocator for SGX.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
To use sgx-untrusted-alloc, place the following line under the `[dependencies]` section in your `Cargo.toml`:
|
||||||
|
|
||||||
|
```
|
||||||
|
sgx-untrusted-alloc = { path = "your_path/sgx-untrusted-alloc" }
|
||||||
|
```
|
190
src/libos/crates/sgx-untrusted-alloc/src/box_.rs
Normal file
190
src/libos/crates/sgx-untrusted-alloc/src/box_.rs
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
use crate::untrusted_allocator::Allocator;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::mem::{align_of, size_of};
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref UNTRUSTED_MEM_INSTANCE: Allocator = Allocator::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::MaybeUntrusted;
|
||||||
|
|
||||||
|
/// A memory location on the heap in untrusted memory.
|
||||||
|
///
|
||||||
|
/// `UntrustedBox<T>` Behaves similar to the standard `Box<T>`, except that
|
||||||
|
/// it requires that the type bound of `T: MaybeUntrusted`. This is a safety
|
||||||
|
/// measure to avoid potential misuses.
|
||||||
|
pub struct UntrustedBox<T: ?Sized> {
|
||||||
|
ptr: NonNull<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Debug for UntrustedBox<T>
|
||||||
|
where
|
||||||
|
T: ?Sized + Debug,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Untrusted T")
|
||||||
|
.field("T", unsafe { &self.ptr.as_ref() })
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: MaybeUntrusted> UntrustedBox<T> {
|
||||||
|
/// Creates a value of `T` on the heap in untrusted memory.
|
||||||
|
pub fn new(val: T) -> Self {
|
||||||
|
let mut new_self = Self::new_uninit();
|
||||||
|
*new_self = val;
|
||||||
|
new_self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an _uninitialized_ value of `T` on the heap in untrusted memory.
|
||||||
|
pub fn new_uninit() -> Self {
|
||||||
|
let ptr = {
|
||||||
|
let raw_ptr = unsafe {
|
||||||
|
UNTRUSTED_MEM_INSTANCE
|
||||||
|
.alloc(size_of::<T>(), None)
|
||||||
|
.expect("memory allocation failure")
|
||||||
|
} as *mut T;
|
||||||
|
assert!(raw_ptr != std::ptr::null_mut());
|
||||||
|
assert!((raw_ptr as usize) % align_of::<T>() == 0);
|
||||||
|
NonNull::new(raw_ptr).unwrap()
|
||||||
|
};
|
||||||
|
Self { ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: MaybeUntrusted + Copy> UntrustedBox<[T]> {
|
||||||
|
/// Creates a slice of `T` on the heap in untrusted memory.
|
||||||
|
///
|
||||||
|
/// Note that the pointer and length of the slice is still kept in trusted memory;
|
||||||
|
/// only the pointer refers to untrusted memory. Thus, there is no risk of buffer
|
||||||
|
/// overflow.
|
||||||
|
pub fn new_slice(slice: &[T]) -> Self {
|
||||||
|
let mut uninit_slice = Self::new_uninit_slice(slice.len());
|
||||||
|
uninit_slice.copy_from_slice(slice);
|
||||||
|
uninit_slice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: MaybeUntrusted> UntrustedBox<[T]> {
|
||||||
|
/// Creates an uninitialized slice of `T` on the heap in untrusted memory.
|
||||||
|
pub fn new_uninit_slice(len: usize) -> Self {
|
||||||
|
let ptr = {
|
||||||
|
let total_bytes = size_of::<T>() * len;
|
||||||
|
let raw_slice_ptr = unsafe {
|
||||||
|
UNTRUSTED_MEM_INSTANCE
|
||||||
|
.alloc(total_bytes, None)
|
||||||
|
.expect("memory allocation failure")
|
||||||
|
} as *mut T;
|
||||||
|
assert!(raw_slice_ptr != std::ptr::null_mut());
|
||||||
|
assert!((raw_slice_ptr as usize) % align_of::<T>() == 0);
|
||||||
|
let untrusted_slice = unsafe { std::slice::from_raw_parts_mut(raw_slice_ptr, len) };
|
||||||
|
// For DST types like slice, NonNull is now a fat pointer.
|
||||||
|
NonNull::new(untrusted_slice as _).unwrap()
|
||||||
|
};
|
||||||
|
Self { ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> UntrustedBox<T> {
|
||||||
|
/// Gets an immutable pointer of the value on the untrusted memory.
|
||||||
|
pub fn as_ptr(&self) -> *const T {
|
||||||
|
self.ptr.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a mutable pointer of the value on the untrusted memory.
|
||||||
|
pub fn as_mut_ptr(&self) -> *mut T {
|
||||||
|
self.ptr.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Drop for UntrustedBox<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
UNTRUSTED_MEM_INSTANCE.free(self.as_mut_ptr() as *mut u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Deref for UntrustedBox<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
unsafe { self.ptr.as_ref() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> DerefMut for UntrustedBox<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut T {
|
||||||
|
unsafe { self.ptr.as_mut() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: MaybeUntrusted + Default> Default for UntrustedBox<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(T::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: MaybeUntrusted + Clone> Clone for UntrustedBox<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self::new(self.deref().clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: ?Sized + Send> Send for UntrustedBox<T> {}
|
||||||
|
unsafe impl<T: ?Sized + Sync> Sync for UntrustedBox<T> {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
}
|
||||||
|
unsafe impl MaybeUntrusted for Point {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn with_i32() {
|
||||||
|
let mut untrusted_i32 = UntrustedBox::new(0i32);
|
||||||
|
assert!(*untrusted_i32 == 0);
|
||||||
|
*untrusted_i32 = 1;
|
||||||
|
assert!(*untrusted_i32 == 1);
|
||||||
|
drop(untrusted_i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn with_point() {
|
||||||
|
let mut untrusted_point = UntrustedBox::new(Point { x: 0, y: 0 });
|
||||||
|
assert!(untrusted_point.x == 0 && untrusted_point.y == 0);
|
||||||
|
untrusted_point.x += 10;
|
||||||
|
untrusted_point.y += 20;
|
||||||
|
assert!(untrusted_point.x == 10 && untrusted_point.y == 20);
|
||||||
|
drop(untrusted_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn with_array() {
|
||||||
|
let mut untrusted_array = UntrustedBox::new([0u8, 1, 2, 3]);
|
||||||
|
untrusted_array
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(pos, i)| assert!(pos as u8 == *i));
|
||||||
|
|
||||||
|
for i in untrusted_array.iter_mut() {
|
||||||
|
*i = 0;
|
||||||
|
}
|
||||||
|
untrusted_array.iter().for_each(|i| assert!(*i == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn with_slice() {
|
||||||
|
let len = 4;
|
||||||
|
let mut untrusted_slice: UntrustedBox<[i32]> = UntrustedBox::new_uninit_slice(len);
|
||||||
|
assert!(untrusted_slice.len() == len);
|
||||||
|
untrusted_slice[1] = 123;
|
||||||
|
assert!(untrusted_slice[1] == 123);
|
||||||
|
}
|
||||||
|
}
|
95
src/libos/crates/sgx-untrusted-alloc/src/lib.rs
Normal file
95
src/libos/crates/sgx-untrusted-alloc/src/lib.rs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
//! Allocation and access of _untrusted_ memory in a _safe_ way.
|
||||||
|
//!
|
||||||
|
//! # Usage
|
||||||
|
//!
|
||||||
|
//! ## Basics
|
||||||
|
//!
|
||||||
|
//! Suppose you have a data structure named `AcceptReq`
|
||||||
|
//! ```rust
|
||||||
|
//! struct AcceptReq {
|
||||||
|
//! addr: libc::sockaddr_storage,
|
||||||
|
//! addr_len: libc::socklen_t,
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//! which is intended to be used as an untrusted buffer shared
|
||||||
|
//! with the host OS to pass arguments of the accept system call.
|
||||||
|
//! And we assume that this buffer must be present during the lifetime
|
||||||
|
//! of a listening socket. So it must be allocated on the heap in
|
||||||
|
//! untrusted memory. So how to do it?
|
||||||
|
//!
|
||||||
|
//! With this crate, it takes two steps.
|
||||||
|
//!
|
||||||
|
//! 1. Implement the [`MaybeUntrusted`] marker trait for the data structure.
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use sgx_untrusted_alloc::MaybeUntrusted;
|
||||||
|
//! # struct AcceptReq;
|
||||||
|
//!
|
||||||
|
//! unsafe impl MaybeUntrusted for AcceptReq { }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! By implementing this trait, you are claiming: "I am fully aware of the
|
||||||
|
//! security risks in communicating with the host through _untrusted,
|
||||||
|
//! shared data structures_. I know that an attacker may peek or tamper with
|
||||||
|
//! the data structure at any possible timing or in an arbitrary way.
|
||||||
|
//! I will be very careful. And I am good to go."
|
||||||
|
//!
|
||||||
|
//! 2. You can now allocate the data structure in untrusted heap with
|
||||||
|
//! [`UntrustedBox`], which is similar to the standard `Box` albeit for the
|
||||||
|
//! untrusted memory.
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # use sgx_untrusted_alloc::MaybeUntrusted;
|
||||||
|
//! # struct AcceptReq;
|
||||||
|
//! # unsafe impl MaybeUntrusted for AcceptReq { }
|
||||||
|
//! #
|
||||||
|
//! use sgx_untrusted_alloc::UntrustedBox;
|
||||||
|
//!
|
||||||
|
//! let accept_req: UntrustedBox<AcceptReq> = UntrustedBox::new_uninit();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Note that the convenient constructor method `UntrustedBox::<T>::new_uninit`
|
||||||
|
//! creates an _uninitialized_ instance of `T` on untrusted heap.
|
||||||
|
//! Alternatively, you can create an _initialized_ instance with `UntrustedBox::new`.
|
||||||
|
//!
|
||||||
|
//! ## Arrays and slices
|
||||||
|
//!
|
||||||
|
//! You can also use `UntrustedBox` to allocate arrays (`[T; N]`) or
|
||||||
|
//! slices (`[T]`) on untrusted heap as long as the trait bound of `T: MaybeUntrusted`
|
||||||
|
//! is held.
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use sgx_untrusted_alloc::{MaybeUntrusted, UntrustedBox};
|
||||||
|
//!
|
||||||
|
//! let untrusted_array: UntrustedBox<[u8; 4]> = UntrustedBox::new_uninit();
|
||||||
|
//!
|
||||||
|
//! let untrusted_slice: UntrustedBox<[u8]> = UntrustedBox::new_uninit_slice(4);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Both `untrusted_array` and `untrusted_slice` above consist of four `u8` integers.
|
||||||
|
|
||||||
|
#![cfg_attr(feature = "sgx", no_std)]
|
||||||
|
#![feature(linked_list_remove)]
|
||||||
|
|
||||||
|
#[cfg(feature = "sgx")]
|
||||||
|
extern crate sgx_libc as libc;
|
||||||
|
#[cfg(feature = "sgx")]
|
||||||
|
extern crate sgx_tstd as std;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate alloc;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
extern crate intrusive_collections;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
extern crate spin;
|
||||||
|
|
||||||
|
mod maybe_untrusted;
|
||||||
|
pub use maybe_untrusted::MaybeUntrusted;
|
||||||
|
|
||||||
|
mod box_;
|
||||||
|
pub use box_::UntrustedBox;
|
||||||
|
|
||||||
|
mod prelude;
|
||||||
|
mod untrusted_allocator;
|
130
src/libos/crates/sgx-untrusted-alloc/src/maybe_untrusted.rs
Normal file
130
src/libos/crates/sgx-untrusted-alloc/src/maybe_untrusted.rs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
/// A marker trait for types that can be used in untrusted memory
|
||||||
|
/// in a relatively safe way.
|
||||||
|
///
|
||||||
|
/// # Overview
|
||||||
|
///
|
||||||
|
/// Not all types are created equal: some of them are difficult---
|
||||||
|
/// if not impossible---to be used safely when residing in untrusted memory.
|
||||||
|
/// One obvious class is heap-backed Rust containers, like `Vec<T>`.
|
||||||
|
/// If such types were put into untrusted memory, then an attacker could manipulate
|
||||||
|
/// the internal pointer. Enums, in general, cannot be used safely in untrusted
|
||||||
|
/// memory since an attacker could break Rust compiler's assumption on its memory
|
||||||
|
/// representation, thus causing undefined behaviors. Surprisingly, even a primitive
|
||||||
|
/// type like `bool` is dangerous to use in untrusted memory. This is also
|
||||||
|
/// due to Rust compiler's strong assumption on memory representation.
|
||||||
|
///
|
||||||
|
/// Here is a list of core types that implements `MaybeUntrusted`:
|
||||||
|
/// * Primitive types: `u8`, `u16`, `u32`, `u64`, `usize` and their signed counterparts;
|
||||||
|
/// * Pointer types: `*const T` and `*mut T`, where `T: MaybeUntrusted`;
|
||||||
|
/// * Array types: `[T; N]`, where `T: MaybeUntrusted` and 1<= `N` <= 32;
|
||||||
|
/// * Core types: `Cell<T>`, where `T: MaybeUntrusted`.
|
||||||
|
/// * Libc types: Most C-style structs defined in libc can have `MaybeUntrusted` implemented.
|
||||||
|
/// But since there are simply too many of them yet most of them are irrelevant for our usage,
|
||||||
|
/// it is more reasonable to implement `MaybeUntrusted` on demand. Currently, the list of libc
|
||||||
|
/// types that have implemented `MaybeUntrusted` is pretty short:
|
||||||
|
/// * `sockaddr_storage`.
|
||||||
|
///
|
||||||
|
/// For user-defined types, the `MaybeUntrusted` trait should be implemented with discretion.
|
||||||
|
/// A good rule of thumb is only implementing `MaybeUntrusted` for C-style structs.
|
||||||
|
///
|
||||||
|
/// # Must implement `Sized`
|
||||||
|
///
|
||||||
|
/// The `MaybeUntrusted` trait requires `Sized`. In other words, it wouldn't be safe to
|
||||||
|
/// store any DST values in untrusted memory since the length part of DST may be tampered by
|
||||||
|
/// attacker, leading to buffer overflow.
|
||||||
|
///
|
||||||
|
/// # Safe to be uninitialized
|
||||||
|
///
|
||||||
|
/// Another implicit property of `MaybeUntrusted` types is that unlike most Rust types it is
|
||||||
|
/// perfectly fine to instantiate _uninitialized_ values for a `MaybeUntrusted` type. After all,
|
||||||
|
/// our thread model assumes an attacker that may tamper with the values of `MaybeUntrusted`
|
||||||
|
/// types in an arbitrary way. The code that handles `MaybeUntrusted` types must be robust
|
||||||
|
/// enough to deal with any possible values, including uninitialized ones.
|
||||||
|
///
|
||||||
|
/// As a result, `MaybeUntrusted` comes with two convenient constructor methods that creates
|
||||||
|
/// an instance whose value is uninitialized or zeroed, respectively.
|
||||||
|
///
|
||||||
|
/// # Good to have `Copy`
|
||||||
|
///
|
||||||
|
/// As an effective defense against against TOCTOU attacks, a common practice is to---before
|
||||||
|
/// actually using a value of `MaybeUntrusted`---first copy the value into trusted memory and
|
||||||
|
/// validate its value. Thus, most types that implement `MaybeUntrusted` should also implement
|
||||||
|
/// `Copy`.
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
|
||||||
|
pub unsafe trait MaybeUntrusted: Sized {
|
||||||
|
fn uninit() -> Self {
|
||||||
|
unsafe { MaybeUninit::uninit().assume_init() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zeroed() -> Self {
|
||||||
|
unsafe { MaybeUninit::zeroed().assume_init() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_maybe_untrusted {
|
||||||
|
($($type:ty),*) => {
|
||||||
|
$(
|
||||||
|
unsafe impl MaybeUntrusted for $type {}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_maybe_untrusted! {
|
||||||
|
// Primitive types
|
||||||
|
u8,
|
||||||
|
u16,
|
||||||
|
u32,
|
||||||
|
u64,
|
||||||
|
usize,
|
||||||
|
i8,
|
||||||
|
i16,
|
||||||
|
i32,
|
||||||
|
i64,
|
||||||
|
isize,
|
||||||
|
|
||||||
|
// Libc types
|
||||||
|
libc::sockaddr_storage,
|
||||||
|
libc::iovec
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for *const T {}
|
||||||
|
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for *mut T {}
|
||||||
|
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 1] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 2] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 3] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 4] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 5] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 6] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 7] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 8] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 9] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 10] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 11] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 12] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 13] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 14] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 15] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 16] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 17] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 18] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 19] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 20] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 21] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 22] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 23] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 24] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 25] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 26] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 27] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 28] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 29] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 30] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 31] {}
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 32] {}
|
||||||
|
|
||||||
|
unsafe impl<T: MaybeUntrusted> MaybeUntrusted for Cell<T> {}
|
5
src/libos/crates/sgx-untrusted-alloc/src/prelude.rs
Normal file
5
src/libos/crates/sgx-untrusted-alloc/src/prelude.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// Convenient reexports for internal uses.
|
||||||
|
pub(crate) use errno::prelude::*;
|
||||||
|
pub(crate) use std::boxed::Box;
|
||||||
|
pub(crate) use std::fmt;
|
||||||
|
pub(crate) use std::vec::Vec;
|
@ -0,0 +1,163 @@
|
|||||||
|
// Implements free space management for memory.
|
||||||
|
// Currently only use simple vector as the base structure.
|
||||||
|
//
|
||||||
|
// Basically use address-ordered first fit to find free ranges.
|
||||||
|
use super::*;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
static INITIAL_SIZE: usize = 100;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct VMFreeSpaceManager {
|
||||||
|
free_manager: Vec<VMRange>, // Address-ordered first fit
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VMFreeSpaceManager {
|
||||||
|
pub fn new(initial_free_range: VMRange) -> Self {
|
||||||
|
let mut free_manager = Vec::with_capacity(INITIAL_SIZE);
|
||||||
|
free_manager.push(initial_free_range);
|
||||||
|
|
||||||
|
VMFreeSpaceManager {
|
||||||
|
free_manager: free_manager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_size(&self) -> usize {
|
||||||
|
self.free_manager
|
||||||
|
.iter()
|
||||||
|
.fold(0, |acc, free_range| acc + free_range.size())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_free_range_internal(&mut self, size: usize, align: usize) -> Result<VMRange> {
|
||||||
|
// Record the minimal free range that satisfies the constraints
|
||||||
|
let mut result_free_range: Option<VMRange> = None;
|
||||||
|
let mut result_idx: Option<usize> = None;
|
||||||
|
let free_list = &self.free_manager;
|
||||||
|
|
||||||
|
for (idx, free_range) in free_list.iter().enumerate() {
|
||||||
|
let free_range = {
|
||||||
|
if free_range.size() < size {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check alignment
|
||||||
|
let start = align_up(free_range.start(), align);
|
||||||
|
let end = start + size;
|
||||||
|
if free_range.end() < end {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unsafe { VMRange::from_unchecked(start, end) }
|
||||||
|
};
|
||||||
|
|
||||||
|
result_free_range = Some(free_range);
|
||||||
|
result_idx = Some(idx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if result_free_range.is_none() {
|
||||||
|
return_errno!(ENOMEM, "not enough memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = result_idx.unwrap();
|
||||||
|
let result_free_range = result_free_range.unwrap();
|
||||||
|
|
||||||
|
self.free_list_update_range(index, result_free_range);
|
||||||
|
return Ok(result_free_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn free_list_update_range(&mut self, index: usize, range: VMRange) {
|
||||||
|
let free_list = &mut self.free_manager;
|
||||||
|
let ranges_after_subtraction = free_list[index].subtract(&range);
|
||||||
|
debug_assert!(ranges_after_subtraction.len() <= 2);
|
||||||
|
if ranges_after_subtraction.len() == 0 {
|
||||||
|
free_list.remove(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
free_list[index] = ranges_after_subtraction[0];
|
||||||
|
if ranges_after_subtraction.len() == 2 {
|
||||||
|
free_list.insert(index + 1, ranges_after_subtraction[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_range_back_to_free_manager(&mut self, dirty_range: &VMRange) -> Result<()> {
|
||||||
|
let free_list = &mut self.free_manager;
|
||||||
|
|
||||||
|
// If the free list is empty, insert the dirty range and it's done.
|
||||||
|
if free_list.is_empty() {
|
||||||
|
free_list.push(*dirty_range);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let dirty_range_start = dirty_range.start();
|
||||||
|
let dirty_range_end = dirty_range.end();
|
||||||
|
|
||||||
|
// If the dirty range is before the first free range or after the last free range
|
||||||
|
let head_range = &mut free_list[0];
|
||||||
|
match dirty_range_end.cmp(&head_range.start()) {
|
||||||
|
Ordering::Equal => {
|
||||||
|
head_range.set_start(dirty_range_start);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Ordering::Less => {
|
||||||
|
free_list.insert(0, *dirty_range);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
let tail_range = free_list.last_mut().unwrap();
|
||||||
|
match dirty_range_start.cmp(&tail_range.end()) {
|
||||||
|
Ordering::Equal => {
|
||||||
|
tail_range.set_end(dirty_range_end);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Ordering::Greater => {
|
||||||
|
free_list.push(*dirty_range);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
// The dirty range must be between some two ranges.
|
||||||
|
debug_assert!(free_list.len() >= 2);
|
||||||
|
let mut idx = 0;
|
||||||
|
|
||||||
|
while idx < free_list.len() - 1 {
|
||||||
|
let left_range = free_list[idx];
|
||||||
|
let right_range = free_list[idx + 1];
|
||||||
|
|
||||||
|
if left_range.end() <= dirty_range_start && dirty_range_end <= right_range.start() {
|
||||||
|
match (
|
||||||
|
dirty_range.is_contiguous_with(&left_range),
|
||||||
|
dirty_range.is_contiguous_with(&right_range),
|
||||||
|
) {
|
||||||
|
(true, true) => {
|
||||||
|
let left_range = &mut free_list[idx];
|
||||||
|
left_range.set_end(right_range.end());
|
||||||
|
free_list.remove(idx + 1);
|
||||||
|
}
|
||||||
|
(true, false) => {
|
||||||
|
let left_range = &mut free_list[idx];
|
||||||
|
left_range.set_end(dirty_range_end);
|
||||||
|
}
|
||||||
|
(false, true) => {
|
||||||
|
let right_range = &mut free_list[idx + 1];
|
||||||
|
right_range.set_start(dirty_range_start);
|
||||||
|
}
|
||||||
|
(false, false) => {
|
||||||
|
free_list.insert(idx + 1, *dirty_range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_free_range(&self, request_range: &VMRange) -> bool {
|
||||||
|
self.free_manager
|
||||||
|
.iter()
|
||||||
|
.any(|free_range| free_range.is_superset_of(request_range))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
mod free_space_manager;
|
||||||
|
mod vm_area;
|
||||||
|
mod vm_chunk_manager;
|
||||||
|
mod vm_range;
|
||||||
|
mod vm_util;
|
||||||
|
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
|
||||||
|
// The address of a block returned by malloc or realloc in GNU systems is always a multiple of eight (or sixteen on 64-bit systems).
|
||||||
|
const DEFAULT_ALIGNMENT: usize = 16;
|
||||||
|
|
||||||
|
const DEFAULT_CHUNK_SIZE: usize = 16 * 1024 * 1024; // 16MB
|
||||||
|
|
||||||
|
use vm_range::*;
|
||||||
|
use vm_util::*;
|
||||||
|
|
||||||
|
use libc::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE};
|
||||||
|
use spin::Mutex;
|
||||||
|
use std::collections::LinkedList;
|
||||||
|
use vm_chunk_manager::ChunkManager;
|
||||||
|
|
||||||
|
pub struct Allocator {
|
||||||
|
inner: Mutex<LinkedList<ChunkManager>>, // Use linked list for fast insert/delete.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Allocator {
|
||||||
|
// Initiate a untrusted memory allocator with default size.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let total_bytes = DEFAULT_CHUNK_SIZE;
|
||||||
|
|
||||||
|
let inner = {
|
||||||
|
let mut new_list = LinkedList::new();
|
||||||
|
let chunk_manager = ChunkManager::new(total_bytes)
|
||||||
|
.expect("Creating untrusted allocator instance failure");
|
||||||
|
new_list.push_back(chunk_manager);
|
||||||
|
Mutex::new(new_list)
|
||||||
|
};
|
||||||
|
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a block of memory and return the start address in c style.
|
||||||
|
// Use exactly like malloc from libc.
|
||||||
|
pub unsafe fn alloc(&self, size: usize, align: Option<usize>) -> Result<*mut u8> {
|
||||||
|
let align = align.unwrap_or(DEFAULT_ALIGNMENT);
|
||||||
|
|
||||||
|
// find a free range in the chunk list
|
||||||
|
for chunk_manager in self.inner.lock().iter_mut() {
|
||||||
|
if size > *chunk_manager.free_size() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(addr) = chunk_manager.alloc(size, align) {
|
||||||
|
return Ok(addr as *mut u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no free range found, create a new chunk and allocate from it.
|
||||||
|
let start_addr = self.alloc_from_new_chunk(size, align)?;
|
||||||
|
Ok(start_addr as *mut u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the memory block with specified start address
|
||||||
|
// For memory allocated with alloc, free must be called.
|
||||||
|
// Use exactly like free from libc.
|
||||||
|
pub unsafe fn free(&self, addr: *mut u8) {
|
||||||
|
let mut chunk_list = self.inner.lock();
|
||||||
|
let (idx, chunk) = chunk_list
|
||||||
|
.iter_mut()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, chunk)| chunk.contains(addr as usize))
|
||||||
|
.expect("free failure");
|
||||||
|
|
||||||
|
chunk.free(addr as usize).expect("free failure");
|
||||||
|
|
||||||
|
// Keep at most one empty chunk in the list. And free other empty chunks:
|
||||||
|
// If the chunk is empty, and all other chunks are in use, push the chunk to the front of the list.
|
||||||
|
// If the chunk is empty and the front chunk is also empty, just remove this chunk.
|
||||||
|
// If this is the last chunk, just keep it.
|
||||||
|
if chunk.is_empty() && chunk_list.len() > 1 {
|
||||||
|
let empty_chunk = chunk_list.remove(idx);
|
||||||
|
if !chunk_list.front().unwrap().is_empty() {
|
||||||
|
chunk_list.push_front(empty_chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_from_new_chunk(&self, size: usize, align: usize) -> Result<usize> {
|
||||||
|
let total_bytes = size.max(DEFAULT_CHUNK_SIZE);
|
||||||
|
|
||||||
|
let mut new_chunk = ChunkManager::new(total_bytes)?;
|
||||||
|
let ret_addr = new_chunk.alloc(size, align)?;
|
||||||
|
|
||||||
|
self.inner.lock().push_front(new_chunk); // Add chunk to the list head to be iterated earlier.
|
||||||
|
Ok(ret_addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Allocator {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
debug!("[untrusted alloc] Drop allocator");
|
||||||
|
self.inner.lock().iter().for_each(|chunk| {
|
||||||
|
assert!(chunk.is_empty() == true);
|
||||||
|
assert!(chunk.is_free_range(chunk.range()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
use super::vm_range::VMRange;
|
||||||
|
use super::*;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use intrusive_collections::rbtree::Link;
|
||||||
|
use intrusive_collections::{intrusive_adapter, KeyAdapter};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct VMArea {
|
||||||
|
range: VMRange,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VMArea {
|
||||||
|
pub fn new(range: VMRange) -> Self {
|
||||||
|
Self { range }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range(&self) -> &VMRange {
|
||||||
|
&self.range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for VMArea {
|
||||||
|
type Target = VMRange;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct VMAObj {
|
||||||
|
link: Link,
|
||||||
|
vma: VMArea,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for VMAObj {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self.vma)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// key adapter for RBTree which is sorted by the start of vma ranges
|
||||||
|
intrusive_adapter!(pub VMAAdapter = Box<VMAObj>: VMAObj { link : Link });
|
||||||
|
impl<'a> KeyAdapter<'a> for VMAAdapter {
|
||||||
|
type Key = usize;
|
||||||
|
fn get_key(&self, vma_obj: &'a VMAObj) -> usize {
|
||||||
|
vma_obj.vma.range().start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VMAObj {
|
||||||
|
pub fn new_vma_obj(vma: VMArea) -> Box<Self> {
|
||||||
|
Box::new(Self {
|
||||||
|
link: Link::new(),
|
||||||
|
vma,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vma(&self) -> &VMArea {
|
||||||
|
&self.vma
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,158 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
use super::free_space_manager::VMFreeSpaceManager as FreeRangeManager;
|
||||||
|
use super::vm_area::*;
|
||||||
|
|
||||||
|
use intrusive_collections::rbtree::RBTree;
|
||||||
|
use intrusive_collections::Bound;
|
||||||
|
|
||||||
|
use libc::c_void;
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "sgx")] {
|
||||||
|
use libc::ocall::{mmap, munmap};
|
||||||
|
} else {
|
||||||
|
use libc::{mmap, munmap};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Memory chunk manager.
|
||||||
|
///
|
||||||
|
/// Chunk is the memory unit for the allocator.
|
||||||
|
/// ChunkManager is implemented basically with two data structures: a red-black tree to track vmas in use and a FreeRangeManager to track
|
||||||
|
/// ranges which are free.
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct ChunkManager {
|
||||||
|
range: VMRange,
|
||||||
|
free_size: usize,
|
||||||
|
vmas: RBTree<VMAAdapter>,
|
||||||
|
free_manager: FreeRangeManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl ChunkManager {
|
||||||
|
pub fn new(total_size: usize) -> Result<Self> {
|
||||||
|
let start_address = {
|
||||||
|
let addr = unsafe {
|
||||||
|
mmap(
|
||||||
|
0 as *mut _,
|
||||||
|
total_size,
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if addr == libc::MAP_FAILED {
|
||||||
|
return_errno!(ENOMEM, "allocate new chunk failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
let addr = addr as usize;
|
||||||
|
assert!(addr.checked_add(total_size).is_some());
|
||||||
|
addr
|
||||||
|
};
|
||||||
|
|
||||||
|
let range = VMRange::new(start_address, start_address + total_size)?;
|
||||||
|
let vmas = RBTree::new(VMAAdapter::new());
|
||||||
|
debug!(
|
||||||
|
"[untrusted alloc] create a new mem chunk, range = {:?}",
|
||||||
|
range
|
||||||
|
);
|
||||||
|
Ok(ChunkManager {
|
||||||
|
range,
|
||||||
|
free_size: range.size(),
|
||||||
|
vmas,
|
||||||
|
free_manager: FreeRangeManager::new(range.clone()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range(&self) -> &VMRange {
|
||||||
|
&self.range
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vmas(&self) -> &RBTree<VMAAdapter> {
|
||||||
|
&self.vmas
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_size(&self) -> &usize {
|
||||||
|
&self.free_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.vmas.iter().count() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc(&mut self, size: usize, align: usize) -> Result<usize> {
|
||||||
|
// Find and allocate a new range for this request
|
||||||
|
let new_range = self.free_manager.find_free_range_internal(size, align)?;
|
||||||
|
let new_addr = new_range.start();
|
||||||
|
let new_vma = VMArea::new(new_range);
|
||||||
|
self.free_size -= new_vma.size();
|
||||||
|
|
||||||
|
debug!("[untrusted alloc] malloc range = {:?}", new_vma.range());
|
||||||
|
self.vmas.insert(VMAObj::new_vma_obj(new_vma));
|
||||||
|
Ok(new_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free(&mut self, addr: usize) -> Result<()> {
|
||||||
|
if addr == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut vmas_cursor = self.vmas.find_mut(&addr);
|
||||||
|
if vmas_cursor.is_null() {
|
||||||
|
return_errno!(EINVAL, "no vma related was found");
|
||||||
|
}
|
||||||
|
|
||||||
|
let vma_obj = vmas_cursor.remove().unwrap();
|
||||||
|
let vma = vma_obj.vma();
|
||||||
|
debug!("[untrusted alloc] free range = {:?}", vma.range());
|
||||||
|
self.free_manager
|
||||||
|
.add_range_back_to_free_manager(vma.range())?;
|
||||||
|
self.free_size += vma.size();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_used_mem_region(&self, addr: usize) -> Result<VMRange> {
|
||||||
|
let vma = self.vmas.upper_bound(Bound::Included(&addr));
|
||||||
|
if vma.is_null() {
|
||||||
|
return_errno!(ESRCH, "no mmap regions that contains the address");
|
||||||
|
}
|
||||||
|
let vma = vma.get().unwrap().vma();
|
||||||
|
if !vma.contains(addr) {
|
||||||
|
return_errno!(ESRCH, "no mmap regions that contains the address");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(vma.range().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn usage_percentage(&self) -> f32 {
|
||||||
|
let total_size = self.range.size();
|
||||||
|
let mut used_size = 0;
|
||||||
|
self.vmas
|
||||||
|
.iter()
|
||||||
|
.for_each(|vma_obj| used_size += vma_obj.vma().size());
|
||||||
|
|
||||||
|
return used_size as f32 / total_size as f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns whether the requested range is free
|
||||||
|
pub fn is_free_range(&self, request_range: &VMRange) -> bool {
|
||||||
|
self.free_manager.is_free_range(request_range)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, addr: usize) -> bool {
|
||||||
|
self.range.contains(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ChunkManager {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
debug_assert!(self.is_empty() == true);
|
||||||
|
debug_assert!(self.free_size == self.range.size());
|
||||||
|
debug_assert!(self.free_manager.free_size() == self.range.size());
|
||||||
|
let ret = unsafe { munmap(self.range().start as *mut c_void, self.range().size()) };
|
||||||
|
assert!(ret == 0);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,159 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default, Eq, PartialEq, Hash)]
|
||||||
|
pub struct VMRange {
|
||||||
|
pub(super) start: usize,
|
||||||
|
pub(super) end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl VMRange {
|
||||||
|
pub fn new(start: usize, end: usize) -> Result<VMRange> {
|
||||||
|
if start > end {
|
||||||
|
return_errno!(EINVAL, "invalid start end address");
|
||||||
|
}
|
||||||
|
Ok(VMRange {
|
||||||
|
start: start,
|
||||||
|
end: end,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_size(start: usize, size: usize) -> Result<VMRange> {
|
||||||
|
Self::new(start, start + size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_empty(start: usize) -> Result<VMRange> {
|
||||||
|
if start % PAGE_SIZE != 0 {
|
||||||
|
return_errno!(EINVAL, "invalid start or end");
|
||||||
|
}
|
||||||
|
Ok(VMRange {
|
||||||
|
start: start,
|
||||||
|
end: start,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn from_unchecked(start: usize, end: usize) -> VMRange {
|
||||||
|
debug_assert!(start <= end);
|
||||||
|
VMRange {
|
||||||
|
start: start,
|
||||||
|
end: end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&self) -> usize {
|
||||||
|
self.start
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(&self) -> usize {
|
||||||
|
self.end
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
self.end - self.start
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_start(&mut self, new_start: usize) {
|
||||||
|
debug_assert!(new_start <= self.end);
|
||||||
|
self.start = new_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_end(&mut self, new_end: usize) {
|
||||||
|
debug_assert!(new_end >= self.start);
|
||||||
|
self.end = new_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty(&self) -> bool {
|
||||||
|
self.start == self.end
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_superset_of(&self, other: &VMRange) -> bool {
|
||||||
|
self.start() <= other.start() && other.end() <= self.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, addr: usize) -> bool {
|
||||||
|
self.start() <= addr && addr < self.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns whether two ranges have non-empty interesection.
|
||||||
|
pub fn overlap_with(&self, other: &VMRange) -> bool {
|
||||||
|
let intersection_start = self.start().max(other.start());
|
||||||
|
let intersection_end = self.end().min(other.end());
|
||||||
|
intersection_start < intersection_end
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_contiguous_with(&self, other: &VMRange) -> bool {
|
||||||
|
self.start == other.end || self.end == other.start
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a set of ranges by subtracting self with the other.
|
||||||
|
//
|
||||||
|
// Post-condition: the returned ranges have non-zero sizes.
|
||||||
|
pub fn subtract(&self, other: &VMRange) -> Vec<VMRange> {
|
||||||
|
if self.size() == 0 {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
let intersection = match self.intersect(other) {
|
||||||
|
None => return vec![*self],
|
||||||
|
Some(intersection) => intersection,
|
||||||
|
};
|
||||||
|
|
||||||
|
let self_start = self.start();
|
||||||
|
let self_end = self.end();
|
||||||
|
let inter_start = intersection.start();
|
||||||
|
let inter_end = intersection.end();
|
||||||
|
debug_assert!(self_start <= inter_start);
|
||||||
|
debug_assert!(inter_end <= self_end);
|
||||||
|
|
||||||
|
match (self_start < inter_start, inter_end < self_end) {
|
||||||
|
(false, false) => Vec::new(),
|
||||||
|
(false, true) => unsafe { vec![VMRange::from_unchecked(inter_end, self_end)] },
|
||||||
|
(true, false) => unsafe { vec![VMRange::from_unchecked(self_start, inter_start)] },
|
||||||
|
(true, true) => unsafe {
|
||||||
|
vec![
|
||||||
|
VMRange::from_unchecked(self_start, inter_start),
|
||||||
|
VMRange::from_unchecked(inter_end, self_end),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an non-empty intersection if where is any
|
||||||
|
pub fn intersect(&self, other: &VMRange) -> Option<VMRange> {
|
||||||
|
let intersection_start = self.start().max(other.start());
|
||||||
|
let intersection_end = self.end().min(other.end());
|
||||||
|
if intersection_start >= intersection_end {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
Some(VMRange::from_unchecked(
|
||||||
|
intersection_start,
|
||||||
|
intersection_end,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_slice(&self) -> &[u8] {
|
||||||
|
let buf_ptr = self.start() as *const u8;
|
||||||
|
let buf_size = self.size() as usize;
|
||||||
|
std::slice::from_raw_parts(buf_ptr, buf_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_slice_mut(&self) -> &mut [u8] {
|
||||||
|
let buf_ptr = self.start() as *mut u8;
|
||||||
|
let buf_size = self.size() as usize;
|
||||||
|
std::slice::from_raw_parts_mut(buf_ptr, buf_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for VMRange {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"VMRange {{ start: 0x{:x?}, end: 0x{:x?}, size: 0x{:x?} }}",
|
||||||
|
self.start,
|
||||||
|
self.end,
|
||||||
|
self.size()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
pub fn align_down(addr: usize, align: usize) -> usize {
|
||||||
|
debug_assert!(align.is_power_of_two());
|
||||||
|
addr & !(align - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn align_up(addr: usize, align: usize) -> usize {
|
||||||
|
debug_assert!(align.is_power_of_two());
|
||||||
|
align_down(addr + (align - 1), align)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user