Skip to content

Commit 1e44dd1

Browse files
Andrei TrandafirAndrei Trandafir
Andrei Trandafir
authored and
Andrei Trandafir
committed
cli: Update user-space to sync with driver v0.5
Following the update of the Nitro Enclaves driver to v0.5, the user-space components need to be updated to match the new driver API and logic. Signed-off-by: Andrei Trandafir <[email protected]>
1 parent a25c0bd commit 1e44dd1

File tree

7 files changed

+274
-317
lines changed

7 files changed

+274
-317
lines changed

Cargo.lock

Lines changed: 0 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ page_size = "0.4"
1919
signal-hook = "0.1"
2020
serde_cbor = "0.10"
2121
procfs = "0.7"
22-
kvm-bindings = "0.2"
2322
eif_loader = { path = "./eif_loader" }
2423
enclave_build = { path = "./enclave_build"}
2524

src/enclave_proc/commands.rs

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::common::json_output::EnclaveTerminateInfo;
1111
use crate::common::NitroCliResult;
1212
use crate::enclave_proc::connection::Connection;
1313
use crate::enclave_proc::connection::{safe_conn_eprintln, safe_conn_println};
14-
use crate::enclave_proc::cpu_info::CpuInfos;
14+
use crate::enclave_proc::cpu_info::CpuInfo;
1515
use crate::enclave_proc::resource_manager::{EnclaveManager, EnclaveState};
1616
use crate::enclave_proc::utils::get_enclave_describe_info;
1717

@@ -21,12 +21,6 @@ pub const VMADDR_CID_PARENT: u32 = 3;
2121
/// The vsock port used to confirm that the enclave has booted.
2222
pub const ENCLAVE_READY_VSOCK_PORT: u32 = 9000;
2323

