mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
Browser memory64: guest addresses to the global kernel are 64-bit (PAddr); the high arena
- gex_kernel::PAddr (and gex_kmem::PAddr): the address type of the process memory as it crosses between a process's shim and the global kernel module (the remote pipe/unix/pty/tty/vfs ops, the shim's k_* imports and rf_*/rp_* wrappers, the kernel's k_* exports). u64 in the wasm64 shim and in the kernel built with process64; u32 everywhere else. The 32-bit kernel's code is byte-identical (names aside, wasm-tools strip); the 32-bit shim's imports and exports keep their types. - The wasm64 shim: its guest slice spans LIMIT64, and backing a high-arena mapping grows the memory without moving the low arena's cursor (which the shim's heap shares, and whose addresses cross to JS as u32). - build.sh checks the wasm64 shim against gex_kernel.x86.m64.wasm. - tests/x86-programs/mem64-io.c: a file, a pipe and a socketpair through buffers above 4 GiB. Under node on the wasm64 shim it and mem64-reserve match native; tests/nt browser 49/49 and gex-kernel's tests pass. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01PeRutwFgZ66XpxjasKrPn1
This commit is contained in:
1 parent
be50f1cb9e
commit
06e6b95b21
15 files changed
+333
-215
No files matched your search
@@ -20,3 +20,6 @@ gex-gl.workspace = true
|
||||
x86 = ["gex-abi/x86"]
|
||||
# Serve a 32-bit big-endian PowerPC guest (docs/ppc.md).
|
||||
ppc = ["gex-abi/ppc"]
|
||||
# The global kernel module serving processes with a 64-bit memory (the
|
||||
# wasm64 browser shim, docs/mem64.md step 6): `PAddr` is u64.
|
||||
process64 = []
|
||||
@@ -11,6 +11,16 @@
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
/// An address in the calling process's memory as it crosses between a
|
||||
/// process's shim and the global kernel module (the remote ops below and the
|
||||
/// browser's `k_*` imports). 64-bit where that memory is a memory64: the
|
||||
/// wasm64 browser shim and the kernel module built to serve it
|
||||
/// (docs/mem64.md step 6). Everywhere else 32-bit, as it always was.
|
||||
#[cfg(any(target_arch = "wasm64", feature = "process64"))]
|
||||
pub type PAddr = u64;
|
||||
#[cfg(not(any(target_arch = "wasm64", feature = "process64")))]
|
||||
pub type PAddr = u32;
|
||||
|
||||
pub mod errno;
|
||||
pub mod eventfd;
|
||||
pub mod file;
|
||||
|
||||
@@ -344,12 +344,12 @@ pub struct Remote {
|
||||
/// How many writes have ever put bytes in. Negative when the pipe is gone.
|
||||
pub writes: extern "C" fn(id: i32) -> i32,
|
||||
/// The last message written, into the caller's memory. Returns its length.
|
||||
pub last_message: extern "C" fn(id: i32, at: u32, cap: u32) -> i32,
|
||||
pub last_message: extern "C" fn(id: i32, at: crate::PAddr, cap: u32) -> i32,
|
||||
/// Drop a descriptor. The last one on an end is what a reader sees as EOF.
|
||||
pub close_end: extern "C" fn(id: i32, write: i32),
|
||||
/// Read into, or write out of, an address in the caller's memory.
|
||||
pub read: extern "C" fn(id: i32, at: u32, len: u32) -> i32,
|
||||
pub write: extern "C" fn(id: i32, at: u32, len: u32) -> i32,
|
||||
pub read: extern "C" fn(id: i32, at: crate::PAddr, len: u32) -> i32,
|
||||
pub write: extern "C" fn(id: i32, at: crate::PAddr, len: u32) -> i32,
|
||||
pub readable: extern "C" fn(id: i32) -> i32,
|
||||
pub writable: extern "C" fn(id: i32) -> i32,
|
||||
pub buffered: extern "C" fn(id: i32) -> i32,
|
||||
@@ -385,7 +385,7 @@ impl crate::vfs::FileHandle for RemotePipeEnd {
|
||||
if self.write {
|
||||
return Err(Errno::EBADF);
|
||||
}
|
||||
match (self.remote.read)(self.id, buf.as_mut_ptr() as u32, buf.len() as u32) {
|
||||
match (self.remote.read)(self.id, buf.as_mut_ptr() as crate::PAddr, buf.len() as u32) {
|
||||
n if n >= 0 => Ok(n as u64),
|
||||
n => Err(Errno(-n)),
|
||||
}
|
||||
@@ -395,7 +395,7 @@ impl crate::vfs::FileHandle for RemotePipeEnd {
|
||||
if !self.write {
|
||||
return Err(Errno::EBADF);
|
||||
}
|
||||
match (self.remote.write)(self.id, buf.as_ptr() as u32, buf.len() as u32) {
|
||||
match (self.remote.write)(self.id, buf.as_ptr() as crate::PAddr, buf.len() as u32) {
|
||||
n if n >= 0 => Ok(n as u64),
|
||||
n => Err(Errno(-n)),
|
||||
}
|
||||
@@ -463,7 +463,7 @@ impl crate::vfs::FileHandle for RemotePipeEnd {
|
||||
};
|
||||
let n = (self.remote.writes)(self.id);
|
||||
let mut buf = [0u8; 180];
|
||||
let got = (self.remote.last_message)(self.id, buf.as_mut_ptr() as u32, buf.len() as u32);
|
||||
let got = (self.remote.last_message)(self.id, buf.as_mut_ptr() as crate::PAddr, buf.len() as u32);
|
||||
let said = if got > 0 {
|
||||
format!(", last {:?}", String::from_utf8_lossy(&buf[..got as usize]))
|
||||
} else {
|
||||
|
||||
@@ -227,16 +227,16 @@ pub struct Remote {
|
||||
pub open_end: extern "C" fn(id: i32, master: i32),
|
||||
/// Drop a descriptor. The last on an end is the other end's EOF/hangup.
|
||||
pub close_end: extern "C" fn(id: i32, master: i32),
|
||||
pub read: extern "C" fn(id: i32, master: i32, at: u32, len: u32) -> i32,
|
||||
pub write: extern "C" fn(id: i32, master: i32, at: u32, len: u32) -> i32,
|
||||
pub read: extern "C" fn(id: i32, master: i32, at: crate::PAddr, len: u32) -> i32,
|
||||
pub write: extern "C" fn(id: i32, master: i32, at: crate::PAddr, len: u32) -> i32,
|
||||
pub readable: extern "C" fn(id: i32, master: i32) -> i32,
|
||||
pub available: extern "C" fn(id: i32, master: i32) -> i32,
|
||||
pub ptn: extern "C" fn(id: i32) -> i32,
|
||||
pub get_lock: extern "C" fn(id: i32) -> i32,
|
||||
pub set_lock: extern "C" fn(id: i32, locked: i32),
|
||||
/// The shared terminal state, in the byte shapes the ioctls exchange.
|
||||
pub get_termios: extern "C" fn(id: i32, at: u32, len: u32) -> i32,
|
||||
pub set_termios: extern "C" fn(id: i32, at: u32, len: u32, flush: i32) -> i32,
|
||||
pub get_termios: extern "C" fn(id: i32, at: crate::PAddr, len: u32) -> i32,
|
||||
pub set_termios: extern "C" fn(id: i32, at: crate::PAddr, len: u32, flush: i32) -> i32,
|
||||
pub winsize: extern "C" fn(id: i32) -> u64,
|
||||
/// Sets the window size and, when it changed, delivers `SIGWINCH` to the
|
||||
/// foreground group itself (only the global kernel can, being next to the
|
||||
@@ -285,14 +285,14 @@ impl RemotePty {
|
||||
|
||||
impl crate::vfs::FileHandle for RemotePty {
|
||||
fn read_at(&self, _offset: u64, buf: &mut [u8]) -> SysResult {
|
||||
match (self.remote.read)(self.id, self.m(), buf.as_mut_ptr() as u32, buf.len() as u32) {
|
||||
match (self.remote.read)(self.id, self.m(), buf.as_mut_ptr() as crate::PAddr, buf.len() as u32) {
|
||||
n if n >= 0 => Ok(n as u64),
|
||||
n => Err(Errno(-n)),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_at(&self, _offset: u64, buf: &[u8]) -> SysResult {
|
||||
match (self.remote.write)(self.id, self.m(), buf.as_ptr() as u32, buf.len() as u32) {
|
||||
match (self.remote.write)(self.id, self.m(), buf.as_ptr() as crate::PAddr, buf.len() as u32) {
|
||||
n if n >= 0 => Ok(n as u64),
|
||||
n => Err(Errno(-n)),
|
||||
}
|
||||
@@ -412,13 +412,13 @@ impl RemotePty {
|
||||
match req {
|
||||
ioc::TCGETS => {
|
||||
let mut buf = [0u8; 60];
|
||||
(r.get_termios)(id, buf.as_mut_ptr() as u32, buf.len() as u32);
|
||||
(r.get_termios)(id, buf.as_mut_ptr() as crate::PAddr, buf.len() as u32);
|
||||
m.write(arg, &buf[..KERNEL_TERMIOS])?;
|
||||
Ok(0)
|
||||
}
|
||||
ioc::TCGETS2 => {
|
||||
let mut buf = [0u8; 60];
|
||||
(r.get_termios)(id, buf.as_mut_ptr() as u32, buf.len() as u32);
|
||||
(r.get_termios)(id, buf.as_mut_ptr() as crate::PAddr, buf.len() as u32);
|
||||
let mut out = [0u8; KERNEL_TERMIOS + 8];
|
||||
out[..KERNEL_TERMIOS].copy_from_slice(&buf[..KERNEL_TERMIOS]);
|
||||
out[KERNEL_TERMIOS..KERNEL_TERMIOS + 4].copy_from_slice(&38400u32.to_le_bytes());
|
||||
@@ -429,13 +429,13 @@ impl RemotePty {
|
||||
ioc::TCSETS | ioc::TCSETSW | ioc::TCSETSF => {
|
||||
let src = m.read(arg, KERNEL_TERMIOS)?.to_vec();
|
||||
let flush = (req == ioc::TCSETSF) as i32;
|
||||
(r.set_termios)(id, src.as_ptr() as u32, src.len() as u32, flush);
|
||||
(r.set_termios)(id, src.as_ptr() as crate::PAddr, src.len() as u32, flush);
|
||||
Ok(0)
|
||||
}
|
||||
ioc::TCSETS2 | ioc::TCSETSW2 | ioc::TCSETSF2 => {
|
||||
let src = m.read(arg, KERNEL_TERMIOS)?.to_vec();
|
||||
let flush = (req == ioc::TCSETSF2) as i32;
|
||||
(r.set_termios)(id, src.as_ptr() as u32, src.len() as u32, flush);
|
||||
(r.set_termios)(id, src.as_ptr() as crate::PAddr, src.len() as u32, flush);
|
||||
Ok(0)
|
||||
}
|
||||
ioc::TIOCGWINSZ => {
|
||||
|
||||
@@ -5582,7 +5582,7 @@ impl Kernel {
|
||||
// and `listen` is local to itself, and what the far end can see is
|
||||
// only whether the name answers.
|
||||
if let Some(r) = self.unix_remote {
|
||||
let id = (r.bind)(path.as_ptr() as u32, path.len() as u32);
|
||||
let id = (r.bind)(path.as_ptr() as crate::PAddr, path.len() as u32);
|
||||
if id < 0 {
|
||||
return Err(Errno(-id));
|
||||
}
|
||||
@@ -5624,7 +5624,7 @@ impl Kernel {
|
||||
// is in *this* process's table and never in the kernel's, so the
|
||||
// local path is still tried when the kernel does not know the name.
|
||||
if let Some(r) = self.unix_remote {
|
||||
let id = (r.connect)(path.as_ptr() as u32, path.len() as u32);
|
||||
let id = (r.connect)(path.as_ptr() as crate::PAddr, path.len() as u32);
|
||||
if id >= 0 {
|
||||
let flags = self.fds.get(fd)?.flags();
|
||||
let cloexec = self.fds.cloexec(fd).unwrap_or(false);
|
||||
|
||||
@@ -645,10 +645,10 @@ impl Tty {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Remote {
|
||||
/// Write the 60-byte structure to `at`.
|
||||
pub get_termios: extern "C" fn(at: u32, len: u32) -> i32,
|
||||
pub set_termios: extern "C" fn(at: u32, len: u32) -> i32,
|
||||
pub get_locked: extern "C" fn(at: u32, len: u32) -> i32,
|
||||
pub set_locked: extern "C" fn(at: u32, len: u32) -> i32,
|
||||
pub get_termios: extern "C" fn(at: crate::PAddr, len: u32) -> i32,
|
||||
pub set_termios: extern "C" fn(at: crate::PAddr, len: u32) -> i32,
|
||||
pub get_locked: extern "C" fn(at: crate::PAddr, len: u32) -> i32,
|
||||
pub set_locked: extern "C" fn(at: crate::PAddr, len: u32) -> i32,
|
||||
/// `rows << 48 | cols << 32 | xpixel << 16 | ypixel`.
|
||||
pub winsize: extern "C" fn() -> u64,
|
||||
/// Returns 1 if it changed, so the caller knows to raise `SIGWINCH`.
|
||||
@@ -668,7 +668,7 @@ pub struct Remote {
|
||||
/// Bytes waiting to be read, for `FIONREAD`.
|
||||
pub queued: extern "C" fn() -> i32,
|
||||
/// Push bytes into the input queue as though typed, for `TIOCSTI`.
|
||||
pub push: extern "C" fn(at: u32, len: u32) -> i32,
|
||||
pub push: extern "C" fn(at: crate::PAddr, len: u32) -> i32,
|
||||
}
|
||||
|
||||
/// The terminal, wherever it is: a local `Tty` with no global kernel, or the
|
||||
@@ -708,7 +708,7 @@ impl TtyRef {
|
||||
match self.remote {
|
||||
Some(r) => {
|
||||
let mut t = [0u8; 60];
|
||||
(r.get_termios)(t.as_mut_ptr() as u32, 60);
|
||||
(r.get_termios)(t.as_mut_ptr() as crate::PAddr, 60);
|
||||
t
|
||||
}
|
||||
None => self.local.termios(),
|
||||
@@ -718,7 +718,7 @@ impl TtyRef {
|
||||
pub fn set_termios(&self, src: &[u8]) {
|
||||
match self.remote {
|
||||
Some(r) => {
|
||||
(r.set_termios)(src.as_ptr() as u32, src.len() as u32);
|
||||
(r.set_termios)(src.as_ptr() as crate::PAddr, src.len() as u32);
|
||||
}
|
||||
None => self.local.set_termios(src),
|
||||
}
|
||||
@@ -728,7 +728,7 @@ impl TtyRef {
|
||||
match self.remote {
|
||||
Some(r) => {
|
||||
let mut t = [0u8; 60];
|
||||
(r.get_locked)(t.as_mut_ptr() as u32, 60);
|
||||
(r.get_locked)(t.as_mut_ptr() as crate::PAddr, 60);
|
||||
t
|
||||
}
|
||||
None => self.local.locked_termios(),
|
||||
@@ -738,7 +738,7 @@ impl TtyRef {
|
||||
pub fn set_locked_termios(&self, src: &[u8]) {
|
||||
match self.remote {
|
||||
Some(r) => {
|
||||
(r.set_locked)(src.as_ptr() as u32, src.len() as u32);
|
||||
(r.set_locked)(src.as_ptr() as crate::PAddr, src.len() as u32);
|
||||
}
|
||||
None => self.local.set_locked_termios(src),
|
||||
}
|
||||
@@ -856,7 +856,7 @@ impl TtyRef {
|
||||
/// `TIOCSTI`: bytes into the input queue as though they had been typed.
|
||||
pub fn push(&self, bytes: &[u8]) -> i32 {
|
||||
match self.remote {
|
||||
Some(r) => (r.push)(bytes.as_ptr() as u32, bytes.len() as u32),
|
||||
Some(r) => (r.push)(bytes.as_ptr() as crate::PAddr, bytes.len() as u32),
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1072,20 +1072,20 @@ mod epoll_tests {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Remote {
|
||||
/// Bind and listen in one. Returns a listener id or a negated errno.
|
||||
pub bind: extern "C" fn(path: u32, path_len: u32) -> i32,
|
||||
pub bind: extern "C" fn(path: crate::PAddr, path_len: u32) -> i32,
|
||||
/// Connect to a name. Returns this end's id, or a negated errno.
|
||||
pub connect: extern "C" fn(path: u32, path_len: u32) -> i32,
|
||||
pub connect: extern "C" fn(path: crate::PAddr, path_len: u32) -> i32,
|
||||
/// Take a waiting connection. Returns the server end's id, or -EAGAIN.
|
||||
pub accept: extern "C" fn(listener: i32) -> i32,
|
||||
pub pending: extern "C" fn(listener: i32) -> i32,
|
||||
pub read: extern "C" fn(id: i32, at: u32, len: u32) -> i32,
|
||||
pub write: extern "C" fn(id: i32, at: u32, len: u32) -> i32,
|
||||
pub read: extern "C" fn(id: i32, at: crate::PAddr, len: u32) -> i32,
|
||||
pub write: extern "C" fn(id: i32, at: crate::PAddr, len: u32) -> i32,
|
||||
pub readable: extern "C" fn(id: i32) -> i32,
|
||||
pub writable: extern "C" fn(id: i32) -> i32,
|
||||
pub available: extern "C" fn(id: i32) -> i32,
|
||||
pub close: extern "C" fn(id: i32),
|
||||
pub shutdown: extern "C" fn(id: i32),
|
||||
pub exists: extern "C" fn(path: u32, path_len: u32) -> i32,
|
||||
pub exists: extern "C" fn(path: crate::PAddr, path_len: u32) -> i32,
|
||||
}
|
||||
|
||||
/// One end of a socket in the global kernel.
|
||||
@@ -1166,7 +1166,7 @@ impl crate::vfs::FileHandle for RemoteUnix {
|
||||
if self.listening {
|
||||
return Err(Errno::ENOTCONN);
|
||||
}
|
||||
match (self.remote.read)(self.id, buf.as_mut_ptr() as u32, buf.len() as u32) {
|
||||
match (self.remote.read)(self.id, buf.as_mut_ptr() as crate::PAddr, buf.len() as u32) {
|
||||
n if n >= 0 => Ok(n as u64),
|
||||
n => Err(Errno(-n)),
|
||||
}
|
||||
@@ -1176,7 +1176,7 @@ impl crate::vfs::FileHandle for RemoteUnix {
|
||||
if self.listening {
|
||||
return Err(Errno::ENOTCONN);
|
||||
}
|
||||
match (self.remote.write)(self.id, buf.as_ptr() as u32, buf.len() as u32) {
|
||||
match (self.remote.write)(self.id, buf.as_ptr() as crate::PAddr, buf.len() as u32) {
|
||||
n if n >= 0 => Ok(n as u64),
|
||||
n => Err(Errno(-n)),
|
||||
}
|
||||
|
||||
@@ -25,34 +25,34 @@ use crate::vfs::{DirEntry, FileHandle, FileSystem, FileType, Metadata, SysResult
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Remote {
|
||||
/// Open, returning a handle id or a negated errno.
|
||||
pub open: extern "C" fn(path: u32, path_len: u32, flags: u64, mode: u32) -> i32,
|
||||
pub open: extern "C" fn(path: crate::PAddr, path_len: u32, flags: u64, mode: u32) -> i32,
|
||||
pub close: extern "C" fn(handle: i32),
|
||||
/// A second id for one open file, for a child to inherit. Returns the
|
||||
/// new id or a negated errno.
|
||||
pub dup: extern "C" fn(handle: i32) -> i32,
|
||||
pub read: extern "C" fn(handle: i32, offset: u64, at: u32, len: u32) -> i32,
|
||||
pub write: extern "C" fn(handle: i32, offset: u64, at: u32, len: u32) -> i32,
|
||||
pub read: extern "C" fn(handle: i32, offset: u64, at: crate::PAddr, len: u32) -> i32,
|
||||
pub write: extern "C" fn(handle: i32, offset: u64, at: crate::PAddr, len: u32) -> i32,
|
||||
/// Write this handle's metadata as [`META_BYTES`] bytes at `at`.
|
||||
pub meta: extern "C" fn(handle: i32, at: u32) -> i32,
|
||||
pub meta: extern "C" fn(handle: i32, at: crate::PAddr) -> i32,
|
||||
pub truncate: extern "C" fn(handle: i32, len: u64) -> i32,
|
||||
/// Write the directory listing at `at`, or `-ERANGE` if `cap` is short.
|
||||
pub read_dir: extern "C" fn(handle: i32, at: u32, cap: u32) -> i32,
|
||||
pub path_meta: extern "C" fn(path: u32, path_len: u32, at: u32) -> i32,
|
||||
pub read_link: extern "C" fn(path: u32, path_len: u32, at: u32, cap: u32) -> i32,
|
||||
pub unlink: extern "C" fn(path: u32, path_len: u32) -> i32,
|
||||
pub mkdir: extern "C" fn(path: u32, path_len: u32, mode: u32) -> i32,
|
||||
pub rmdir: extern "C" fn(path: u32, path_len: u32) -> i32,
|
||||
pub rename: extern "C" fn(from: u32, from_len: u32, to: u32, to_len: u32) -> i32,
|
||||
pub chmod: extern "C" fn(path: u32, path_len: u32, mode: u32) -> i32,
|
||||
pub symlink: extern "C" fn(target: u32, target_len: u32, path: u32, path_len: u32) -> i32,
|
||||
pub link: extern "C" fn(from: u32, from_len: u32, to: u32, to_len: u32) -> i32,
|
||||
pub read_dir: extern "C" fn(handle: i32, at: crate::PAddr, cap: u32) -> i32,
|
||||
pub path_meta: extern "C" fn(path: crate::PAddr, path_len: u32, at: crate::PAddr) -> i32,
|
||||
pub read_link: extern "C" fn(path: crate::PAddr, path_len: u32, at: crate::PAddr, cap: u32) -> i32,
|
||||
pub unlink: extern "C" fn(path: crate::PAddr, path_len: u32) -> i32,
|
||||
pub mkdir: extern "C" fn(path: crate::PAddr, path_len: u32, mode: u32) -> i32,
|
||||
pub rmdir: extern "C" fn(path: crate::PAddr, path_len: u32) -> i32,
|
||||
pub rename: extern "C" fn(from: crate::PAddr, from_len: u32, to: crate::PAddr, to_len: u32) -> i32,
|
||||
pub chmod: extern "C" fn(path: crate::PAddr, path_len: u32, mode: u32) -> i32,
|
||||
pub symlink: extern "C" fn(target: crate::PAddr, target_len: u32, path: crate::PAddr, path_len: u32) -> i32,
|
||||
pub link: extern "C" fn(from: crate::PAddr, from_len: u32, to: crate::PAddr, to_len: u32) -> i32,
|
||||
/// Put a socket node at a path.
|
||||
///
|
||||
/// Not read -- only stat'd: Xvfb binds `/tmp/.X11-unix/X0` and its startup
|
||||
/// script waits for that path to exist before launching a client.
|
||||
pub mksocket: extern "C" fn(path: u32, path_len: u32) -> i32,
|
||||
pub mksocket: extern "C" fn(path: crate::PAddr, path_len: u32) -> i32,
|
||||
/// Set a path's modification time (`utimensat`, a bundle's file times).
|
||||
pub set_mtime: extern "C" fn(path: u32, path_len: u32, secs: i64) -> i32,
|
||||
pub set_mtime: extern "C" fn(path: crate::PAddr, path_len: u32, secs: i64) -> i32,
|
||||
/// Set an open file's modification time. The global kernel has no clock
|
||||
/// of its own, so a write's time is the writing process's: it stamps the
|
||||
/// file after each write.
|
||||
@@ -163,8 +163,8 @@ fn ok(v: i32) -> Result<i32, Errno> {
|
||||
|
||||
/// Where a path is staged for the other side to copy out. The address must be
|
||||
/// real in this process's memory and stay valid across the call.
|
||||
fn at(b: &[u8]) -> (u32, u32) {
|
||||
(b.as_ptr() as u32, b.len() as u32)
|
||||
fn at(b: &[u8]) -> (crate::PAddr, u32) {
|
||||
(b.as_ptr() as crate::PAddr, b.len() as u32)
|
||||
}
|
||||
|
||||
/// A filesystem served by the global kernel.
|
||||
@@ -209,7 +209,7 @@ impl FileSystem for RemoteFs {
|
||||
let p = path.as_bytes();
|
||||
let (a, n) = at(p);
|
||||
let mut buf = [0u8; META_BYTES];
|
||||
ok((self.remote.path_meta)(a, n, buf.as_mut_ptr() as u32))?;
|
||||
ok((self.remote.path_meta)(a, n, buf.as_mut_ptr() as crate::PAddr))?;
|
||||
Ok(decode_meta(&buf))
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ impl FileSystem for RemoteFs {
|
||||
let got = ok((self.remote.read_link)(
|
||||
a,
|
||||
n,
|
||||
buf.as_mut_ptr() as u32,
|
||||
buf.as_mut_ptr() as crate::PAddr,
|
||||
buf.len() as u32,
|
||||
))? as usize;
|
||||
Ok(String::from_utf8_lossy(&buf[..got]).into_owned())
|
||||
@@ -311,7 +311,7 @@ impl FileHandle for RemoteHandle {
|
||||
let n = ok((self.remote.read)(
|
||||
self.id,
|
||||
offset,
|
||||
buf.as_mut_ptr() as u32,
|
||||
buf.as_mut_ptr() as crate::PAddr,
|
||||
buf.len() as u32,
|
||||
))?;
|
||||
Ok(n as u64)
|
||||
@@ -321,7 +321,7 @@ impl FileHandle for RemoteHandle {
|
||||
let n = ok((self.remote.write)(
|
||||
self.id,
|
||||
offset,
|
||||
buf.as_ptr() as u32,
|
||||
buf.as_ptr() as crate::PAddr,
|
||||
buf.len() as u32,
|
||||
))?;
|
||||
if n > 0 {
|
||||
@@ -336,7 +336,7 @@ impl FileHandle for RemoteHandle {
|
||||
|
||||
fn metadata(&self) -> Result<Metadata, Errno> {
|
||||
let mut buf = [0u8; META_BYTES];
|
||||
ok((self.remote.meta)(self.id, buf.as_mut_ptr() as u32))?;
|
||||
ok((self.remote.meta)(self.id, buf.as_mut_ptr() as crate::PAddr))?;
|
||||
Ok(decode_meta(&buf))
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ impl FileHandle for RemoteHandle {
|
||||
let mut cap = 4096;
|
||||
loop {
|
||||
let mut buf = vec![0u8; cap];
|
||||
match (self.remote.read_dir)(self.id, buf.as_mut_ptr() as u32, cap as u32) {
|
||||
match (self.remote.read_dir)(self.id, buf.as_mut_ptr() as crate::PAddr, cap as u32) {
|
||||
n if n >= 0 => {
|
||||
buf.truncate(n as usize);
|
||||
return Ok(decode_dir(&buf));
|
||||
|
||||
+17
-14
@@ -18,13 +18,20 @@
|
||||
/// 0; the process memory is appended to the import section, landing at 1.
|
||||
pub const PROCESS_MEMORY: u32 = 1;
|
||||
|
||||
/// An address in the process memory: 64-bit when it is a memory64
|
||||
/// (`process64`, docs/mem64.md step 6), as `gex_kernel::PAddr`.
|
||||
#[cfg(feature = "process64")]
|
||||
pub type PAddr = u64;
|
||||
#[cfg(not(feature = "process64"))]
|
||||
pub type PAddr = u32;
|
||||
|
||||
/// How many 64 KiB pages the calling process's memory has.
|
||||
///
|
||||
/// `memory.size` can name a memory index (unlike a load), so this is how the
|
||||
/// kernel tells a valid guest pointer from one off the end and answers `EFAULT`
|
||||
/// rather than trapping the Worker mid-syscall with the kernel lock held.
|
||||
#[inline]
|
||||
pub fn process_pages() -> u32 {
|
||||
pub fn process_pages() -> PAddr {
|
||||
#[cfg(not(feature = "process64"))]
|
||||
let n: u32;
|
||||
#[cfg(feature = "process64")]
|
||||
@@ -36,15 +43,15 @@ pub fn process_pages() -> u32 {
|
||||
n = out(local) n,
|
||||
);
|
||||
}
|
||||
n as u32
|
||||
n
|
||||
}
|
||||
|
||||
/// Whether `at..at+len` is inside the calling process's memory. Every `push`
|
||||
/// and `pull` should be guarded by this: `memory.copy` traps rather than
|
||||
/// returning, and a trap mid-syscall kills the Worker holding the kernel lock.
|
||||
#[inline]
|
||||
pub fn in_process(at: u32, len: u32) -> bool {
|
||||
let end = match at.checked_add(len) {
|
||||
pub fn in_process(at: PAddr, len: u32) -> bool {
|
||||
let end = match at.checked_add(len as PAddr) {
|
||||
Some(e) => e as u64,
|
||||
None => return false,
|
||||
};
|
||||
@@ -59,11 +66,9 @@ pub fn in_process(at: u32, len: u32) -> bool {
|
||||
/// rather than corrupting anything if it is not, but a trap here unwinds a
|
||||
/// syscall that has already taken the kernel lock.
|
||||
#[inline]
|
||||
pub unsafe fn pull(dst: *mut u8, src: u32, len: u32) {
|
||||
pub unsafe fn pull(dst: *mut u8, src: PAddr, len: u32) {
|
||||
// A memory64 process memory takes an i64 address; the kernel's own (and
|
||||
// the length, the smaller of the two index types) stay i32.
|
||||
#[cfg(feature = "process64")]
|
||||
let src = src as u64;
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"local.get {dst}",
|
||||
@@ -82,9 +87,7 @@ pub unsafe fn pull(dst: *mut u8, src: u32, len: u32) {
|
||||
/// # Safety
|
||||
/// As [`pull`], with the roles reversed.
|
||||
#[inline]
|
||||
pub unsafe fn push(dst: u32, src: *const u8, len: u32) {
|
||||
#[cfg(feature = "process64")]
|
||||
let dst = dst as u64;
|
||||
pub unsafe fn push(dst: PAddr, src: *const u8, len: u32) {
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"local.get {dst}",
|
||||
@@ -104,7 +107,7 @@ pub unsafe fn push(dst: u32, src: *const u8, len: u32) {
|
||||
/// # Safety
|
||||
/// `at..at+8` must be in bounds of the process memory.
|
||||
#[inline]
|
||||
pub unsafe fn read_u64(at: u32) -> u64 {
|
||||
pub unsafe fn read_u64(at: PAddr) -> u64 {
|
||||
let mut buf = [0u8; 8];
|
||||
unsafe { pull(buf.as_mut_ptr(), at, 8) };
|
||||
u64::from_le_bytes(buf)
|
||||
@@ -115,7 +118,7 @@ pub unsafe fn read_u64(at: u32) -> u64 {
|
||||
/// # Safety
|
||||
/// `at..at+8` must be in bounds of the process memory.
|
||||
#[inline]
|
||||
pub unsafe fn write_u64(at: u32, v: u64) {
|
||||
pub unsafe fn write_u64(at: PAddr, v: u64) {
|
||||
let buf = v.to_le_bytes();
|
||||
unsafe { push(at, buf.as_ptr(), 8) };
|
||||
}
|
||||
@@ -129,7 +132,7 @@ pub unsafe fn write_u64(at: u32, v: u64) {
|
||||
/// `dst` must be valid for `len` bytes in the kernel's own memory. The process
|
||||
/// side is checked here.
|
||||
#[inline]
|
||||
pub unsafe fn pull_checked(dst: *mut u8, src: u32, len: u32) -> bool {
|
||||
pub unsafe fn pull_checked(dst: *mut u8, src: PAddr, len: u32) -> bool {
|
||||
if !in_process(src, len) {
|
||||
return false;
|
||||
}
|
||||
@@ -142,7 +145,7 @@ pub unsafe fn pull_checked(dst: *mut u8, src: u32, len: u32) -> bool {
|
||||
/// # Safety
|
||||
/// `src` must be valid for `len` bytes in the kernel's own memory.
|
||||
#[inline]
|
||||
pub unsafe fn push_checked(dst: u32, src: *const u8, len: u32) -> bool {
|
||||
pub unsafe fn push_checked(dst: PAddr, src: *const u8, len: u32) -> bool {
|
||||
if !in_process(dst, len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -237,6 +237,10 @@ if [ -n "${GEX_MEM64:-}" ] && [ -n "$ARCH_FEAT" ]; then
|
||||
echo "shims/browser: the wasm64 build FAILED"; exit 1
|
||||
fi
|
||||
cp "$M64OUT" "$PUB/gex_browser$INFIX.m64.shared.wasm"
|
||||
if [ -f "$PUB/gex_kernel$INFIX.m64.wasm" ] \
|
||||
&& ! node shims/browser/js/check-kernel.mjs "$M64OUT" "$PUB/gex_kernel$INFIX.m64.wasm"; then
|
||||
echo "shims/browser: the wasm64 shim and gex_kernel$INFIX.m64.wasm disagree (GEX_MEM64=1 shims/kernel/build.sh)"; exit 1
|
||||
fi
|
||||
$GEXC seed --mem64 -o web/public/gex/seed$INFIX.m64.shared.wasm >/dev/null
|
||||
echo "shims/browser: $(stat -c%s "$M64OUT") bytes (wasm64, memory64), seed$INFIX.m64.shared.wasm"
|
||||
fi
|
||||
|
||||
+115
-93
@@ -255,27 +255,27 @@ extern "C" {
|
||||
// other side copies across with `memory.copy`, which is the only way it
|
||||
// can reach a memory it does not own. See `gex_kernel::vfs::remote` for
|
||||
// the two layouts that cross.
|
||||
fn k_fs_open(path: u32, path_len: u32, flags: u64, mode: u32) -> i32;
|
||||
fn k_fs_open(path: gex_kernel::PAddr, path_len: u32, flags: u64, mode: u32) -> i32;
|
||||
fn k_fs_close(handle: i32);
|
||||
fn k_fs_dup(handle: i32) -> i32;
|
||||
fn k_fs_read(handle: i32, offset: u64, at: u32, len: u32) -> i32;
|
||||
fn k_fs_write(handle: i32, offset: u64, at: u32, len: u32) -> i32;
|
||||
fn k_fs_meta(handle: i32, at: u32) -> i32;
|
||||
fn k_fs_read(handle: i32, offset: u64, at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_fs_write(handle: i32, offset: u64, at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_fs_meta(handle: i32, at: gex_kernel::PAddr) -> i32;
|
||||
fn k_fs_truncate(handle: i32, len: u64) -> i32;
|
||||
fn k_fs_read_dir(handle: i32, at: u32, cap: u32) -> i32;
|
||||
fn k_fs_path_meta(path: u32, path_len: u32, at: u32) -> i32;
|
||||
fn k_fs_read_link(path: u32, path_len: u32, at: u32, cap: u32) -> i32;
|
||||
fn k_fs_unlink(path: u32, path_len: u32) -> i32;
|
||||
fn k_fs_mkdir(path: u32, path_len: u32, mode: u32) -> i32;
|
||||
fn k_fs_rmdir(path: u32, path_len: u32) -> i32;
|
||||
fn k_fs_rename(from: u32, from_len: u32, to: u32, to_len: u32) -> i32;
|
||||
fn k_fs_chmod(path: u32, path_len: u32, mode: u32) -> i32;
|
||||
fn k_fs_set_mtime(path: u32, path_len: u32, secs: i64) -> i32;
|
||||
fn k_fs_read_dir(handle: i32, at: gex_kernel::PAddr, cap: u32) -> i32;
|
||||
fn k_fs_path_meta(path: gex_kernel::PAddr, path_len: u32, at: gex_kernel::PAddr) -> i32;
|
||||
fn k_fs_read_link(path: gex_kernel::PAddr, path_len: u32, at: gex_kernel::PAddr, cap: u32) -> i32;
|
||||
fn k_fs_unlink(path: gex_kernel::PAddr, path_len: u32) -> i32;
|
||||
fn k_fs_mkdir(path: gex_kernel::PAddr, path_len: u32, mode: u32) -> i32;
|
||||
fn k_fs_rmdir(path: gex_kernel::PAddr, path_len: u32) -> i32;
|
||||
fn k_fs_rename(from: gex_kernel::PAddr, from_len: u32, to: gex_kernel::PAddr, to_len: u32) -> i32;
|
||||
fn k_fs_chmod(path: gex_kernel::PAddr, path_len: u32, mode: u32) -> i32;
|
||||
fn k_fs_set_mtime(path: gex_kernel::PAddr, path_len: u32, secs: i64) -> i32;
|
||||
fn k_fs_touch(handle: i32, secs: i64) -> i32;
|
||||
fn k_fs_symlink(target: u32, target_len: u32, path: u32, path_len: u32) -> i32;
|
||||
fn k_fs_link(from: u32, from_len: u32, to: u32, to_len: u32) -> i32;
|
||||
fn k_fs_mksocket(path: u32, path_len: u32) -> i32;
|
||||
fn k_fs_add(path: u32, path_len: u32, at: u32, len: u32) -> i32;
|
||||
fn k_fs_symlink(target: gex_kernel::PAddr, target_len: u32, path: gex_kernel::PAddr, path_len: u32) -> i32;
|
||||
fn k_fs_link(from: gex_kernel::PAddr, from_len: u32, to: gex_kernel::PAddr, to_len: u32) -> i32;
|
||||
fn k_fs_mksocket(path: gex_kernel::PAddr, path_len: u32) -> i32;
|
||||
fn k_fs_add(path: gex_kernel::PAddr, path_len: u32, at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
/// The system-wide map from a page of a mapped object to the dispatch slots
|
||||
/// its code was translated into; zero means nobody has. In the kernel so
|
||||
/// the second process to run `ls` finds what the first one translated.
|
||||
@@ -285,17 +285,17 @@ extern "C" {
|
||||
/// Standard input, shared. Whoever has a keystroke writes it here and
|
||||
/// wakes a parked reader, rather than posting a message to a Worker that
|
||||
/// is asleep inside wasm and cannot receive one.
|
||||
fn k_stdin_write(at: u32, len: u32) -> i32;
|
||||
fn k_stdin_read(at: u32, len: u32) -> i32;
|
||||
fn k_stdin_write(at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_stdin_read(at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_stdin_ready() -> i32;
|
||||
fn k_stdin_eof();
|
||||
/// The terminal: one `struct termios`, one size, one foreground process
|
||||
/// group and one line discipline, for every process at once. The page
|
||||
/// pushes keystrokes straight into it; these are the guest's half.
|
||||
fn k_tty_get_termios(at: u32, len: u32) -> i32;
|
||||
fn k_tty_set_termios(at: u32, len: u32) -> i32;
|
||||
fn k_tty_get_locked(at: u32, len: u32) -> i32;
|
||||
fn k_tty_set_locked(at: u32, len: u32) -> i32;
|
||||
fn k_tty_get_termios(at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_tty_set_termios(at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_tty_get_locked(at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_tty_set_locked(at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_tty_winsize() -> u64;
|
||||
fn k_tty_set_winsize(packed: u64) -> i32;
|
||||
fn k_tty_fg() -> i32;
|
||||
@@ -308,7 +308,7 @@ extern "C" {
|
||||
fn k_tty_set_bits(which: i32, on: i32);
|
||||
fn k_tty_flush(which: i32) -> i32;
|
||||
fn k_tty_queued() -> i32;
|
||||
fn k_tty_push(at: u32, len: u32) -> i32;
|
||||
fn k_tty_push(at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
/// Slots nobody else will be given, and the mark that keeps runtime
|
||||
/// allocation clear of what static recompilation already assigned.
|
||||
fn k_slot_alloc(count: u64) -> u64;
|
||||
@@ -318,7 +318,7 @@ extern "C" {
|
||||
fn k_slot_left() -> u64;
|
||||
/// Translated modules, once for the whole system: a dispatch table belongs
|
||||
/// to a module instance, so the module bytes themselves must be shareable.
|
||||
fn k_mod_store(ptr: u32, len: u32, slots: u64, dirs_ptr: u32, dirs_len: u32) -> u32;
|
||||
fn k_mod_store(ptr: gex_kernel::PAddr, len: u32, slots: u64, dirs_ptr: gex_kernel::PAddr, dirs_len: u32) -> u32;
|
||||
fn k_mod_claim(page: u64, key: u64, id: u32);
|
||||
fn k_mod_for(page: u64, key: u64, n: u32) -> u32;
|
||||
fn k_mod_claim_object(obj: u64, id: u32);
|
||||
@@ -329,17 +329,17 @@ extern "C" {
|
||||
fn k_mod_slots(id: u32) -> u64;
|
||||
fn k_mod_len(id: u32) -> u32;
|
||||
fn k_mod_dirs_len(id: u32) -> u32;
|
||||
fn k_mod_read(id: u32, out: u32, cap: u32) -> u32;
|
||||
fn k_mod_dirs_read(id: u32, out: u32, cap: u32) -> u32;
|
||||
fn k_mod_read(id: u32, out: gex_kernel::PAddr, cap: u32) -> u32;
|
||||
fn k_mod_dirs_read(id: u32, out: gex_kernel::PAddr, cap: u32) -> u32;
|
||||
/// Has some process already filled the shared root? Only the first
|
||||
/// should: the rest would unpack the same rootfs over the top of it.
|
||||
fn k_place(key: u64, len: u64) -> u64;
|
||||
fn k_place_set(key: u64, base: u64, len: u64);
|
||||
/// The linked address space of a program, kept so the next process of it
|
||||
/// does not have to run the dynamic linker again.
|
||||
fn k_snap_store(key: u64, ptr: u32, len: u32);
|
||||
fn k_snap_store(key: u64, ptr: gex_kernel::PAddr, len: u32);
|
||||
fn k_snap_len(key: u64) -> u32;
|
||||
fn k_snap_get(key: u64, at: u32, cap: u32) -> u32;
|
||||
fn k_snap_get(key: u64, at: gex_kernel::PAddr, cap: u32) -> u32;
|
||||
fn k_fs_populated() -> i32;
|
||||
fn k_fs_populate_done();
|
||||
|
||||
@@ -351,11 +351,11 @@ extern "C" {
|
||||
fn k_pipe_ends(id: i32) -> i32;
|
||||
fn k_pipe_write_stats(id: i32) -> i32;
|
||||
fn k_pipe_writes(id: i32) -> i32;
|
||||
fn k_pipe_last_message(id: i32, at: u32, cap: u32) -> i32;
|
||||
fn k_pipe_last_message(id: i32, at: gex_kernel::PAddr, cap: u32) -> i32;
|
||||
fn k_pipe_close_end(id: i32, write: i32, pid: i32);
|
||||
fn k_pipe_forget(pid: i32) -> u32;
|
||||
fn k_pipe_read(id: i32, at: u32, len: u32) -> i32;
|
||||
fn k_pipe_write(id: i32, at: u32, len: u32) -> i32;
|
||||
fn k_pipe_read(id: i32, at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_pipe_write(id: i32, at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_pipe_readable(id: i32) -> i32;
|
||||
fn k_pipe_writable(id: i32) -> i32;
|
||||
fn k_pipe_buffered(id: i32) -> i32;
|
||||
@@ -368,8 +368,8 @@ extern "C" {
|
||||
fn k_pty_open_end(id: i32, master: i32, pid: i32);
|
||||
fn k_pty_close_end(id: i32, master: i32, pid: i32);
|
||||
fn k_pty_forget(pid: i32) -> u32;
|
||||
fn k_pty_read(id: i32, master: i32, at: u32, len: u32) -> i32;
|
||||
fn k_pty_write(id: i32, master: i32, at: u32, len: u32) -> i32;
|
||||
fn k_pty_read(id: i32, master: i32, at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_pty_write(id: i32, master: i32, at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_pty_readable(id: i32, master: i32) -> i32;
|
||||
fn k_pty_counts(id: i32) -> u32;
|
||||
fn k_pty_for_sid(sid: i32) -> i32;
|
||||
@@ -379,8 +379,8 @@ extern "C" {
|
||||
fn k_pty_ptn(id: i32) -> i32;
|
||||
fn k_pty_get_lock(id: i32) -> i32;
|
||||
fn k_pty_set_lock(id: i32, locked: i32);
|
||||
fn k_pty_get_termios(id: i32, at: u32, len: u32) -> i32;
|
||||
fn k_pty_set_termios(id: i32, at: u32, len: u32, flush: i32) -> i32;
|
||||
fn k_pty_get_termios(id: i32, at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_pty_set_termios(id: i32, at: gex_kernel::PAddr, len: u32, flush: i32) -> i32;
|
||||
fn k_pty_winsize(id: i32) -> u64;
|
||||
fn k_pty_set_winsize(id: i32, packed: u64) -> i32;
|
||||
fn k_pty_fg(id: i32) -> i32;
|
||||
@@ -392,18 +392,18 @@ extern "C" {
|
||||
// Unix sockets. An X server binds a name and its client connects to it,
|
||||
// and with a Worker per process those are two memories -- so the name has
|
||||
// to be somewhere neither of them owns.
|
||||
fn k_sock_bind(path: u32, path_len: u32) -> i32;
|
||||
fn k_sock_connect(path: u32, path_len: u32) -> i32;
|
||||
fn k_sock_bind(path: gex_kernel::PAddr, path_len: u32) -> i32;
|
||||
fn k_sock_connect(path: gex_kernel::PAddr, path_len: u32) -> i32;
|
||||
fn k_sock_accept(listener: i32) -> i32;
|
||||
fn k_sock_pending(listener: i32) -> i32;
|
||||
fn k_sock_read(id: i32, at: u32, len: u32) -> i32;
|
||||
fn k_sock_write(id: i32, at: u32, len: u32) -> i32;
|
||||
fn k_sock_read(id: i32, at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_sock_write(id: i32, at: gex_kernel::PAddr, len: u32) -> i32;
|
||||
fn k_sock_readable(id: i32) -> i32;
|
||||
fn k_sock_writable(id: i32) -> i32;
|
||||
fn k_sock_available(id: i32) -> i32;
|
||||
fn k_sock_close(id: i32);
|
||||
fn k_sock_shutdown(id: i32);
|
||||
fn k_sock_exists(path: u32, path_len: u32) -> i32;
|
||||
fn k_sock_exists(path: gex_kernel::PAddr, path_len: u32) -> i32;
|
||||
|
||||
}
|
||||
|
||||
@@ -646,7 +646,7 @@ fn global_procs() -> Option<Arc<gex_kernel::proc::ProcTable>> {
|
||||
)))
|
||||
}
|
||||
|
||||
extern "C" fn rf_open(path: u32, path_len: u32, flags: u64, mode: u32) -> i32 {
|
||||
extern "C" fn rf_open(path: gex_kernel::PAddr, path_len: u32, flags: u64, mode: u32) -> i32 {
|
||||
unsafe { k_fs_open(path, path_len, flags, mode) }
|
||||
}
|
||||
extern "C" fn rf_close(handle: i32) {
|
||||
@@ -655,55 +655,55 @@ extern "C" fn rf_close(handle: i32) {
|
||||
extern "C" fn rf_dup(handle: i32) -> i32 {
|
||||
unsafe { k_fs_dup(handle) }
|
||||
}
|
||||
extern "C" fn rf_read(handle: i32, offset: u64, at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rf_read(handle: i32, offset: u64, at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_fs_read(handle, offset, at, len) }
|
||||
}
|
||||
extern "C" fn rf_write(handle: i32, offset: u64, at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rf_write(handle: i32, offset: u64, at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_fs_write(handle, offset, at, len) }
|
||||
}
|
||||
extern "C" fn rf_meta(handle: i32, at: u32) -> i32 {
|
||||
extern "C" fn rf_meta(handle: i32, at: gex_kernel::PAddr) -> i32 {
|
||||
unsafe { k_fs_meta(handle, at) }
|
||||
}
|
||||
extern "C" fn rf_truncate(handle: i32, len: u64) -> i32 {
|
||||
unsafe { k_fs_truncate(handle, len) }
|
||||
}
|
||||
extern "C" fn rf_read_dir(handle: i32, at: u32, cap: u32) -> i32 {
|
||||
extern "C" fn rf_read_dir(handle: i32, at: gex_kernel::PAddr, cap: u32) -> i32 {
|
||||
unsafe { k_fs_read_dir(handle, at, cap) }
|
||||
}
|
||||
extern "C" fn rf_path_meta(path: u32, path_len: u32, at: u32) -> i32 {
|
||||
extern "C" fn rf_path_meta(path: gex_kernel::PAddr, path_len: u32, at: gex_kernel::PAddr) -> i32 {
|
||||
unsafe { k_fs_path_meta(path, path_len, at) }
|
||||
}
|
||||
extern "C" fn rf_read_link(path: u32, path_len: u32, at: u32, cap: u32) -> i32 {
|
||||
extern "C" fn rf_read_link(path: gex_kernel::PAddr, path_len: u32, at: gex_kernel::PAddr, cap: u32) -> i32 {
|
||||
unsafe { k_fs_read_link(path, path_len, at, cap) }
|
||||
}
|
||||
extern "C" fn rf_unlink(path: u32, path_len: u32) -> i32 {
|
||||
extern "C" fn rf_unlink(path: gex_kernel::PAddr, path_len: u32) -> i32 {
|
||||
unsafe { k_fs_unlink(path, path_len) }
|
||||
}
|
||||
extern "C" fn rf_mkdir(path: u32, path_len: u32, mode: u32) -> i32 {
|
||||
extern "C" fn rf_mkdir(path: gex_kernel::PAddr, path_len: u32, mode: u32) -> i32 {
|
||||
unsafe { k_fs_mkdir(path, path_len, mode) }
|
||||
}
|
||||
extern "C" fn rf_rmdir(path: u32, path_len: u32) -> i32 {
|
||||
extern "C" fn rf_rmdir(path: gex_kernel::PAddr, path_len: u32) -> i32 {
|
||||
unsafe { k_fs_rmdir(path, path_len) }
|
||||
}
|
||||
extern "C" fn rf_rename(from: u32, from_len: u32, to: u32, to_len: u32) -> i32 {
|
||||
extern "C" fn rf_rename(from: gex_kernel::PAddr, from_len: u32, to: gex_kernel::PAddr, to_len: u32) -> i32 {
|
||||
unsafe { k_fs_rename(from, from_len, to, to_len) }
|
||||
}
|
||||
extern "C" fn rf_chmod(path: u32, path_len: u32, mode: u32) -> i32 {
|
||||
unsafe { k_fs_chmod(path, path_len, mode) }
|
||||
extern "C" fn rf_chmod(path: gex_kernel::PAddr, path_len: u32, mode: u32) -> i32 {
|
||||
unsafe { k_fs_chmod(gex_kernel::PAddr::from(path), path_len, mode) }
|
||||
}
|
||||
extern "C" fn rf_set_mtime(path: u32, path_len: u32, secs: i64) -> i32 {
|
||||
unsafe { k_fs_set_mtime(path, path_len, secs) }
|
||||
extern "C" fn rf_set_mtime(path: gex_kernel::PAddr, path_len: u32, secs: i64) -> i32 {
|
||||
unsafe { k_fs_set_mtime(gex_kernel::PAddr::from(path), path_len, secs) }
|
||||
}
|
||||
extern "C" fn rf_touch(handle: i32, secs: i64) -> i32 {
|
||||
unsafe { k_fs_touch(handle, secs) }
|
||||
}
|
||||
extern "C" fn rf_symlink(target: u32, target_len: u32, path: u32, path_len: u32) -> i32 {
|
||||
extern "C" fn rf_symlink(target: gex_kernel::PAddr, target_len: u32, path: gex_kernel::PAddr, path_len: u32) -> i32 {
|
||||
unsafe { k_fs_symlink(target, target_len, path, path_len) }
|
||||
}
|
||||
extern "C" fn rf_link(from: u32, from_len: u32, to: u32, to_len: u32) -> i32 {
|
||||
extern "C" fn rf_link(from: gex_kernel::PAddr, from_len: u32, to: gex_kernel::PAddr, to_len: u32) -> i32 {
|
||||
unsafe { k_fs_link(from, from_len, to, to_len) }
|
||||
}
|
||||
extern "C" fn rf_mksocket(path: u32, path_len: u32) -> i32 {
|
||||
extern "C" fn rf_mksocket(path: gex_kernel::PAddr, path_len: u32) -> i32 {
|
||||
unsafe { k_fs_mksocket(path, path_len) }
|
||||
}
|
||||
|
||||
@@ -755,17 +755,17 @@ extern "C" fn rp_write_stats(id: i32) -> i32 {
|
||||
extern "C" fn rp_writes(id: i32) -> i32 {
|
||||
unsafe { k_pipe_writes(id) }
|
||||
}
|
||||
extern "C" fn rp_last_message(id: i32, at: u32, cap: u32) -> i32 {
|
||||
extern "C" fn rp_last_message(id: i32, at: gex_kernel::PAddr, cap: u32) -> i32 {
|
||||
unsafe { k_pipe_last_message(id, at, cap) }
|
||||
}
|
||||
extern "C" fn rp_close_end(id: i32, write: i32) {
|
||||
unsafe { k_pipe_close_end(id, write, my_pid()) }
|
||||
pipe_note("close", id, write);
|
||||
}
|
||||
extern "C" fn rp_read(id: i32, at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rp_read(id: i32, at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_pipe_read(id, at, len) }
|
||||
}
|
||||
extern "C" fn rp_write(id: i32, at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rp_write(id: i32, at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_pipe_write(id, at, len) }
|
||||
}
|
||||
extern "C" fn rp_readable(id: i32) -> i32 {
|
||||
@@ -780,16 +780,16 @@ extern "C" fn rp_buffered(id: i32) -> i32 {
|
||||
|
||||
// The terminal, which is one object for the whole system and not one per
|
||||
// process. See `gex_kernel::tty`.
|
||||
extern "C" fn rt_get_termios(at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rt_get_termios(at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_tty_get_termios(at, len) }
|
||||
}
|
||||
extern "C" fn rt_set_termios(at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rt_set_termios(at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_tty_set_termios(at, len) }
|
||||
}
|
||||
extern "C" fn rt_get_locked(at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rt_get_locked(at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_tty_get_locked(at, len) }
|
||||
}
|
||||
extern "C" fn rt_set_locked(at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rt_set_locked(at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_tty_set_locked(at, len) }
|
||||
}
|
||||
extern "C" fn rt_winsize() -> u64 {
|
||||
@@ -828,7 +828,7 @@ extern "C" fn rt_flush(which: i32) -> i32 {
|
||||
extern "C" fn rt_queued() -> i32 {
|
||||
unsafe { k_tty_queued() }
|
||||
}
|
||||
extern "C" fn rt_push(at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rt_push(at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_tty_push(at, len) }
|
||||
}
|
||||
|
||||
@@ -946,7 +946,7 @@ extern "C" fn rpty_close_end(id: i32, master: i32) {
|
||||
unsafe { k_pty_close_end(id, master, my_pid()) };
|
||||
pty_note("close", id, master, my_pid());
|
||||
}
|
||||
extern "C" fn rpty_read(id: i32, master: i32, at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rpty_read(id: i32, master: i32, at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
let r = unsafe { k_pty_read(id, master, at, len) };
|
||||
// End of file on the master is how a terminal emulator learns its shell is
|
||||
// gone -- and if it is wrong, the emulator kills the session. Worth saying.
|
||||
@@ -955,7 +955,7 @@ extern "C" fn rpty_read(id: i32, master: i32, at: u32, len: u32) -> i32 {
|
||||
}
|
||||
r
|
||||
}
|
||||
extern "C" fn rpty_write(id: i32, master: i32, at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rpty_write(id: i32, master: i32, at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_pty_write(id, master, at, len) }
|
||||
}
|
||||
extern "C" fn rpty_readable(id: i32, master: i32) -> i32 {
|
||||
@@ -973,10 +973,10 @@ extern "C" fn rpty_get_lock(id: i32) -> i32 {
|
||||
extern "C" fn rpty_set_lock(id: i32, locked: i32) {
|
||||
unsafe { k_pty_set_lock(id, locked) }
|
||||
}
|
||||
extern "C" fn rpty_get_termios(id: i32, at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rpty_get_termios(id: i32, at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_pty_get_termios(id, at, len) }
|
||||
}
|
||||
extern "C" fn rpty_set_termios(id: i32, at: u32, len: u32, flush: i32) -> i32 {
|
||||
extern "C" fn rpty_set_termios(id: i32, at: gex_kernel::PAddr, len: u32, flush: i32) -> i32 {
|
||||
unsafe { k_pty_set_termios(id, at, len, flush) }
|
||||
}
|
||||
extern "C" fn rpty_winsize(id: i32) -> u64 {
|
||||
@@ -1038,10 +1038,10 @@ fn global_ptys() -> Option<gex_kernel::pty::Remote> {
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn rs_bind(path: u32, path_len: u32) -> i32 {
|
||||
extern "C" fn rs_bind(path: gex_kernel::PAddr, path_len: u32) -> i32 {
|
||||
unsafe { k_sock_bind(path, path_len) }
|
||||
}
|
||||
extern "C" fn rs_connect(path: u32, path_len: u32) -> i32 {
|
||||
extern "C" fn rs_connect(path: gex_kernel::PAddr, path_len: u32) -> i32 {
|
||||
unsafe { k_sock_connect(path, path_len) }
|
||||
}
|
||||
extern "C" fn rs_accept(listener: i32) -> i32 {
|
||||
@@ -1050,10 +1050,10 @@ extern "C" fn rs_accept(listener: i32) -> i32 {
|
||||
extern "C" fn rs_pending(listener: i32) -> i32 {
|
||||
unsafe { k_sock_pending(listener) }
|
||||
}
|
||||
extern "C" fn rs_read(id: i32, at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rs_read(id: i32, at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_sock_read(id, at, len) }
|
||||
}
|
||||
extern "C" fn rs_write(id: i32, at: u32, len: u32) -> i32 {
|
||||
extern "C" fn rs_write(id: i32, at: gex_kernel::PAddr, len: u32) -> i32 {
|
||||
unsafe { k_sock_write(id, at, len) }
|
||||
}
|
||||
extern "C" fn rs_readable(id: i32) -> i32 {
|
||||
@@ -1071,7 +1071,7 @@ extern "C" fn rs_close(id: i32) {
|
||||
extern "C" fn rs_shutdown(id: i32) {
|
||||
unsafe { k_sock_shutdown(id) }
|
||||
}
|
||||
extern "C" fn rs_exists(path: u32, path_len: u32) -> i32 {
|
||||
extern "C" fn rs_exists(path: gex_kernel::PAddr, path_len: u32) -> i32 {
|
||||
unsafe { k_sock_exists(path, path_len) }
|
||||
}
|
||||
|
||||
@@ -1204,7 +1204,7 @@ impl FileHandle for StdinQueue {
|
||||
// SAFETY: scalars, and the kernel refuses a range outside this
|
||||
// process's memory rather than writing it.
|
||||
if unsafe { k_present() } != 0 {
|
||||
let n = unsafe { k_stdin_read(buf.as_mut_ptr() as u32, buf.len() as u32) };
|
||||
let n = unsafe { k_stdin_read(buf.as_mut_ptr() as gex_kernel::PAddr, buf.len() as u32) };
|
||||
if n > 0 {
|
||||
return Ok(n as u64);
|
||||
}
|
||||
@@ -2191,6 +2191,20 @@ fn arena_take(len: u64) -> Option<u64> {
|
||||
/// stepped over as arena of its own (see `MemoryMap::grows_on_demand`), or
|
||||
/// `None` when the memory would not grow.
|
||||
fn arena_back(end: u64) -> Option<u64> {
|
||||
// The high arena of a 64-bit memory (docs/mem64.md §3): the memory grows
|
||||
// to cover it (V8 commits lazily), but the low arena's cursor -- which
|
||||
// this module's heap also takes from, and whose addresses cross to JS as
|
||||
// u32 -- stays below 4 GiB.
|
||||
if end > gex_abi::HIGH_ARENA_BASE && gex_abi::mem64() {
|
||||
if !grow_memory_to(end) {
|
||||
return None;
|
||||
}
|
||||
let prev = HIGH_NEXT.fetch_max(end, std::sync::atomic::Ordering::Relaxed).max(gex_abi::HIGH_ARENA_BASE);
|
||||
if end > prev {
|
||||
memmap::claim(memmap::Kind::Guest, 0, prev, end, "guest mapping (high arena)");
|
||||
}
|
||||
return Some(prev);
|
||||
}
|
||||
if !grow_memory_to(end) {
|
||||
return None;
|
||||
}
|
||||
@@ -2201,6 +2215,9 @@ fn arena_back(end: u64) -> Option<u64> {
|
||||
Some(prev)
|
||||
}
|
||||
|
||||
/// How far the high arena has been backed (claimed in `memmap`).
|
||||
static HIGH_NEXT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
||||
|
||||
/// What the kernel's memory map calls to claim a span of arena.
|
||||
///
|
||||
/// The same cursor this module's heap takes from, so the two can never hand
|
||||
@@ -2614,7 +2631,12 @@ static mut HOST: Option<Host> = None;
|
||||
/// than failing cleanly. The guest gets everything below this line; the shim
|
||||
/// takes what is above through ordinary pointers. The guest's stack, its
|
||||
/// highest thing, is placed here rather than at `STACK_TOP`.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
const GUEST_LIMIT: u64 = 0x7fff_0000;
|
||||
/// A wasm64 shim's slice may span the whole memory64, high arena included
|
||||
/// (docs/mem64.md step 6).
|
||||
#[cfg(target_arch = "wasm64")]
|
||||
const GUEST_LIMIT: u64 = gex_abi::LIMIT64;
|
||||
|
||||
/// The guest's memory, as a slice.
|
||||
///
|
||||
@@ -4050,14 +4072,14 @@ pub extern "C" fn gex_add_file_part(path: u32, path_len: u32, offset: u64, data:
|
||||
// SAFETY: scalars and pointers into this module's memory, which the
|
||||
// kernel copies from.
|
||||
unsafe {
|
||||
let h = k_fs_open(path, path_len, 1 /* O_WRONLY */, 0o644);
|
||||
let h = k_fs_open(gex_kernel::PAddr::from(path), path_len, 1 /* O_WRONLY */, 0o644);
|
||||
if h < 0 {
|
||||
warn_line(&format!("gex_add_file_part: cannot reopen a staged file ({h})\n"));
|
||||
return;
|
||||
}
|
||||
let mut done = 0u32;
|
||||
while done < data_len {
|
||||
let n = k_fs_write(h, offset + done as u64, data + done, data_len - done);
|
||||
let n = k_fs_write(h, offset + done as u64, gex_kernel::PAddr::from(data + done), data_len - done);
|
||||
if n <= 0 {
|
||||
warn_line("gex_add_file_part: a staged write failed\n");
|
||||
break;
|
||||
@@ -4095,7 +4117,7 @@ pub extern "C" fn gex_add_file(path: u32, path_len: u32, data: u32, data_len: u3
|
||||
// cost the rootfs's size in every process that mounted it.
|
||||
if unsafe { k_present() } != 0 {
|
||||
if unsafe { k_fs_populated() } == 0 {
|
||||
unsafe { k_fs_add(path, path_len, data, data_len) };
|
||||
unsafe { k_fs_add(gex_kernel::PAddr::from(path), path_len, gex_kernel::PAddr::from(data), data_len) };
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -4117,7 +4139,7 @@ pub extern "C" fn gex_add_hardlink(target: u32, target_len: u32, path: u32, path
|
||||
// SAFETY: scalars naming ranges of this module's memory, no borrow held.
|
||||
if unsafe { k_present() } != 0 {
|
||||
if unsafe { k_fs_populated() } == 0 {
|
||||
return (unsafe { k_fs_link(target, target_len, path, path_len) } == 0) as i32;
|
||||
return (unsafe { k_fs_link(gex_kernel::PAddr::from(target), target_len, gex_kernel::PAddr::from(path), path_len) } == 0) as i32;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -4135,7 +4157,7 @@ pub extern "C" fn gex_set_mtime(path: u32, path_len: u32, secs: i64) {
|
||||
// SAFETY: scalars naming a range of this module's memory, no borrow held.
|
||||
if unsafe { k_present() } != 0 {
|
||||
if unsafe { k_fs_populated() } == 0 {
|
||||
unsafe { k_fs_set_mtime(path, path_len, secs) };
|
||||
unsafe { k_fs_set_mtime(gex_kernel::PAddr::from(path), path_len, secs) };
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -4153,7 +4175,7 @@ pub extern "C" fn gex_chmod(path: u32, path_len: u32, mode: u32) {
|
||||
// SAFETY: scalars naming a range of this module's memory, no borrow held.
|
||||
if unsafe { k_present() } != 0 {
|
||||
if unsafe { k_fs_populated() } == 0 {
|
||||
unsafe { k_fs_chmod(path, path_len, mode) };
|
||||
unsafe { k_fs_chmod(gex_kernel::PAddr::from(path), path_len, mode) };
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -4171,7 +4193,7 @@ pub extern "C" fn gex_add_dir(path: u32, path_len: u32) {
|
||||
// SAFETY: as `gex_add_file` -- scalars, no borrow held.
|
||||
if unsafe { k_present() } != 0 {
|
||||
if unsafe { k_fs_populated() } == 0 {
|
||||
unsafe { k_fs_mkdir(path, path_len, 0o755) };
|
||||
unsafe { k_fs_mkdir(gex_kernel::PAddr::from(path), path_len, 0o755) };
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -6054,7 +6076,7 @@ pub extern "C" fn gex_syscall(ctx: i32, pc: i64) {
|
||||
// the call.
|
||||
if snapshots_shared() {
|
||||
let n = blob.len().min(snapshot_share_cap()) as u32;
|
||||
unsafe { k_snap_store(key, blob.as_ptr() as u32, n) };
|
||||
unsafe { k_snap_store(key, blob.as_ptr() as gex_kernel::PAddr, n) };
|
||||
}
|
||||
link_state(3);
|
||||
// Under tracing only: the per-process line already says
|
||||
@@ -8343,7 +8365,7 @@ fn snap_lookup(key: u64) -> Option<Vec<u8>> {
|
||||
return None;
|
||||
}
|
||||
let mut blob = vec![0u8; len as usize];
|
||||
let got = k_snap_get(key, blob.as_mut_ptr() as u32, len);
|
||||
let got = k_snap_get(key, blob.as_mut_ptr() as gex_kernel::PAddr, len);
|
||||
(got == len).then_some(blob)
|
||||
}
|
||||
}
|
||||
@@ -8398,10 +8420,10 @@ fn share_module(wasm: &[u8], slots: u64, dirs: &[(u64, u32)]) -> u32 {
|
||||
raw.extend_from_slice(&base.to_le_bytes());
|
||||
}
|
||||
k_mod_store(
|
||||
wasm.as_ptr() as u32,
|
||||
wasm.as_ptr() as gex_kernel::PAddr,
|
||||
wasm.len() as u32,
|
||||
slots,
|
||||
raw.as_ptr() as u32,
|
||||
raw.as_ptr() as gex_kernel::PAddr,
|
||||
raw.len() as u32,
|
||||
)
|
||||
}
|
||||
@@ -8509,12 +8531,12 @@ fn shared_modules_of_object(
|
||||
}
|
||||
took += len as u64;
|
||||
let mut wasm = vec![0u8; len];
|
||||
if k_mod_read(id, wasm.as_mut_ptr() as u32, len as u32) as usize != len {
|
||||
if k_mod_read(id, wasm.as_mut_ptr() as gex_kernel::PAddr, len as u32) as usize != len {
|
||||
continue;
|
||||
}
|
||||
let dlen = k_mod_dirs_len(id) as usize;
|
||||
let mut raw = vec![0u8; dlen];
|
||||
if dlen > 0 && k_mod_dirs_read(id, raw.as_mut_ptr() as u32, dlen as u32) as usize != dlen
|
||||
if dlen > 0 && k_mod_dirs_read(id, raw.as_mut_ptr() as gex_kernel::PAddr, dlen as u32) as usize != dlen
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -8579,7 +8601,7 @@ fn backfill_registry(h: &mut Host) {
|
||||
continue;
|
||||
}
|
||||
let mut wasm = vec![0u8; len];
|
||||
if unsafe { k_mod_read(id, wasm.as_mut_ptr() as u32, len as u32) } as usize != len {
|
||||
if unsafe { k_mod_read(id, wasm.as_mut_ptr() as gex_kernel::PAddr, len as u32) } as usize != len {
|
||||
continue;
|
||||
}
|
||||
// As this process installed it: at its dense base, or not at all.
|
||||
@@ -8681,12 +8703,12 @@ fn shared_module_at(
|
||||
return None;
|
||||
}
|
||||
let mut wasm = vec![0u8; len];
|
||||
if k_mod_read(id, wasm.as_mut_ptr() as u32, len as u32) as usize != len {
|
||||
if k_mod_read(id, wasm.as_mut_ptr() as gex_kernel::PAddr, len as u32) as usize != len {
|
||||
return None;
|
||||
}
|
||||
let dlen = k_mod_dirs_len(id) as usize;
|
||||
let mut raw = vec![0u8; dlen];
|
||||
if dlen > 0 && k_mod_dirs_read(id, raw.as_mut_ptr() as u32, dlen as u32) as usize != dlen {
|
||||
if dlen > 0 && k_mod_dirs_read(id, raw.as_mut_ptr() as gex_kernel::PAddr, dlen as u32) as usize != dlen {
|
||||
return None;
|
||||
}
|
||||
let dirs = raw
|
||||
|
||||
@@ -27,7 +27,7 @@ pub fn connect(kernel: &Kernel) -> Option<Xwm> {
|
||||
fn open(kernel: &Kernel) -> Option<Arc<dyn FileHandle>> {
|
||||
for name in X_SOCKETS {
|
||||
if let Some(r) = kernel.unix_remote {
|
||||
let id = (r.connect)(name.as_ptr() as u32, name.len() as u32);
|
||||
let id = (r.connect)(name.as_ptr() as gex_kernel::PAddr, name.len() as u32);
|
||||
if id >= 0 {
|
||||
unsafe {
|
||||
crate::write_stderr(&format!(
|
||||
|
||||
@@ -15,6 +15,6 @@ gex-kernel.workspace = true
|
||||
# Serve an x86-64 guest. See `gex_abi::X86`.
|
||||
x86 = ["gex-abi/x86", "gex-kernel/x86"]
|
||||
# Processes with a 64-bit memory (the wasm64 browser shim, docs/mem64.md step 6).
|
||||
process64 = ["gex-kmem/process64"]
|
||||
process64 = ["gex-kmem/process64", "gex-kernel/process64"]
|
||||
# Host a 32-bit big-endian PowerPC guest (docs/ppc.md).
|
||||
ppc = ["gex-abi/ppc", "gex-kernel/ppc"]
|
||||
+47
-47
@@ -261,7 +261,7 @@ fn stdin() -> &'static Mutex<(std::collections::VecDeque<u8>, bool)> {
|
||||
|
||||
/// Append what was typed, from the caller's process memory.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_stdin_write(at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_stdin_write(at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
// SAFETY: the same copy every pipe write makes, and `pull_checked`
|
||||
// refuses a range outside the process memory rather than reading it.
|
||||
@@ -289,7 +289,7 @@ pub extern "C" fn k_stdin_ready() -> i32 {
|
||||
|
||||
/// Take up to `len` bytes into the caller's process memory.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_stdin_read(at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_stdin_read(at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let mut s = stdin().lock().unwrap_or_else(|e| e.into_inner());
|
||||
let n = (len as usize).min(s.0.len());
|
||||
if n == 0 {
|
||||
@@ -334,7 +334,7 @@ fn fg_group() -> i32 {
|
||||
/// group is what could only be done here, next to the process table. Returns
|
||||
/// how many bytes of echo are queued, so the caller knows whether to drain.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_tty_input(at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_tty_input(at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
// SAFETY: the same copy `k_stdin_write` makes, and `pull_checked` refuses
|
||||
// a range outside the caller's memory rather than reading it.
|
||||
@@ -369,7 +369,7 @@ pub extern "C" fn k_tty_input(at: u32, len: u32) -> i32 {
|
||||
|
||||
/// Take what the terminal should display, into the caller's memory.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_tty_echo(at: u32, cap: u32) -> i32 {
|
||||
pub extern "C" fn k_tty_echo(at: gex_kmem::PAddr, cap: u32) -> i32 {
|
||||
let mut q = echoed().lock().unwrap_or_else(|e| e.into_inner());
|
||||
let n = (cap as usize).min(q.len());
|
||||
if n == 0 {
|
||||
@@ -382,7 +382,7 @@ pub extern "C" fn k_tty_echo(at: u32, cap: u32) -> i32 {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_tty_get_termios(at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_tty_get_termios(at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let t = tty().termios();
|
||||
let n = (len as usize).min(t.len());
|
||||
unsafe { gex_kmem::push_checked(at, t.as_ptr(), n as u32) };
|
||||
@@ -390,7 +390,7 @@ pub extern "C" fn k_tty_get_termios(at: u32, len: u32) -> i32 {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_tty_set_termios(at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_tty_set_termios(at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let mut buf = vec![0u8; (len as usize).min(60)];
|
||||
let n = buf.len() as u32;
|
||||
unsafe { gex_kmem::pull_checked(buf.as_mut_ptr(), at, n) };
|
||||
@@ -399,7 +399,7 @@ pub extern "C" fn k_tty_set_termios(at: u32, len: u32) -> i32 {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_tty_get_locked(at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_tty_get_locked(at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let t = tty().locked_termios();
|
||||
let n = (len as usize).min(t.len());
|
||||
unsafe { gex_kmem::push_checked(at, t.as_ptr(), n as u32) };
|
||||
@@ -407,7 +407,7 @@ pub extern "C" fn k_tty_get_locked(at: u32, len: u32) -> i32 {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_tty_set_locked(at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_tty_set_locked(at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let mut buf = vec![0u8; (len as usize).min(60)];
|
||||
let n = buf.len() as u32;
|
||||
unsafe { gex_kmem::pull_checked(buf.as_mut_ptr(), at, n) };
|
||||
@@ -532,7 +532,7 @@ pub extern "C" fn k_tty_queued() -> i32 {
|
||||
/// go through the discipline, because that is what "as though typed" means --
|
||||
/// a `\n` pushed this way completes a line.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_tty_push(at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_tty_push(at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
k_tty_input(at, len)
|
||||
}
|
||||
|
||||
@@ -689,10 +689,10 @@ fn modules() -> &'static Mutex<ModStore> {
|
||||
/// memory, so the bytes must be pulled across rather than read directly.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn k_mod_store(
|
||||
ptr: u32,
|
||||
ptr: gex_kmem::PAddr,
|
||||
len: u32,
|
||||
slots: u64,
|
||||
dirs_ptr: u32,
|
||||
dirs_ptr: gex_kmem::PAddr,
|
||||
dirs_len: u32,
|
||||
) -> u32 {
|
||||
let mut m = modules().lock().unwrap_or_else(|e| e.into_inner());
|
||||
@@ -855,7 +855,7 @@ pub extern "C" fn k_mod_dirs_len(id: u32) -> u32 {
|
||||
///
|
||||
/// SAFETY: `out` and `cap` describe a range of the calling process's memory.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn k_mod_read(id: u32, out: u32, cap: u32) -> u32 {
|
||||
pub unsafe extern "C" fn k_mod_read(id: u32, out: gex_kmem::PAddr, cap: u32) -> u32 {
|
||||
let m = modules().lock().unwrap_or_else(|e| e.into_inner());
|
||||
let Some(sm) = m.mods.get((id as usize).wrapping_sub(1)) else {
|
||||
return 0;
|
||||
@@ -871,7 +871,7 @@ pub unsafe extern "C" fn k_mod_read(id: u32, out: u32, cap: u32) -> u32 {
|
||||
///
|
||||
/// SAFETY: as above.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn k_mod_dirs_read(id: u32, out: u32, cap: u32) -> u32 {
|
||||
pub unsafe extern "C" fn k_mod_dirs_read(id: u32, out: gex_kmem::PAddr, cap: u32) -> u32 {
|
||||
let m = modules().lock().unwrap_or_else(|e| e.into_inner());
|
||||
let Some(sm) = m.mods.get((id as usize).wrapping_sub(1)) else {
|
||||
return 0;
|
||||
@@ -1004,7 +1004,7 @@ fn snaps() -> &'static Mutex<Snaps> {
|
||||
/// SAFETY: `ptr` names the calling process's memory, pulled across not read. See
|
||||
/// `k_mod_store`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn k_snap_store(key: u64, ptr: u32, len: u32) {
|
||||
pub unsafe extern "C" fn k_snap_store(key: u64, ptr: gex_kmem::PAddr, len: u32) {
|
||||
let mut s = snaps().lock().unwrap_or_else(|e| e.into_inner());
|
||||
if key == 0 || len == 0 || s.n == SNAP_MAX {
|
||||
return;
|
||||
@@ -1043,7 +1043,7 @@ pub extern "C" fn k_snap_len(key: u64) -> u32 {
|
||||
/// Copy the linked image of `key` into the caller's memory. Returns how many
|
||||
/// bytes were written.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn k_snap_get(key: u64, at: u32, cap: u32) -> u32 {
|
||||
pub unsafe extern "C" fn k_snap_get(key: u64, at: gex_kmem::PAddr, cap: u32) -> u32 {
|
||||
let s = snaps().lock().unwrap_or_else(|e| e.into_inner());
|
||||
let Some(&(_, off, len)) = s.at[..s.n].iter().find(|(k, _, _)| *k == key) else {
|
||||
return 0;
|
||||
@@ -1370,7 +1370,7 @@ pub extern "C" fn k_pipe_new(pid: i32) -> i32 {
|
||||
/// The last message written to a pipe, into the caller's memory. A diagnostic;
|
||||
/// see `Pipe::last_message`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_pipe_last_message(id: i32, at: u32, cap: u32) -> i32 {
|
||||
pub extern "C" fn k_pipe_last_message(id: i32, at: gex_kmem::PAddr, cap: u32) -> i32 {
|
||||
let Some(p) = pipe_at(id) else { return -1 };
|
||||
let text = p.last_message();
|
||||
let n = text.len().min(cap as usize);
|
||||
@@ -1428,7 +1428,7 @@ pub extern "C" fn k_pipe_close_end(id: i32, write: i32, pid: i32) {
|
||||
/// out first (this module cannot address the process's memory) -- which is what
|
||||
/// lets the two ends live in different address spaces. Returns bytes or -errno.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_pipe_write(id: i32, at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_pipe_write(id: i32, at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let Some(p) = pipe_at(id) else { return -9 }; // EBADF
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
unsafe { gex_kmem::pull_checked(buf.as_mut_ptr(), at, len) };
|
||||
@@ -1440,7 +1440,7 @@ pub extern "C" fn k_pipe_write(id: i32, at: u32, len: u32) -> i32 {
|
||||
|
||||
/// Read up to `len` bytes into the caller's memory. See [`k_pipe_write`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_pipe_read(id: i32, at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_pipe_read(id: i32, at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let Some(p) = pipe_at(id) else { return -9 };
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
match p.read(&mut buf) {
|
||||
@@ -1696,7 +1696,7 @@ fn last_slave_closer() -> &'static Mutex<BTreeMap<i32, i32>> {
|
||||
/// terminal input: the line discipline runs and `^C`-style signals reach the
|
||||
/// foreground group. On the slave it is program output. Returns bytes or -errno.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_pty_write(id: i32, master: i32, at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_pty_write(id: i32, master: i32, at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let Some(p) = pty_at(id) else { return -9 }; // EBADF
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
unsafe { gex_kmem::pull_checked(buf.as_mut_ptr(), at, len) };
|
||||
@@ -1715,7 +1715,7 @@ pub extern "C" fn k_pty_write(id: i32, master: i32, at: u32, len: u32) -> i32 {
|
||||
|
||||
/// Read up to `len` bytes from an end into the caller's memory.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_pty_read(id: i32, master: i32, at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_pty_read(id: i32, master: i32, at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let Some(p) = pty_at(id) else { return -9 };
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
let r = if master != 0 {
|
||||
@@ -1786,7 +1786,7 @@ pub extern "C" fn k_pty_set_lock(id: i32, locked: i32) {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_pty_get_termios(id: i32, at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_pty_get_termios(id: i32, at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let Some(p) = pty_at(id) else { return -1 };
|
||||
let t = p.tty.termios();
|
||||
let n = (len as usize).min(t.len());
|
||||
@@ -1795,7 +1795,7 @@ pub extern "C" fn k_pty_get_termios(id: i32, at: u32, len: u32) -> i32 {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_pty_set_termios(id: i32, at: u32, len: u32, flush: i32) -> i32 {
|
||||
pub extern "C" fn k_pty_set_termios(id: i32, at: gex_kmem::PAddr, len: u32, flush: i32) -> i32 {
|
||||
let Some(p) = pty_at(id) else { return -1 };
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
unsafe { gex_kmem::pull_checked(buf.as_mut_ptr(), at, len) };
|
||||
@@ -1877,7 +1877,7 @@ pub extern "C" fn k_pty_flush(id: i32, which: i32) {
|
||||
/// Copy `len` bytes of the calling process's memory in, and report their sum. A
|
||||
/// live check that the two-memory pull path works.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_checksum(at: u32, len: u32) -> u32 {
|
||||
pub extern "C" fn k_checksum(at: gex_kmem::PAddr, len: u32) -> u32 {
|
||||
let n = (len as usize).min(4096);
|
||||
let buf = unsafe { &mut *core::ptr::addr_of_mut!(SCRATCH) };
|
||||
unsafe { gex_kmem::pull_checked(buf.as_mut_ptr(), at, n as u32) };
|
||||
@@ -1933,7 +1933,7 @@ fn handle_at(id: i32) -> Option<std::sync::Arc<dyn FileHandle>> {
|
||||
|
||||
/// A path argument, copied out of the caller's memory (this module cannot
|
||||
/// dereference an address in it).
|
||||
fn path_of(at: u32, len: u32) -> String {
|
||||
fn path_of(at: gex_kmem::PAddr, len: u32) -> String {
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
if len > 0 {
|
||||
unsafe { gex_kmem::pull_checked(buf.as_mut_ptr(), at, len) };
|
||||
@@ -1962,7 +1962,7 @@ fn as_unit(r: Result<(), gex_kernel::errno::Errno>) -> i32 {
|
||||
|
||||
/// Open a path, returning a handle id or a negated errno.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_open(path: u32, path_len: u32, flags: u64, mode: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_open(path: gex_kmem::PAddr, path_len: u32, flags: u64, mode: u32) -> i32 {
|
||||
let p = path_of(path, path_len);
|
||||
match rootfs().open(&p, flags, mode) {
|
||||
Ok(h) => {
|
||||
@@ -2019,7 +2019,7 @@ pub extern "C" fn k_fs_close(handle: i32) {
|
||||
|
||||
/// Read into the caller's memory. Returns bytes read, or a negated errno.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_read(handle: i32, offset: u64, at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_read(handle: i32, offset: u64, at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let Some(h) = handle_at(handle) else { return -9 };
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
match h.read_at(offset, &mut buf) {
|
||||
@@ -2035,7 +2035,7 @@ pub extern "C" fn k_fs_read(handle: i32, offset: u64, at: u32, len: u32) -> i32
|
||||
|
||||
/// Write from the caller's memory.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_write(handle: i32, offset: u64, at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_write(handle: i32, offset: u64, at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let Some(h) = handle_at(handle) else { return -9 };
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
if len > 0 {
|
||||
@@ -2046,7 +2046,7 @@ pub extern "C" fn k_fs_write(handle: i32, offset: u64, at: u32, len: u32) -> i32
|
||||
|
||||
/// This handle's metadata, as `remote::META_BYTES` bytes at `at`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_meta(handle: i32, at: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_meta(handle: i32, at: gex_kmem::PAddr) -> i32 {
|
||||
let Some(h) = handle_at(handle) else { return -9 };
|
||||
match h.metadata() {
|
||||
Ok(m) => {
|
||||
@@ -2066,7 +2066,7 @@ pub extern "C" fn k_fs_truncate(handle: i32, len: u64) -> i32 {
|
||||
|
||||
/// A directory listing, or `-ERANGE` when `cap` will not hold it.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_read_dir(handle: i32, at: u32, cap: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_read_dir(handle: i32, at: gex_kmem::PAddr, cap: u32) -> i32 {
|
||||
let Some(h) = handle_at(handle) else { return -9 };
|
||||
match h.read_dir() {
|
||||
Ok(entries) => {
|
||||
@@ -2086,7 +2086,7 @@ pub extern "C" fn k_fs_read_dir(handle: i32, at: u32, cap: u32) -> i32 {
|
||||
/// A path's metadata, without opening it -- which matters for a symlink and
|
||||
/// for anything an open would refuse.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_path_meta(path: u32, path_len: u32, at: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_path_meta(path: gex_kmem::PAddr, path_len: u32, at: gex_kmem::PAddr) -> i32 {
|
||||
let p = path_of(path, path_len);
|
||||
match rootfs().metadata(&p) {
|
||||
Ok(m) => {
|
||||
@@ -2099,7 +2099,7 @@ pub extern "C" fn k_fs_path_meta(path: u32, path_len: u32, at: u32) -> i32 {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_read_link(path: u32, path_len: u32, at: u32, cap: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_read_link(path: gex_kmem::PAddr, path_len: u32, at: gex_kmem::PAddr, cap: u32) -> i32 {
|
||||
let p = path_of(path, path_len);
|
||||
match rootfs().read_link(&p) {
|
||||
Ok(t) => {
|
||||
@@ -2117,33 +2117,33 @@ pub extern "C" fn k_fs_read_link(path: u32, path_len: u32, at: u32, cap: u32) ->
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_unlink(path: u32, path_len: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_unlink(path: gex_kmem::PAddr, path_len: u32) -> i32 {
|
||||
as_unit(rootfs().unlink(&path_of(path, path_len)))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_mkdir(path: u32, path_len: u32, mode: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_mkdir(path: gex_kmem::PAddr, path_len: u32, mode: u32) -> i32 {
|
||||
as_unit(rootfs().mkdir(&path_of(path, path_len), mode))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_rmdir(path: u32, path_len: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_rmdir(path: gex_kmem::PAddr, path_len: u32) -> i32 {
|
||||
as_unit(rootfs().rmdir(&path_of(path, path_len)))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_rename(from: u32, from_len: u32, to: u32, to_len: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_rename(from: gex_kmem::PAddr, from_len: u32, to: gex_kmem::PAddr, to_len: u32) -> i32 {
|
||||
as_unit(rootfs().rename(&path_of(from, from_len), &path_of(to, to_len)))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_chmod(path: u32, path_len: u32, mode: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_chmod(path: gex_kmem::PAddr, path_len: u32, mode: u32) -> i32 {
|
||||
as_unit(rootfs().chmod(&path_of(path, path_len), mode))
|
||||
}
|
||||
|
||||
/// A path's modification time: `utimensat`, or a bundle's file times.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_set_mtime(path: u32, path_len: u32, secs: i64) -> i32 {
|
||||
pub extern "C" fn k_fs_set_mtime(path: gex_kmem::PAddr, path_len: u32, secs: i64) -> i32 {
|
||||
as_unit(rootfs().set_mtime(&path_of(path, path_len), secs))
|
||||
}
|
||||
|
||||
@@ -2160,12 +2160,12 @@ pub extern "C" fn k_fs_touch(handle: i32, secs: i64) -> i32 {
|
||||
/// absolute-path target says, which would then resolve against the link's own
|
||||
/// directory and reach nothing).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_symlink(target: u32, target_len: u32, path: u32, path_len: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_symlink(target: gex_kmem::PAddr, target_len: u32, path: gex_kmem::PAddr, path_len: u32) -> i32 {
|
||||
as_unit(rootfs().symlink(&path_of_raw(target, target_len), &path_of(path, path_len)))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_link(from: u32, from_len: u32, to: u32, to_len: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_link(from: gex_kmem::PAddr, from_len: u32, to: gex_kmem::PAddr, to_len: u32) -> i32 {
|
||||
as_unit(rootfs().link(&path_of(from, from_len), &path_of(to, to_len)))
|
||||
}
|
||||
|
||||
@@ -2173,7 +2173,7 @@ pub extern "C" fn k_fs_link(from: u32, from_len: u32, to: u32, to_len: u32) -> i
|
||||
/// to: Xvfb binds `/tmp/.X11-unix/X0` and its start script waits for that path
|
||||
/// to exist before running a client.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_mksocket(path: u32, path_len: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_mksocket(path: gex_kmem::PAddr, path_len: u32) -> i32 {
|
||||
rootfs().add_socket(&path_of(path, path_len));
|
||||
0
|
||||
}
|
||||
@@ -2181,7 +2181,7 @@ pub extern "C" fn k_fs_mksocket(path: u32, path_len: u32) -> i32 {
|
||||
/// Put a file into the shared root before anything runs -- how a page's bundle
|
||||
/// gets in, once, rather than through one process only it could then see.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_fs_add(path: u32, path_len: u32, at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_fs_add(path: gex_kmem::PAddr, path_len: u32, at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let p = path_of(path, path_len);
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
if len > 0 {
|
||||
@@ -2256,7 +2256,7 @@ fn keep(s: std::sync::Arc<UnixSocket>) -> i32 {
|
||||
/// Bind and listen in one: a remote socket has no state between `bind` and
|
||||
/// `listen` worth modelling. Returns a listener id, or a negated errno.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_sock_bind(path: u32, path_len: u32) -> i32 {
|
||||
pub extern "C" fn k_sock_bind(path: gex_kmem::PAddr, path_len: u32) -> i32 {
|
||||
let p = path_of_raw(path, path_len);
|
||||
let s = std::sync::Arc::new(UnixSocket::new(true));
|
||||
if let Err(e) = s.bind(&p) {
|
||||
@@ -2273,7 +2273,7 @@ pub extern "C" fn k_sock_bind(path: u32, path_len: u32) -> i32 {
|
||||
|
||||
/// Connect to a name. Returns this end's id, or a negated errno.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_sock_connect(path: u32, path_len: u32) -> i32 {
|
||||
pub extern "C" fn k_sock_connect(path: gex_kmem::PAddr, path_len: u32) -> i32 {
|
||||
let p = path_of_raw(path, path_len);
|
||||
let s = std::sync::Arc::new(UnixSocket::new(true));
|
||||
match names().connect(&s, &p) {
|
||||
@@ -2315,7 +2315,7 @@ pub extern "C" fn k_sock_pending(listener: i32) -> i32 {
|
||||
/// Read into the caller's memory. The bytes cross by `memory.copy`, which is
|
||||
/// what makes two ends in two address spaces possible at all.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_sock_read(id: i32, at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_sock_read(id: i32, at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let Some(s) = sock_at(id) else { return -9 };
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
match s.recv(&mut buf, 0, false) {
|
||||
@@ -2330,7 +2330,7 @@ pub extern "C" fn k_sock_read(id: i32, at: u32, len: u32) -> i32 {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_sock_write(id: i32, at: u32, len: u32) -> i32 {
|
||||
pub extern "C" fn k_sock_write(id: i32, at: gex_kmem::PAddr, len: u32) -> i32 {
|
||||
let Some(s) = sock_at(id) else { return -9 };
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
if len > 0 {
|
||||
@@ -2385,14 +2385,14 @@ pub extern "C" fn k_sock_shutdown(id: i32) {
|
||||
/// Whether anything is listening on a name -- which is what a client that
|
||||
/// looks before it leaps is asking. Xlib does; Mesa does not.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn k_sock_exists(path: u32, path_len: u32) -> i32 {
|
||||
pub extern "C" fn k_sock_exists(path: gex_kmem::PAddr, path_len: u32) -> i32 {
|
||||
names().exists(&path_of_raw(path, path_len)) as i32
|
||||
}
|
||||
|
||||
/// A path exactly as the caller wrote it. A socket name is compared for
|
||||
/// equality, never looked up, so the leading slash must survive -- trimming one
|
||||
/// end's would make two different names.
|
||||
fn path_of_raw(at: u32, len: u32) -> String {
|
||||
fn path_of_raw(at: gex_kmem::PAddr, len: u32) -> String {
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
if len > 0 {
|
||||
unsafe { gex_kmem::pull_checked(buf.as_mut_ptr(), at, len) };
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/* I/O into buffers above 4 GiB (docs/mem64.md step 6): a file, a pipe and
|
||||
* a socketpair read into and written from anonymous memory in the high arena.
|
||||
* In the browser these bytes cross between the process's shim and the global
|
||||
* kernel module by address, which was 32-bit before the wasm64 shim. Under
|
||||
* `gexc run --mem64` (run.sh passes it for mem64-*); with a wasm32 memory, or
|
||||
* natively, the buffers are low and the output is the same. x86-64 only. */
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define LEN 100000
|
||||
|
||||
static unsigned sum(const unsigned char *p, size_t n) {
|
||||
unsigned h = 2166136261u;
|
||||
for (size_t i = 0; i < n; i++) h = (h ^ p[i]) * 16777619u;
|
||||
return h;
|
||||
}
|
||||
|
||||
static char *high(size_t n) {
|
||||
char *p = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
return p == MAP_FAILED ? NULL : p;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
char *a = high(1 << 20), *b = high(1 << 20);
|
||||
if (!a || !b) { puts("mmap failed"); return 1; }
|
||||
for (int i = 0; i < LEN; i++) a[i] = (char)(i * 7 + i / 251);
|
||||
unsigned want = sum((unsigned char *)a, LEN);
|
||||
|
||||
/* A file: write from a, read back into b. */
|
||||
const char *path = "/tmp/mem64-io.bin";
|
||||
int fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0644);
|
||||
if (fd < 0) { puts("open failed"); return 1; }
|
||||
if (write(fd, a, LEN) != LEN) { puts("file write short"); return 1; }
|
||||
memset(b, 0, LEN);
|
||||
if (pread(fd, b, LEN, 0) != LEN) { puts("file read short"); return 1; }
|
||||
printf("file %s\n", sum((unsigned char *)b, LEN) == want ? "ok" : "MISMATCH");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
/* A pipe, in chunks the pipe buffer takes. */
|
||||
int p[2];
|
||||
if (pipe(p) != 0) { puts("pipe failed"); return 1; }
|
||||
memset(b, 0, LEN);
|
||||
size_t done = 0;
|
||||
while (done < LEN) {
|
||||
size_t n = LEN - done < 4096 ? LEN - done : 4096;
|
||||
if (write(p[1], a + done, n) != (ssize_t)n) { puts("pipe write short"); return 1; }
|
||||
size_t got = 0;
|
||||
while (got < n) {
|
||||
ssize_t r = read(p[0], b + done + got, n - got);
|
||||
if (r <= 0) { puts("pipe read failed"); return 1; }
|
||||
got += r;
|
||||
}
|
||||
done += n;
|
||||
}
|
||||
printf("pipe %s\n", sum((unsigned char *)b, LEN) == want ? "ok" : "MISMATCH");
|
||||
|
||||
/* A socketpair. */
|
||||
int s[2];
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) != 0) { puts("socketpair failed"); return 1; }
|
||||
memset(b, 0, 4096);
|
||||
if (write(s[0], a, 4096) != 4096) { puts("socket write short"); return 1; }
|
||||
size_t got = 0;
|
||||
while (got < 4096) {
|
||||
ssize_t r = read(s[1], b + got, 4096 - got);
|
||||
if (r <= 0) { puts("socket read failed"); return 1; }
|
||||
got += r;
|
||||
}
|
||||
printf("socket %s\n", sum((unsigned char *)b, 4096) == sum((unsigned char *)a, 4096) ? "ok" : "MISMATCH");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user