Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion os/StarryOS/kernel/src/file/ion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl FileLike for IonBufferFile {
}

fn device_mmap(&self, _offset: u64) -> AxResult<DeviceMmap> {
Ok(DeviceMmap::Physical(self.phys_range()))
Ok(DeviceMmap::Physical(self.phys_range(), None))
}
}

Expand Down
617 changes: 361 additions & 256 deletions os/StarryOS/kernel/src/pseudofs/dev/card0.rs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions os/StarryOS/kernel/src/pseudofs/dev/card1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ impl DeviceOps for Card1 {
warn!("card1: mmap could not resolve handle {handle}");
return DeviceMmap::None;
};
DeviceMmap::Physical(exported.range)
DeviceMmap::Physical(exported.range, None)
}
}

Expand All @@ -211,7 +211,7 @@ impl FileLike for ExportedGemBuffer {
}

fn device_mmap(&self, _offset: u64) -> AxResult<DeviceMmap> {
Ok(DeviceMmap::Physical(self.range))
Ok(DeviceMmap::Physical(self.range, None))
}
}

Expand Down Expand Up @@ -547,7 +547,7 @@ mod tests {
let exported = ExportedGemBuffer::new(range);

assert!(
matches!(exported.device_mmap(0).unwrap(), DeviceMmap::Physical(actual) if actual == range)
matches!(exported.device_mmap(0).unwrap(), DeviceMmap::Physical(actual, None) if actual == range)
);
}
}
Expand Down
10 changes: 6 additions & 4 deletions os/StarryOS/kernel/src/pseudofs/dev/fb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,12 @@ impl DeviceOps for FrameBuffer {
}

fn mmap(&self, _offset: u64, _length: u64) -> DeviceMmap {
DeviceMmap::Physical(PhysAddrRange::from_start_size(
virt_to_phys(self.base),
self.size,
))
// Framebuffer scanout is owned by axdisplay for the program's
// lifetime; no retainer needed.
DeviceMmap::Physical(
PhysAddrRange::from_start_size(virt_to_phys(self.base), self.size),
None,
)
}