24-
/// The size of an internal data buffer.
25-
pub const BUFFER_SIZE: usize = 1024;
26-
27-
/// The bit indicating if an enclave has been launched in debug mode.
28-
pub const DEBUG_FLAG: u16 = 0x1;
29-
3024
/// Launch an enclave with the specified arguments and provide the launch status through the given connection.
3125
pub fn run_enclaves(
3226
args: &RunEnclavesArgs,
@@ -37,21 +31,11 @@ pub fn run_enclaves(
3731
let eif_file =
3832
File::open(&args.eif_path).map_err(|e| format!("Failed to open the eif file: {}", e))?;
3933

40-
let cpu_infos = CpuInfos::new()?;
41-
let cpu_ids = if let Some(cpu_ids) = args.cpu_ids.clone() {
42-
cpu_infos.check_cpu_ids(&cpu_ids)?;
43-
Some(cpu_ids)
44-
} else if let Some(cpu_count) = args.cpu_count {
45-
Some(cpu_infos.get_cpu_ids(cpu_count)?)
46-
} else {
47-
// Should not happen
48-
None
49-
};
50-
34+
let cpu_ids = CpuInfo::new()?.get_cpu_config(args)?;
5135
let mut enclave_manager = EnclaveManager::new(
5236
args.enclave_cid,
5337
args.memory_mib,
54-
cpu_ids.unwrap(),
38+
cpu_ids,
5539
eif_file,
5640
args.debug_mode.unwrap_or(false),
5741
)

src/enclave_proc/cpu_info.rs

Lines changed: 83 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -3,231 +3,123 @@
33
#![deny(missing_docs)]
44
#![deny(warnings)]
55

6-
use std::collections::HashSet;
7-
use std::fs::File;
8-
use std::io::{BufRead, BufReader};
9-
use std::str::FromStr;
6+
use std::collections::BTreeSet;
7+
use std::process::Command;
8+
9+
use crate::common::commands_parser::RunEnclavesArgs;
10+
use crate::common::NitroCliResult;
11+
12+
/// The CPU configuration requested by the user.
13+
#[derive(Clone, PartialEq)]
14+
pub enum EnclaveCpuConfig {
15+
/// A list with the desired CPU IDs.
16+
List(Vec<u32>),
17+
/// The number of desired CPU IDs.
18+
Count(u32),
19+
}
1020

11-
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
12-
/// A CPU mapping structure which links a CPU ID with its corresponding core ID.
21+
/// Aggregate CPU information for multiple CPUs.
22+
#[derive(Debug)]
1323
pub struct CpuInfo {
14-
core_id: u32,
15-
cpu_id: u32,
24+
/// The list with the CPUs available for enclaves.
25+
cpu_ids: Vec<u32>,
1626
}
1727

18-
impl CpuInfo {
19-
/// Create a new `CpuInfo` instance from the given CPU and core IDs.
20-
pub fn new(cpu_id: u32, core_id: u32) -> Self {
21-
CpuInfo { cpu_id, core_id }
28+
impl Default for EnclaveCpuConfig {
29+
fn default() -> Self {
30+
EnclaveCpuConfig::Count(0)
2231
}
2332
}
2433

25-
#[derive(Debug)]
26-
/// Aggregate CPU information for multiple CPUs.
27-
pub struct CpuInfos {
28-
/// The list with CPU - core mappings.
29-
pub core_ids: Vec<CpuInfo>,
30-
/// The flag indicating if hyper-threading is enabled.
31-
pub hyper_threading: bool,
32-
}
33-
34-
impl CpuInfos {
35-
/// Create a new `CpuInfos` instance from the current system configuration.
34+
impl CpuInfo {
35+
/// Create a new `CpuInfo` instance from the current system configuration.
3636
pub fn new() -> Result<Self, String> {
37-
let core_ids = CpuInfos::get_cpu_info()?;
38-
let hyper_threading = CpuInfos::is_hyper_threading_on(&core_ids);
39-
Ok(CpuInfos {
40-
core_ids,
41-
hyper_threading,
37+
Ok(CpuInfo {
38+
cpu_ids: CpuInfo::get_cpu_info()?,
4239
})
4340
}
4441

45-
/// Parse a `/proc/cpuinfo` line to obtain a numeric value.
46-
pub fn get_value(mut line: String) -> Result<u32, String> {
47-
line.retain(|c| !c.is_whitespace());
48-
let tokens: Vec<&str> = line.split(':').collect();
49-
let token = *tokens.get(1).unwrap();
50-
51-
u32::from_str(token).map_err(|err| format!("{}", err))
52-
}
53-
54-
/// Parse `/proc/cpuinfo` and build the list of CPU - core mappings.
55-
pub fn get_cpu_info() -> Result<Vec<CpuInfo>, String> {
56-
let mut result: Vec<CpuInfo> = Vec::new();
57-
let mut ids: Vec<u32> = Vec::new();
58-
let file = File::open("/proc/cpuinfo")
59-
.map_err(|err| format!("Could not open /proc/cpuinfo: {}", err))?;
60-
let mut reader = BufReader::new(file);
61-
62-
loop {
63-
let mut line = String::new();
64-
let len = reader
65-
.read_line(&mut line)
66-
.map_err(|err| format!("{}", err))?;
67-
68-
if len == 0 {
69-
break;
70-
}
71-
72-
// Given CPU i, its ID will be 2 * i and its core ID will be 2 * i + 1.
73-
if line.contains("processor") {
74-
ids.push(CpuInfos::get_value(line)?);
75-
} else if line.contains("apicid")
76-
&& !line.contains("initial")
77-
&& !line.contains("flags")
78-
{
79-
let id = CpuInfos::get_value(line)?;
80-
ids.push(id >> 1);
81-
}
82-
}
83-
84-
for i in 0..ids.len() / 2 {
85-
result.push(CpuInfo::new(
86-
*ids.get(2 * i).unwrap(),
87-
*ids.get(2 * i + 1).unwrap(),
88-
));
89-
}
90-
91-
// Sort by core ID.
92-
result.sort_by(|a, b| a.core_id.cmp(&b.core_id));
93-
Ok(result)
94-
}
95-
96-
/// Get the ID of the core which holds the specifided CPU.
97-
pub fn get_core_id(&self, cpu_id: u32) -> Option<u32> {
98-
for info in self.core_ids.iter() {
99-
if info.cpu_id == cpu_id {
100-
return Some(info.core_id);
42+
/// Get the CPU configuration from the command-line arguments.
43+
pub fn get_cpu_config(&self, args: &RunEnclavesArgs) -> NitroCliResult<EnclaveCpuConfig> {
44+
if let Some(cpu_ids) = args.cpu_ids.clone() {
45+
self.check_cpu_ids(&cpu_ids)?;
46+
Ok(EnclaveCpuConfig::List(cpu_ids))
47+
} else if let Some(cpu_count) = args.cpu_count {
48+
if self.cpu_ids.len() < cpu_count as usize {
49+
return Err(format!(
50+
"Insufficient CPUs available (requested {}, but maximum is {}).",
51+
cpu_count,
52+
self.cpu_ids.len()
53+
));
10154
}
55+
Ok(EnclaveCpuConfig::Count(cpu_count))
56+
} else {
57+
// Should not happen.
58+
Err("Invalid CPU configuration argument.".to_string())
10259
}
103-
None
10460
}
10561

106-
/// Determine if hyper-threading is enabled.
107-
pub fn is_hyper_threading_on(cpu_info: &[CpuInfo]) -> bool {
108-
for i in 0..cpu_info.len() - 1 {
109-
if cpu_info.get(i).unwrap().core_id == cpu_info.get(i + 1).unwrap().core_id {
110-
return true;
111-
}
112-
}
113-
false
114-
}
62+
/// Verify that a provided list of CPU IDs is valid.
63+
pub fn check_cpu_ids(&self, cpu_ids: &[u32]) -> Result<(), String> {
64+
// Ensure there are no duplicate IDs.
65+
let mut unique_ids = BTreeSet::new();
11566

116-
/// Get a list of specified size which contains valid CPU IDs.
117-
pub fn get_cpu_ids(&self, cpu_count: u32) -> Result<Vec<u32>, String> {
118-
if self.hyper_threading && cpu_count % 2 != 0 {
119-
return Err("cpu_count should be an even number.".to_string());
67+
for cpu_id in cpu_ids {
68+
unique_ids.insert(cpu_id);
12069
}
12170

122-
let mut count: u32 = cpu_count;
123-
let mut result: Vec<u32> = Vec::new();
124-
125-
for info in self.core_ids.iter() {
126-
if info.core_id != 0 {
127-
result.push(info.cpu_id);
128-
count -= 1;
129-
}
130-
131-
if count == 0 {
132-
return Ok(result);
133-
}
71+
if unique_ids.len() < cpu_ids.len() {
72+
return Err(format!(
73+
"The CPU ID list contains {} duplicate(s).",
74+
cpu_ids.len() - unique_ids.len()
75+
));
13476
}
13577

136-
let valid_cpus = if self.hyper_threading {
137-
self.core_ids.len() - 2
138-
} else {
139-
self.core_ids.len() - 1
140-
};
141-
142-
Err(format!(
143-
"Could not find the requested number of cpus. Maximum number of available cpus: {}",
144-
valid_cpus
145-
))
146-
}
147-
148-
/// Check if a list of CPU IDs contains only pairs of siblings (belonging to the same core).
149-
pub fn contains_sibling_pairs(&self, cpu_ids: &[u32]) -> bool {
150-
let mut core_ids: HashSet<u32> = HashSet::new();
151-
152-
for id in cpu_ids.iter() {
153-
match self.get_core_id(*id) {
154-
Some(core_id) => {
155-
if !core_ids.contains(&core_id) {
156-
core_ids.insert(core_id);
157-
} else {
158-
core_ids.remove(&core_id);
159-
}
160-
}
161-
_ => return false,
78+
// Ensure the requested CPUs are available in the CPU pool.
79+
for cpu_id in unique_ids {
80+
if !self.cpu_ids.contains(cpu_id) {
81+
return Err(format!("The CPU with ID {} is not available.", cpu_id));
16282
}
16383
}
16484

165-
core_ids.is_empty()
85+
// At this point, all requested CPU IDs are part of the enclave CPU pool.
86+
Ok(())
16687
}
16788

16889
/// Get a list of all available CPU IDs.
16990
pub fn get_cpu_candidates(&self) -> Vec<u32> {
170-
let mut result: Vec<u32> = Vec::new();
171-
172-
for info in self.core_ids.iter() {
173-
if info.core_id != 0 {
174-
result.push(info.cpu_id);
175-
}
176-
}
177-
result
91+
self.cpu_ids.clone()
17892
}
17993

180-
/// Verify that a provided list of CPU IDs is valid.
181-
pub fn check_cpu_ids(&self, cpu_ids: &[u32]) -> Result<(), String> {
182-
if self.hyper_threading && cpu_ids.len() % 2 != 0 {
183-
return Err(
184-
"Hyper-threading is enabled, so sibling pairs need to be provided".to_string(),
185-
);
186-
}
187-
188-
for id in cpu_ids.iter() {
189-
match self.get_core_id(*id) {
190-
Some(0) => {
191-
return Err(format!(
192-
"Cpus with core id 0 can't be used. Hint: You can use {:?}",
193-
self.get_cpu_candidates()
194-
))
195-
}
196-
None => {
197-
return Err(format!(
198-
"Cpus ids are not valid. Hint: You can use {:?}",
199-
self.get_cpu_candidates()
200-
))
201-
}
202-
_ => (),
203-
}
204-
}
205-
206-
if self.hyper_threading && !self.contains_sibling_pairs(cpu_ids) {
207-
return Err(format!(
208-
"Hyper-threading is enabled, cpus must be on the same physical core. Hint: The following cpus are siblings {:?}",
209-
self.get_siblings()
210-
));
211-
}
212-
213-
Ok(())
94+
/// Parse a `lscpu` line to obtain a numeric value.
95+
pub fn get_value(line: &str) -> Result<u32, String> {
96+
let mut line_str = line.to_string();
97+
line_str.retain(|c| !c.is_whitespace());
98+
line_str
99+
.parse::<u32>()
100+
.map_err(|e| format!("Failed to parse CPU ID: {}", e))
214101
}
215102

216-
/// Generate all pairs of sibling CPUs (which belong to the same core).
217-
pub fn get_siblings(&self) -> Vec<(u32, u32)> {
218-
let mut result: Vec<(u32, u32)> = Vec::new();
219-
220-
// Find the pairs of CPU IDs that have the same core ID.
221-
for i in 0..self.core_ids.len() - 1 {
222-
let info1 = self.core_ids.get(i).unwrap();
223-
let info2 = self.core_ids.get(i + 1).unwrap();
224-
225-
if info1.core_id == info2.core_id && info1.core_id != 0 {
226-
result.push((info1.cpu_id, info2.cpu_id));
103+
/// Parse `lscpu -p=cpu -c` and build the list of off-line CPUs.
104+
fn get_cpu_info() -> NitroCliResult<Vec<u32>> {
105+
let mut result: Vec<u32> = Vec::new();
106+
let output = Command::new("lscpu")
107+
.arg("-p=cpu")
108+
.arg("-c")
109+
.output()
110+
.map_err(|e| format!("Failed to execute \"lscpu -p=cpu -c\": {}", e))?;
111+
112+
let output_str = String::from_utf8_lossy(&output.stdout);
113+
for line in output_str.lines() {
114+
if line.starts_with('#') {
115+
continue;
227116
}
117+
118+
let cpu_id = CpuInfo::get_value(line)?;
119+
result.push(cpu_id);
228120
}
229121

230-
result
122+
Ok(result)
231123
}
232124
}
233125

0 commit comments

Comments
 (0)