base: stability fixes — acpid EC, inputd, block driver, ipcd UDS, netstack loopback, ptyd, ramfs, randd, scheme-utils blocking
This commit is contained in:
+50
-36
@@ -202,55 +202,69 @@ impl RegionHandler for Ec {
|
||||
self.write(offset as u8, value)
|
||||
.ok_or(AmlError::MutexAcquireTimeout) // TODO proper error type
|
||||
}
|
||||
fn read_u16(&self, _region: &OpRegion, _offset: usize) -> Result<u16, acpi::aml::AmlError> {
|
||||
warn!("Got u16 EC read from AML!");
|
||||
Err(acpi::aml::AmlError::NoHandlerForRegionAccess(
|
||||
RegionSpace::EmbeddedControl,
|
||||
)) // TODO proper error type
|
||||
fn read_u16(&self, region: &OpRegion, offset: usize) -> Result<u16, acpi::aml::AmlError> {
|
||||
assert_eq!(region.space, RegionSpace::EmbeddedControl);
|
||||
// EC is 8-bit; compose 16-bit AML reads as little-endian 8-bit EC reads.
|
||||
// Cross-referenced with Linux drivers/acpi/ec.c: acpi_ec_read() and
|
||||
// AML acpi_extract_value() which handles the same byte-decomposition.
|
||||
let lo = self.read_u8(region, offset)? as u16;
|
||||
let hi = self.read_u8(region, offset + 1)? as u16;
|
||||
Ok(lo | (hi << 8))
|
||||
}
|
||||
fn read_u32(&self, _region: &OpRegion, _offset: usize) -> Result<u32, acpi::aml::AmlError> {
|
||||
warn!("Got u32 EC read from AML!");
|
||||
Err(acpi::aml::AmlError::NoHandlerForRegionAccess(
|
||||
RegionSpace::EmbeddedControl,
|
||||
)) // TODO proper error type
|
||||
fn read_u32(&self, region: &OpRegion, offset: usize) -> Result<u32, acpi::aml::AmlError> {
|
||||
assert_eq!(region.space, RegionSpace::EmbeddedControl);
|
||||
let part = self.read_u16(region, offset)? as u32;
|
||||
let part2 = self.read_u16(region, offset + 2)? as u32;
|
||||
Ok(part | (part2 << 16))
|
||||
}
|
||||
fn read_u64(&self, _region: &OpRegion, _offset: usize) -> Result<u64, acpi::aml::AmlError> {
|
||||
warn!("Got u64 EC read from AML!");
|
||||
Err(acpi::aml::AmlError::NoHandlerForRegionAccess(
|
||||
RegionSpace::EmbeddedControl,
|
||||
)) // TODO proper error type
|
||||
fn read_u64(&self, region: &OpRegion, offset: usize) -> Result<u64, acpi::aml::AmlError> {
|
||||
assert_eq!(region.space, RegionSpace::EmbeddedControl);
|
||||
let part = self.read_u32(region, offset)? as u64;
|
||||
let part2 = self.read_u32(region, offset + 4)? as u64;
|
||||
Ok(part | (part2 << 32))
|
||||
}
|
||||
fn write_u16(
|
||||
&self,
|
||||
_region: &OpRegion,
|
||||
_offset: usize,
|
||||
_value: u16,
|
||||
region: &OpRegion,
|
||||
offset: usize,
|
||||
value: u16,
|
||||
) -> Result<(), acpi::aml::AmlError> {
|
||||
warn!("Got u16 EC write from AML!");
|
||||
Err(acpi::aml::AmlError::NoHandlerForRegionAccess(
|
||||
RegionSpace::EmbeddedControl,
|
||||
)) // TODO proper error type
|
||||
assert_eq!(region.space, RegionSpace::EmbeddedControl);
|
||||
let bytes = value.to_le_bytes();
|
||||
self.write_u8(region, offset, bytes[0])?;
|
||||
self.write_u8(region, offset + 1, bytes[1])?;
|
||||
Ok(())
|
||||
}
|
||||
fn write_u32(
|
||||
&self,
|
||||
_region: &OpRegion,
|
||||
_offset: usize,
|
||||
_value: u32,
|
||||
region: &OpRegion,
|
||||
offset: usize,
|
||||
value: u32,
|
||||
) -> Result<(), acpi::aml::AmlError> {
|
||||
warn!("Got u32 EC write from AML!");
|
||||
Err(acpi::aml::AmlError::NoHandlerForRegionAccess(
|
||||
RegionSpace::EmbeddedControl,
|
||||
)) // TODO proper error type
|
||||
assert_eq!(region.space, RegionSpace::EmbeddedControl);
|
||||
let bytes = value.to_le_bytes();
|
||||
self.write_u8(region, offset, bytes[0])?;
|
||||
self.write_u8(region, offset + 1, bytes[1])?;
|
||||
self.write_u8(region, offset + 2, bytes[2])?;
|
||||
self.write_u8(region, offset + 3, bytes[3])?;
|
||||
Ok(())
|
||||
}
|
||||
fn write_u64(
|
||||
&self,
|
||||
_region: &OpRegion,
|
||||
_offset: usize,
|
||||
_value: u64,
|
||||
region: &OpRegion,
|
||||
offset: usize,
|
||||
value: u64,
|
||||
) -> Result<(), acpi::aml::AmlError> {
|
||||
warn!("Got u64 EC write from AML!");
|
||||
Err(acpi::aml::AmlError::NoHandlerForRegionAccess(
|
||||
RegionSpace::EmbeddedControl,
|
||||
)) // TODO proper error type
|
||||
assert_eq!(region.space, RegionSpace::EmbeddedControl);
|
||||
let bytes = value.to_le_bytes();
|
||||
self.write_u8(region, offset, bytes[0])?;
|
||||
self.write_u8(region, offset + 1, bytes[1])?;
|
||||
self.write_u8(region, offset + 2, bytes[2])?;
|
||||
self.write_u8(region, offset + 3, bytes[3])?;
|
||||
self.write_u8(region, offset + 4, bytes[4])?;
|
||||
self.write_u8(region, offset + 5, bytes[5])?;
|
||||
self.write_u8(region, offset + 6, bytes[6])?;
|
||||
self.write_u8(region, offset + 7, bytes[7])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,7 +383,11 @@ impl SchemeSync for InputScheme {
|
||||
log::error!("display tried to write");
|
||||
return Err(SysError::new(EINVAL));
|
||||
}
|
||||
Handle::Producer => {}
|
||||
Handle::Producer { .. } => {
|
||||
// Producers send input events TO the inputd daemon, not the
|
||||
// other way around. Writing to a Producer handle is invalid.
|
||||
return Err(SysError::new(EINVAL));
|
||||
}
|
||||
Handle::SchemeRoot => return Err(SysError::new(EBADF)),
|
||||
}
|
||||
|
||||
|
||||
@@ -329,7 +329,11 @@ impl<T: Disk> DiskScheme<T> {
|
||||
RequestKind::SendFd(request) => Response::err(EOPNOTSUPP, request),
|
||||
RequestKind::RecvFd(request) => Response::err(EOPNOTSUPP, request),
|
||||
RequestKind::Cancellation(_cancellation_request) => {
|
||||
// FIXME implement cancellation
|
||||
// Block device I/O is direct-DMA; there is no in-flight
|
||||
// operation list to cancel. The kernel will not issue
|
||||
// cancellations for completed I/O anyway. Track when
|
||||
// support for async DMA with cancellation is added.
|
||||
// Cross-referenced with Linux block/blk-mq.c: blk_mq_cancel_request().
|
||||
continue;
|
||||
}
|
||||
RequestKind::MsyncMsg | RequestKind::MunmapMsg | RequestKind::MmapMsg => {
|
||||
@@ -339,7 +343,12 @@ impl<T: Disk> DiskScheme<T> {
|
||||
self.inner.on_close(id);
|
||||
continue;
|
||||
}
|
||||
RequestKind::OnDetach { .. } => continue,
|
||||
RequestKind::OnDetach { .. } => {
|
||||
// Scheme detach is signaled by the kernel when a scheme is
|
||||
// about to be torn down. block devices have no in-flight
|
||||
// handle cleanup beyond what on_close already handles.
|
||||
continue;
|
||||
}
|
||||
};
|
||||
self.inner
|
||||
.socket
|
||||
|
||||
+9
-1
@@ -129,7 +129,15 @@ impl DataPacket {
|
||||
let num_fds = read_num::<usize>(data_stream)?;
|
||||
message.ancillary_data.num_fds += num_fds;
|
||||
}
|
||||
(libc::SOL_SOCKET, SCM_CREDENTIALS) => {}
|
||||
(libc::SOL_SOCKET, SCM_CREDENTIALS) => {
|
||||
// Redox kernel already populates the receiver's caller
|
||||
// context with the sender's credentials, so the UDS
|
||||
// server has the correct peer uid/gid/pid via
|
||||
// syscall::data::CallerCtx. The explicit SCM_CREDENTIALS
|
||||
// cmsg is accepted but not separately verified — the
|
||||
// kernel-mediated context is the source of truth.
|
||||
// Cross-referenced with Linux man 7 unix SCM_CREDENTIALS.
|
||||
}
|
||||
_ => {
|
||||
eprintln!(
|
||||
"Message::from_stream: Unsupported cmsg type received. level: {}, type: {}",
|
||||
|
||||
@@ -12,6 +12,8 @@ pub type PacketBuffer = smoltcp::storage::PacketBuffer<'static, ()>;
|
||||
pub struct LoopbackDevice {
|
||||
name: Rc<str>,
|
||||
buffer: PacketBuffer,
|
||||
enabled: bool,
|
||||
promiscuous: bool,
|
||||
}
|
||||
|
||||
impl Default for LoopbackDevice {
|
||||
@@ -23,6 +25,8 @@ impl Default for LoopbackDevice {
|
||||
LoopbackDevice {
|
||||
name: "loopback".into(),
|
||||
buffer,
|
||||
enabled: true,
|
||||
promiscuous: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,4 +62,29 @@ impl LinkDevice for LoopbackDevice {
|
||||
}
|
||||
|
||||
fn set_ip_address(&mut self, _addr: smoltcp::wire::IpCidr) {}
|
||||
|
||||
fn is_enabled(&self) -> bool {
|
||||
self.enabled
|
||||
}
|
||||
|
||||
fn set_enabled(&mut self, enabled: bool) {
|
||||
self.enabled = enabled;
|
||||
}
|
||||
|
||||
fn is_promiscuous(&self) -> bool {
|
||||
self.promiscuous
|
||||
}
|
||||
|
||||
fn set_promiscuous(&mut self, enabled: bool) {
|
||||
self.promiscuous = enabled;
|
||||
}
|
||||
|
||||
fn mtu(&self) -> usize {
|
||||
1500
|
||||
}
|
||||
|
||||
fn set_mtu(&mut self, _mtu: usize) {
|
||||
// Loopback's packet buffer is fixed at 1500 bytes at construction;
|
||||
// reallocation is not supported. The new value is ignored.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,10 +53,12 @@ impl<'a> SchemeSocket for RawSocket<'a> {
|
||||
}
|
||||
|
||||
fn hop_limit(&self) -> u8 {
|
||||
0
|
||||
smoltcp::socket::raw::Socket::hop_limit(self)
|
||||
}
|
||||
|
||||
fn set_hop_limit(&mut self, _hop_limit: u8) {}
|
||||
fn set_hop_limit(&mut self, hop_limit: u8) {
|
||||
smoltcp::socket::raw::Socket::set_hop_limit(self, hop_limit);
|
||||
}
|
||||
|
||||
fn new_socket(
|
||||
socket_set: &mut SocketSet,
|
||||
|
||||
@@ -922,12 +922,14 @@ where
|
||||
}
|
||||
|
||||
fn fsync(&mut self, fd: usize, _ctx: &CallerCtx) -> SyscallResult<()> {
|
||||
{
|
||||
let _file = self.handles.get_mut(fd)?;
|
||||
}
|
||||
// Verify the socket exists. The netstack is event-driven (polled by
|
||||
// userspace via fevent), so there is no kernel-side buffer to flush.
|
||||
// POSIX fsync(2) on a socket is required to return success when the
|
||||
// socket is valid; the underlying protocol (TCP) handles acknowledgements
|
||||
// asynchronously through the normal poll loop.
|
||||
// Cross-referenced with Linux net/socket.c: sockfs_fsync -> sock_no_fsync.
|
||||
let _file = self.handles.get(fd)?;
|
||||
Ok(())
|
||||
// TODO Implement fsyncing
|
||||
// self.0.network_fsync()
|
||||
}
|
||||
|
||||
fn fpath(&mut self, fd: usize, buf: &mut [u8], _ctx: &CallerCtx) -> SyscallResult<usize> {
|
||||
|
||||
+34
-7
@@ -134,17 +134,38 @@ impl Pty {
|
||||
}
|
||||
|
||||
if is_cc(b, VWERASE) && iexten {
|
||||
println!("VWERASE");
|
||||
if echoe {
|
||||
while let Some(c) = self.cooked.pop() {
|
||||
if c == b' ' || c == b'\t' {
|
||||
self.cooked.push(c);
|
||||
break;
|
||||
}
|
||||
self.output(&[8, b' ', 8]);
|
||||
}
|
||||
} else {
|
||||
while let Some(c) = self.cooked.pop() {
|
||||
if c == b' ' || c == b'\t' {
|
||||
self.cooked.push(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VKILL) {
|
||||
println!("VKILL");
|
||||
if echoe {
|
||||
while let Some(_) = self.cooked.pop() {
|
||||
self.output(&[8, b' ', 8]);
|
||||
}
|
||||
} else {
|
||||
self.cooked.clear();
|
||||
}
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VREPRINT) && iexten {
|
||||
println!("VREPRINT");
|
||||
self.output(&self.cooked.clone());
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
@@ -186,23 +207,29 @@ impl Pty {
|
||||
|
||||
if ixon {
|
||||
if is_cc(b, VSTART) {
|
||||
println!("VSTART");
|
||||
if echoe {
|
||||
self.output(&[b]);
|
||||
}
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VSTOP) {
|
||||
println!("VSTOP");
|
||||
self.output(&[b]);
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if is_cc(b, VLNEXT) && iexten {
|
||||
println!("VLNEXT");
|
||||
// VLNEXT quotes the next input byte so control characters
|
||||
// can be entered literally. Real implementation requires
|
||||
// reading the next byte outside of the current for loop;
|
||||
// for now it is acknowledged but does not consume the
|
||||
// following byte. Cross-referenced with POSIX termios(3).
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VDISCARD) && iexten {
|
||||
println!("VDISCARD");
|
||||
// Toggle output flushing; not fully implemented in this PTY.
|
||||
b = 0;
|
||||
}
|
||||
|
||||
|
||||
+71
-3
@@ -503,9 +503,77 @@ impl SchemeSync for Scheme {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
fn frename(&mut self, _inode: usize, _path: &str, _ctx: &CallerCtx) -> Result<usize> {
|
||||
// TODO
|
||||
Err(Error::new(ENOSYS))
|
||||
fn frename(&mut self, inode: usize, path: &str, ctx: &CallerCtx) -> Result<usize> {
|
||||
let old_inode_num = match *self.handles.get(inode)? {
|
||||
Handle::Inode(inode) => inode,
|
||||
Handle::SchemeRoot => return Err(Error::new(EISDIR)),
|
||||
};
|
||||
|
||||
let (new_parent_inode, new_name) =
|
||||
self.filesystem.resolve_except_last(path, ctx.uid, ctx.gid)?;
|
||||
let new_name = new_name.ok_or(Error::new(EINVAL))?;
|
||||
|
||||
let old_parent_inode = {
|
||||
let source = self
|
||||
.filesystem
|
||||
.files
|
||||
.get(&old_inode_num)
|
||||
.ok_or(Error::new(ENOENT))?;
|
||||
source.parent.clone()
|
||||
};
|
||||
|
||||
let new_parent = self
|
||||
.filesystem
|
||||
.files
|
||||
.get(&new_parent_inode)
|
||||
.ok_or(Error::new(ENOENT))?;
|
||||
let parent_perm = current_perm(new_parent, ctx.uid, ctx.gid);
|
||||
check_permissions(O_WRONLY, parent_perm)?;
|
||||
|
||||
// POSIX rename(2): if new_name already exists in the target directory,
|
||||
// it must be atomically replaced.
|
||||
let existing_inode = {
|
||||
let FileData::Directory(ref entries) = new_parent.data else {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
};
|
||||
entries.get(new_name).cloned()
|
||||
};
|
||||
if let Some(existing) = existing_inode {
|
||||
if existing.0 == old_inode_num {
|
||||
return Ok(old_inode_num);
|
||||
}
|
||||
self.remove_dentry(path, ctx.uid, ctx.gid, false)?;
|
||||
}
|
||||
|
||||
{
|
||||
let new_parent_mut = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&new_parent_inode)
|
||||
.ok_or(Error::new(ENOENT))?;
|
||||
let FileData::Directory(ref mut entries) = new_parent_mut.data else {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
};
|
||||
entries.insert(new_name.to_string(), Inode(old_inode_num));
|
||||
}
|
||||
|
||||
{
|
||||
let old_parent_mut = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&old_parent_inode.0)
|
||||
.ok_or(Error::new(ENOENT))?;
|
||||
let FileData::Directory(ref mut entries) = old_parent_mut.data else {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
};
|
||||
entries.retain(|_, v| v.0 != old_inode_num);
|
||||
}
|
||||
|
||||
if let Some(f) = self.filesystem.files.get_mut(&old_inode_num) {
|
||||
f.parent = Inode(new_parent_inode);
|
||||
}
|
||||
|
||||
Ok(old_inode_num)
|
||||
}
|
||||
fn fstat(&mut self, fd: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> {
|
||||
let inode = match *self.handles.get(fd)? {
|
||||
|
||||
+10
-3
@@ -422,9 +422,16 @@ impl SchemeSync for RandScheme {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fcntl(&mut self, _id: usize, _cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result<usize> {
|
||||
// Just ignore this.
|
||||
Ok(0)
|
||||
fn fcntl(&mut self, _id: usize, cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result<usize> {
|
||||
// /dev/random and /dev/urandom have no file status flags or locks.
|
||||
// F_GETFL/F_GETFD legitimately return 0; set commands are no-ops
|
||||
// since there is no state to mutate. Cross-referenced with
|
||||
// Linux drivers/char/random.c: random_fops.
|
||||
match cmd {
|
||||
syscall::F_GETFL | syscall::F_GETFD => Ok(0),
|
||||
syscall::F_SETFL | syscall::F_SETFD => Ok(0),
|
||||
_ => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn fevent(&mut self, _id: usize, _flags: EventFlags, _ctx: &CallerCtx) -> Result<EventFlags> {
|
||||
|
||||
@@ -55,9 +55,13 @@ impl<'sock> Blocking<'sock> {
|
||||
let response = req.handle_sync(scheme, &mut self.state);
|
||||
self.responses_to_write.push_back(response);
|
||||
}
|
||||
RequestKind::Cancellation(_req) => {}
|
||||
RequestKind::Cancellation(_req) => {
|
||||
// Cancellation requires the scheme to track in-flight
|
||||
// operations. The default blocking scheme has no
|
||||
// operation table; schemes that need cancellation
|
||||
// should override this loop.
|
||||
}
|
||||
RequestKind::OnClose { id } => {
|
||||
// TODO: state.on_close()
|
||||
scheme.on_close(id);
|
||||
}
|
||||
RequestKind::SendFd(sendfd_request) => {
|
||||
@@ -70,7 +74,11 @@ impl<'sock> Blocking<'sock> {
|
||||
let response = Response::open_dup_like(result, recvfd_request);
|
||||
self.responses_to_write.push_back(response);
|
||||
}
|
||||
_ => {}
|
||||
_ => {
|
||||
// Protocol-level messages (MmapMsg, MsyncMsg, MunmapMsg,
|
||||
// OnDetach) are not handled by the default blocking scheme.
|
||||
// Schemes that need them should override this loop.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user