Skip to content

Commit 5567ba7

Browse files
committed
Expand raw system APIs and fix header checking
1 parent 12f52b3 commit 5567ba7

File tree

25 files changed

+405
-302
lines changed

25 files changed

+405
-302
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/target
22
/crates/*/header-check/target
3+
/crates/*/header-check/Cargo.lock
34
debug.out
45
core
56
out/

Cargo.lock

+19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
members = [
44
"crates/*",
5+
"crates/*/sys",
56
"bin/*",
67
"lib/*",
78
"packaging/propolis-package",
@@ -10,6 +11,7 @@ members = [
1011

1112
default-members = [
1213
"crates/*",
14+
"crates/*/sys",
1315
"lib/*",
1416
"bin/propolis-cli",
1517
"bin/propolis-server",
@@ -47,6 +49,7 @@ backoff = "0.4.0"
4749
backtrace = "0.3.66"
4850
base64 = "0.21"
4951
bhyve_api = { path = "crates/bhyve-api" }
52+
bhyve_api_sys = { path = "crates/bhyve-api/sys" }
5053
bit_field = "0.10.1"
5154
bitflags = "1.3"
5255
bitstruct = "0.1"
@@ -127,6 +130,7 @@ usdt = { version = "0.3.2", default-features = false }
127130
uuid = "1.0.0"
128131
version_check = "0.9"
129132
viona_api = { path = "crates/viona-api" }
133+
viona_api_sys = { path = "crates/viona-api/sys" }
130134
vte = "0.10.1"
131135

132136
[workspace.dependencies.crucible-client-types]

crates/bhyve-api/Cargo.toml

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
[package]
22
name = "bhyve_api"
33
version = "0.0.0"
4-
authors = ["Patrick Mooney <pmooney@oxide.computer>"]
54
license = "MPL-2.0"
65
edition = "2018"
76

87
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
98

109
[dependencies]
1110
libc.workspace = true
12-
num_enum.workspace = true
13-
serde = { workspace = true, features = ["derive"] }
14-
serde_arrays = { workspace = true }
11+
bhyve_api_sys.workspace = true

crates/bhyve-api/header-check/Cargo.toml

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
[package]
2-
name = "header-check"
2+
name = "bhyve_api-hdrchk"
33
version = "0.0.0"
4-
authors = ["Patrick Mooney <pmooney@oxide.computer>"]
54
license = "MPL-2.0"
65
build = "build.rs"
76
publish = false
87

98
[dependencies]
10-
bhyve_api = { path = ".." }
9+
bhyve_api_sys = { path = "../sys" }
1110
libc = "0.2"
1211

1312
[build-dependencies]
14-
cc.workspace = true
15-
ctest2.workspace = true
13+
cc = "1"
14+
ctest2 = "0.4"
1615

1716
[[test]]
1817
name = "main"

crates/bhyve-api/header-check/build.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,5 +80,5 @@ fn main() {
8080
_ => false,
8181
});
8282

83-
cfg.generate("../src/lib.rs", "main.rs");
83+
cfg.generate("../sys/src/lib.rs", "main.rs");
8484
}
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
extern crate bhyve_api;
1+
extern crate bhyve_api_sys;
22
extern crate libc;
33

4-
use bhyve_api::*;
4+
use bhyve_api_sys::*;
55
use libc::*;
66

77
include!(concat!(env!("OUT_DIR"), "/main.rs"));

crates/bhyve-api/src/lib.rs

+135-38
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,153 @@
1-
mod enums;
2-
mod ioctls;
3-
mod structs;
4-
mod vmm_data;
1+
use std::fs::{File, OpenOptions};
2+
use std::io::{Error, ErrorKind, Result};
3+
use std::os::fd::*;
4+
use std::os::unix::fs::OpenOptionsExt;
5+
use std::path::PathBuf;
56

6-
pub use enums::*;
7-
pub use ioctls::*;
8-
pub use structs::*;
9-
pub use vmm_data::*;
10-
11-
pub const VM_MAXCPU: usize = 32;
7+
pub use bhyve_api_sys::*;
128

139
pub const VMM_PATH_PREFIX: &str = "/dev/vmm";
1410
pub const VMM_CTL_PATH: &str = "/dev/vmmctl";
1511

16-
/// This is the VMM interface version against which bhyve_api expects to operate
17-
/// against. All constants and structs defined by the crate are done so in
18-
/// terms of that specific version.
19-
pub const VMM_CURRENT_INTERFACE_VERSION: u32 = 8;
12+
pub struct VmmCtlFd(File);
13+
impl VmmCtlFd {
14+
pub fn open() -> Result<Self> {
15+
let ctl = OpenOptions::new()
16+
.write(true)
17+
.custom_flags(libc::O_EXCL)
18+
.open(VMM_CTL_PATH)?;
19+
Ok(Self(ctl))
20+
}
21+
22+
/// Issue ioctl against open vmmctl handle
23+
///
24+
/// # Safety
25+
///
26+
/// Caller is charged with providing `data` argument which is adequate for
27+
/// any copyin/copyout actions which may occur as part of the ioctl
28+
/// processing.
29+
pub unsafe fn ioctl<T>(&self, cmd: i32, data: *mut T) -> Result<i32> {
30+
ioctl(self.as_raw_fd(), cmd, data as *mut libc::c_void)
31+
}
32+
pub fn ioctl_usize(&self, cmd: i32, data: usize) -> Result<i32> {
33+
if !Self::ioctl_usize_safe(cmd) {
34+
return Err(Error::new(
35+
ErrorKind::InvalidInput,
36+
"unsafe cmd provided",
37+
));
38+
}
39+
// Safety: Since we are explicitly filtering for vmm ioctls which will
40+
// not assume the data argument is a pointer for copyin/copyout, we can
41+
// dismiss those dangers. The caller is assumed to be cognizant of
42+
// other potential side effects.
43+
unsafe { ioctl(self.as_raw_fd(), cmd, data as *mut libc::c_void) }
44+
}
45+
46+
/// Check VMM ioctl command against those known to not require any
47+
/// copyin/copyout to function.
48+
const fn ioctl_usize_safe(cmd: i32) -> bool {
49+
matches!(cmd, ioctls::VMM_INTERFACE_VERSION,)
50+
}
51+
}
52+
53+
impl AsRawFd for VmmCtlFd {
54+
fn as_raw_fd(&self) -> RawFd {
55+
self.0.as_raw_fd()
56+
}
57+
}
58+
59+
pub struct VmmFd(File);
60+
impl VmmFd {
61+
pub fn open(name: &str) -> Result<Self> {
62+
let mut vmpath = PathBuf::from(VMM_PATH_PREFIX);
63+
vmpath.push(name);
2064

21-
use std::io::Result;
65+
let fp = OpenOptions::new().write(true).read(true).open(vmpath)?;
66+
Ok(Self(fp))
67+
}
68+
69+
/// Create new instance from raw `File` resource
70+
///
71+
/// # Safety
72+
///
73+
/// Caller is expected to provide `File` resource which which is a valid vmm
74+
/// resource. (Or alternatively, is not to make any vmm-related ioctls, if
75+
/// this instance was created for unit-testing purposes.)
76+
pub unsafe fn new_raw(fp: File) -> Self {
77+
Self(fp)
78+
}
79+
80+
/// Issue ioctl against open vmm instance
81+
///
82+
/// # Safety
83+
///
84+
/// Caller is charged with providing `data` argument which is adequate for
85+
/// any copyin/copyout actions which may occur as part of the ioctl
86+
/// processing.
87+
pub unsafe fn ioctl<T>(&self, cmd: i32, data: *mut T) -> Result<i32> {
88+
ioctl(self.as_raw_fd(), cmd, data as *mut libc::c_void)
89+
}
90+
pub fn ioctl_usize(&self, cmd: i32, data: usize) -> Result<i32> {
91+
if !Self::ioctl_usize_safe(cmd) {
92+
return Err(Error::new(
93+
ErrorKind::InvalidInput,
94+
"unsafe cmd provided",
95+
));
96+
}
97+
// Safety: Since we are explicitly filtering for vmm ioctls which will
98+
// not assume the data argument is a pointer for copyin/copyout, we can
99+
// dismiss those dangers. The caller is assumed to be cognizant of
100+
// other potential side effects.
101+
unsafe { ioctl(self.as_raw_fd(), cmd, data as *mut libc::c_void) }
102+
}
103+
104+
/// Check VMM ioctl command against those known to not require any
105+
/// copyin/copyout to function.
106+
const fn ioctl_usize_safe(cmd: i32) -> bool {
107+
matches!(
108+
cmd,
109+
ioctls::VM_PAUSE
110+
| ioctls::VM_RESUME
111+
| ioctls::VM_DESTROY_SELF
112+
| ioctls::VM_SET_AUTODESTRUCT,
113+
)
114+
}
115+
}
116+
117+
impl AsRawFd for VmmFd {
118+
fn as_raw_fd(&self) -> RawFd {
119+
self.0.as_raw_fd()
120+
}
121+
}
22122

23123
/// Check that bhyve kernel VMM component matches version underlying interfaces
24124
/// defined in bhyve-api. Can use a user-provided version (via `version` arg)
25125
/// or the current one known to bhyve_api.
26-
#[cfg(target_os = "illumos")]
27126
pub fn check_version(version: Option<u32>) -> Result<bool> {
28-
use std::fs::OpenOptions;
29-
use std::io::Error;
30-
use std::os::unix::fs::OpenOptionsExt;
31-
use std::os::unix::io::AsRawFd;
32-
33-
let ctl = OpenOptions::new()
34-
.write(true)
35-
.custom_flags(libc::O_EXCL)
36-
.open(VMM_CTL_PATH)?;
37-
let ctlfd = ctl.as_raw_fd();
38-
let res = unsafe {
39-
libc::ioctl(
40-
ctlfd,
41-
VMM_INTERFACE_VERSION,
127+
let ctl = VmmCtlFd::open()?;
128+
let current_vers = unsafe {
129+
ctl.ioctl(
130+
ioctls::VMM_INTERFACE_VERSION,
42131
std::ptr::null_mut() as *mut libc::c_void,
43132
)
44-
};
45-
if res < 0 {
46-
return Err(Error::last_os_error());
47-
}
133+
}?;
48134

49-
let check_against = version.unwrap_or(VMM_CURRENT_INTERFACE_VERSION);
50-
Ok(check_against == res as u32)
135+
Ok(current_vers as u32 == version.unwrap_or(VMM_CURRENT_INTERFACE_VERSION))
136+
}
137+
138+
#[cfg(target_os = "illumos")]
139+
unsafe fn ioctl(fd: RawFd, cmd: i32, data: *mut libc::c_void) -> Result<i32> {
140+
match libc::ioctl(fd, cmd, data) {
141+
-1 => Err(Error::last_os_error()),
142+
other => Ok(other),
143+
}
51144
}
52145

53146
#[cfg(not(target_os = "illumos"))]
54-
pub fn check_version(_version: Option<u32>) -> Result<bool> {
55-
Ok(false)
147+
unsafe fn ioctl(
148+
_fd: RawFd,
149+
_cmd: i32,
150+
_data: *mut libc::c_void,
151+
) -> Result<i32> {
152+
Err(Error::new(ErrorKind::Other, "illumos required"))
56153
}

crates/bhyve-api/sys/Cargo.toml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "bhyve_api_sys"
3+
version = "0.0.0"
4+
license = "MPL-2.0"
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
libc.workspace = true
11+
num_enum.workspace = true
12+
serde = { workspace = true, features = ["derive"] }
13+
serde_arrays = { workspace = true }
File renamed without changes.
File renamed without changes.

crates/bhyve-api/sys/src/lib.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
mod enums;
2+
pub mod ioctls;
3+
mod structs;
4+
mod vmm_data;
5+
6+
pub use enums::*;
7+
pub use ioctls::*;
8+
pub use structs::*;
9+
pub use vmm_data::*;
10+
11+
pub const VM_MAXCPU: usize = 32;
12+
13+
/// This is the VMM interface version against which bhyve_api expects to operate
14+
/// against. All constants and structs defined by the crate are done so in
15+
/// terms of that specific version.
16+
pub const VMM_CURRENT_INTERFACE_VERSION: u32 = 8;
File renamed without changes.
File renamed without changes.

crates/viona-api/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
[package]
22
name = "viona_api"
33
version = "0.0.0"
4-
authors = ["Patrick Mooney <pmooney@oxide.computer>"]
54
license = "MPL-2.0"
65
edition = "2018"
76

87
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
98

109
[dependencies]
10+
libc.workspace = true
11+
viona_api_sys.workspace = true

crates/viona-api/header-check/Cargo.toml

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
[package]
2-
name = "header-check"
2+
name = "viona_api-hdrchk"
33
version = "0.0.0"
4-
authors = ["Patrick Mooney <pmooney@oxide.computer>"]
54
license = "MPL-2.0"
65
build = "build.rs"
76
publish = false
87

98
[dependencies]
10-
viona_api.workspace = true
11-
libc.workspace = true
9+
viona_api_sys = { path = "../sys" }
10+
libc = "0.2"
1211

1312
[build-dependencies]
14-
cc.workspace = true
15-
ctest2.workspace = true
13+
cc = "1"
14+
ctest2 = "0.4"
1615

1716
[[test]]
1817
name = "main"

crates/viona-api/header-check/build.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,5 @@ fn main() {
4949
_ => false,
5050
});
5151

52-
cfg.generate("../src/lib.rs", "main.rs");
52+
cfg.generate("../sys/src/lib.rs", "main.rs");
5353
}

0 commit comments

Comments
 (0)