fn flags(&self) -> NodeFlags {
Expand Down
8 changes: 4 additions & 4 deletions os/StarryOS/kernel/src/pseudofs/dev/ion/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,10 +288,10 @@ impl DeviceOps for IonDevice {
offset, phys_addr, size
);

DeviceMmap::Physical(PhysAddrRange::from_start_size(
ax_memory_addr::PhysAddr::from(phys_addr),
size,
))
DeviceMmap::Physical(
PhysAddrRange::from_start_size(ax_memory_addr::PhysAddr::from(phys_addr), size),
None,
)
}
Err(e) => {
warn!(
Expand Down
10 changes: 10 additions & 0 deletions os/StarryOS/kernel/src/pseudofs/dev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod memtrack;
mod rknpu_card;
#[cfg(all(feature = "rknpu", not(any(windows, unix))))]
mod rknpu_drm;
mod rtc;
Comment thread
ZR233 marked this conversation as resolved.
#[cfg(all(feature = "sg2002", not(feature = "plat-dyn")))]
pub mod tpu;
pub mod tty;
Expand Down Expand Up @@ -292,6 +293,15 @@ fn builder(fs: Arc<SimpleFs>) -> DirMaker {
Arc::new(CpuDmaLatency),
),
);
root.add(
"rtc0",
Device::new(
fs.clone(),
NodeType::CharacterDevice,
rtc::RTC0_DEVICE_ID,
Arc::new(rtc::Rtc),
),
);

// This is mounted to a tmpfs in `new_procfs`
root.add(
Expand Down
69 changes: 69 additions & 0 deletions os/StarryOS/kernel/src/pseudofs/dev/rtc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use core::{any::Any, ffi::c_int};

use axfs_ng_vfs::{DeviceId, NodeFlags, VfsError, VfsResult};
use chrono::{Datelike, Timelike};
use linux_raw_sys::ioctl::RTC_RD_TIME;
use starry_vm::VmMutPtr;

use crate::pseudofs::DeviceOps;

/// The device ID for /dev/rtc0
pub const RTC0_DEVICE_ID: DeviceId = DeviceId::new(250, 0);

#[repr(C)]
#[allow(non_camel_case_types, dead_code)]
struct rtc_time {
tm_sec: c_int,
tm_min: c_int,
tm_hour: c_int,
tm_mday: c_int,
tm_mon: c_int,
tm_year: c_int,
tm_wday: c_int,
tm_yday: c_int,
tm_isdst: c_int,
}

/// RTC device
pub struct Rtc;

impl DeviceOps for Rtc {
fn read_at(&self, _buf: &mut [u8], _offset: u64) -> VfsResult<usize> {
Ok(0)
}

fn write_at(&self, _buf: &[u8], _offset: u64) -> VfsResult<usize> {
Ok(0)
}

fn ioctl(&self, cmd: u32, arg: usize) -> VfsResult<usize> {
match cmd {
RTC_RD_TIME => {
let wall = chrono::DateTime::from_timestamp_nanos(
ax_runtime::hal::time::wall_time_nanos() as _,
);
(arg as *mut rtc_time).vm_write(rtc_time {
tm_sec: wall.second() as _,
tm_min: wall.minute() as _,
tm_hour: wall.hour() as _,
tm_mday: wall.day() as _,
tm_mon: wall.month0() as _,
tm_year: (wall.year() - 1900) as _,
tm_wday: 0,
tm_yday: 0,
tm_isdst: 0,
})?;
}
_ => return Err(VfsError::NotATty),
}
Ok(0)
}

fn as_any(&self) -> &dyn Any {
self
}

fn flags(&self) -> NodeFlags {
NodeFlags::NON_CACHEABLE | NodeFlags::STREAM
}
}
10 changes: 6 additions & 4 deletions os/StarryOS/kernel/src/pseudofs/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ use super::{SimpleFs, SimpleFsNode};
pub enum DeviceMmap {
/// The device is not mappable (→ ENODEV, matches Linux).
None,

/// Maps to a physical address range.
Physical(PhysAddrRange),

/// Maps to a physical address range. The optional retainer keeps
/// driver-owned backing pages alive for as long as any VMA built
/// from this mapping exists — pinned by the resulting
/// [`LinearBackend`] so userspace can't observe freed memory if
/// the device drops the buffer before munmap.
Physical(PhysAddrRange, Option<Arc<dyn Any + Send + Sync>>),
/// Maps to a cached file.
Cache(CachedFile),
}
Expand Down
36 changes: 22 additions & 14 deletions os/StarryOS/kernel/src/syscall/mm/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ pub fn sys_mmap(
.as_ref()
.expect("file-backed mmap has cached device_mmap")
{
Ok(DeviceMmap::Physical(_)) | Ok(DeviceMmap::Cache(_)) => false,
Ok(DeviceMmap::Physical(..)) | Ok(DeviceMmap::Cache(_)) => false,
Ok(DeviceMmap::None) | Err(_) => true,
}
}
Expand Down Expand Up @@ -296,18 +296,21 @@ pub fn sys_mmap(
.take()
.expect("file-backed mmap has cached device_mmap")
{
Ok(DeviceMmap::Physical(mut range)) => {
Ok(DeviceMmap::Physical(mut range, retain)) => {
mapping_flags |= MappingFlags::UNCACHED;
range.start += offset;
if range.is_empty() {
return Err(AxError::InvalidInput);
}
length = length.min(range.size().align_down(page_size));
Backend::new_linear(
start,
start.as_usize() as isize - range.start.as_usize() as isize,
true,
)
let pa_va_offset =
start.as_usize() as isize - range.start.as_usize() as isize;
match retain {
Some(retain) => {
Backend::new_linear_anchored(start, pa_va_offset, true, retain)
}
None => Backend::new_linear(start, pa_va_offset, true),
}
}
Ok(DeviceMmap::None) => return Err(AxError::NoSuchDevice),
Ok(_) => return Err(AxError::InvalidInput),
Expand Down Expand Up @@ -347,19 +350,24 @@ pub fn sys_mmap(
DeviceMmap::None => {
return Err(AxError::NoSuchDevice);
}
DeviceMmap::Physical(range) => {
DeviceMmap::Physical(range, retain) => {
mapping_flags |= MappingFlags::UNCACHED;
if range.is_empty() {
return Err(AxError::InvalidInput);
}
length =
capped_device_map_len(length, range.size(), page_size);
Backend::new_linear(
start,
start.as_usize() as isize
- range.start.as_usize() as isize,
true,
)
let pa_va_offset = start.as_usize() as isize
- range.start.as_usize() as isize;
match retain {
Some(retain) => Backend::new_linear_anchored(
start,
pa_va_offset,
true,
retain,
),
None => Backend::new_linear(start, pa_va_offset, true),
}
}
DeviceMmap::Cache(cache) => Backend::new_file(
start,
Expand Down
11 changes: 10 additions & 1 deletion test-suit/starryos/normal/qemu-smp1/busybox/sh/busybox-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1286,7 +1286,16 @@ _rc=$?; if [ "$_rc" -eq 0 ] && echo "$_t" | grep -q "[0-9]"; then echo "PASS: bl
# hwclock — read hardware clock
bb_case_start "busybox_hwclock"
_t=$({ timeout 10 sh -c "busybox hwclock -r 2>&1"; } 2>&1)
if echo "$_t" | grep -qF "hwclock"; then echo "PASS: busybox_hwclock"; bb_case_pass; else echo "FAIL_DETAIL: busybox_hwclock"; echo "$_t"; bb_case_fail; fi
_rc=$?
if [ "$_rc" -eq 0 ] && echo "$_t" | grep -qE "[0-9]{4}"; then
bb_case_pass
elif echo "$_t" | grep -qF "hwclock"; then
bb_case_pass
else
echo "FAIL_DETAIL: busybox_hwclock"
echo "$_t (rc=$_rc)"
bb_case_fail
fi

bb_case_start "busybox_run_parts"
_t=$({ timeout 10 sh -c "busybox sh -c 'mkdir -p /tmp/bb_rp/d && busybox echo rp_ok > /tmp/bb_rp/d/00t && chmod +x /tmp/bb_rp/d/00t && busybox run-parts /tmp/bb_rp/d' 2>&1"; } 2>&1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,12 +336,48 @@ int main(void)
CHECK(obj_prop_value(fd, crtcs[0], DRM_MODE_OBJECT_CRTC, P_CRTC_ACTIVE) == 1,
"rejected commit left state intact");

/* --- destroy blob,再读应该 ENOENT --- */
/* --- destroy-after-commit blob lifecycle ---
* Linux DRM keeps a kernel reference on a `MODE_ID` blob from the
* CRTC state that committed it. A user `DESTROYPROPBLOB` only
* releases the user-publish handle; `GETPROPBLOB` on the same id
* keeps succeeding until a later atomic commit replaces the
* `MODE_ID` (or clears it). */
struct drm_mode_destroy_blob db = { .blob_id = cb.blob_id };
CHECK_RET(ioctl(fd, DRM_IOCTL_MODE_DESTROYPROPBLOB, &db), 0,
"DESTROYPROPBLOB");
CHECK_ERR(ioctl(fd, DRM_IOCTL_MODE_GETPROPBLOB, &gb), ENOENT,
"destroyed blob is ENOENT");
"DESTROYPROPBLOB releases user publish");
/* OBJ_GETPROPERTIES still reports the committed MODE_ID. */
CHECK(obj_prop_value(fd, crtcs[0], DRM_MODE_OBJECT_CRTC, P_CRTC_MODE_ID)
== cb.blob_id,
"MODE_ID survives DESTROYPROPBLOB while CRTC state references it");
/* GETPROPBLOB on the still-referenced id keeps working. */
struct drm_mode_get_blob gb_after = {
.blob_id = cb.blob_id, .length = sizeof(struct drm_mode_mode_info),
};
struct drm_mode_mode_info readback = {0};
gb_after.data = (uint64_t)(uintptr_t)&readback;
CHECK_RET(ioctl(fd, DRM_IOCTL_MODE_GETPROPBLOB, &gb_after), 0,
"GETPROPBLOB on destroyed-but-committed blob succeeds");
CHECK(gb_after.length == sizeof(readback),
"post-destroy GETPROPBLOB returns the full mode payload");

/* A blob that was never referenced by committed state should
* disappear on DESTROYPROPBLOB. Build a second blob, do not commit
* it, then destroy and confirm ENOENT. */
struct drm_mode_create_blob cb_orphan = {
.data = (uint64_t)(uintptr_t)&modes[0],
.length = sizeof(modes[0]),
};
CHECK_RET(ioctl(fd, DRM_IOCTL_MODE_CREATEPROPBLOB, &cb_orphan), 0,
"CREATEPROPBLOB (orphan)");
struct drm_mode_destroy_blob db_orphan = { .blob_id = cb_orphan.blob_id };
CHECK_RET(ioctl(fd, DRM_IOCTL_MODE_DESTROYPROPBLOB, &db_orphan), 0,
"DESTROYPROPBLOB (orphan)");
struct drm_mode_get_blob gb_orphan = {
.blob_id = cb_orphan.blob_id, .length = sizeof(struct drm_mode_mode_info),
.data = (uint64_t)(uintptr_t)&readback,
};
CHECK_ERR(ioctl(fd, DRM_IOCTL_MODE_GETPROPBLOB, &gb_orphan), ENOENT,
"orphan blob is ENOENT after destroy");

close(fd);
TEST_DONE();
Expand Down
Loading