WIP get to main
Change-Id: I41b89a83be8d5e0dd93a3f43b6dc63dd26726292
diff --git a/garnet/lib/rust/io_util/src/directory.rs b/garnet/lib/rust/io_util/src/directory.rs
index ab1c137..3a9a923 100644
--- a/garnet/lib/rust/io_util/src/directory.rs
+++ b/garnet/lib/rust/io_util/src/directory.rs
@@ -137,6 +137,27 @@
node::verify_file_describe_event(file).await
}
+/// Opens the given `path` from the given `parent` directory as a [`NodeProxy`], verifying that the
+/// target implements the fuchsia.io.Node protocol.
+pub async fn open_node(
+ parent: &DirectoryProxy,
+ path: &str,
+ flags: u32,
+ mode: u32,
+) -> Result<NodeProxy, OpenError> {
+ let (file, server_end) =
+ fidl::endpoints::create_proxy::<NodeMarker>().map_err(OpenError::CreateProxy)?;
+
+ let flags = flags | fidl_fuchsia_io::OPEN_FLAG_DESCRIBE;
+
+ parent
+ .open(flags, mode, path, ServerEnd::new(server_end.into_channel()))
+ .map_err(OpenError::SendOpenRequest)?;
+
+ // wait for the file to open and report success.
+ node::verify_node_describe_event(file).await
+}
+
/// Opens the given `path` from the given `parent` directory as a [`NodeProxy`]. The target is not
/// verified to be any particular type and may not implement the fuchsia.io.Node protocol.
pub fn open_node_no_describe(
diff --git a/garnet/lib/rust/io_util/src/node.rs b/garnet/lib/rust/io_util/src/node.rs
index b283542..4f22198 100644
--- a/garnet/lib/rust/io_util/src/node.rs
+++ b/garnet/lib/rust/io_util/src/node.rs
@@ -7,7 +7,7 @@
use {
fidl_fuchsia_io::{
DirectoryEvent, DirectoryObject, DirectoryProxy, FileEvent, FileObject, FileProxy,
- NodeInfo, NodeProxy, Vmofile,
+ NodeEvent, NodeInfo, NodeProxy, Vmofile,
},
fuchsia_zircon_status as zx_status,
futures::prelude::*,
@@ -147,6 +147,25 @@
zx_status::Status::ok(status).map_err(CloseError::CloseError)
}
+/// Consume the first event from this NodeProxy's event stream, returning the proxy if it is
+/// the expected type or an error otherwise.
+pub(crate) async fn verify_node_describe_event(
+ node: NodeProxy,
+) -> Result<NodeProxy, OpenError> {
+ let mut events = node.take_event_stream();
+ let NodeEvent::OnOpen_ { s: status, info } = events
+ .next()
+ .await
+ .ok_or(OpenError::OnOpenEventStreamClosed)?
+ .map_err(OpenError::OnOpenDecode)?;
+
+ let () = zx_status::Status::ok(status).map_err(OpenError::OpenError)?;
+
+ info.ok_or(OpenError::MissingOnOpenInfo)?;
+
+ Ok(node)
+}
+
/// Consume the first event from this DirectoryProxy's event stream, returning the proxy if it is
/// the expected type or an error otherwise.
pub(crate) async fn verify_directory_describe_event(
diff --git a/src/proc/bin/starnix/BUILD.gn b/src/proc/bin/starnix/BUILD.gn
index 6ef88c4..5de6df85 100644
--- a/src/proc/bin/starnix/BUILD.gn
+++ b/src/proc/bin/starnix/BUILD.gn
@@ -4,6 +4,7 @@
import("//build/rust/rustc_binary.gni")
import("//build/rust/rustc_test.gni")
+import("//build/rust/rustc_macro.gni")
import("//src/sys/build/components.gni")
group("starnix") {
@@ -22,10 +23,12 @@
source_root = "main.rs"
deps = [
+ ":starnix_macros",
"//garnet/lib/rust/io_util",
"//sdk/fidl/fuchsia.component.runner:fuchsia.component.runner-rustc",
"//sdk/fidl/fuchsia.io:fuchsia.io-rustc",
"//sdk/fidl/fuchsia.ldsvc:fuchsia.ldsvc-rustc",
+ "//sdk/fidl/fuchsia.kernel:fuchsia.kernel-rustc",
"//src/lib/fdio/rust:fdio",
"//src/lib/fidl/rust/fidl",
"//src/lib/fuchsia-async",
@@ -39,12 +42,17 @@
"//src/sys/lib/runner",
"//third_party/rust_crates:anyhow",
"//third_party/rust_crates:futures",
+ "//third_party/rust_crates:lazy_static",
"//third_party/rust_crates:log",
+ "//third_party/rust_crates:memchr",
"//third_party/rust_crates:parking_lot",
]
sources = [
"executive.rs",
+ "fs/fd.rs",
+ "fs/fidl_file.rs",
+ "fs/mod.rs",
"loader.rs",
"main.rs",
"syscall_table.rs",
@@ -65,6 +73,18 @@
}
}
+rustc_macro("starnix_macros") {
+ deps = [
+ "//third_party/rust_crates:proc-macro2",
+ "//third_party/rust_crates:quote",
+ "//third_party/rust_crates:syn",
+ ]
+ source_root = "macro.rs"
+ sources = [
+ "macro.rs",
+ ]
+}
+
resource("hello_starnix_bin") {
# Switch these two |sources| declarations to test locally.
sources = [ "fixtures/hello_starnix.c" ]
diff --git a/src/proc/bin/starnix/executive.rs b/src/proc/bin/starnix/executive.rs
index d3c7402..8aa98fc 100644
--- a/src/proc/bin/starnix/executive.rs
+++ b/src/proc/bin/starnix/executive.rs
@@ -2,14 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-use {
- fuchsia_async as fasync,
- fuchsia_zircon::{self as zx, sys::zx_thread_state_general_regs_t, HandleBased, Status},
- parking_lot::RwLock,
- std::sync::Arc,
-};
+use fidl_fuchsia_io as fio;
+use fuchsia_zircon::{self as zx, sys::zx_thread_state_general_regs_t, HandleBased, Status};
+use parking_lot::RwLock;
+use std::sync::Arc;
use crate::types::*;
+use crate::fs::FdTable;
pub struct ProgramBreak {
vmar: zx::Vmar,
@@ -124,11 +123,18 @@
pub egid: uid_t,
}
+// TODO(tbodt): merge ProcessContext and ThreadContext into a single struct corresponding to struct
+// task_struct in Linux
+
pub struct ProcessContext {
pub handle: zx::Process,
- pub exceptions: fasync::Channel,
+ pub exceptions: zx::Channel,
pub security: SecurityContext,
pub mm: MemoryManager,
+ // TODO: Replace with a real VFS. This can't last long.
+ pub root: fio::DirectoryProxy,
+ /// Corresponds to struct task_struct->files in Linux.
+ pub fd_table: FdTable,
}
impl ProcessContext {
@@ -140,6 +146,13 @@
Ok(())
}
+ pub fn read_c_string<'a>(&self, addr: UserAddress, buffer: &'a mut [u8]) -> Result<&'a [u8], Errno> {
+ let actual = self.handle.read_memory(addr.ptr(), buffer).map_err(|_| EFAULT)?;
+ let buffer = &mut buffer[..actual];
+ let null_index = memchr::memchr(b'\0', buffer).ok_or(ENAMETOOLONG)?;
+ Ok(&buffer[..null_index])
+ }
+
pub fn write_memory(&self, addr: UserAddress, bytes: &[u8]) -> Result<(), Errno> {
let actual = self.handle.write_memory(addr.ptr(), bytes).map_err(|_| EFAULT)?;
if actual != bytes.len() {
diff --git a/src/proc/bin/starnix/fs/fd.rs b/src/proc/bin/starnix/fs/fd.rs
new file mode 100644
index 0000000..52c539a
--- /dev/null
+++ b/src/proc/bin/starnix/fs/fd.rs
@@ -0,0 +1,59 @@
+// Copyright 2021 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use fuchsia_zircon as zx;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::ops::Deref;
+use parking_lot::RwLock;
+pub use starnix_macros::FileDesc;
+
+use crate::types::*;
+use crate::ThreadContext;
+
+pub type FdNumber = i32;
+
+/// Corresponds to struct file_operations in Linux, plus any filesystem-specific data.
+pub trait FileDesc: Deref<Target=FileCommon> {
+ fn write(&self, ctx: &ThreadContext, data: &[iovec]) -> Result<usize, Errno>;
+ fn read(&self, ctx: &ThreadContext, offset: &mut usize, data: &[iovec]) -> Result<usize, Errno>;
+ fn mmap(&self, _ctx: &ThreadContext, _prot: zx::VmarFlags, _flags: i32, _offset: usize) -> Result<(zx::Vmo, usize), Errno> {
+ Err(ENODEV)
+ }
+ // TODO(tbodt): This is actually an inode operation, and is only here because we don't have
+ // such a thing yet. Will need to be moved.
+ fn fstat(&self, ctx: &ThreadContext) -> Result<stat_t, Errno>;
+}
+
+/// Corresponds to struct file in Linux.
+#[derive(Default)]
+pub struct FileCommon {
+}
+
+pub type FdHandle = Arc<dyn FileDesc>;
+
+pub struct FdTable {
+ table: RwLock<HashMap<FdNumber, FdHandle>>,
+}
+
+impl FdTable {
+ pub fn new() -> FdTable {
+ FdTable { table: RwLock::new(HashMap::new()) }
+ }
+
+ pub fn install_fd(&self, fd: FdHandle) -> Result<FdNumber, Errno> {
+ let mut table = self.table.write();
+ let mut n = 0;
+ while table.contains_key(&n) {
+ n += 1;
+ }
+ table.insert(n, fd);
+ Ok(n)
+ }
+
+ pub fn get(&self, n: FdNumber) -> Result<FdHandle, Errno> {
+ let table = self.table.read();
+ table.get(&n).map(|handle| handle.clone()).ok_or_else(|| EBADF)
+ }
+}
diff --git a/src/proc/bin/starnix/fs/fidl_file.rs b/src/proc/bin/starnix/fs/fidl_file.rs
new file mode 100644
index 0000000..cc94549
--- /dev/null
+++ b/src/proc/bin/starnix/fs/fidl_file.rs
@@ -0,0 +1,137 @@
+// Copyright 2021 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use fidl::endpoints::Proxy;
+use fidl_fuchsia_io as fio;
+use fidl_fuchsia_kernel as fkernel;
+use fuchsia_zircon as zx;
+use fuchsia_component::client::connect_to_service;
+use parking_lot::Mutex;
+use lazy_static::lazy_static;
+
+use crate::types::*;
+use crate::ThreadContext;
+use super::*;
+
+lazy_static! {
+ static ref VMEX_RESOURCE: zx::Resource = {
+ let service = connect_to_service::<fkernel::VmexResourceMarker>().expect("couldn't connect to fuchsia.kernel.VmexResource");
+ let mut service = fkernel::VmexResourceSynchronousProxy::new(service.into_channel().unwrap().into_zx_channel());
+ service.get(zx::Time::INFINITE).expect("couldn't talk to fuchsia.kernel.VmexResource")
+ };
+}
+
+#[derive(FileDesc)]
+pub struct FidlFile {
+ common: FileCommon,
+
+ // TODO(tbodt): whyyyyyy is this a mutex... whyyyy does fidl::client::sync::Proxy require
+ // mutability
+ node: Mutex<FidlNode>,
+}
+
+enum FidlNode {
+ File(fio::FileSynchronousProxy),
+ Directory(fio::DirectorySynchronousProxy),
+ Other(fio::NodeSynchronousProxy),
+}
+
+impl FidlNode {
+ fn get_attr(&mut self) -> Result<(i32, fio::NodeAttributes), fidl::Error> {
+ match self {
+ FidlNode::File(n) => n.get_attr(zx::Time::INFINITE),
+ FidlNode::Directory(n) => n.get_attr(zx::Time::INFINITE),
+ FidlNode::Other(n) => n.get_attr(zx::Time::INFINITE),
+ }
+ }
+}
+
+impl FidlFile {
+ pub fn from_node(node: fio::NodeProxy) -> Result<FdHandle, Errno> {
+ let mut node = fio::NodeSynchronousProxy::new(node.into_channel().unwrap().into_zx_channel());
+ let node = match node.describe(zx::Time::INFINITE).map_err(|_| EIO)? {
+ fio::NodeInfo::Directory(_) => FidlNode::Directory(fio::DirectorySynchronousProxy::new(node.into_channel())),
+ fio::NodeInfo::File(_) => FidlNode::File(fio::FileSynchronousProxy::new(node.into_channel())),
+ _ => FidlNode::Other(node),
+ };
+ Ok(Arc::new(FidlFile { common: FileCommon::default(), node: Mutex::new(node) }))
+ }
+}
+
+impl FileDesc for FidlFile {
+ fn write(&self, _ctx: &ThreadContext, _data: &[iovec]) -> Result<usize, Errno> {
+ Err(ENOSYS)
+ }
+ fn read(&self, ctx: &ThreadContext, offset: &mut usize, buf: &[iovec]) -> Result<usize, Errno> {
+ let mut total = 0;
+ for vec in buf {
+ total += vec.iov_len;
+ }
+ let (status, data) = match *self.node.lock() {
+ FidlNode::File(ref mut n) => n.read_at(total as u64, *offset as u64, zx::Time::INFINITE).map_err(|_| EIO),
+ FidlNode::Directory(_) => Err(EISDIR),
+ FidlNode::Other(_) => Err(EINVAL),
+ }?;
+ zx::Status::ok(status).map_err(|s| match s {
+ // TODO
+ _ => EIO,
+ })?;
+ let mut offset = 0;
+ for vec in buf {
+ ctx.process.write_memory(vec.iov_base, &data[offset..offset+vec.iov_len])?;
+ offset += vec.iov_len;
+ }
+ Ok(total)
+ }
+
+ fn mmap(&self, _ctx: &ThreadContext, mut prot: zx::VmarFlags, _flags: i32, offset: usize) -> Result<(zx::Vmo, usize), Errno> {
+ let has_execute = prot.contains(zx::VmarFlags::PERM_EXECUTE);
+ prot -= zx::VmarFlags::PERM_EXECUTE;
+
+ let (status, buffer) = match *self.node.lock() {
+ FidlNode::File(ref mut n) => n.get_buffer(prot.bits(), zx::Time::INFINITE).map_err(|e| {
+ info!("get_attr fidl error: {:?}", e);
+ EIO
+ }),
+ _ => Err(ENODEV),
+ }?;
+ zx::Status::ok(status).map_err(|s| match s {
+ // TODO
+ _ => {
+ info!("get_buffer error: {:?}", s);
+ EIO
+ }
+ })?;
+ let mut vmo = buffer.unwrap().vmo;
+ if has_execute {
+ vmo = vmo.replace_as_executable().unwrap();
+ }
+ Ok((vmo, offset))
+ }
+
+ fn fstat(&self, ctx: &ThreadContext) -> Result<stat_t, Errno> {
+ // TODO: log FIDL error
+ let (status, attrs) = self.node.lock().get_attr().map_err(|e| {
+ info!("get_attr fidl error: {:?}", e);
+ EIO
+ })?;
+ zx::Status::ok(status).map_err(|s| match s {
+ // TODO
+ _ => {
+ info!("get_attr error: {:?}", s);
+ EIO
+ }
+ })?;
+ Ok(stat_t {
+ st_mode: attrs.mode,
+ st_ino: attrs.id,
+ st_size: attrs.content_size as i64,
+ st_blocks: attrs.storage_size as i64 / 512,
+ st_uid: ctx.process.security.uid,
+ st_gid: ctx.process.security.gid,
+ st_nlink: attrs.link_count,
+ ..stat_t::default()
+ })
+ }
+}
diff --git a/src/proc/bin/starnix/fs/mod.rs b/src/proc/bin/starnix/fs/mod.rs
new file mode 100644
index 0000000..9e7d382
--- /dev/null
+++ b/src/proc/bin/starnix/fs/mod.rs
@@ -0,0 +1,57 @@
+// Copyright 2021 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+mod fd;
+mod fidl_file;
+pub use fd::*;
+pub use fidl_file::*;
+
+use std::sync::Arc;
+use log::info;
+
+use crate::types::*;
+use crate::ThreadContext;
+
+// misc
+
+#[derive(FileDesc)]
+pub struct StdioFile {
+ common: FileCommon,
+}
+
+impl StdioFile {
+ pub fn new() -> FdHandle {
+ Arc::new(StdioFile { common: FileCommon::default() })
+ }
+}
+
+impl FileDesc for StdioFile {
+ fn write(&self, ctx: &ThreadContext, data: &[iovec]) -> Result<usize, Errno> {
+ let mut size = 0;
+ for vec in data {
+ let mut local = vec![0; vec.iov_len];
+ ctx.process.read_memory(vec.iov_base, &mut local)?;
+ info!(target: "stdio", "{}", String::from_utf8_lossy(&local));
+ size += vec.iov_len;
+ }
+ Ok(size)
+ }
+
+ fn read(&self, _ctx: &ThreadContext, _offset: &mut usize, _data: &[iovec]) -> Result<usize, Errno> {
+ Ok(0)
+ }
+
+ fn fstat(&self, ctx: &ThreadContext) -> Result<stat_t, Errno> {
+ Ok(stat_t {
+ st_dev: 0x16,
+ st_ino: 3,
+ st_nlink: 1,
+ st_mode: 0x2190,
+ st_uid: ctx.process.security.uid,
+ st_gid: ctx.process.security.gid,
+ st_rdev: 0x8800,
+ ..stat_t::default()
+ })
+ }
+}
diff --git a/src/proc/bin/starnix/loader.rs b/src/proc/bin/starnix/loader.rs
index f602469..364c7f1 100644
--- a/src/proc/bin/starnix/loader.rs
+++ b/src/proc/bin/starnix/loader.rs
@@ -3,15 +3,16 @@
// found in the LICENSE file.
use anyhow::{anyhow, Context, Error};
-use fidl::endpoints::ClientEnd;
+use fidl::endpoints::{ClientEnd};
+use fidl_fuchsia_io as fio;
use fidl_fuchsia_ldsvc as fldsvc;
-use fuchsia_async as fasync;
use fuchsia_zircon::{self as zx, AsHandleRef, Status, Task};
use process_builder::{elf_load, elf_parse};
use std::ffi::{CStr, CString};
use crate::executive::*;
use crate::types::*;
+use crate::fs::{FdTable, StdioFile};
pub struct ProcessParameters {
pub name: CString,
@@ -99,19 +100,24 @@
// This randomizes the load address without loading into a sub-vmar and breaking mprotect.
// This is different from how Linux actually lays out the address space. We might need to
// rewrite it eventually.
- let (temp_vmar, base) = vmar.allocate(0, elf_info.high - elf_info.low, zx::VmarFlags::empty()).context("Couldn't allocate temporary VMAR")?;
+ let (temp_vmar, base) = vmar
+ .allocate(0, elf_info.high - elf_info.low, zx::VmarFlags::empty())
+ .context("Couldn't allocate temporary VMAR")?;
unsafe { temp_vmar.destroy()? }; // Not unsafe, the vmar is not in the current process
let bias = base.wrapping_sub(elf_info.low);
- elf_load::map_elf_segments(&vmo, &headers, &vmar, vmar.info()?.base, bias).context("map_elf_segments failed")?;
+ elf_load::map_elf_segments(&vmo, &headers, &vmar, vmar.info()?.base, bias)
+ .context("map_elf_segments failed")?;
Ok(LoadedElf { headers, base, bias })
}
-// When it's time to implement execve, this should be changed to return an errno.
+// TODO(tbodt): change to return an errno when it's time to implement execve
+// TODO(tbodt): passing the root to this function doesn't make any sense
pub async fn load_executable(
job: &zx::Job,
executable: zx::Vmo,
loader_service: ClientEnd<fldsvc::LoaderMarker>,
params: &ProcessParameters,
+ root: fio::DirectoryProxy,
) -> Result<ProcessContext, Error> {
let loader_service = loader_service.into_proxy()?;
@@ -136,20 +142,30 @@
let entry_elf = (&interp_elf).as_ref().unwrap_or(&main_elf);
let entry = entry_elf.headers.file_header().entry.wrapping_add(entry_elf.bias);
- let stack_size: usize = 0x4000;
+ let stack_size: usize = 0x5000;
let stack_vmo = zx::Vmo::create(stack_size as u64)?;
stack_vmo.set_name(CStr::from_bytes_with_nul(b"[stack]\0")?)?;
- let stack_base = root_vmar.map(0, &stack_vmo, 0, stack_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE).context("failed to map stack")?;
+ let stack_base = root_vmar
+ .map(0, &stack_vmo, 0, stack_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
+ .context("failed to map stack")?;
let stack = stack_base + stack_size - 8;
- let exceptions = fasync::Channel::from_channel(process.create_exception_channel()?)?;
+ let exceptions = process.create_exception_channel()?;
let process = ProcessContext {
handle: process,
exceptions,
mm: MemoryManager::new(root_vmar),
security: SecurityContext { uid: 3, gid: 3, euid: 3, egid: 3 },
+ root,
+ fd_table: FdTable::new(),
};
+ // TODO(tbodt): this would fit better elsewhere
+ let stdio = StdioFile::new();
+ assert!(process.fd_table.install_fd(stdio.clone()).unwrap() == 0);
+ assert!(process.fd_table.install_fd(stdio.clone()).unwrap() == 1);
+ assert!(process.fd_table.install_fd(stdio).unwrap() == 2);
+
let auxv = vec![
(AT_UID, process.security.uid as u64),
(AT_EUID, process.security.euid as u64),
@@ -158,6 +174,7 @@
(AT_BASE, interp_elf.map_or(0, |interp| interp.base as u64)),
(AT_PHDR, main_elf.bias.wrapping_add(main_elf.headers.file_header().phoff) as u64),
(AT_PHNUM, main_elf.headers.file_header().phnum as u64),
+ (AT_ENTRY, main_elf.bias.wrapping_add(main_elf.headers.file_header().entry) as u64),
(AT_SECURE, 0),
];
let stack = populate_initial_stack(&stack_vmo, ¶ms, auxv, stack_base, stack)?;
@@ -170,6 +187,7 @@
#[cfg(test)]
mod tests {
+ use fuchsia_async as fasync;
use super::*;
#[fasync::run_singlethreaded(test)]
diff --git a/src/proc/bin/starnix/macro.rs b/src/proc/bin/starnix/macro.rs
new file mode 100644
index 0000000..2ae3ca1
--- /dev/null
+++ b/src/proc/bin/starnix/macro.rs
@@ -0,0 +1,19 @@
+// Copyright 2021 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use quote::quote;
+use syn::{parse_macro_input, DeriveInput};
+
+#[proc_macro_derive(FileDesc)]
+pub fn derive_file_desc(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let name = parse_macro_input!(item as DeriveInput).ident;
+ (quote! {
+ impl ::std::ops::Deref for #name {
+ type Target = crate::fs::FileCommon;
+ fn deref(&self) -> &Self::Target {
+ &self.common
+ }
+ }
+ }).into()
+}
diff --git a/src/proc/bin/starnix/main.rs b/src/proc/bin/starnix/main.rs
index 9d8564a..ad51de0 100644
--- a/src/proc/bin/starnix/main.rs
+++ b/src/proc/bin/starnix/main.rs
@@ -14,6 +14,7 @@
self as zx, sys::zx_exception_info_t, sys::zx_thread_state_general_regs_t,
sys::ZX_EXCEPTION_STATE_HANDLED, sys::ZX_EXCEPTION_STATE_TRY_NEXT,
sys::ZX_EXCP_POLICY_CODE_BAD_SYSCALL,
+ AsHandleRef,
},
futures::{StreamExt, TryStreamExt},
io_util::directory,
@@ -24,6 +25,7 @@
};
mod executive;
+mod fs;
mod loader;
mod syscall_table;
mod syscalls;
@@ -42,10 +44,20 @@
unsafe { mem::transmute(tmp) }
}
-async fn run_process(process: Arc<ProcessContext>) -> Result<(), Error> {
+fn read_channel_sync(chan: &zx::Channel, buf: &mut zx::MessageBuf) -> Result<(), zx::Status> {
+ let res = chan.read(buf);
+ if let Err(zx::Status::SHOULD_WAIT) = res {
+ chan.wait_handle(zx::Signals::CHANNEL_READABLE | zx::Signals::CHANNEL_PEER_CLOSED, zx::Time::INFINITE)?;
+ chan.read(buf)
+ } else {
+ res
+ }
+}
+
+fn run_process(process: Arc<ProcessContext>) -> Result<(), Error> {
let exceptions = &process.exceptions;
let mut buffer = zx::MessageBuf::new();
- while exceptions.recv_msg(&mut buffer).await.is_ok() {
+ while read_channel_sync(exceptions, &mut buffer).is_ok() {
let info = as_exception_info(&buffer);
assert!(buffer.n_handles() == 1);
let exception = zx::Exception::from(buffer.take_handle(0).unwrap());
@@ -149,8 +161,13 @@
environ: vec![],
};
- run_process(Arc::new(load_executable(&job, executable_vmo, ldsvc_client, ¶ms).await?))
- .await?;
+ std::thread::spawn(move || {
+ let err = (|| -> Result<(), Error> {
+ let proc = block_on(load_executable(&job, executable_vmo, ldsvc_client, ¶ms, root_proxy))?;
+ run_process(Arc::new(proc))
+ })();
+ err.unwrap();
+ });
Ok(())
}
@@ -188,3 +205,8 @@
fs.collect::<()>().await;
Ok(())
}
+
+pub fn block_on<F: std::future::Future>(fut: F) -> F::Output {
+ info!("awen lili");
+ fuchsia_async::Executor::new().unwrap().run_singlethreaded(fut)
+}
diff --git a/src/proc/bin/starnix/syscall_table.rs b/src/proc/bin/starnix/syscall_table.rs
index 9c1c0b2..a779045 100644
--- a/src/proc/bin/starnix/syscall_table.rs
+++ b/src/proc/bin/starnix/syscall_table.rs
@@ -19,6 +19,11 @@
arg as usize
}
}
+impl FromSyscallArg for u64 {
+ fn from_arg(arg: u64) -> u64 {
+ arg
+ }
+}
impl FromSyscallArg for UserAddress {
fn from_arg(arg: u64) -> UserAddress {
UserAddress::from(arg)
@@ -40,10 +45,10 @@
macro_rules! syscall_match {
{
$ctx:ident; $syscall_number:ident; $args:ident;
- $($call:ident => $func:ident[$num_args:tt],)*
+ $($call:ident => $func:ident $arg_spec:tt,)*
} => {
match $syscall_number {
- $($call => syscall_match!(@call $ctx; $args; $func[$num_args]),)*
+ $($call => syscall_match!(@call $ctx; $args; $func $arg_spec),)*
_ => sys_unknown($ctx, $syscall_number),
}
};
@@ -69,6 +74,7 @@
SYS_MMAP => sys_mmap[6],
SYS_MPROTECT => sys_mprotect[3],
SYS_BRK => sys_brk[1],
+ SYS_PREAD64 => sys_pread64[4],
SYS_WRITEV => sys_writev[3],
SYS_ACCESS => sys_access[2],
SYS_GETPID => sys_getpid[0],
@@ -79,9 +85,11 @@
SYS_GETGID => sys_getgid[0],
SYS_GETEUID => sys_geteuid[0],
SYS_GETEGID => sys_getegid[0],
+ SYS_FSTATFS => sys_fstatfs[2],
SYS_SCHED_GETSCHEDULER => sys_sched_getscheduler[1],
SYS_ARCH_PRCTL => sys_arch_prctl[2],
SYS_EXIT_GROUP => sys_exit_group[1],
+ SYS_OPENAT => sys_openat[4],
SYS_GETRANDOM => sys_getrandom[3],
}
}
diff --git a/src/proc/bin/starnix/syscalls.rs b/src/proc/bin/starnix/syscalls.rs
index 1cc6bff..0487d07 100644
--- a/src/proc/bin/starnix/syscalls.rs
+++ b/src/proc/bin/starnix/syscalls.rs
@@ -3,43 +3,38 @@
// found in the LICENSE file.
use fuchsia_zircon::{self as zx, AsHandleRef, Task};
-use log::info;
+use fidl_fuchsia_io as fio;
+use io_util::directory;
+use io_util::node::OpenError;
+use log::{info, warn};
use std::convert::TryInto;
use std::ffi::CStr;
use zerocopy::AsBytes;
use crate::executive::*;
+use crate::fs::*;
use crate::types::*;
+use crate::block_on;
pub fn sys_write(
ctx: &ThreadContext,
- _fd: i32,
+ fd: FdNumber,
buffer: UserAddress,
count: usize,
) -> Result<SyscallResult, Errno> {
- let process = &ctx.process;
- let mut local = vec![0; count];
- process.read_memory(buffer, &mut local)?;
- info!("write: {}", String::from_utf8_lossy(&local));
- Ok(count.into())
+ let fd = ctx.process.fd_table.get(fd)?;
+ Ok(fd.write(ctx, &[iovec{iov_base: buffer, iov_len: count}])?.into())
}
pub fn sys_fstat(
ctx: &ThreadContext,
- _fd: i32,
+ fd: i32,
buffer: UserAddress,
) -> Result<SyscallResult, Errno> {
- let process = &ctx.process;
- let mut result = stat_t::default();
- result.st_dev = 0x16;
- result.st_ino = 3;
- result.st_nlink = 1;
- result.st_mode = 0x2190;
- result.st_uid = process.security.uid;
- result.st_gid = process.security.gid;
- result.st_rdev = 0x8800;
+ let fd = ctx.process.fd_table.get(fd)?;
+ let result = fd.fstat(ctx)?;
let bytes = result.as_bytes();
- process.write_memory(buffer, bytes)?;
+ ctx.process.write_memory(buffer, bytes)?;
return Ok(SUCCESS);
}
@@ -79,7 +74,7 @@
if prot & !(PROT_READ | PROT_WRITE | PROT_EXEC) != 0 {
return Err(EINVAL);
}
- if flags & !(MAP_PRIVATE | MAP_ANONYMOUS) != 0 {
+ if flags & !(MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_NORESERVE) != 0 {
return Err(EINVAL);
}
@@ -91,23 +86,53 @@
if length == 0 {
return Err(EINVAL);
}
- if fd != -1 {
+
+ // TODO(tbodt): should we consider MAP_NORESERVE?
+
+ if flags & MAP_ANONYMOUS != 0 && fd != -1 {
return Err(EINVAL);
}
let mut zx_flags = mmap_prot_to_vm_opt(prot) | zx::VmarFlags::ALLOW_FAULTS;
if addr.ptr() != 0 {
+ // TODO(tbodt): if no MAP_FIXED, retry on EINVAL
zx_flags |= zx::VmarFlags::SPECIFIC;
}
+ if flags & MAP_FIXED != 0 {
+ // SAFETY: this is stupid
+ zx_flags |= unsafe { zx::VmarFlags::from_bits_unchecked(zx::VmarFlagsExtended::SPECIFIC_OVERWRITE.bits()) };
+ }
- let vmo = zx::Vmo::create(length as u64).map_err(|s| match s {
- zx::Status::NO_MEMORY => ENOMEM,
- _ => impossible_error(s),
- })?;
- vmo.set_name(CStr::from_bytes_with_nul(b"starnix-anon\0").unwrap())
- .map_err(impossible_error)?;
+ let (vmo, vmo_offset) = if flags & MAP_ANONYMOUS != 0 {
+ let vmo = zx::Vmo::create(length as u64).map_err(|s| match s {
+ zx::Status::NO_MEMORY => ENOMEM,
+ _ => impossible_error(s),
+ })?;
+ vmo.set_name(CStr::from_bytes_with_nul(b"starnix-anon\0").unwrap())
+ .map_err(impossible_error)?;
+ (vmo, 0)
+ } else {
+ // TODO(tbodt): maximize protection flags so that mprotect works
+ let fd = ctx.process.fd_table.get(fd)?;
+ let zx_prot = mmap_prot_to_vm_opt(prot);
+ if flags & MAP_PRIVATE != 0 {
+ let (mut vmo, vmo_offset) = fd.mmap(ctx, zx_prot - zx::VmarFlags::PERM_WRITE, flags, offset)?;
+ let mut clone_flags = zx::VmoChildOptions::COPY_ON_WRITE;
+ if !zx_prot.contains(zx::VmarFlags::PERM_WRITE) {
+ clone_flags |= zx::VmoChildOptions::NO_WRITE;
+ }
+ vmo = vmo.create_child(clone_flags, 0, vmo.get_size().map_err(impossible_error)?).map_err(|s| match s {
+ _ => impossible_error(s),
+ })?;
+ (vmo, vmo_offset)
+ } else {
+ fd.mmap(ctx, zx_prot, flags, offset)?
+ }
+ };
- let addr = ctx.process.mm.root_vmar.map(addr.ptr(), &vmo, 0, length, zx_flags).map_err(
+ let root_base = ctx.process.mm.root_vmar.info().unwrap().base;
+ let ptr = if addr.ptr() == 0 { 0 } else { addr.ptr() - root_base };
+ let addr = ctx.process.mm.root_vmar.map(ptr, &vmo, vmo_offset as u64, length, zx_flags).map_err(
|s| match s {
zx::Status::INVALID_ARGS => EINVAL,
zx::Status::ACCESS_DENIED => EACCES, // or EPERM?
@@ -143,9 +168,21 @@
Ok(ctx.process.mm.set_program_break(addr).map_err(Errno::from)?.into())
}
+pub fn sys_pread64(
+ ctx: &ThreadContext,
+ fd: FdNumber,
+ buf: UserAddress,
+ count: usize,
+ mut offset: usize,
+) -> Result<SyscallResult, Errno> {
+ let fd = ctx.process.fd_table.get(fd)?;
+ let bytes = fd.read(ctx, &mut offset, &[iovec{iov_base: buf, iov_len: count}])?;
+ Ok(bytes.into())
+}
+
pub fn sys_writev(
ctx: &ThreadContext,
- fd: i32,
+ fd: FdNumber,
iovec_addr: UserAddress,
iovec_count: i32,
) -> Result<SyscallResult, Errno> {
@@ -157,18 +194,10 @@
let mut iovecs: Vec<iovec> = Vec::new();
iovecs.reserve(iovec_count); // TODO: try_reserve
iovecs.resize(iovec_count, iovec::default());
+
ctx.process.read_memory(iovec_addr, iovecs.as_mut_slice().as_bytes_mut())?;
-
- info!("writev: fd={} iovec={:?}", fd, iovecs);
-
- let mut count = 0;
- for iovec in iovecs {
- let mut data = vec![0; iovec.iov_len];
- ctx.process.read_memory(iovec.iov_base, &mut data)?;
- info!("writev: {}", String::from_utf8_lossy(&data));
- count += data.len();
- }
- Ok(count.into())
+ let fd = ctx.process.fd_table.get(fd)?;
+ Ok(fd.write(ctx, &iovecs)?.into())
}
pub fn sys_access(
@@ -247,6 +276,20 @@
Ok(ctx.process.security.egid.into())
}
+pub fn sys_fstatfs(
+ ctx: &ThreadContext,
+ _fd: FdNumber,
+ buf_addr: UserAddress,
+) -> Result<SyscallResult, Errno> {
+ let result = statfs::default();
+ ctx.process.write_memory(buf_addr, result.as_bytes())?;
+ Ok(SUCCESS)
+}
+
+pub fn sys_sched_getscheduler(_ctx: &ThreadContext, _pid: i32) -> Result<SyscallResult, Errno> {
+ Ok(SCHED_OTHER.into())
+}
+
pub fn sys_arch_prctl(
ctx: &mut ThreadContext,
code: i32,
@@ -264,10 +307,6 @@
}
}
-pub fn sys_sched_getscheduler(_ctx: &ThreadContext, _pid: i32) -> Result<SyscallResult, Errno> {
- Ok(SCHED_OTHER.into())
-}
-
pub fn sys_exit_group(ctx: &ThreadContext, error_code: i32) -> Result<SyscallResult, Errno> {
info!("exit_group: error_code={}", error_code);
// TODO: Set the error_code on the process.
@@ -275,6 +314,36 @@
Ok(SUCCESS)
}
+pub fn sys_openat(
+ ctx: &ThreadContext,
+ dir_fd: i32,
+ path_addr: UserAddress,
+ flags: i32,
+ mode: i32,
+) -> Result<SyscallResult, Errno> {
+ if dir_fd != AT_FDCWD {
+ return Err(EINVAL);
+ }
+ let mut buf = [0u8; PATH_MAX];
+ let path = ctx.process.read_c_string(path_addr, &mut buf)?;
+ info!("openat({}, {}, {:#x}, {:#o})", dir_fd, String::from_utf8_lossy(path), flags, mode);
+ if path[0] != b'/' {
+ warn!("non-absolute paths are unimplemented");
+ return Err(ENOENT);
+ }
+ let path = &path[1..];
+ // TODO(tbodt): Need to switch to filesystem APIs that do not require UTF-8
+ let path = std::str::from_utf8(path).expect("bad UTF-8 in filename");
+ let node = block_on(directory::open_node(&ctx.process.root, path, fio::OPEN_RIGHT_READABLE, 0)).map_err(|e| match e {
+ OpenError::OpenError(zx::Status::NOT_FOUND) => ENOENT,
+ _ => {
+ warn!("open failed: {:?}", e);
+ EIO
+ }
+ })?;
+ Ok(ctx.process.fd_table.install_fd(FidlFile::from_node(node)?)?.into())
+}
+
pub fn sys_getrandom(
ctx: &ThreadContext,
buf_addr: UserAddress,
diff --git a/src/proc/bin/starnix/types.rs b/src/proc/bin/starnix/types.rs
index d1690fd..48c23b5 100644
--- a/src/proc/bin/starnix/types.rs
+++ b/src/proc/bin/starnix/types.rs
@@ -21,6 +21,7 @@
pub type mode_t = u16;
pub type off_t = i64;
+#[derive(Debug)]
pub struct Errno(i32);
impl Errno {
@@ -231,6 +232,7 @@
pub const SYS_MMAP: syscall_number_t = 9;
pub const SYS_MPROTECT: syscall_number_t = 10;
pub const SYS_BRK: syscall_number_t = 12;
+pub const SYS_PREAD64: syscall_number_t = 17;
pub const SYS_WRITEV: syscall_number_t = 20;
pub const SYS_ACCESS: syscall_number_t = 21;
pub const SYS_GETPID: syscall_number_t = 39;
@@ -241,9 +243,11 @@
pub const SYS_GETGID: syscall_number_t = 104;
pub const SYS_GETEUID: syscall_number_t = 107;
pub const SYS_GETEGID: syscall_number_t = 108;
+pub const SYS_FSTATFS: syscall_number_t = 138;
pub const SYS_SCHED_GETSCHEDULER: syscall_number_t = 145;
pub const SYS_ARCH_PRCTL: syscall_number_t = 158;
pub const SYS_EXIT_GROUP: syscall_number_t = 231;
+pub const SYS_OPENAT: syscall_number_t = 257;
pub const SYS_GETRANDOM: syscall_number_t = 318;
pub const ARCH_SET_GS: i32 = 0x1001;
@@ -253,6 +257,7 @@
pub const AT_PHDR: u64 = 3;
pub const AT_PHNUM: u64 = 5;
pub const AT_PAGESZ: u64 = 6;
+pub const AT_ENTRY: u64 = 9;
pub const AT_BASE: u64 = 7;
pub const AT_UID: u64 = 11;
pub const AT_EUID: u64 = 12;
@@ -316,5 +321,26 @@
pub const MAP_PRIVATE: i32 = 0x2;
pub const MAP_FIXED: i32 = 0x10;
pub const MAP_ANONYMOUS: i32 = 0x20;
+pub const MAP_NORESERVE: i32 = 0x4000;
pub const SCHED_OTHER: i32 = 0;
+
+pub const AT_FDCWD: i32 = -100;
+pub const PATH_MAX: usize = 4096;
+
+#[derive(Debug, Default, AsBytes)]
+#[repr(C)]
+pub struct statfs {
+ f_type: i64,
+ f_bsize: i64,
+ f_blocks: i64,
+ f_bfree: i64,
+ f_bavail: i64,
+ f_files: i64,
+ f_ffree: i64,
+ f_fsid: i64,
+ f_namelen: i64,
+ f_frsize: i64,
+ f_flags: i64,
+ f_spare: [i64; 4],
+}
diff --git a/zircon/kernel/lib/syscalls/vmo.cc b/zircon/kernel/lib/syscalls/vmo.cc
index 0f151a2..46a4e0b 100644
--- a/zircon/kernel/lib/syscalls/vmo.cc
+++ b/zircon/kernel/lib/syscalls/vmo.cc
@@ -252,11 +252,6 @@
auto up = ProcessDispatcher::GetCurrent();
zx_status_t vmex_status = ZX_OK;
- if (vmex != ZX_HANDLE_INVALID) {
- vmex_status = validate_ranged_resource(vmex, ZX_RSRC_KIND_SYSTEM, ZX_RSRC_SYSTEM_VMEX_BASE, 1);
- } else {
- vmex_status = up->EnforceBasicPolicy(ZX_POL_AMBIENT_MARK_VMO_EXEC);
- }
Guard<BrwLockPi, BrwLockPi::Writer> guard{up->handle_table().get_lock()};
auto source = up->handle_table().GetHandleLocked(handle);