Skip to content

Commit c8b89e8

Browse files
committed
Implement seperate syscall for switching to an application
1 parent 4a29c69 commit c8b89e8

File tree

9 files changed

+136
-52
lines changed

9 files changed

+136
-52
lines changed

enclave_apps/oak_orchestrator/src/main.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ fn start() -> ! {
5050
.expect("failed to write dice data");
5151
attested_app.dice_data.as_bytes_mut().zeroize();
5252

53-
log::info!("Exiting and launching application.");
54-
syscall::unstable_switch_proccess(attested_app.elf_binary.as_slice())
53+
let pid = syscall::unstable_create_proccess(attested_app.elf_binary.as_slice())
54+
.expect("failed to create app process");
55+
syscall::unstable_switch_proccess(pid)
5556
}

oak_restricted_kernel/src/lib.rs

+26-4
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,30 @@ pub static VMA_ALLOCATOR: Spinlock<VirtualAddressAllocator<Size2MiB>> =
124124
},
125125
)));
126126

127+
static PROCCESSES: Processes = Processes { list: Spinlock::new(alloc::vec::Vec::new()) };
128+
129+
struct Processes {
130+
list: Spinlock<alloc::vec::Vec<Process>>,
131+
}
132+
133+
impl Processes {
134+
/// # Safety
135+
///
136+
/// Caller must ensure to no lock is currently being held.
137+
unsafe fn add(&self, process: Process) -> usize {
138+
self.list.force_unlock();
139+
let mut processes = self.list.lock();
140+
let pid: usize = processes.len();
141+
processes.push(process);
142+
log::debug!("Created process (pid: {})", pid);
143+
pid
144+
}
145+
fn execute(&self, pid: usize) -> ! {
146+
log::debug!("Executing process (pid: {})", pid);
147+
self.list.lock().get_mut(pid).expect("PID not mapped to a process").execute()
148+
}
149+
}
150+
127151
/// Main entry point for the kernel, to be called from bootloader.
128152
pub fn start_kernel(info: &BootParams) -> ! {
129153
avx::enable_avx();
@@ -471,11 +495,9 @@ pub fn start_kernel(info: &BootParams) -> ! {
471495

472496
// Ensure new process is not dropped.
473497
// Safety: The application is assumed to be a valid ELF file.
474-
let process = Box::leak(Box::new(unsafe {
475-
Process::from_application(&application).expect("failed to create process")
476-
}));
498+
let pid = unsafe { Process::from_application(&application).expect("failed to create process") };
477499

478-
process.execute()
500+
PROCCESSES.execute(pid)
479501
}
480502

481503
#[derive(EnumIter, EnumString)]

oak_restricted_kernel/src/payload.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ use goblin::{
2525
use oak_restricted_kernel_interface::syscalls::{MmapFlags, MmapProtection};
2626
use self_cell::self_cell;
2727
use x86_64::{
28-
structures::paging::{PageSize, Size2MiB},
28+
structures::paging::{PageSize, PhysFrame, Size2MiB},
2929
VirtAddr,
3030
};
3131

32-
use crate::syscall::mmap::mmap;
32+
use crate::{syscall::mmap::mmap, PROCCESSES};
3333

3434
// Set up the userspace stack at the end of the lower half of the virtual
3535
// address space. Well... almost. It's one page lower than the very end, as
@@ -160,19 +160,21 @@ pub fn identify_pml4_frame(
160160
}
161161

162162
pub struct Process {
163-
pml4: x86_64::structures::paging::PageTable,
163+
pml4_frame: PhysFrame,
164164
entry: VirtAddr,
165165
}
166166

167167
impl Process {
168-
/// Creates a process from the application, without executing it.
168+
/// Creates a process from the application, without executing it. Returns
169+
/// the PID of the new process.
169170
///
170171
/// # Safety
171172
///
172173
/// The application must be built from a valid ELF file representing an Oak
173174
/// Restricted Application.
174-
pub unsafe fn from_application(application: &Application) -> Result<Self, anyhow::Error> {
175+
pub unsafe fn from_application(application: &Application) -> Result<usize, anyhow::Error> {
175176
let pml4 = crate::BASE_L4_PAGE_TABLE.get().context("base l4 table should be set")?.clone();
177+
let pml4_frame: PhysFrame = identify_pml4_frame(&pml4)?;
176178
// Load the process's page table, so the application can be loaded into its
177179
// memory. Hold onto the previous PT, so we can revert to it once the
178180
// application has been mapped into the process pt.
@@ -198,17 +200,15 @@ impl Process {
198200
// Safety: the new page table maintains the same mappings for kernel space.
199201
unsafe { crate::PAGE_TABLES.lock().replace(pml4_frame) };
200202
}
201-
202-
Ok(Self { pml4, entry })
203+
let pid = PROCCESSES.add(Self { pml4_frame, entry });
204+
Ok(pid)
203205
}
204206
/// Executes the process.
205207
pub fn execute(&self) -> ! {
206-
let pml4_frame = identify_pml4_frame(&self.pml4).expect("could not get pml4 frame");
207208
// Safety: the new page table maintains the same mappings for kernel space.
208-
unsafe { crate::PAGE_TABLES.lock().replace(pml4_frame) };
209+
unsafe { crate::PAGE_TABLES.lock().replace(self.pml4_frame) };
209210

210211
let entry = self.entry;
211-
log::info!("Running application");
212212
// Enter Ring 3 and jump to user code.
213213
// Safety: by now, if we're here, we've loaded a valid ELF file. It's up to the
214214
// user to guarantee that the file made sense.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// Copyright 2024 The Project Oak Authors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//
16+
17+
use core::{
18+
ffi::{c_size_t, c_ssize_t, c_void},
19+
slice,
20+
};
21+
22+
use oak_restricted_kernel_interface::Errno;
23+
24+
use crate::payload::Process;
25+
26+
pub fn syscall_unstable_create_proccess(buf: *mut c_void, count: c_size_t) -> c_ssize_t {
27+
// We should validate that the pointer and count are valid, as these come from
28+
// userspace and therefore are not to be trusted, but right now everything
29+
// is in kernel space so there is nothing to check.
30+
let elf_binary_buffer = unsafe { slice::from_raw_parts(buf as *mut u8, count) };
31+
match unstable_create_proccess(elf_binary_buffer) {
32+
Ok(pid) => pid.try_into().expect("pid so large, it could not be represented as isize"),
33+
Err(err) => err as isize,
34+
}
35+
}
36+
37+
fn unstable_create_proccess(buf: &[u8]) -> Result<usize, Errno> {
38+
// Copy the ELF file into kernel space.
39+
let copied_elf_binary: alloc::vec::Vec<u8> = buf.to_vec();
40+
41+
let application = crate::payload::Application::new(copied_elf_binary.into_boxed_slice())
42+
.inspect_err(|err| log::error!("failed to create application: {:?}", err))
43+
.map_err(|_| Errno::EINVAL)?;
44+
45+
Ok(
46+
// Safety: application is assumed to be a valid ELF file.
47+
unsafe { Process::from_application(&application).expect("failed to create process") },
48+
)
49+
}

oak_restricted_kernel/src/syscall/mod.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ pub mod mmap;
2222
mod process;
2323
mod stdio;
2424

25+
#[cfg(feature = "initrd")]
26+
mod create_process;
2527
#[cfg(feature = "initrd")]
2628
mod switch_process;
2729

@@ -44,7 +46,10 @@ use x86_64::{
4446
};
4547

4648
#[cfg(feature = "initrd")]
47-
use self::switch_process::syscall_unstable_switch_proccess;
49+
use self::{
50+
create_process::syscall_unstable_create_proccess,
51+
switch_process::syscall_unstable_switch_proccess,
52+
};
4853
use self::{
4954
fd::{syscall_fsync, syscall_read, syscall_write},
5055
mmap::syscall_mmap,
@@ -131,9 +136,13 @@ extern "sysv64" fn syscall_handler(
131136
}
132137
Some(Syscall::Fsync) => syscall_fsync(arg1 as i32),
133138
#[cfg(feature = "initrd")]
134-
Some(Syscall::UnstableSwitchProcess) => {
135-
syscall_unstable_switch_proccess(arg1 as *mut c_void, arg2)
139+
Some(Syscall::UnstableCreateProcess) => {
140+
syscall_unstable_create_proccess(arg1 as *mut c_void, arg2)
136141
}
142+
#[cfg(feature = "initrd")]
143+
Some(Syscall::UnstableSwitchProcess) => syscall_unstable_switch_proccess(arg1),
144+
#[cfg(not(feature = "initrd"))]
145+
Some(Syscall::UnstableCreateProcess) => Errno::ENOSYS as isize,
137146
#[cfg(not(feature = "initrd"))]
138147
Some(Syscall::UnstableSwitchProcess) => Errno::ENOSYS as isize,
139148
None => Errno::ENOSYS as isize,

oak_restricted_kernel/src/syscall/switch_process.rs

+4-26
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,9 @@
1414
// limitations under the License.
1515
//
1616

17-
use alloc::boxed::Box;
18-
use core::{
19-
ffi::{c_size_t, c_void},
20-
slice,
21-
};
17+
use core::ffi::c_size_t;
2218

23-
use crate::payload::Process;
24-
25-
pub fn syscall_unstable_switch_proccess(buf: *mut c_void, count: c_size_t) -> ! {
26-
// We should validate that the pointer and count are valid, as these come from
27-
// userspace and therefore are not to be trusted, but right now everything
28-
// is in kernel space so there is nothing to check.
29-
let elf_binary_buffer = unsafe { slice::from_raw_parts(buf as *mut u8, count) };
30-
31-
// Copy the ELF file into kernel space.
32-
let copied_elf_binary = elf_binary_buffer.to_vec();
33-
34-
let application = crate::payload::Application::new(copied_elf_binary.into_boxed_slice())
35-
.expect("failed to parse application");
36-
37-
// Ensure the new process is not dropped.
38-
let process = Box::leak(Box::new(
39-
// Safety: application is assumed to be a valid ELF file.
40-
unsafe { Process::from_application(&application).expect("failed to create process") },
41-
));
42-
43-
process.execute()
19+
pub fn syscall_unstable_switch_proccess(pid: c_size_t) -> ! {
20+
log::debug!("Switching to a different process (pid: {})", pid);
21+
crate::PROCCESSES.execute(pid)
4422
}

oak_restricted_kernel_interface/src/syscall.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,28 @@ pub fn exit(status: i32) -> ! {
121121
}
122122

123123
#[no_mangle]
124-
pub extern "C" fn sys_unstable_switch_proccess(buf: *const c_void, count: c_size_t) {
125-
unsafe { syscall!(Syscall::UnstableSwitchProcess, buf, count) };
124+
pub extern "C" fn sys_unstable_create_proccess(buf: *const c_void, count: c_size_t) -> c_ssize_t {
125+
unsafe { syscall!(Syscall::UnstableCreateProcess, buf, count) }
126126
}
127127

128-
pub fn unstable_switch_proccess(buf: &[u8]) -> ! {
129-
sys_unstable_switch_proccess(buf.as_ptr() as *const c_void, buf.len());
128+
pub fn unstable_create_proccess(buf: &[u8]) -> Result<usize, Errno> {
129+
let ret = sys_unstable_create_proccess(buf.as_ptr() as *const c_void, buf.len());
130+
if ret <= 0 {
131+
Err(Errno::from_repr(ret).unwrap_or_else(|| {
132+
panic!("unexpected error from unstable_create_proccess syscall: {}", ret)
133+
}))
134+
} else {
135+
Ok(ret.try_into().expect("pid could not be represented as isize"))
136+
}
137+
}
138+
139+
#[no_mangle]
140+
pub extern "C" fn sys_unstable_switch_proccess(pid: c_size_t) {
141+
unsafe { syscall!(Syscall::UnstableSwitchProcess, pid) };
142+
}
143+
144+
pub fn unstable_switch_proccess(pid: usize) -> ! {
145+
sys_unstable_switch_proccess(pid);
130146
unreachable!();
131147
}
132148

oak_restricted_kernel_interface/src/syscalls.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,22 @@ pub enum Syscall {
9696
/// a value of <errno::Errno> on failure; 0, otherwise.
9797
Fsync = 74,
9898

99-
/// Terminates the calling process and executes the supplied ELF binary
100-
/// instead.
99+
/// Create new process from ELF file, without starting it.
101100
///
102101
/// Arguments:
103102
/// - arg0 (*mut c_void): pointer to the a buffer holding an ELF file
104103
/// - arg1 (c_size_t): size of the buffer
105104
/// Returns:
105+
/// a value of <errno::Errno> on failure; Otherwise the PID of the new
106+
/// process.
107+
UnstableCreateProcess = UNSTABLE_SYSCALL_SPACE,
108+
109+
/// Switch the active execution execution to the process with the provided
110+
/// PID.
111+
///
112+
/// Arguments:
113+
/// - arg0 (c_size_t): PID of the process.
114+
/// Returns:
106115
/// a value of <errno::Errno> on failure; 0, otherwise.
107116
UnstableSwitchProcess = UNSTABLE_SYSCALL_SPACE + 1,
108117
}

oak_restricted_kernel_launcher/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ must be built.
1818

1919
```shell
2020
# Stage0, the restricted kernel, and an enclave app may be built like so:
21-
just stage0_bin oak_restricted_kernel_wrapper oak_orchestrator && \
21+
just stage0_bin oak_restricted_kernel_wrapper oak_orchestrator oak_echo_raw_enclave_app && \
2222

2323
# After building dependencies, an enclave app may be run like so:
2424
RUST_LOG=DEBUG \

0 commit comments

Comments
 (0)