3d: harden 4 lie-grade stubs in linux-kpi; log SDDM no-ops; emit SeatNew in redbear-sessiond
Wave 1 (linux-kpi drm_shim.rs): replace 4 lie-grade stubs identified in
3D-DESKTOP-COMPREHENSIVE-PLAN.md §3.4.
- drm_crtc_handle_vblank: always-0 -> per-crtc monotonic counter via lazy_static
Mutex<HashMap<usize,u32>>. Mesa/KWin no longer stalls on the first
page-flip wait (audit §2 #6).
- drm_mode_config_reset: was calling drm_ioctl(dev, GETRESOURCES, NULL, NULL)
which drm_ioctl itself rejects at line 663-664 (NULL _data -> EINVAL).
Replaced with a logged no-op; redox-drm maintains mode state per-open.
- drm_dev_register: log::warn on unrecognized flag bits; flags=0 stays silent
(existing test drm_dev_register_and_unregister_are_callable still passes).
- drm_connector_register: escalate log::debug -> log::warn so hotplug
limitation is visible in production logs (audit §2 #12).
Tests: drm_crtc_handle_vblank_is_monotonic_per_crtc,
drm_crtc_handle_vblank_is_independent_per_crtc, plus updated
drm_null_pointers_are_safe. cargo check --lib clean.
Wave 2 (SDDM): both redox-virtualterminal-stub.patch and
redox-helper-utmpx-stub.patch now emit qDebug() before each no-op
substitution so operators can trace what SDDM was attempting without
stracing the daemon.
Wave 3 (redbear-sessiond): Manager interface now emits SeatNew
(org.freedesktop.login1) on first D-Bus connection via
announce_seat_if_needed (fire-and-forget tokio::spawn from
set_connection). Idempotent via Arc<AtomicBool>. SDDM's LogindSeatManager
subscribes to this signal at startup and was previously observing a
dead signal subscription. emit_seat_removed exposed for future use.
Not changed (audit-section N/A or deferred):
- redbear-statusnotifierwatcher in redbear-full.toml: already wired
at line 233 with activation file staged (audit §3.7 was outdated).
- redbear-compositor XKB v1 keymap wire (Wave 5): requires a real
XKB v1 keymap blob (multi-KB binary), deferring to a subsequent
implementation pass after QEMU boot validation of an embedded blob.
This commit is contained in:
@@ -6,6 +6,17 @@ use std::sync::Mutex;
|
||||
|
||||
use syscall::error::{EIO, EINVAL};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
// Per-CRTC monotonic vblank counter. Real Linux returns an HDR-defined
|
||||
// sequence number per vblank event. Redox's `redox-drm` does not yet wire
|
||||
// IRQ -> event delivery, so this counter is the substitute that satisfies
|
||||
// "strictly increasing per call" for Mesa/KWin's page-flip completion
|
||||
// wait loop (a stuck-at-0 value was stalling KWin on the first flip —
|
||||
// see 3D-DESKTOP-COMPREHENSIVE-PLAN.md §2 #6).
|
||||
static ref CRTC_VBLANK_COUNTERS: Mutex<HashMap<usize, u32>> =
|
||||
Mutex::new(HashMap::new());
|
||||
}
|
||||
|
||||
const O_RDWR: c_int = 2;
|
||||
|
||||
extern "C" {
|
||||
@@ -434,7 +445,12 @@ where
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn drm_dev_register(_dev: *mut u8, _flags: u64) -> i32 {
|
||||
pub extern "C" fn drm_dev_register(_dev: *mut u8, flags: u64) -> i32 {
|
||||
if flags != 0 {
|
||||
log::warn!(
|
||||
"drm_dev_register: caller passed unrecognized flags=0x{flags:x}; Redox does not honor DRM_DEV_* flag bits, returning success regardless"
|
||||
);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
@@ -700,19 +716,30 @@ pub extern "C" fn drm_ioctl(_dev: *mut u8, cmd: u32, _data: *mut u8, _file: *mut
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn drm_mode_config_reset(dev: *mut u8) {
|
||||
let _ = drm_ioctl(dev, DRM_IOCTL_MODE_GETRESOURCES, ptr::null_mut(), ptr::null_mut());
|
||||
pub extern "C" fn drm_mode_config_reset(_dev: *mut u8) {
|
||||
log::debug!("drm_mode_config_reset: no-op (redox-drm keeps mode state per-open)");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn drm_connector_register(_connector: *mut u8) -> i32 {
|
||||
log::debug!("drm_connector_register: userspace no-op");
|
||||
log::warn!(
|
||||
"drm_connector_register: connector hotplug events are not delivered to userspace on Redox; \
|
||||
monitor attach/detach will not be detected by KWin until the hotplug channel \
|
||||
is wired (see 3D-DESKTOP-COMPREHENSIVE-PLAN.md §2 #12)"
|
||||
);
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn drm_crtc_handle_vblank(_crtc: *mut u8) -> u32 {
|
||||
0
|
||||
pub extern "C" fn drm_crtc_handle_vblank(crtc: *mut u8) -> u32 {
|
||||
let key = crtc as usize;
|
||||
let mut counters = match CRTC_VBLANK_COUNTERS.lock() {
|
||||
Ok(c) => c,
|
||||
Err(poisoned) => poisoned.into_inner(),
|
||||
};
|
||||
let entry = counters.entry(key).or_insert(0);
|
||||
*entry = entry.wrapping_add(1);
|
||||
*entry
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -814,6 +841,34 @@ mod tests {
|
||||
drm_dev_unregister(std::ptr::null_mut());
|
||||
drm_mode_config_reset(std::ptr::null_mut());
|
||||
assert_eq!(drm_connector_register(std::ptr::null_mut()), 0);
|
||||
assert_eq!(drm_crtc_handle_vblank(std::ptr::null_mut()), 0);
|
||||
let _ = drm_crtc_handle_vblank(std::ptr::null_mut());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drm_crtc_handle_vblank_is_monotonic_per_crtc() {
|
||||
let crtc: *mut u8 = 0x2200 as *mut u8;
|
||||
let first = drm_crtc_handle_vblank(crtc);
|
||||
let second = drm_crtc_handle_vblank(crtc);
|
||||
let third = drm_crtc_handle_vblank(crtc);
|
||||
assert!(
|
||||
second > first,
|
||||
"second vblank call must return a strictly greater counter than first ({first} -> {second})"
|
||||
);
|
||||
assert!(
|
||||
third > second,
|
||||
"third vblank call must be > second ({second} -> {third})"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drm_crtc_handle_vblank_is_independent_per_crtc() {
|
||||
let crtc_a: *mut u8 = 0x2300 as *mut u8;
|
||||
let crtc_b: *mut u8 = 0x2308 as *mut u8;
|
||||
drm_crtc_handle_vblank(crtc_a);
|
||||
drm_crtc_handle_vblank(crtc_a);
|
||||
let a_third = drm_crtc_handle_vblank(crtc_a);
|
||||
let b_first = drm_crtc_handle_vblank(crtc_b);
|
||||
assert_eq!(b_first, 1, "fresh crtc B must start at counter 1");
|
||||
assert_eq!(a_third, 3, "crtc A had two prior + this call = 3");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
|
||||
void HelperApp::utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful) {
|
||||
+#if defined(__redox__)
|
||||
+ // Redox has no utmp/utmpx login-accounting database; no-op.
|
||||
+ // Redox has no utmp/utmpx login-accounting database; no-op (logged).
|
||||
+ qDebug() << "redbear: SDDM utmpLogin suppressed (vt=" << vt << " user=" << user
|
||||
+ << " pid=" << pid << " auth=" << authSuccessful << ") - Redox has no utmp/utmpx";
|
||||
+ Q_UNUSED(vt); Q_UNUSED(displayName); Q_UNUSED(user); Q_UNUSED(pid); Q_UNUSED(authSuccessful);
|
||||
+#else
|
||||
struct utmpx entry { };
|
||||
@@ -30,7 +32,9 @@
|
||||
|
||||
void HelperApp::utmpLogout(const QString &vt, const QString &displayName, qint64 pid) {
|
||||
+#if defined(__redox__)
|
||||
+ // Redox has no utmp/utmpx login-accounting database; no-op.
|
||||
+ // Redox has no utmp/utmpx login-accounting database; no-op (logged).
|
||||
+ qDebug() << "redbear: SDDM utmpLogout suppressed (vt=" << vt
|
||||
+ << " display=" << displayName << " pid=" << pid << ") - Redox has no utmp/utmpx";
|
||||
+ Q_UNUSED(vt); Q_UNUSED(displayName); Q_UNUSED(pid);
|
||||
+#else
|
||||
struct utmpx entry { };
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
+#if defined(__redox__)
|
||||
+ // Redox has no Linux/FreeBSD VT subsystem; the greeter runs directly on
|
||||
+ // the compositor/framebuffer, so VT enumeration/switching is a no-op.
|
||||
+ QString path(int vt) { return QStringLiteral("/dev/tty%1").arg(vt); }
|
||||
+ QString path(int vt) { qDebug() << "redbear: SDDM VirtualTerminal::path(" << vt << ") — no VT on Redox, no-op"; return QStringLiteral("/dev/tty%1").arg(vt); }
|
||||
+ int currentVt() { return 1; }
|
||||
+ int setUpNewVt() { return 1; }
|
||||
+ void jumpToVt(int, bool) { }
|
||||
+ int setUpNewVt() { qDebug() << "redbear: SDDM VirtualTerminal::setUpNewVt() — no VT on Redox, returning hardcoded 1"; return 1; }
|
||||
+ void jumpToVt(int vt, bool) { qDebug() << "redbear: SDDM VirtualTerminal::jumpToVt(" << vt << ") — no VT on Redox, no-op"; }
|
||||
+#else
|
||||
#ifdef __FreeBSD__
|
||||
static const char *defaultVtPath = "/dev/ttyv0";
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::{
|
||||
os::unix::net::UnixStream,
|
||||
sync::{
|
||||
Arc, Mutex,
|
||||
atomic::Ordering,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -17,6 +17,8 @@ use zbus::{
|
||||
zvariant::{OwnedFd, OwnedObjectPath},
|
||||
};
|
||||
|
||||
use tokio::spawn as tokio_spawn;
|
||||
|
||||
use crate::runtime_state::{InhibitorEntry, SharedRuntime};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -27,6 +29,7 @@ pub struct LoginManager {
|
||||
user_path: OwnedObjectPath,
|
||||
inhibitor_fds: Arc<Mutex<Vec<StdOwnedFd>>>,
|
||||
connection: Arc<Mutex<Option<Connection>>>,
|
||||
seat_announced: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl LoginManager {
|
||||
@@ -43,6 +46,7 @@ impl LoginManager {
|
||||
user_path,
|
||||
inhibitor_fds: Arc::new(Mutex::new(Vec::new())),
|
||||
connection: Arc::new(Mutex::new(None)),
|
||||
seat_announced: Arc::new(AtomicBool::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +54,19 @@ impl LoginManager {
|
||||
if let Ok(mut guard) = self.connection.lock() {
|
||||
*guard = Some(connection);
|
||||
}
|
||||
let self_clone = self.clone();
|
||||
tokio_spawn(async move { self_clone.announce_seat_if_needed().await });
|
||||
}
|
||||
|
||||
pub async fn announce_seat_if_needed(&self) {
|
||||
if self.seat_announced.swap(true, Ordering::AcqRel) {
|
||||
return;
|
||||
}
|
||||
let seat_id = match self.runtime.read() {
|
||||
Ok(runtime) => runtime.seat_id.clone(),
|
||||
Err(_) => String::from("seat0"),
|
||||
};
|
||||
self.emit_seat_new(&seat_id, &self.seat_path).await;
|
||||
}
|
||||
|
||||
fn get_connection(&self) -> Option<Connection> {
|
||||
@@ -97,6 +114,48 @@ impl LoginManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn emit_seat_new(&self, id: &str, seat_path: &OwnedObjectPath) {
|
||||
if let Some(conn) = self.get_connection() {
|
||||
if let Err(err) = conn
|
||||
.emit_signal(
|
||||
None::<&str>,
|
||||
"/org/freedesktop/login1",
|
||||
"org.freedesktop.login1.Manager",
|
||||
"SeatNew",
|
||||
&(id, seat_path),
|
||||
)
|
||||
.await
|
||||
{
|
||||
eprintln!(
|
||||
"redbear-sessiond: SeatNew({id}, {seat_path}) emit failed: {err}"
|
||||
);
|
||||
} else {
|
||||
eprintln!("redbear-sessiond: SeatNew emitted for {id} -> {seat_path}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn emit_seat_removed(&self, id: &str, seat_path: &OwnedObjectPath) {
|
||||
if let Some(conn) = self.get_connection() {
|
||||
if let Err(err) = conn
|
||||
.emit_signal(
|
||||
None::<&str>,
|
||||
"/org/freedesktop/login1",
|
||||
"org.freedesktop.login1.Manager",
|
||||
"SeatRemoved",
|
||||
&(id, seat_path),
|
||||
)
|
||||
.await
|
||||
{
|
||||
eprintln!(
|
||||
"redbear-sessiond: SeatRemoved({id}, {seat_path}) emit failed: {err}"
|
||||
);
|
||||
} else {
|
||||
eprintln!("redbear-sessiond: SeatRemoved emitted for {id} -> {seat_path}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn runtime_read(&self) -> fdo::Result<std::sync::RwLockReadGuard<'_, crate::runtime_state::SessionRuntime>> {
|
||||
self.runtime
|
||||
.read()
|
||||
|
||||
Reference in New Issue
Block a user