[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