feat: VirtIO GPU driver, libdrm DRM ioctls, KWin/KF6 build fixes, display stack additions
- Add full VirtIO GPU driver with command submission, resource management, VirtQueue implementation, and transport layer; includes diagnostic probes for resource_create_2d ERR_INVALID_RESOURCE_ID investigation - Expand libdrm Redox support with DRM ioctl wrappers (ADDFB, RMFB, CREATE_DUMB, MAP_DUMB, DESTROY_DUMB, GET_RESOURCES, GET_CONNECTOR, GET_CRTC, SET_CRTC, MODE_OBJ_GET_PROPERTIES, etc.) and xf86drm_redox.h - Add redox-drm scheme handlers for VirtIO GPU-specific DRM ioctls (VIRTGPU_RESOURCE_CREATE, VIRTGPU_MAP, VIRTGPU_WAIT, VIRTGPU_INFO, etc.) - Add display stack recipes: freetype2, lcms2, libdisplay-info, libepoxy, libxcvt - Fix KWin build (recipe.toml expanded, kf6-ksvg added) - Fix KF6 CMakeLists for cross-compilation (attica, kcmutils, kcolorscheme, kcompletion, kconfigwidgets, kdeclarative, kiconthemes, kitemmodels, kitemviews, kjobwidgets, ktextwidgets, kwayland, kxmlgui, kpty, solid) - Add Qt6 futex support patch - Add relibc patches: P3 strtold, P3 ld-so search path, P5 DRM ioctl removal - Add base P4 pcid config scheme patch - Update driver-manager hotplug/config, PCI config in redox-driver-sys - Update greeter compositor and KDE session scripts - Update AGENTS.md with zero-tolerance stubs policy and project knowledge
This commit is contained in:
@@ -196,6 +196,91 @@ make all
|
||||
- **Syscall ABI**: Unstable intentionally. Stability via `libredox` and `relibc`
|
||||
- **Drivers**: ALL userspace daemons via scheme system. No kernel-space drivers (except serio)
|
||||
|
||||
## SYSTEM-CRITICAL INFRASTRUCTURE MUST BE RUST
|
||||
|
||||
All Red Bear OS system-critical infrastructure **must** be written in Rust. C and C++ are
|
||||
acceptable only for ported upstream applications (KDE Plasma, Qt, games, third-party tools)
|
||||
where the original source is not Rust.
|
||||
|
||||
### What counts as system-critical
|
||||
|
||||
| Layer | Component | Language | Status |
|
||||
|-------|-----------|----------|--------|
|
||||
| Kernel | microkernel | Rust | ✅ |
|
||||
| C library | relibc | Rust | ✅ |
|
||||
| Init | service manager | Rust | ✅ |
|
||||
| Filesystems | redoxfs, ext4d, fatd | Rust | ✅ |
|
||||
| Driver infrastructure | redox-driver-sys, linux-kpi headers | Rust + C headers | ✅ |
|
||||
| Display/compositor | Wayland compositor | Rust | required |
|
||||
| Session/auth | redbear-sessiond, redbear-authd | Rust | ✅ |
|
||||
| D-Bus broker | session/system bus | Rust | ✅ |
|
||||
| Network stack | TCP/IP, Wi-Fi control plane | Rust | required |
|
||||
| Bluetooth stack | host/controller path | Rust | required |
|
||||
| USB stack | controller drivers, hub driver | Rust | required |
|
||||
| Input stack | evdev, libinput adapter | Rust | required |
|
||||
| Firmware loading | scheme:firmware daemon | Rust | ✅ |
|
||||
| Core utilities | shell, fileutils, process tools | Rust | required |
|
||||
| Bootloader | UEFI bootloader | Rust | ✅ |
|
||||
| Installer | redox_installer | Rust | ✅ |
|
||||
| Build tooling | cookbook, repo binary | Rust | ✅ |
|
||||
|
||||
### What does NOT need to be Rust
|
||||
|
||||
- **Ported desktop applications**: KDE Plasma, Qt apps, KDE Frameworks — these are upstream
|
||||
C++ codebases and remain C++. The boundary is at the platform adapter layer: anything Red Bear
|
||||
writes to integrate them ( Wayland protocol bridges, D-Bus service implementations, platform
|
||||
plugins) must be Rust even if the upstream consumer is C++.
|
||||
- **Ported libraries**: mesa, wayland, libxkbcommon, libinput, fontconfig, etc. — upstream C.
|
||||
- **Games and end-user applications**: upstream code in any language.
|
||||
|
||||
### Decision rule
|
||||
|
||||
When writing new code for Red Bear OS, or when choosing between writing new code vs porting
|
||||
existing code, the rule is:
|
||||
|
||||
> If the component runs below the application layer — kernel, libc, drivers, filesystems,
|
||||
> compositor, session management, networking, input, USB, Bluetooth, core utilities —
|
||||
> it must be written in Rust.
|
||||
>
|
||||
> If the component is an application or library that users would recognize as a separate
|
||||
> upstream project (KDE, Qt, Firefox, etc.), it may remain in its upstream language.
|
||||
>
|
||||
> The integration layer between Rust infrastructure and upstream C/C++ code must be Rust.
|
||||
> Platform adapters, D-Bus service implementations, Wayland protocol bridges, and plugin
|
||||
> shims are infrastructure, not applications.
|
||||
|
||||
### Enforcement
|
||||
|
||||
- New recipes under `local/recipes/` for system-critical components must use `template = "cargo"`.
|
||||
- C/C++ build templates (`cmake`, `meson`, `make`, `configure`) are only for ported upstream
|
||||
packages and their direct dependencies.
|
||||
- If a ported C/C++ package needs a companion daemon, helper, or bridge, that companion must
|
||||
be a separate Rust recipe — not embedded C in the ported package.
|
||||
|
||||
### Conflicting implementations: always prefer Rust
|
||||
|
||||
When both a Rust implementation and a C/C++ implementation exist for the same functionality,
|
||||
Red Bear OS **always** prefers the Rust implementation. This applies even when the C version is
|
||||
from upstream Redox or appears more complete.
|
||||
|
||||
The rationale: Rust provides memory safety, type safety, and panic-based error recovery at the
|
||||
language level. For an OS with no ASLR, no stack canaries, and a minimal kernel, the language
|
||||
itself is the primary defense boundary. A C implementation of equivalent functionality is always
|
||||
a strictly weaker choice.
|
||||
|
||||
Examples:
|
||||
|
||||
| Situation | Correct choice |
|
||||
|-----------|---------------|
|
||||
| relibc (Rust) vs newlib/glibc (C) | relibc — always |
|
||||
| redoxfs (Rust) vs an imported C filesystem driver | redoxfs — always |
|
||||
| redbear-sessiond (Rust) vs dbus-daemon (C) | redbear-sessiond — always |
|
||||
| A Rust crate for a protocol vs the reference C library | Rust crate — always |
|
||||
| Upstream Redox provides a Rust driver; we also have a C port | Rust driver — always |
|
||||
|
||||
If a Rust implementation is less feature-complete than the C alternative, the correct response
|
||||
is to improve the Rust implementation — not to fall back to C.
|
||||
|
||||
## INSTALLER FILE LAYERING
|
||||
|
||||
The installer creates filesystem images in four layers. Understanding this ordering is critical
|
||||
@@ -253,6 +338,66 @@ See `local/docs/BUILD-SYSTEM-HARDENING-PLAN.md` for the full plan.
|
||||
- **DO NOT** remove patches from `recipe.toml` to fix build failures — rebase the patch instead (see `local/docs/PATCH-GOVERNANCE.md`)
|
||||
- **DO NOT** remove BINS entries to fix build failures — fix the source or use EXISTING_BINS filtering
|
||||
|
||||
## ZERO TOLERANCE FOR STUBS
|
||||
|
||||
**Red Bear OS has zero tolerance for stubs, placeholders, fake implementations, skeleton code,
|
||||
unimplemented functions, or any other "make it compile" shortcut.**
|
||||
|
||||
### What counts as a stub
|
||||
|
||||
Any of the following:
|
||||
|
||||
- A function or method that returns a hardcoded value instead of computing the real result
|
||||
- A trait implementation where methods log "not implemented" or return empty/default values
|
||||
- A struct that stores fields but never uses them for real work
|
||||
- A file that exists to satisfy an import but contains no real logic
|
||||
- A `// TODO: implement` comment followed by a dummy return
|
||||
- A driver that creates synthetic data (fake EDID, hardcoded modes) instead of querying the hardware
|
||||
- A `todo!()` or `unimplemented!()` macro anywhere in non-test code
|
||||
|
||||
### The rule
|
||||
|
||||
> **As soon as a stub is detected, it must be replaced with real, sophisticated, comprehensive code
|
||||
> in the same work session.** No exceptions. No "I'll come back to it." No "this is good enough for now."
|
||||
|
||||
A stub found in the tree is a **bug** — not a precedent to follow, not a placeholder to defer,
|
||||
not an acceptable intermediate state. Every stub is technical debt that compounds daily.
|
||||
|
||||
### Required response to a stub
|
||||
|
||||
When you encounter a stub:
|
||||
|
||||
1. **Stop what you are doing.** The stub takes priority.
|
||||
2. **Understand the interface.** Read the trait, API contract, or specification the stub is supposed to implement.
|
||||
3. **Implement it comprehensively.** Real logic, real hardware communication, real error handling, real edge cases.
|
||||
4. **Verify it works.** Run `cargo check`, tests, or build verification.
|
||||
|
||||
| Stub pattern | Required fix |
|
||||
|---|---|
|
||||
| `fn detect_connectors() -> Vec<ConnectorInfo> { vec![] }` | Implement real hardware enumeration with proper protocol |
|
||||
| `fn handle_irq() -> Ok(None)` | Implement real interrupt handling with event dispatch |
|
||||
| `synthetic_edid()` when hardware can provide real EDID | Query the device via the proper protocol |
|
||||
| Hardcoded mode "1280x720" | Query the display hardware for actual supported modes |
|
||||
| `_firmware: HashMap<String, Vec<u8>>` (unused parameter) | Use the firmware data in device initialization |
|
||||
| `Ok(self.vblank_count.fetch_add(1, Ordering::SeqCst))` in page_flip | Submit real buffer to hardware and wait for display |
|
||||
| `todo!()` / `unimplemented!()` | Replace with full implementation |
|
||||
|
||||
### Why this matters
|
||||
|
||||
Stubs are worse than missing code because they:
|
||||
|
||||
- **Hide missing functionality** — the system appears to work but silently does nothing
|
||||
- **Block real testing** — you can't verify behavior against hardware when the code doesn't talk to hardware
|
||||
- **Create false confidence** — "it compiles" becomes a substitute for "it works"
|
||||
- **Compound over time** — one stub leads to another as callers assume the interface is real
|
||||
- **Waste debugging time** — hours spent tracing why something "doesn't display" when the driver never sent a command
|
||||
|
||||
### Enforcement
|
||||
|
||||
- Code reviews must reject any PR containing stubs
|
||||
- Any agent or developer that introduces a stub must replace it before the session ends
|
||||
- If a stub cannot be replaced (missing specification, blocked dependency), document it as a known gap in `local/docs/` — but never leave it in the code as a stub. Remove the code path entirely and add a clear error message instead.
|
||||
|
||||
## LINUX REFERENCE SOURCE POLICY
|
||||
|
||||
`local/reference/linux-7.0/` (or later) contains a full Linux kernel source tree for
|
||||
|
||||
+31
-25
@@ -47,14 +47,21 @@ numad = {}
|
||||
redox-drm = {}
|
||||
mesa = {}
|
||||
libdrm = {}
|
||||
libepoxy = {}
|
||||
libdisplay-info = {}
|
||||
libxcvt = {}
|
||||
lcms2 = {}
|
||||
freetype2 = {}
|
||||
fontconfig = {}
|
||||
|
||||
libwayland = {}
|
||||
wayland-protocols = {}
|
||||
redbear-compositor = {}
|
||||
plasma-wayland-protocols = {}
|
||||
redbear-compositor = "ignore" # replaced by kwin
|
||||
|
||||
# Keyboard/input
|
||||
# libxkbcommon = {} # build needed
|
||||
# xkeyboard-config = {} # build needed
|
||||
libxkbcommon = {}
|
||||
xkeyboard-config = {}
|
||||
libevdev = {}
|
||||
libinput = {}
|
||||
redbear-keymapd = {}
|
||||
@@ -93,7 +100,7 @@ kf6-kded6 = {}
|
||||
kf6-kguiaddons = {}
|
||||
kf6-ki18n = {}
|
||||
kf6-kiconthemes = {}
|
||||
kf6-kidletime = "ignore"
|
||||
kf6-kidletime = {}
|
||||
kf6-kitemmodels = {}
|
||||
kf6-kitemviews = {}
|
||||
kf6-kjobwidgets = {}
|
||||
@@ -101,12 +108,14 @@ kf6-knotifications = {}
|
||||
kf6-kpackage = {}
|
||||
kf6-kservice = {}
|
||||
kf6-ktextwidgets = {}
|
||||
kf6-kwayland = "ignore"
|
||||
kf6-kwayland = {}
|
||||
kf6-kwidgetsaddons = {}
|
||||
kf6-kwindowsystem = {}
|
||||
kf6-kxmlgui = {}
|
||||
kf6-prison = {}
|
||||
kf6-solid = {}
|
||||
kf6-sonnet = {}
|
||||
kf6-ksvg = {}
|
||||
kf6-knewstuff = {}
|
||||
kf6-kwallet = {}
|
||||
kf6-kglobalaccel = {}
|
||||
@@ -186,7 +195,7 @@ name = "redox-drm"
|
||||
description = "DRM/KMS display driver (AMD + Intel + VirtIO)"
|
||||
priority = 60
|
||||
command = ["/usr/bin/redox-drm"]
|
||||
depends_on = ["pci", "firmware", "iommu"]
|
||||
depends_on = ["pci"]
|
||||
|
||||
[[driver.match]]
|
||||
class = 0x03
|
||||
@@ -246,6 +255,21 @@ cmd = "/usr/bin/iommu"
|
||||
type = "oneshot_async"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/14_redox-drm.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "DRM/KMS display driver (AMD + Intel + VirtIO)"
|
||||
requires_weak = [
|
||||
"05_boot-essential.target",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "/usr/bin/sh"
|
||||
args = ["-c", "if ! head -c 1 /scheme/drm/card0 >/dev/null 2>&1; then exec /usr/bin/redox-drm; fi; echo 'scheme:drm already registered, skipping'"]
|
||||
type = "oneshot_async"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/12_dbus.service"
|
||||
data = """
|
||||
@@ -395,25 +419,6 @@ envs = { QT_PLUGIN_PATH = "/usr/plugins", QT_QPA_PLATFORM_PLUGIN_PATH = "/usr/pl
|
||||
type = "oneshot_async"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/20_display.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "KDE session assembly helper"
|
||||
requires_weak = [
|
||||
"12_dbus.service",
|
||||
"13_redbear-sessiond.service",
|
||||
"13_seatd.service",
|
||||
"19_redbear-authd.service",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "/usr/bin/redbear-session-launch"
|
||||
args = ["--username", "root", "--mode", "session", "--session", "kde-wayland", "--vt", "4", "--runtime-dir", "/tmp/run/redbear-display-session", "--wayland-display", "wayland-display"]
|
||||
envs = { QT_PLUGIN_PATH = "/usr/plugins", QT_QPA_PLATFORM_PLUGIN_PATH = "/usr/plugins/platforms", QML2_IMPORT_PATH = "/usr/qml", XCURSOR_THEME = "Pop", XKB_CONFIG_ROOT = "/usr/share/X11/xkb", REDBEAR_KDE_SESSION_BACKEND = "virtual", REDBEAR_KDE_SESSION_STATE_DIR = "/run/redbear-display-session" }
|
||||
type = "oneshot_async"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/20_greeter.service"
|
||||
data = """
|
||||
@@ -421,6 +426,7 @@ data = """
|
||||
description = "Red Bear greeter service"
|
||||
requires_weak = [
|
||||
"00_driver-manager.service",
|
||||
"14_redox-drm.service",
|
||||
"12_dbus.service",
|
||||
"13_redbear-sessiond.service",
|
||||
"13_seatd.service",
|
||||
|
||||
@@ -15,14 +15,25 @@ description = "DRM/KMS display driver (AMD + Intel + VirtIO)"
|
||||
priority = 60
|
||||
command = ["/usr/bin/redox-drm"]
|
||||
|
||||
# Only match known GPU vendors. Class 0x03 alone catches QEMU VGA
|
||||
# (vendor 0x1234) which redox-drm rejects with a fatal error.
|
||||
[[driver.match]]
|
||||
vendor = 0x1002
|
||||
class = 0x03
|
||||
|
||||
[[driver.match]]
|
||||
vendor = 0x8086
|
||||
class = 0x03
|
||||
|
||||
[[driver.match]]
|
||||
vendor = 0x1AF4
|
||||
class = 0x03
|
||||
|
||||
[[driver]]
|
||||
name = "virtio-gpud"
|
||||
description = "VirtIO GPU driver"
|
||||
priority = 60
|
||||
command = ["/usr/lib/drivers/virtio-gpud"]
|
||||
name = "redox-drm-virtio"
|
||||
description = "VirtIO GPU DRM/KMS driver"
|
||||
priority = 61
|
||||
command = ["/usr/bin/redox-drm"]
|
||||
|
||||
[[driver.match]]
|
||||
vendor = 0x1AF4
|
||||
|
||||
@@ -6,18 +6,26 @@ description = "USB CDC ACM serial driver"
|
||||
priority = 70
|
||||
command = ["/usr/bin/redbear-acmd"]
|
||||
|
||||
[[driver.match]]
|
||||
vendor = 0xFFFF
|
||||
device = 0xFFFF
|
||||
|
||||
[[driver]]
|
||||
name = "redbear-ecmd"
|
||||
description = "USB CDC ECM/NCM ethernet driver"
|
||||
priority = 70
|
||||
command = ["/usr/bin/redbear-ecmd"]
|
||||
|
||||
[[driver.match]]
|
||||
vendor = 0xFFFF
|
||||
device = 0xFFFF
|
||||
|
||||
[[driver]]
|
||||
name = "redbear-usbaudiod"
|
||||
description = "USB Audio Class driver"
|
||||
priority = 70
|
||||
command = ["/usr/bin/redbear-usbaudiod"]
|
||||
|
||||
# USB class drivers are spawned by the USB host controller (xhcid/ehcid)
|
||||
# when matching USB devices are detected, not by PCI bus scanning.
|
||||
# Match entries below use USB interface class codes for host-controller-side matching.
|
||||
[[driver.match]]
|
||||
vendor = 0xFFFF
|
||||
device = 0xFFFF
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
diff --git a/drivers/pcid/src/cfg_access/fallback.rs b/drivers/pcid/src/cfg_access/fallback.rs
|
||||
--- a/drivers/pcid/src/cfg_access/fallback.rs
|
||||
+++ b/drivers/pcid/src/cfg_access/fallback.rs
|
||||
@@ -61,8 +61,11 @@
|
||||
|
||||
Self::set_iopl();
|
||||
|
||||
- let offset =
|
||||
- u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space");
|
||||
+ let Ok(offset) = u8::try_from(offset) else {
|
||||
+ // Offset exceeds 256-byte PCI 3.0 config space — return all-ones
|
||||
+ // (standard response for non-existent config space, matching Linux behavior).
|
||||
+ return 0xFFFF_FFFF;
|
||||
+ };
|
||||
let address = Self::address(address, offset);
|
||||
|
||||
Pio::<u32>::new(0xCF8).write(address);
|
||||
@@ -74,8 +77,9 @@
|
||||
|
||||
Self::set_iopl();
|
||||
|
||||
- let offset =
|
||||
- u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space");
|
||||
+ let Ok(offset) = u8::try_from(offset) else {
|
||||
+ return;
|
||||
+ };
|
||||
let address = Self::address(address, offset);
|
||||
|
||||
Pio::<u32>::new(0xCF8).write(address);
|
||||
diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs
|
||||
--- a/drivers/pcid/src/scheme.rs
|
||||
+++ b/drivers/pcid/src/scheme.rs
|
||||
@@ -22,6 +22,7 @@
|
||||
Access,
|
||||
Device,
|
||||
Channel { addr: PciAddress, st: ChannelState },
|
||||
+ Config { addr: PciAddress },
|
||||
SchemeRoot,
|
||||
}
|
||||
struct HandleWrapper {
|
||||
@@ -30,14 +31,20 @@
|
||||
}
|
||||
impl Handle {
|
||||
fn is_file(&self) -> bool {
|
||||
- matches!(self, Self::Access | Self::Channel { .. })
|
||||
+ matches!(
|
||||
+ self,
|
||||
+ Self::Access | Self::Channel { .. } | Self::Config { .. }
|
||||
+ )
|
||||
}
|
||||
fn is_dir(&self) -> bool {
|
||||
!self.is_file()
|
||||
}
|
||||
// TODO: capability rather than root
|
||||
fn requires_root(&self) -> bool {
|
||||
- matches!(self, Self::Access | Self::Channel { .. })
|
||||
+ matches!(
|
||||
+ self,
|
||||
+ Self::Access | Self::Channel { .. } | Self::Config { .. }
|
||||
+ )
|
||||
}
|
||||
fn is_scheme_root(&self) -> bool {
|
||||
matches!(self, Self::SchemeRoot)
|
||||
@@ -49,7 +56,8 @@
|
||||
AwaitingResponseRead(VecDeque<u8>),
|
||||
}
|
||||
|
||||
-const DEVICE_CONTENTS: &[&str] = &["channel"];
|
||||
+const DEVICE_CONTENTS: &[&str] = &["channel", "config"];
|
||||
+const STANDARD_CONFIG_SPACE_SIZE: usize = 256;
|
||||
|
||||
impl PciScheme {
|
||||
pub fn access(&mut self) -> usize {
|
||||
@@ -133,6 +141,7 @@
|
||||
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
|
||||
Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
||||
Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600),
|
||||
+ Handle::Config { .. } => (STANDARD_CONFIG_SPACE_SIZE, MODE_CHR | 0o600),
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
stat.st_size = len as u64;
|
||||
@@ -143,7 +152,7 @@
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
- _offset: u64,
|
||||
+ offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
@@ -160,6 +169,7 @@
|
||||
addr: _,
|
||||
ref mut st,
|
||||
} => Self::read_channel(st, buf),
|
||||
+ Handle::Config { addr } => Self::read_config(&self.pcie, addr, buf, offset),
|
||||
Handle::SchemeRoot => Err(Error::new(EBADF)),
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
@@ -193,7 +203,9 @@
|
||||
return Ok(buf);
|
||||
}
|
||||
Handle::Device => DEVICE_CONTENTS,
|
||||
- Handle::Access | Handle::Channel { .. } => return Err(Error::new(ENOTDIR)),
|
||||
+ Handle::Access | Handle::Channel { .. } | Handle::Config { .. } => {
|
||||
+ return Err(Error::new(ENOTDIR));
|
||||
+ }
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
|
||||
@@ -212,7 +224,7 @@
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &[u8],
|
||||
- _offset: u64,
|
||||
+ offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
@@ -226,6 +238,7 @@
|
||||
Handle::Channel { addr, ref mut st } => {
|
||||
Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf)
|
||||
}
|
||||
+ Handle::Config { addr } => Self::write_config(&self.pcie, addr, buf, offset),
|
||||
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
@@ -356,11 +369,73 @@
|
||||
st: ChannelState::AwaitingData,
|
||||
}
|
||||
}
|
||||
+ "config" => Handle::Config { addr },
|
||||
_ => return Err(Error::new(ENOENT)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+ fn read_config(pcie: &Pcie, addr: PciAddress, buf: &mut [u8], offset: u64) -> Result<usize> {
|
||||
+ let start = usize::try_from(offset).map_err(|_| Error::new(EINVAL))?;
|
||||
+ let end = start.saturating_add(buf.len()).min(STANDARD_CONFIG_SPACE_SIZE);
|
||||
+ if start >= end {
|
||||
+ return Ok(0);
|
||||
+ }
|
||||
+
|
||||
+ let aligned_start = start & !0x3;
|
||||
+ let aligned_end = (end + 3) & !0x3;
|
||||
+
|
||||
+ let mut bytes_read = 0;
|
||||
+ for dword_offset in (aligned_start..aligned_end).step_by(4) {
|
||||
+ let bytes = unsafe { pcie.read(addr, dword_offset as u16) }.to_le_bytes();
|
||||
+
|
||||
+ let chunk_start = start.max(dword_offset);
|
||||
+ let chunk_end = end.min(dword_offset + bytes.len());
|
||||
+ let src_start = chunk_start - dword_offset;
|
||||
+ let dst_start = chunk_start - start;
|
||||
+ let len = chunk_end - chunk_start;
|
||||
+
|
||||
+ buf[dst_start..dst_start + len].copy_from_slice(&bytes[src_start..src_start + len]);
|
||||
+ bytes_read += len;
|
||||
+ }
|
||||
+
|
||||
+ Ok(bytes_read)
|
||||
+ }
|
||||
+
|
||||
+ fn write_config(pcie: &Pcie, addr: PciAddress, buf: &[u8], offset: u64) -> Result<usize> {
|
||||
+ let start = usize::try_from(offset).map_err(|_| Error::new(EINVAL))?;
|
||||
+ let end = start.saturating_add(buf.len()).min(STANDARD_CONFIG_SPACE_SIZE);
|
||||
+ if start >= end {
|
||||
+ return Ok(0);
|
||||
+ }
|
||||
+
|
||||
+ let aligned_start = start & !0x3;
|
||||
+ let aligned_end = (end + 3) & !0x3;
|
||||
+
|
||||
+ let mut bytes_written = 0;
|
||||
+ for dword_offset in (aligned_start..aligned_end).step_by(4) {
|
||||
+ let chunk_start = start.max(dword_offset);
|
||||
+ let chunk_end = end.min(dword_offset + 4);
|
||||
+ let dst_start = chunk_start - dword_offset;
|
||||
+ let src_start = chunk_start - start;
|
||||
+ let len = chunk_end - chunk_start;
|
||||
+
|
||||
+ let mut bytes = if dst_start == 0 && len == 4 {
|
||||
+ [0; 4]
|
||||
+ } else {
|
||||
+ unsafe { pcie.read(addr, dword_offset as u16) }.to_le_bytes()
|
||||
+ };
|
||||
+ bytes[dst_start..dst_start + len].copy_from_slice(&buf[src_start..src_start + len]);
|
||||
+
|
||||
+ unsafe {
|
||||
+ pcie.write(addr, dword_offset as u16, u32::from_le_bytes(bytes));
|
||||
+ }
|
||||
+ bytes_written += len;
|
||||
+ }
|
||||
+
|
||||
+ Ok(bytes_written)
|
||||
+ }
|
||||
+
|
||||
fn read_channel(state: &mut ChannelState, buf: &mut [u8]) -> Result<usize> {
|
||||
match *state {
|
||||
ChannelState::AwaitingResponseRead(ref mut queue) => {
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,126 @@
|
||||
diff --git a/src/corelib/thread/qfutex_p.h b/src/corelib/thread/qfutex_p.h
|
||||
--- a/src/corelib/thread/qfutex_p.h
|
||||
+++ b/src/corelib/thread/qfutex_p.h
|
||||
@@ -41,6 +41,9 @@
|
||||
#elif defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
|
||||
// use Linux mutexes everywhere except for LSB builds
|
||||
# include "qfutex_linux_p.h"
|
||||
+#elif defined(Q_OS_REDOX)
|
||||
+// Redox kernel implements the same futex syscall as Linux (SYS_FUTEX = 202)
|
||||
+# include "qfutex_redox_p.h"
|
||||
#elif defined(Q_OS_WIN)
|
||||
# include "qfutex_win_p.h"
|
||||
#else
|
||||
diff --git a/src/corelib/thread/qfutex_redox_p.h b/src/corelib/thread/qfutex_redox_p.h
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/src/corelib/thread/qfutex_redox_p.h
|
||||
@@ -0,0 +1,97 @@
|
||||
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
+
|
||||
+#ifndef QFUTEX_REDOX_P_H
|
||||
+#define QFUTEX_REDOX_P_H
|
||||
+
|
||||
+#include <qdeadlinetimer.h>
|
||||
+#include <qtsan_impl.h>
|
||||
+
|
||||
+#include <errno.h>
|
||||
+#include <limits.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+#define QT_ALWAYS_USE_FUTEX
|
||||
+
|
||||
+#ifndef __NR_futex
|
||||
+# define __NR_futex 202
|
||||
+#endif
|
||||
+
|
||||
+QT_BEGIN_NAMESPACE
|
||||
+
|
||||
+namespace QtRedoxFutex {
|
||||
+
|
||||
+constexpr inline bool futexAvailable() { return true; }
|
||||
+
|
||||
+constexpr int RedoxFutexWait = 0;
|
||||
+constexpr int RedoxFutexWake = 1;
|
||||
+
|
||||
+inline long redox_syscall6(long nr, long a1, long a2, long a3, long a4, long a5, long a6) noexcept
|
||||
+{
|
||||
+ register long r10 __asm__("r10") = a4;
|
||||
+ register long r8 __asm__("r8") = a5;
|
||||
+ register long r9 __asm__("r9") = a6;
|
||||
+ long ret;
|
||||
+ __asm__ __volatile__(
|
||||
+ "syscall"
|
||||
+ : "=a"(ret)
|
||||
+ : "a"(nr), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8), "r"(r9)
|
||||
+ : "rcx", "r11", "memory"
|
||||
+ );
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+inline long _q_futex(int *addr, int op, int val, quintptr val2 = 0,
|
||||
+ int *addr2 = nullptr, int val3 = 0) noexcept
|
||||
+{
|
||||
+ QtTsan::futexRelease(addr, addr2);
|
||||
+ long result = redox_syscall6(__NR_futex, (long)addr, (long)op, (long)val, (long)val2, (long)addr2, (long)val3);
|
||||
+ QtTsan::futexAcquire(addr, addr2);
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+template <typename T> int *addr(T *ptr)
|
||||
+{
|
||||
+ int *int_addr = reinterpret_cast<int *>(ptr);
|
||||
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||
+ if (sizeof(T) > sizeof(int))
|
||||
+ int_addr++;
|
||||
+#endif
|
||||
+ return int_addr;
|
||||
+}
|
||||
+
|
||||
+template <typename Atomic>
|
||||
+inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
|
||||
+{
|
||||
+ _q_futex(addr(&futex), RedoxFutexWait, qintptr(expectedValue));
|
||||
+}
|
||||
+
|
||||
+template <typename Atomic>
|
||||
+inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, QDeadlineTimer deadline)
|
||||
+{
|
||||
+ auto timeout = deadline.deadline<std::chrono::steady_clock>().time_since_epoch();
|
||||
+ struct timespec ts;
|
||||
+ ts.tv_sec = timeout.count() < 0 ? 0 : static_cast<long>(std::chrono::duration_cast<std::chrono::seconds>(timeout).count());
|
||||
+ auto ns_remainder = timeout - std::chrono::seconds(ts.tv_sec);
|
||||
+ ts.tv_nsec = static_cast<long>(std::chrono::duration_cast<std::chrono::nanoseconds>(ns_remainder).count());
|
||||
+ long r = _q_futex(addr(&futex), RedoxFutexWait, qintptr(expectedValue), quintptr(&ts),
|
||||
+ nullptr, 0);
|
||||
+ return r == 0 || errno != ETIMEDOUT;
|
||||
+}
|
||||
+
|
||||
+template <typename Atomic> inline void futexWakeOne(Atomic &futex)
|
||||
+{
|
||||
+ _q_futex(addr(&futex), RedoxFutexWake, 1);
|
||||
+}
|
||||
+
|
||||
+template <typename Atomic> inline void futexWakeAll(Atomic &futex)
|
||||
+{
|
||||
+ _q_futex(addr(&futex), RedoxFutexWake, INT_MAX);
|
||||
+}
|
||||
+
|
||||
+} // namespace QtRedoxFutex
|
||||
+
|
||||
+namespace QtFutex = QtRedoxFutex;
|
||||
+
|
||||
+QT_END_NAMESPACE
|
||||
+
|
||||
+#endif // QFUTEX_REDOX_P_H
|
||||
diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h
|
||||
--- a/src/corelib/thread/qmutex.h
|
||||
+++ b/src/corelib/thread/qmutex.h
|
||||
@@ -26,6 +26,6 @@
|
||||
protected:
|
||||
static constexpr bool FutexAlwaysAvailable =
|
||||
-#if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX) || defined(Q_OS_WIN) // these platforms use futex
|
||||
+#if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX) || defined(Q_OS_WIN) || defined(Q_OS_REDOX) // these platforms use futex
|
||||
true
|
||||
#else
|
||||
false
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs
|
||||
index 4a01b0d9..00271fa9 100644
|
||||
--- a/src/ld_so/linker.rs
|
||||
+++ b/src/ld_so/linker.rs
|
||||
@@ -928,6 +928,7 @@ impl Linker {
|
||||
search_paths.extend(ld_path.split(PATH_SEP));
|
||||
}
|
||||
search_paths.push("/lib");
|
||||
+ search_paths.push("/usr/lib");
|
||||
for part in search_paths.iter() {
|
||||
full_path = format!("{}/{}", part, name);
|
||||
if debug {
|
||||
@@ -0,0 +1,35 @@
|
||||
diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs
|
||||
--- a/src/header/stdlib/mod.rs
|
||||
+++ b/src/header/stdlib/mod.rs
|
||||
@@ -31,8 +31,8 @@ use crate::{
|
||||
platform::{
|
||||
self, Pal, Sys,
|
||||
types::{
|
||||
- c_char, c_double, c_float, c_int, c_long, c_longlong, c_uint, c_ulong, c_ulonglong,
|
||||
- c_ushort, c_void, size_t, ssize_t, uintptr_t, wchar_t,
|
||||
+ c_char, c_double, c_float, c_int, c_long, c_longdouble, c_longlong, c_uint, c_ulong,
|
||||
+ c_ulonglong, c_ushort, c_void, size_t, ssize_t, uintptr_t, wchar_t,
|
||||
},
|
||||
},
|
||||
raw_cell::RawCell,
|
||||
@@ -1506,7 +1506,19 @@ pub unsafe extern "C" fn strtol(s: *const c_char, endptr: *mut *mut c_char, base
|
||||
strto_impl!(c_long, true, c_long::MAX, c_long::MIN, s, endptr, base)
|
||||
}
|
||||
|
||||
-// TODO: strtold(), when long double is available
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtod.html>.
|
||||
+///
|
||||
+/// Note: delegates to strtod and converts via relibc_dtold because c_longdouble
|
||||
+/// is u128 (soft float) on this target. Precision is limited to double.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn strtold(s: *const c_char, endptr: *mut *mut c_char) -> c_longdouble {
|
||||
+ let mut result: c_longdouble = 0;
|
||||
+ unsafe {
|
||||
+ let d = strtod(s, endptr);
|
||||
+ crate::header::stdio::printf::relibc_dtold(d, &mut result);
|
||||
+ }
|
||||
+ result
|
||||
+}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtol.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
@@ -0,0 +1,42 @@
|
||||
diff --git a/src/header/sys_ioctl/redox/mod.rs b/src/header/sys_ioctl/redox/mod.rs
|
||||
index 8b3cf8f..c3e925e 100644
|
||||
--- a/src/header/sys_ioctl/redox/mod.rs
|
||||
+++ b/src/header/sys_ioctl/redox/mod.rs
|
||||
@@ -4,7 +4,7 @@ use syscall;
|
||||
|
||||
use crate::{
|
||||
error::{Errno, Result, ResultExt},
|
||||
- header::{errno::EINVAL, fcntl, termios},
|
||||
+ header::{errno::{EINVAL, ENOTTY}, fcntl, termios},
|
||||
platform::{
|
||||
Pal, Sys,
|
||||
types::{c_int, c_ulong, c_ulonglong, c_void, pid_t},
|
||||
@@ -166,25 +166,9 @@ unsafe fn ioctl_inner(fd: c_int, request: c_ulong, out: *mut c_void) -> Result<c
|
||||
todo_skip!(0, "ioctl SIOCATMARK");
|
||||
}
|
||||
_ => {
|
||||
- // See https://docs.kernel.org/userspace-api/ioctl/ioctl-decoding.html for details
|
||||
- let dir = (request >> 30) & 0b11;
|
||||
- let size = ((request >> 16) & 0x3FFF) as usize;
|
||||
- let name = (((request >> 8) & 0xFF) as u8) as char;
|
||||
- let func = (request & 0xFF) as u8;
|
||||
- match name {
|
||||
- 'd' => {
|
||||
- let buf = match dir {
|
||||
- 0b10 => IoctlBuffer::Read(out, size),
|
||||
- 0b01 => IoctlBuffer::Write(out, size),
|
||||
- 0b11 => IoctlBuffer::ReadWrite(out, size),
|
||||
- _ => IoctlBuffer::None,
|
||||
- };
|
||||
- return unsafe { drm::ioctl(fd, func, buf) };
|
||||
- }
|
||||
- _ => {
|
||||
- return Err(Errno(EINVAL));
|
||||
- }
|
||||
- }
|
||||
+ // libdrm handles DRM ioctls via redox_drm_simple_ioctl() with
|
||||
+ // its own scheme:drm wire format; relibc must not intercept.
|
||||
+ return Err(Errno(ENOTTY));
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
@@ -1,7 +1,17 @@
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::{DriverError, Result};
|
||||
|
||||
/// Method for accessing PCI configuration space.
|
||||
enum ConfigAccess {
|
||||
/// Access via /scheme/pci/.../config file (normal path)
|
||||
SchemeFile(std::fs::File),
|
||||
/// Access via x86 I/O ports 0xCF8/0xCFC (fallback when scheme:pci is stale)
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
IoPorts,
|
||||
}
|
||||
|
||||
pub const PCI_VENDOR_ID_AMD: u16 = 0x1002;
|
||||
pub const PCI_VENDOR_ID_INTEL: u16 = 0x8086;
|
||||
pub const PCI_VENDOR_ID_NVIDIA: u16 = 0x10DE;
|
||||
@@ -11,6 +21,10 @@ pub const PCI_CLASS_DISPLAY_VGA: u8 = 0x00;
|
||||
pub const PCI_CLASS_DISPLAY_3D: u8 = 0x02;
|
||||
|
||||
pub const PCI_HEADER_TYPE_NORMAL: u8 = 0x00;
|
||||
const PCI_CONFIG_ADDRESS_PORT: u16 = 0xCF8;
|
||||
const PCI_CONFIG_DATA_PORT: u16 = 0xCFC;
|
||||
const PCI_CONFIG_SPACE_SIZE: usize = 256;
|
||||
static PCI_IOPL_INIT: OnceLock<std::result::Result<(), String>> = OnceLock::new();
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct PciLocation {
|
||||
@@ -241,7 +255,7 @@ impl PciDeviceInfo {
|
||||
|
||||
pub struct PciDevice {
|
||||
location: PciLocation,
|
||||
config_fd: std::fs::File,
|
||||
access: ConfigAccess,
|
||||
}
|
||||
|
||||
impl PciDevice {
|
||||
@@ -257,16 +271,31 @@ impl PciDevice {
|
||||
|
||||
pub fn open_location(loc: &PciLocation) -> Result<Self> {
|
||||
let config_path = format!("{}/config", loc.scheme_path());
|
||||
let fd = std::fs::OpenOptions::new()
|
||||
let access = match std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(&config_path)
|
||||
.map_err(|e| {
|
||||
DriverError::Pci(format!("cannot open PCI config at {}: {}", config_path, e))
|
||||
{
|
||||
Ok(fd) => ConfigAccess::SchemeFile(fd),
|
||||
Err(err) => {
|
||||
ensure_io_port_access().map_err(|fallback_err| {
|
||||
DriverError::Pci(format!(
|
||||
"cannot open PCI config at {}: {}; I/O port fallback unavailable: {}",
|
||||
config_path, err, fallback_err
|
||||
))
|
||||
})?;
|
||||
log::debug!(
|
||||
"PCI: scheme path {} unavailable, using I/O port fallback for {}",
|
||||
config_path,
|
||||
loc
|
||||
);
|
||||
ConfigAccess::IoPorts
|
||||
}
|
||||
};
|
||||
|
||||
Ok(PciDevice {
|
||||
location: *loc,
|
||||
config_fd: fd,
|
||||
access,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -279,42 +308,57 @@ impl PciDevice {
|
||||
}
|
||||
|
||||
pub fn read_config_dword(&mut self, offset: u64) -> Result<u32> {
|
||||
self.config_fd.seek(SeekFrom::Start(offset))?;
|
||||
match &mut self.access {
|
||||
ConfigAccess::SchemeFile(fd) => {
|
||||
let mut buf = [0u8; 4];
|
||||
self.config_fd.read_exact(&mut buf)?;
|
||||
read_scheme_config(fd, offset, &mut buf)?;
|
||||
Ok(u32::from_le_bytes(buf))
|
||||
}
|
||||
ConfigAccess::IoPorts => io_read_config_dword(&self.location, offset),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_config_word(&mut self, offset: u64) -> Result<u16> {
|
||||
self.config_fd.seek(SeekFrom::Start(offset))?;
|
||||
match &mut self.access {
|
||||
ConfigAccess::SchemeFile(fd) => {
|
||||
let mut buf = [0u8; 2];
|
||||
self.config_fd.read_exact(&mut buf)?;
|
||||
read_scheme_config(fd, offset, &mut buf)?;
|
||||
Ok(u16::from_le_bytes(buf))
|
||||
}
|
||||
ConfigAccess::IoPorts => io_read_config_word(&self.location, offset),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_config_byte(&mut self, offset: u64) -> Result<u8> {
|
||||
self.config_fd.seek(SeekFrom::Start(offset))?;
|
||||
match &mut self.access {
|
||||
ConfigAccess::SchemeFile(fd) => {
|
||||
let mut buf = [0u8; 1];
|
||||
self.config_fd.read_exact(&mut buf)?;
|
||||
read_scheme_config(fd, offset, &mut buf)?;
|
||||
Ok(buf[0])
|
||||
}
|
||||
ConfigAccess::IoPorts => io_read_config_byte(&self.location, offset),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_config_dword(&mut self, offset: u64, val: u32) -> Result<()> {
|
||||
self.config_fd.seek(SeekFrom::Start(offset))?;
|
||||
self.config_fd.write_all(&val.to_le_bytes())?;
|
||||
Ok(())
|
||||
match &mut self.access {
|
||||
ConfigAccess::SchemeFile(fd) => write_scheme_config(fd, offset, &val.to_le_bytes()),
|
||||
ConfigAccess::IoPorts => io_write_config_dword(&self.location, offset, val),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_config_word(&mut self, offset: u64, val: u16) -> Result<()> {
|
||||
self.config_fd.seek(SeekFrom::Start(offset))?;
|
||||
self.config_fd.write_all(&val.to_le_bytes())?;
|
||||
Ok(())
|
||||
match &mut self.access {
|
||||
ConfigAccess::SchemeFile(fd) => write_scheme_config(fd, offset, &val.to_le_bytes()),
|
||||
ConfigAccess::IoPorts => io_write_config_word(&self.location, offset, val),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_config_byte(&mut self, offset: u64, val: u8) -> Result<()> {
|
||||
self.config_fd.seek(SeekFrom::Start(offset))?;
|
||||
self.config_fd.write_all(&[val])?;
|
||||
Ok(())
|
||||
match &mut self.access {
|
||||
ConfigAccess::SchemeFile(fd) => write_scheme_config(fd, offset, &[val]),
|
||||
ConfigAccess::IoPorts => io_write_config_byte(&self.location, offset, val),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vendor_id(&mut self) -> Result<u16> {
|
||||
@@ -657,34 +701,268 @@ impl PciDevice {
|
||||
|
||||
impl std::io::Write for PciDevice {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.config_fd.write(buf)
|
||||
match &mut self.access {
|
||||
ConfigAccess::SchemeFile(fd) => fd.write(buf),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
ConfigAccess::IoPorts => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Unsupported,
|
||||
"raw write not supported for I/O port PCI config access",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
self.config_fd.flush()
|
||||
match &mut self.access {
|
||||
ConfigAccess::SchemeFile(fd) => fd.flush(),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
ConfigAccess::IoPorts => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_scheme_config(fd: &mut std::fs::File, offset: u64, buf: &mut [u8]) -> Result<()> {
|
||||
fd.seek(SeekFrom::Start(offset))?;
|
||||
fd.read_exact(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_scheme_config(fd: &mut std::fs::File, offset: u64, buf: &[u8]) -> Result<()> {
|
||||
fd.seek(SeekFrom::Start(offset))?;
|
||||
fd.write_all(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_io_port_access() -> Result<()> {
|
||||
match PCI_IOPL_INIT.get_or_init(init_io_port_access) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) => Err(DriverError::Pci(err.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
fn init_io_port_access() -> std::result::Result<(), String> {
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "redox"))]
|
||||
{
|
||||
crate::io::acquire_iopl().map_err(|err| {
|
||||
format!(
|
||||
"cannot acquire I/O privilege level for PCI config fallback: {}",
|
||||
err
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "x86_64", not(target_os = "redox")))]
|
||||
{
|
||||
Err(String::from(
|
||||
"PCI I/O port fallback requires Redox I/O privilege and is unavailable on non-Redox targets",
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
{
|
||||
Err(String::from(
|
||||
"PCI I/O port fallback is only supported on x86_64",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn io_read_config_dword(location: &PciLocation, offset: u64) -> Result<u32> {
|
||||
if offset & 0x3 == 0 {
|
||||
let address = pci_config_address(location, offset)?;
|
||||
unsafe {
|
||||
outl(PCI_CONFIG_ADDRESS_PORT, address);
|
||||
Ok(inl(PCI_CONFIG_DATA_PORT))
|
||||
}
|
||||
} else {
|
||||
let mut buf = [0u8; 4];
|
||||
for (index, byte) in buf.iter_mut().enumerate() {
|
||||
*byte = io_read_config_byte(location, offset + index as u64)?;
|
||||
}
|
||||
Ok(u32::from_le_bytes(buf))
|
||||
}
|
||||
}
|
||||
|
||||
fn io_read_config_word(location: &PciLocation, offset: u64) -> Result<u16> {
|
||||
let low_bits = (offset & 0x3) as u16;
|
||||
if low_bits <= 2 {
|
||||
let address = pci_config_address(location, offset)?;
|
||||
unsafe {
|
||||
outl(PCI_CONFIG_ADDRESS_PORT, address);
|
||||
Ok(inw(PCI_CONFIG_DATA_PORT + low_bits))
|
||||
}
|
||||
} else {
|
||||
let lo = io_read_config_byte(location, offset)?;
|
||||
let hi = io_read_config_byte(location, offset + 1)?;
|
||||
Ok(u16::from_le_bytes([lo, hi]))
|
||||
}
|
||||
}
|
||||
|
||||
fn io_read_config_byte(location: &PciLocation, offset: u64) -> Result<u8> {
|
||||
let low_bits = (offset & 0x3) as u16;
|
||||
let address = pci_config_address(location, offset)?;
|
||||
unsafe {
|
||||
outl(PCI_CONFIG_ADDRESS_PORT, address);
|
||||
Ok(inb(PCI_CONFIG_DATA_PORT + low_bits))
|
||||
}
|
||||
}
|
||||
|
||||
fn io_write_config_dword(location: &PciLocation, offset: u64, val: u32) -> Result<()> {
|
||||
if offset & 0x3 == 0 {
|
||||
let address = pci_config_address(location, offset)?;
|
||||
unsafe {
|
||||
outl(PCI_CONFIG_ADDRESS_PORT, address);
|
||||
outl(PCI_CONFIG_DATA_PORT, val);
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
for (index, byte) in val.to_le_bytes().into_iter().enumerate() {
|
||||
io_write_config_byte(location, offset + index as u64, byte)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn io_write_config_word(location: &PciLocation, offset: u64, val: u16) -> Result<()> {
|
||||
let low_bits = (offset & 0x3) as u16;
|
||||
if low_bits <= 2 {
|
||||
let address = pci_config_address(location, offset)?;
|
||||
unsafe {
|
||||
outl(PCI_CONFIG_ADDRESS_PORT, address);
|
||||
outw(PCI_CONFIG_DATA_PORT + low_bits, val);
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
let [lo, hi] = val.to_le_bytes();
|
||||
io_write_config_byte(location, offset, lo)?;
|
||||
io_write_config_byte(location, offset + 1, hi)
|
||||
}
|
||||
}
|
||||
|
||||
fn io_write_config_byte(location: &PciLocation, offset: u64, val: u8) -> Result<()> {
|
||||
let low_bits = (offset & 0x3) as u16;
|
||||
let address = pci_config_address(location, offset)?;
|
||||
unsafe {
|
||||
outl(PCI_CONFIG_ADDRESS_PORT, address);
|
||||
outb(PCI_CONFIG_DATA_PORT + low_bits, val);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pci_config_address(location: &PciLocation, offset: u64) -> Result<u32> {
|
||||
let register = u32::try_from(offset)
|
||||
.map_err(|_| DriverError::InvalidParam("PCI config offset exceeds u32"))?;
|
||||
Ok(
|
||||
0x8000_0000
|
||||
| ((location.bus as u32) << 16)
|
||||
| ((location.device as u32) << 11)
|
||||
| ((location.function as u32) << 8)
|
||||
| (register & 0xFC),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
unsafe fn inb(port: u16) -> u8 {
|
||||
let val: u8;
|
||||
core::arch::asm!("in al, dx", in("dx") port, out("al") val, options(nomem, nostack));
|
||||
val
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
unsafe fn outb(port: u16, val: u8) {
|
||||
core::arch::asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack));
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
unsafe fn inw(port: u16) -> u16 {
|
||||
let val: u16;
|
||||
core::arch::asm!("in ax, dx", in("dx") port, out("ax") val, options(nomem, nostack));
|
||||
val
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
unsafe fn outw(port: u16, val: u16) {
|
||||
core::arch::asm!("out dx, ax", in("dx") port, in("ax") val, options(nomem, nostack));
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
unsafe fn inl(port: u16) -> u32 {
|
||||
let val: u32;
|
||||
core::arch::asm!("in eax, dx", in("dx") port, out("eax") val, options(nomem, nostack));
|
||||
val
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
unsafe fn outl(port: u16, val: u32) {
|
||||
core::arch::asm!("out dx, eax", in("dx") port, in("eax") val, options(nomem, nostack));
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
#[inline]
|
||||
unsafe fn inb(_port: u16) -> u8 {
|
||||
unreachable!("PCI I/O port access is only built for x86_64")
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
#[inline]
|
||||
unsafe fn outb(_port: u16, _val: u8) {
|
||||
unreachable!("PCI I/O port access is only built for x86_64")
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
#[inline]
|
||||
unsafe fn inw(_port: u16) -> u16 {
|
||||
unreachable!("PCI I/O port access is only built for x86_64")
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
#[inline]
|
||||
unsafe fn outw(_port: u16, _val: u16) {
|
||||
unreachable!("PCI I/O port access is only built for x86_64")
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
#[inline]
|
||||
unsafe fn inl(_port: u16) -> u32 {
|
||||
unreachable!("PCI I/O port access is only built for x86_64")
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
#[inline]
|
||||
unsafe fn outl(_port: u16, _val: u32) {
|
||||
unreachable!("PCI I/O port access is only built for x86_64")
|
||||
}
|
||||
|
||||
|
||||
fn enumerate_pci_filtered(class: Option<u8>) -> Result<Vec<PciDeviceInfo>> {
|
||||
let entries = std::fs::read_dir("/scheme/pci")?;
|
||||
let class_desc = class
|
||||
.map(|class| format!(" for class {class:#04x}"))
|
||||
.unwrap_or_default();
|
||||
|
||||
let devices = match std::fs::read_dir("/scheme/pci") {
|
||||
Ok(entries) => {
|
||||
let mut devices = Vec::new();
|
||||
let mut saw_entry = false;
|
||||
|
||||
for entry in entries {
|
||||
let entry = entry?;
|
||||
saw_entry = true;
|
||||
|
||||
let name = entry.file_name();
|
||||
let name_str = match name.to_str() {
|
||||
Some(s) => s,
|
||||
None => continue,
|
||||
let Some(name_str) = name.to_str() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// pcid scheme entries use format: segment--bus--device.function
|
||||
let location = match parse_scheme_entry(name_str) {
|
||||
Some(loc) => loc,
|
||||
None => continue,
|
||||
let Some(location) = parse_scheme_entry(name_str) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let config_path = format!("{}/config", location.scheme_path());
|
||||
if let Ok(data) = std::fs::read(&config_path) {
|
||||
if let Some(data) = read_pci_config_space(location)? {
|
||||
if let Some(info) = parse_device_info_from_config_space(location, &data)
|
||||
.filter(|info| class.is_none_or(|class| info.class_code == class))
|
||||
{
|
||||
@@ -693,17 +971,107 @@ fn enumerate_pci_filtered(class: Option<u8>) -> Result<Vec<PciDeviceInfo>> {
|
||||
}
|
||||
}
|
||||
|
||||
if saw_entry {
|
||||
devices
|
||||
} else {
|
||||
log::debug!(
|
||||
"PCI enumeration{}: /scheme/pci is empty, falling back to direct probing",
|
||||
class_desc
|
||||
);
|
||||
direct_probe_pci_bus(class)?
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log::debug!(
|
||||
"PCI enumeration{}: cannot read /scheme/pci ({}), falling back to direct probing",
|
||||
class_desc,
|
||||
err
|
||||
);
|
||||
direct_probe_pci_bus(class)?
|
||||
}
|
||||
};
|
||||
|
||||
log::debug!(
|
||||
"PCI enumeration{}: found {} devices",
|
||||
class
|
||||
.map(|class| format!(" for class {class:#04x}"))
|
||||
.unwrap_or_default(),
|
||||
class_desc,
|
||||
devices.len()
|
||||
);
|
||||
Ok(devices)
|
||||
}
|
||||
|
||||
pub fn parse_device_info_from_config_space(location: PciLocation, data: &[u8]) -> Option<PciDeviceInfo> {
|
||||
fn direct_probe_pci_bus(class: Option<u8>) -> Result<Vec<PciDeviceInfo>> {
|
||||
let mut devices = Vec::new();
|
||||
|
||||
for device in 0..=31 {
|
||||
let function_zero = PciLocation {
|
||||
segment: 0,
|
||||
bus: 0,
|
||||
device,
|
||||
function: 0,
|
||||
};
|
||||
|
||||
let Some(data) = read_pci_config_space(function_zero)? else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let is_multifunction = data.get(0x0e).is_some_and(|header_type| header_type & 0x80 != 0);
|
||||
|
||||
if let Some(info) = parse_device_info_from_config_space(function_zero, &data)
|
||||
.filter(|info| class.is_none_or(|class| info.class_code == class))
|
||||
{
|
||||
devices.push(info);
|
||||
}
|
||||
|
||||
if !is_multifunction {
|
||||
continue;
|
||||
}
|
||||
|
||||
for function in 1..=7 {
|
||||
let location = PciLocation {
|
||||
segment: 0,
|
||||
bus: 0,
|
||||
device,
|
||||
function,
|
||||
};
|
||||
|
||||
let Some(data) = read_pci_config_space(location)? else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some(info) = parse_device_info_from_config_space(location, &data)
|
||||
.filter(|info| class.is_none_or(|class| info.class_code == class))
|
||||
{
|
||||
devices.push(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(devices)
|
||||
}
|
||||
|
||||
fn read_pci_config_space(location: PciLocation) -> Result<Option<Vec<u8>>> {
|
||||
// Try scheme:pci file first, then fall back to I/O ports via PciDevice.
|
||||
let mut device = match PciDevice::open_location(&location) {
|
||||
Ok(dev) => dev,
|
||||
Err(_) => return Ok(None),
|
||||
};
|
||||
|
||||
if device.vendor_id()? == 0xFFFF {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut data = vec![0u8; PCI_CONFIG_SPACE_SIZE];
|
||||
for offset in (0..PCI_CONFIG_SPACE_SIZE).step_by(4) {
|
||||
let dword = device.read_config_dword(offset as u64)?;
|
||||
data[offset..offset + 4].copy_from_slice(&dword.to_le_bytes());
|
||||
}
|
||||
Ok(Some(data))
|
||||
}
|
||||
|
||||
pub fn parse_device_info_from_config_space(
|
||||
location: PciLocation,
|
||||
data: &[u8],
|
||||
) -> Option<PciDeviceInfo> {
|
||||
if data.len() < 64 {
|
||||
return None;
|
||||
}
|
||||
@@ -1055,4 +1423,17 @@ mod tests {
|
||||
assert_eq!(info.interrupt_support(), InterruptSupport::LegacyOnly);
|
||||
assert_eq!(info.interrupt_support().as_str(), "legacy");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pci_config_address_encodes_bus_device_function_and_register() {
|
||||
let location = PciLocation {
|
||||
segment: 0,
|
||||
bus: 0x80,
|
||||
device: 0x1f,
|
||||
function: 0x03,
|
||||
};
|
||||
|
||||
let address = pci_config_address(&location, 0x15).expect("PCI address should encode");
|
||||
assert_eq!(address, 0x8080_fb14);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,23 @@ pub struct RedoxPrivateCsWaitResult {
|
||||
pub completed_seqno: u64,
|
||||
}
|
||||
|
||||
// Minimal virgl response types for the GpuDriver trait.
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct VirglCapsetInfo {
|
||||
pub capset_id: u32,
|
||||
pub capset_max_version: u32,
|
||||
pub capset_max_size: u32,
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct VirglCapset {
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DriverError {
|
||||
#[error("driver initialization failed: {0}")]
|
||||
@@ -86,7 +103,7 @@ pub trait GpuDriver: Send + Sync {
|
||||
fn page_flip(&self, crtc_id: u32, fb_handle: u32, flags: u32) -> Result<u64>;
|
||||
fn get_vblank(&self, crtc_id: u32) -> Result<u64>;
|
||||
|
||||
fn gem_create(&self, size: u64) -> Result<GemHandle>;
|
||||
fn gem_create(&self, size: u64, width: u32, height: u32) -> Result<GemHandle>;
|
||||
fn gem_close(&self, handle: GemHandle) -> Result<()>;
|
||||
fn gem_mmap(&self, handle: GemHandle) -> Result<usize>;
|
||||
fn gem_size(&self, handle: GemHandle) -> Result<u64>;
|
||||
@@ -112,6 +129,93 @@ pub trait GpuDriver: Send + Sync {
|
||||
"private command completion waits are unavailable on this backend",
|
||||
))
|
||||
}
|
||||
|
||||
fn has_virgl_3d(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn virgl_get_capset_info(&self, _capset_index: u32) -> Result<VirglCapsetInfo> {
|
||||
Err(DriverError::Unsupported("virgl capset info"))
|
||||
}
|
||||
|
||||
fn virgl_get_capset(&self, _capset_id: u32, _capset_version: u32) -> Result<VirglCapset> {
|
||||
Err(DriverError::Unsupported("virgl capset"))
|
||||
}
|
||||
|
||||
fn virgl_ctx_create(&self, _ctx_id: u32, _debug_name: &str, _context_init: u32) -> Result<()> {
|
||||
Err(DriverError::Unsupported("virgl context creation"))
|
||||
}
|
||||
|
||||
fn virgl_ctx_destroy(&self, _ctx_id: u32) -> Result<()> {
|
||||
Err(DriverError::Unsupported("virgl context destruction"))
|
||||
}
|
||||
|
||||
fn virgl_resource_create_3d(
|
||||
&self,
|
||||
_resource_id: u32,
|
||||
_target: u32,
|
||||
_format: u32,
|
||||
_bind: u32,
|
||||
_width: u32,
|
||||
_height: u32,
|
||||
_depth: u32,
|
||||
_array_size: u32,
|
||||
_last_level: u32,
|
||||
_nr_samples: u32,
|
||||
_flags: u32,
|
||||
_ctx_id: u32,
|
||||
) -> Result<()> {
|
||||
Err(DriverError::Unsupported("virgl 3D resource creation"))
|
||||
}
|
||||
|
||||
fn virgl_submit_3d(&self, _ctx_id: u32, _command_data: &[u8]) -> Result<()> {
|
||||
Err(DriverError::Unsupported("virgl submit 3D"))
|
||||
}
|
||||
|
||||
fn virgl_transfer_to_host_3d(
|
||||
&self,
|
||||
_ctx_id: u32,
|
||||
_resource_id: u32,
|
||||
_x: u32,
|
||||
_y: u32,
|
||||
_z: u32,
|
||||
_w: u32,
|
||||
_h: u32,
|
||||
_d: u32,
|
||||
_offset: u64,
|
||||
_level: u32,
|
||||
_stride: u32,
|
||||
_layer_stride: u32,
|
||||
) -> Result<()> {
|
||||
Err(DriverError::Unsupported("virgl transfer to host"))
|
||||
}
|
||||
|
||||
fn virgl_transfer_from_host_3d(
|
||||
&self,
|
||||
_ctx_id: u32,
|
||||
_resource_id: u32,
|
||||
_x: u32,
|
||||
_y: u32,
|
||||
_z: u32,
|
||||
_w: u32,
|
||||
_h: u32,
|
||||
_d: u32,
|
||||
_offset: u64,
|
||||
_level: u32,
|
||||
_stride: u32,
|
||||
_layer_stride: u32,
|
||||
) -> Result<()> {
|
||||
Err(DriverError::Unsupported("virgl transfer from host"))
|
||||
}
|
||||
|
||||
fn virgl_resource_attach_backing(
|
||||
&self,
|
||||
_resource_id: u32,
|
||||
_phys_addr: u64,
|
||||
_length: u32,
|
||||
) -> Result<()> {
|
||||
Err(DriverError::Unsupported("virgl resource attach backing"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -473,7 +473,7 @@ impl GpuDriver for AmdDriver {
|
||||
Ok(self.vblank_count.load(Ordering::SeqCst))
|
||||
}
|
||||
|
||||
fn gem_create(&self, size: u64) -> Result<GemHandle> {
|
||||
fn gem_create(&self, size: u64, _width: u32, _height: u32) -> Result<GemHandle> {
|
||||
let mut gem = self
|
||||
.gem
|
||||
.lock()
|
||||
|
||||
@@ -420,7 +420,7 @@ impl GpuDriver for IntelDriver {
|
||||
Ok(self.vblank_count.load(Ordering::SeqCst))
|
||||
}
|
||||
|
||||
fn gem_create(&self, size: u64) -> Result<GemHandle> {
|
||||
fn gem_create(&self, size: u64, _width: u32, _height: u32) -> Result<GemHandle> {
|
||||
let handle = {
|
||||
let mut gem = self
|
||||
.gem
|
||||
|
||||
@@ -0,0 +1,404 @@
|
||||
use core::mem::size_of;
|
||||
|
||||
use crate::driver::{DriverError, Result};
|
||||
|
||||
pub const VIRTIO_GPU_F_EDID: u64 = 1 << 1;
|
||||
pub const VIRTIO_GPU_F_VIRGL: u64 = 1 << 0;
|
||||
pub const VIRTIO_GPU_F_RESOURCE_UUID: u64 = 1 << 2;
|
||||
pub const VIRTIO_GPU_F_RESOURCE_BLOB: u64 = 1 << 3;
|
||||
pub const VIRTIO_GPU_F_CONTEXT_INIT: u64 = 1 << 4;
|
||||
pub const VIRTIO_F_VERSION_1: u64 = 1 << 32;
|
||||
|
||||
pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100;
|
||||
pub const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x0101;
|
||||
pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0102;
|
||||
pub const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x0103;
|
||||
pub const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x0104;
|
||||
pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x0105;
|
||||
pub const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x0106;
|
||||
pub const VIRTIO_GPU_CMD_GET_EDID: u32 = 0x0107;
|
||||
pub const VIRTIO_GPU_CMD_GET_CAPSET_INFO: u32 = 0x0108;
|
||||
pub const VIRTIO_GPU_CMD_GET_CAPSET: u32 = 0x0109;
|
||||
pub const VIRTIO_GPU_CMD_CTX_CREATE: u32 = 0x0200;
|
||||
pub const VIRTIO_GPU_CMD_CTX_DESTROY: u32 = 0x0201;
|
||||
pub const VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE: u32 = 0x0202;
|
||||
pub const VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE: u32 = 0x0203;
|
||||
pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_3D: u32 = 0x0204;
|
||||
pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D: u32 = 0x0205;
|
||||
pub const VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D: u32 = 0x0206;
|
||||
pub const VIRTIO_GPU_CMD_SUBMIT_3D: u32 = 0x0207;
|
||||
pub const VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB: u32 = 0x0208;
|
||||
pub const VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB: u32 = 0x0209;
|
||||
|
||||
pub const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100;
|
||||
pub const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101;
|
||||
pub const VIRTIO_GPU_RESP_OK_CAPSET_INFO: u32 = 0x1102;
|
||||
pub const VIRTIO_GPU_RESP_OK_CAPSET: u32 = 0x1103;
|
||||
pub const VIRTIO_GPU_RESP_OK_MAP_INFO: u32 = 0x1106;
|
||||
pub const VIRTIO_GPU_RESP_OK_EDID: u32 = 0x1107;
|
||||
|
||||
pub const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200;
|
||||
pub const VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY: u32 = 0x1201;
|
||||
pub const VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID: u32 = 0x1202;
|
||||
pub const VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID: u32 = 0x1203;
|
||||
pub const VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID: u32 = 0x1204;
|
||||
pub const VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: u32 = 0x1205;
|
||||
|
||||
pub const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1;
|
||||
pub const VIRTIO_GPU_MAX_SCANOUTS: usize = 16;
|
||||
pub const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
|
||||
pub const VIRTIO_GPU_CAPSET_VIRGL: u32 = 1;
|
||||
pub const VIRTIO_GPU_CAPSET_VIRGL2: u32 = 2;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuCtrlHeader {
|
||||
pub type_: u32,
|
||||
pub flags: u32,
|
||||
pub fence_id: u64,
|
||||
pub ctx_id: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
impl VirtioGpuCtrlHeader {
|
||||
pub fn command(type_: u32) -> Self {
|
||||
Self {
|
||||
type_,
|
||||
flags: 0,
|
||||
fence_id: 0,
|
||||
ctx_id: 0,
|
||||
padding: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuRect {
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuBox {
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
pub z: u32,
|
||||
pub w: u32,
|
||||
pub h: u32,
|
||||
pub d: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuResourceUnref {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub resource_id: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuResourceCreate2d {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub resource_id: u32,
|
||||
pub format: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuSetScanout {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub rect: VirtioGpuRect,
|
||||
pub scanout_id: u32,
|
||||
pub resource_id: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuResourceFlush {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub rect: VirtioGpuRect,
|
||||
pub resource_id: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuTransferToHost2d {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub rect: VirtioGpuRect,
|
||||
pub offset: u64,
|
||||
pub resource_id: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuTransferHost3d {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub box_: VirtioGpuBox,
|
||||
pub offset: u64,
|
||||
pub resource_id: u32,
|
||||
pub level: u32,
|
||||
pub stride: u32,
|
||||
pub layer_stride: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuMemEntry {
|
||||
pub addr: u64,
|
||||
pub length: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuResourceAttachBacking {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub resource_id: u32,
|
||||
pub nr_entries: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuResourceCreate3d {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub resource_id: u32,
|
||||
pub target: u32,
|
||||
pub format: u32,
|
||||
pub bind: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub depth: u32,
|
||||
pub array_size: u32,
|
||||
pub last_level: u32,
|
||||
pub nr_samples: u32,
|
||||
pub flags: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct VirtioGpuCtxCreate {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub nlen: u32,
|
||||
pub context_init: u32,
|
||||
pub debug_name: [u8; 64],
|
||||
}
|
||||
|
||||
impl Default for VirtioGpuCtxCreate {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hdr: VirtioGpuCtrlHeader::default(),
|
||||
nlen: 0,
|
||||
context_init: 0,
|
||||
debug_name: [0; 64],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuCtxDestroy {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuCtxResource {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub resource_id: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuCmdSubmit {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub size: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuGetCapsetInfo {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub capset_index: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuRespCapsetInfo {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub capset_id: u32,
|
||||
pub capset_max_version: u32,
|
||||
pub capset_max_size: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuGetCapset {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub capset_id: u32,
|
||||
pub capset_version: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct VirtioGpuRespCapset {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub capset_data: [u8; 8192],
|
||||
}
|
||||
|
||||
impl Default for VirtioGpuRespCapset {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hdr: VirtioGpuCtrlHeader::default(),
|
||||
capset_data: [0u8; 8192],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuResourceMapBlob {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub resource_id: u32,
|
||||
pub padding: u32,
|
||||
pub offset: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuRespMapInfo {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub map_info: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuCmdGetEdid {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub scanout: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuDisplayOne {
|
||||
pub rect: VirtioGpuRect,
|
||||
pub enabled: u32,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct VirtioGpuRespDisplayInfo {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub pmodes: [VirtioGpuDisplayOne; VIRTIO_GPU_MAX_SCANOUTS],
|
||||
}
|
||||
|
||||
impl Default for VirtioGpuRespDisplayInfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hdr: VirtioGpuCtrlHeader::default(),
|
||||
pmodes: [VirtioGpuDisplayOne::default(); VIRTIO_GPU_MAX_SCANOUTS],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct VirtioGpuRespEdid {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub size: u32,
|
||||
pub padding: u32,
|
||||
pub edid: [u8; 1024],
|
||||
}
|
||||
|
||||
impl Default for VirtioGpuRespEdid {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hdr: VirtioGpuCtrlHeader::default(),
|
||||
size: 0,
|
||||
padding: 0,
|
||||
edid: [0; 1024],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuConfig {
|
||||
pub events_read: u32,
|
||||
pub events_clear: u32,
|
||||
pub num_scanouts: u32,
|
||||
pub num_capsets: u32,
|
||||
}
|
||||
|
||||
pub fn bytes_of<T>(value: &T) -> &[u8] {
|
||||
let len = size_of::<T>();
|
||||
let ptr = value as *const T as *const u8;
|
||||
// SAFETY: The caller only uses this with #[repr(C)] plain-old-data command structs.
|
||||
unsafe { core::slice::from_raw_parts(ptr, len) }
|
||||
}
|
||||
|
||||
pub fn append_bytes<T>(bytes: &mut Vec<u8>, value: &T) {
|
||||
bytes.extend_from_slice(bytes_of(value));
|
||||
}
|
||||
|
||||
pub fn read_struct<T: Copy>(bytes: &[u8]) -> Result<T> {
|
||||
if bytes.len() < size_of::<T>() {
|
||||
return Err(DriverError::Io(format!(
|
||||
"short VirtIO GPU response: got {} bytes, need {}",
|
||||
bytes.len(),
|
||||
size_of::<T>()
|
||||
)));
|
||||
}
|
||||
|
||||
let ptr = bytes.as_ptr() as *const T;
|
||||
// SAFETY: We validated the buffer length and use read_unaligned for the DMA byte buffer.
|
||||
Ok(unsafe { ptr.read_unaligned() })
|
||||
}
|
||||
|
||||
pub fn validate_response_type(header: &VirtioGpuCtrlHeader, expected: u32) -> Result<()> {
|
||||
if header.type_ == expected {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(DriverError::Io(format!(
|
||||
"VirtIO GPU response type mismatch: expected {} ({:#06x}), got {} ({:#06x})",
|
||||
response_type_name(expected),
|
||||
expected,
|
||||
response_type_name(header.type_),
|
||||
header.type_
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn response_type_name(type_: u32) -> &'static str {
|
||||
match type_ {
|
||||
VIRTIO_GPU_RESP_OK_NODATA => "OK_NODATA",
|
||||
VIRTIO_GPU_RESP_OK_DISPLAY_INFO => "OK_DISPLAY_INFO",
|
||||
VIRTIO_GPU_RESP_OK_CAPSET_INFO => "OK_CAPSET_INFO",
|
||||
VIRTIO_GPU_RESP_OK_CAPSET => "OK_CAPSET",
|
||||
VIRTIO_GPU_RESP_OK_MAP_INFO => "OK_MAP_INFO",
|
||||
VIRTIO_GPU_RESP_OK_EDID => "OK_EDID",
|
||||
VIRTIO_GPU_RESP_ERR_UNSPEC => "ERR_UNSPEC",
|
||||
VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY => "ERR_OUT_OF_MEMORY",
|
||||
VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID => "ERR_INVALID_SCANOUT_ID",
|
||||
VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID => "ERR_INVALID_RESOURCE_ID",
|
||||
VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID => "ERR_INVALID_CONTEXT_ID",
|
||||
VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER => "ERR_INVALID_PARAMETER",
|
||||
_ => "UNKNOWN",
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,118 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::driver::{DriverError, Result};
|
||||
use crate::gem::GemHandle;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct VirtioResource {
|
||||
pub handle: GemHandle,
|
||||
pub resource_id: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub stride: u32,
|
||||
pub size: u64,
|
||||
}
|
||||
|
||||
pub struct ResourceManager {
|
||||
next_resource_id: u32,
|
||||
resources: BTreeMap<GemHandle, VirtioResource>,
|
||||
}
|
||||
|
||||
impl ResourceManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
next_resource_id: 256,
|
||||
resources: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_resource(
|
||||
&mut self,
|
||||
handle: GemHandle,
|
||||
size: u64,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Result<VirtioResource> {
|
||||
if self.resources.contains_key(&handle) {
|
||||
return Err(DriverError::Buffer(format!(
|
||||
"VirtIO resource already exists for GEM handle {handle}"
|
||||
)));
|
||||
}
|
||||
|
||||
let (res_width, res_height) = if width > 0 && height > 0 {
|
||||
(width, height)
|
||||
} else {
|
||||
let bytes_per_pixel = 4u64;
|
||||
let pixel_count = size.div_ceil(bytes_per_pixel).max(1);
|
||||
let side = (pixel_count as f64).sqrt() as u32;
|
||||
let side = side.max(1);
|
||||
// width * height * 4 must cover the full buffer size
|
||||
let adjusted_height = if side > 0 {
|
||||
u32::try_from(pixel_count.div_ceil(u64::from(side))).unwrap_or(u32::MAX).max(1)
|
||||
} else {
|
||||
u32::try_from(pixel_count).unwrap_or(u32::MAX).max(1)
|
||||
};
|
||||
(side, adjusted_height)
|
||||
};
|
||||
|
||||
let stride = res_width
|
||||
.checked_mul(4)
|
||||
.ok_or_else(|| DriverError::Buffer(format!(
|
||||
"VirtIO resource stride overflow for width {res_width}"
|
||||
)))?;
|
||||
|
||||
let resource = VirtioResource {
|
||||
handle,
|
||||
resource_id: self.next_resource_id,
|
||||
width: res_width,
|
||||
height: res_height,
|
||||
stride,
|
||||
size,
|
||||
};
|
||||
self.next_resource_id = self.next_resource_id.saturating_add(1);
|
||||
self.resources.insert(handle, resource);
|
||||
Ok(resource)
|
||||
}
|
||||
|
||||
pub fn get(&self, handle: GemHandle) -> Result<VirtioResource> {
|
||||
self.resources.get(&handle).copied().ok_or_else(|| {
|
||||
DriverError::NotFound(format!("unknown VirtIO resource for GEM handle {handle}"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn allocate_resource_id(&mut self, handle: GemHandle) -> Result<u32> {
|
||||
if !self.resources.contains_key(&handle) {
|
||||
return Err(DriverError::NotFound(format!(
|
||||
"unknown VirtIO resource for GEM handle {handle}"
|
||||
)));
|
||||
}
|
||||
|
||||
let resource_id = self.next_resource_id;
|
||||
self.next_resource_id = self.next_resource_id.saturating_add(1);
|
||||
Ok(resource_id)
|
||||
}
|
||||
|
||||
pub fn update_layout(
|
||||
&mut self,
|
||||
handle: GemHandle,
|
||||
resource_id: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
stride: u32,
|
||||
size: u64,
|
||||
) -> Result<VirtioResource> {
|
||||
let resource = self.resources.get_mut(&handle).ok_or_else(|| {
|
||||
DriverError::NotFound(format!("unknown VirtIO resource for GEM handle {handle}"))
|
||||
})?;
|
||||
resource.resource_id = resource_id;
|
||||
resource.width = width;
|
||||
resource.height = height;
|
||||
resource.stride = stride;
|
||||
resource.size = size;
|
||||
Ok(*resource)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, handle: GemHandle) -> Option<VirtioResource> {
|
||||
self.resources.remove(&handle)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
use log::info;
|
||||
use redox_driver_sys::memory::{CacheType, MmioProt, MmioRegion};
|
||||
use redox_driver_sys::pci::{PciDevice, PciDeviceInfo, PCI_CAP_ID_VNDR};
|
||||
|
||||
use crate::driver::{DriverError, Result};
|
||||
|
||||
pub const VIRTIO_PCI_CAP_COMMON_CFG: u8 = 1;
|
||||
pub const VIRTIO_PCI_CAP_NOTIFY_CFG: u8 = 2;
|
||||
pub const VIRTIO_PCI_CAP_ISR_CFG: u8 = 3;
|
||||
pub const VIRTIO_PCI_CAP_DEVICE_CFG: u8 = 4;
|
||||
|
||||
const DEVICE_STATUS_ACKNOWLEDGE: u8 = 0x01;
|
||||
const DEVICE_STATUS_DRIVER: u8 = 0x02;
|
||||
const DEVICE_STATUS_DRIVER_OK: u8 = 0x04;
|
||||
const DEVICE_STATUS_FEATURES_OK: u8 = 0x08;
|
||||
const DEVICE_STATUS_FAILED: u8 = 0x80;
|
||||
|
||||
const COMMON_DEVICE_FEATURE_SELECT: usize = 0x00;
|
||||
const COMMON_DEVICE_FEATURE: usize = 0x04;
|
||||
const COMMON_DRIVER_FEATURE_SELECT: usize = 0x08;
|
||||
const COMMON_DRIVER_FEATURE: usize = 0x0C;
|
||||
const COMMON_MSIX_CONFIG: usize = 0x10;
|
||||
const COMMON_NUM_QUEUES: usize = 0x12;
|
||||
const COMMON_DEVICE_STATUS: usize = 0x14;
|
||||
const COMMON_QUEUE_SELECT: usize = 0x16;
|
||||
const COMMON_QUEUE_SIZE: usize = 0x18;
|
||||
const COMMON_QUEUE_MSIX_VECTOR: usize = 0x1A;
|
||||
const COMMON_QUEUE_ENABLE: usize = 0x1C;
|
||||
const COMMON_QUEUE_NOTIFY_OFF: usize = 0x1E;
|
||||
const COMMON_QUEUE_DESC_LO: usize = 0x20;
|
||||
const COMMON_QUEUE_DESC_HI: usize = 0x24;
|
||||
const COMMON_QUEUE_AVAIL_LO: usize = 0x28;
|
||||
const COMMON_QUEUE_AVAIL_HI: usize = 0x2C;
|
||||
const COMMON_QUEUE_USED_LO: usize = 0x30;
|
||||
const COMMON_QUEUE_USED_HI: usize = 0x34;
|
||||
const COMMON_CFG_REQUIRED_BYTES: usize = COMMON_QUEUE_USED_HI + core::mem::size_of::<u32>();
|
||||
|
||||
const ISR_STATUS_OFFSET: usize = 0;
|
||||
const ISR_CFG_REQUIRED_BYTES: usize = ISR_STATUS_OFFSET + core::mem::size_of::<u8>();
|
||||
const DEVICE_CFG_EVENTS_READ: usize = 0;
|
||||
const DEVICE_CFG_EVENTS_CLEAR: usize = 4;
|
||||
const DEVICE_CFG_NUM_SCANOUTS: usize = 8;
|
||||
const DEVICE_CFG_REQUIRED_BYTES: usize = DEVICE_CFG_NUM_SCANOUTS + core::mem::size_of::<u32>();
|
||||
const NOTIFY_CFG_REQUIRED_BYTES: usize = core::mem::size_of::<u16>();
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct VirtioPciCap {
|
||||
cap_vndr: u8,
|
||||
cap_next: u8,
|
||||
cap_len: u8,
|
||||
cfg_type: u8,
|
||||
bar: u8,
|
||||
id: u8,
|
||||
padding: [u8; 2],
|
||||
offset: u32,
|
||||
length: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct VirtioPciNotifyCap {
|
||||
cap: VirtioPciCap,
|
||||
notify_off_multiplier: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct QueueConfig {
|
||||
pub index: u16,
|
||||
pub size: u16,
|
||||
pub notify_off: u16,
|
||||
}
|
||||
|
||||
pub struct VirtioModernPciTransport {
|
||||
common_cfg: MmioRegion,
|
||||
notify_cfg: MmioRegion,
|
||||
isr_cfg: MmioRegion,
|
||||
device_cfg: MmioRegion,
|
||||
notify_off_multiplier: u32,
|
||||
}
|
||||
|
||||
impl VirtioModernPciTransport {
|
||||
pub fn new(info: &PciDeviceInfo, pci: &mut PciDevice) -> Result<Self> {
|
||||
let mut common_cap = None;
|
||||
let mut notify_cap = None;
|
||||
let mut isr_cap = None;
|
||||
let mut device_cap = None;
|
||||
|
||||
// Walk the PCI capability linked list directly from config space.
|
||||
// The pcid-spawner handoff provides an empty capabilities vector,
|
||||
// so we cannot rely on info.capabilities.
|
||||
let cap_ptr = pci.read_config_byte(0x34).map_err(pci_error)?;
|
||||
if cap_ptr == 0 {
|
||||
return Err(DriverError::Pci(
|
||||
"VirtIO GPU has no PCI capabilities".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut offset = cap_ptr;
|
||||
let mut visited = 0u8;
|
||||
const MAX_CAPS: u8 = 48;
|
||||
while offset != 0 && visited < MAX_CAPS {
|
||||
visited += 1;
|
||||
let cap_id = pci.read_config_byte(offset as u64).map_err(pci_error)?;
|
||||
let cap_next = pci.read_config_byte(offset as u64 + 1).map_err(pci_error)?;
|
||||
|
||||
if cap_id == PCI_CAP_ID_VNDR {
|
||||
let raw = read_pci_cap(pci, offset)?;
|
||||
match raw.cfg_type {
|
||||
VIRTIO_PCI_CAP_COMMON_CFG => common_cap = Some(raw),
|
||||
VIRTIO_PCI_CAP_NOTIFY_CFG => notify_cap = Some(read_notify_cap(pci, offset)?),
|
||||
VIRTIO_PCI_CAP_ISR_CFG => isr_cap = Some(raw),
|
||||
VIRTIO_PCI_CAP_DEVICE_CFG => device_cap = Some(raw),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
offset = cap_next;
|
||||
}
|
||||
|
||||
info!(
|
||||
"redox-drm: VirtIO PCI capability scan found {} caps, common={} notify={} isr={} device={}",
|
||||
visited,
|
||||
common_cap.is_some(),
|
||||
notify_cap.is_some(),
|
||||
isr_cap.is_some(),
|
||||
device_cap.is_some(),
|
||||
);
|
||||
|
||||
let common_cap = common_cap.ok_or_else(|| {
|
||||
DriverError::Pci("VirtIO modern common_cfg capability is missing".into())
|
||||
})?;
|
||||
let notify_cap = notify_cap.ok_or_else(|| {
|
||||
DriverError::Pci("VirtIO modern notify_cfg capability is missing".into())
|
||||
})?;
|
||||
let isr_cap = isr_cap.ok_or_else(|| {
|
||||
DriverError::Pci("VirtIO modern isr_cfg capability is missing".into())
|
||||
})?;
|
||||
let device_cap = device_cap.ok_or_else(|| {
|
||||
DriverError::Pci("VirtIO modern device_cfg capability is missing".into())
|
||||
})?;
|
||||
|
||||
let common_cfg =
|
||||
map_cap_region(info, &common_cap, "common_cfg", COMMON_CFG_REQUIRED_BYTES)?;
|
||||
let notify_cfg = map_cap_region(
|
||||
info,
|
||||
¬ify_cap.cap,
|
||||
"notify_cfg",
|
||||
NOTIFY_CFG_REQUIRED_BYTES,
|
||||
)?;
|
||||
let isr_cfg = map_cap_region(info, &isr_cap, "isr_cfg", ISR_CFG_REQUIRED_BYTES)?;
|
||||
let device_cfg =
|
||||
map_cap_region(info, &device_cap, "device_cfg", DEVICE_CFG_REQUIRED_BYTES)?;
|
||||
|
||||
info!(
|
||||
"redox-drm: VirtIO modern PCI transport mapped for {} (notify multiplier {})",
|
||||
info.location, notify_cap.notify_off_multiplier
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
common_cfg,
|
||||
notify_cfg,
|
||||
isr_cfg,
|
||||
device_cfg,
|
||||
notify_off_multiplier: notify_cap.notify_off_multiplier,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn initialize_device(&mut self, requested_features: u64) -> Result<u64> {
|
||||
info!("redox-drm: VirtIO reset device");
|
||||
self.write_device_status(0);
|
||||
|
||||
self.write_device_status(DEVICE_STATUS_ACKNOWLEDGE);
|
||||
self.write_device_status(DEVICE_STATUS_ACKNOWLEDGE | DEVICE_STATUS_DRIVER);
|
||||
|
||||
let available = self.read_device_features();
|
||||
if (available & requested_features) & (1u64 << 32) == 0 {
|
||||
self.fail(format!(
|
||||
"VirtIO GPU missing required VIRTIO_F_VERSION_1 feature (device features={available:#x})"
|
||||
))?;
|
||||
}
|
||||
|
||||
let negotiated = available & requested_features;
|
||||
self.write_driver_features(negotiated);
|
||||
|
||||
let mut status = self.device_status();
|
||||
status |= DEVICE_STATUS_FEATURES_OK;
|
||||
self.write_device_status(status);
|
||||
|
||||
if self.device_status() & DEVICE_STATUS_FEATURES_OK == 0 {
|
||||
self.fail("VirtIO GPU rejected FEATURES_OK during negotiation".into())?;
|
||||
}
|
||||
|
||||
info!("redox-drm: VirtIO negotiated features device={available:#x} driver={negotiated:#x}");
|
||||
Ok(negotiated)
|
||||
}
|
||||
|
||||
pub fn finalize_device(&mut self) {
|
||||
let status = self.device_status() | DEVICE_STATUS_DRIVER_OK;
|
||||
self.write_device_status(status);
|
||||
}
|
||||
|
||||
pub fn device_status(&self) -> u8 {
|
||||
self.common_cfg.read8(COMMON_DEVICE_STATUS)
|
||||
}
|
||||
|
||||
pub fn read_isr_status(&self) -> u8 {
|
||||
self.isr_cfg.read8(ISR_STATUS_OFFSET)
|
||||
}
|
||||
|
||||
pub fn read_events(&self) -> u32 {
|
||||
self.device_cfg.read32(DEVICE_CFG_EVENTS_READ)
|
||||
}
|
||||
|
||||
pub fn clear_events(&self, mask: u32) {
|
||||
self.device_cfg.write32(DEVICE_CFG_EVENTS_CLEAR, mask);
|
||||
}
|
||||
|
||||
pub fn num_scanouts(&self) -> u32 {
|
||||
self.device_cfg.read32(DEVICE_CFG_NUM_SCANOUTS)
|
||||
}
|
||||
|
||||
pub fn prepare_queue(&self, index: u16, requested_size: u16) -> Result<QueueConfig> {
|
||||
self.select_queue(index);
|
||||
let device_size = self.common_cfg.read16(COMMON_QUEUE_SIZE);
|
||||
if device_size == 0 {
|
||||
return Err(DriverError::Initialization(format!(
|
||||
"VirtIO queue {index} reports size 0"
|
||||
)));
|
||||
}
|
||||
|
||||
let size = device_size.min(requested_size);
|
||||
let notify_off = self.common_cfg.read16(COMMON_QUEUE_NOTIFY_OFF);
|
||||
Ok(QueueConfig {
|
||||
index,
|
||||
size,
|
||||
notify_off,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn activate_queue(
|
||||
&self,
|
||||
index: u16,
|
||||
size: u16,
|
||||
desc_addr: u64,
|
||||
avail_addr: u64,
|
||||
used_addr: u64,
|
||||
msix_vector: Option<u16>,
|
||||
) -> Result<()> {
|
||||
self.select_queue(index);
|
||||
self.common_cfg.write16(COMMON_QUEUE_SIZE, size);
|
||||
self.common_cfg
|
||||
.write16(COMMON_QUEUE_MSIX_VECTOR, msix_vector.unwrap_or(u16::MAX));
|
||||
self.write_u64_pair(COMMON_QUEUE_DESC_LO, COMMON_QUEUE_DESC_HI, desc_addr);
|
||||
self.write_u64_pair(COMMON_QUEUE_AVAIL_LO, COMMON_QUEUE_AVAIL_HI, avail_addr);
|
||||
self.write_u64_pair(COMMON_QUEUE_USED_LO, COMMON_QUEUE_USED_HI, used_addr);
|
||||
self.common_cfg.write16(COMMON_QUEUE_ENABLE, 1);
|
||||
|
||||
if self.common_cfg.read16(COMMON_QUEUE_ENABLE) != 1 {
|
||||
return Err(DriverError::Initialization(format!(
|
||||
"VirtIO queue {index} refused queue_enable"
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_config_msix_vector(&self, vector: Option<u16>) {
|
||||
self.common_cfg
|
||||
.write16(COMMON_MSIX_CONFIG, vector.unwrap_or(u16::MAX));
|
||||
}
|
||||
|
||||
pub fn notify_queue(&self, queue_index: u16, notify_off: u16) -> Result<()> {
|
||||
let byte_offset = usize::from(notify_off)
|
||||
.checked_mul(self.notify_off_multiplier as usize)
|
||||
.ok_or_else(|| DriverError::Mmio("VirtIO notify offset overflow".into()))?;
|
||||
let end = byte_offset
|
||||
.checked_add(core::mem::size_of::<u16>())
|
||||
.ok_or_else(|| DriverError::Mmio("VirtIO notify MMIO overflow".into()))?;
|
||||
if end > self.notify_cfg.size() {
|
||||
return Err(DriverError::Mmio(format!(
|
||||
"VirtIO queue notify outside notify_cfg window: end={end:#x} size={:#x}",
|
||||
self.notify_cfg.size()
|
||||
)));
|
||||
}
|
||||
|
||||
self.notify_cfg.write16(byte_offset, queue_index);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fail<T>(&mut self, reason: String) -> Result<T> {
|
||||
let status = self.device_status() | DEVICE_STATUS_FAILED;
|
||||
self.write_device_status(status);
|
||||
Err(DriverError::Initialization(reason))
|
||||
}
|
||||
|
||||
fn read_device_features(&self) -> u64 {
|
||||
self.common_cfg.write32(COMMON_DEVICE_FEATURE_SELECT, 0);
|
||||
let low = self.common_cfg.read32(COMMON_DEVICE_FEATURE) as u64;
|
||||
self.common_cfg.write32(COMMON_DEVICE_FEATURE_SELECT, 1);
|
||||
let high = self.common_cfg.read32(COMMON_DEVICE_FEATURE) as u64;
|
||||
low | (high << 32)
|
||||
}
|
||||
|
||||
fn write_driver_features(&self, features: u64) {
|
||||
self.common_cfg.write32(COMMON_DRIVER_FEATURE_SELECT, 0);
|
||||
self.common_cfg
|
||||
.write32(COMMON_DRIVER_FEATURE, features as u32);
|
||||
self.common_cfg.write32(COMMON_DRIVER_FEATURE_SELECT, 1);
|
||||
self.common_cfg
|
||||
.write32(COMMON_DRIVER_FEATURE, (features >> 32) as u32);
|
||||
}
|
||||
|
||||
fn write_device_status(&self, status: u8) {
|
||||
self.common_cfg.write8(COMMON_DEVICE_STATUS, status);
|
||||
}
|
||||
|
||||
fn select_queue(&self, index: u16) {
|
||||
self.common_cfg.write16(COMMON_QUEUE_SELECT, index);
|
||||
}
|
||||
|
||||
fn write_u64_pair(&self, lo_offset: usize, hi_offset: usize, value: u64) {
|
||||
self.common_cfg.write32(lo_offset, value as u32);
|
||||
self.common_cfg.write32(hi_offset, (value >> 32) as u32);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_pci_cap(pci: &mut PciDevice, offset: u8) -> Result<VirtioPciCap> {
|
||||
Ok(VirtioPciCap {
|
||||
cap_vndr: pci.read_config_byte(offset as u64).map_err(pci_error)?,
|
||||
cap_next: pci.read_config_byte(offset as u64 + 1).map_err(pci_error)?,
|
||||
cap_len: pci.read_config_byte(offset as u64 + 2).map_err(pci_error)?,
|
||||
cfg_type: pci.read_config_byte(offset as u64 + 3).map_err(pci_error)?,
|
||||
bar: pci.read_config_byte(offset as u64 + 4).map_err(pci_error)?,
|
||||
id: pci.read_config_byte(offset as u64 + 5).map_err(pci_error)?,
|
||||
padding: [
|
||||
pci.read_config_byte(offset as u64 + 6).map_err(pci_error)?,
|
||||
pci.read_config_byte(offset as u64 + 7).map_err(pci_error)?,
|
||||
],
|
||||
offset: pci
|
||||
.read_config_dword(offset as u64 + 8)
|
||||
.map_err(pci_error)?,
|
||||
length: pci
|
||||
.read_config_dword(offset as u64 + 12)
|
||||
.map_err(pci_error)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_notify_cap(pci: &mut PciDevice, offset: u8) -> Result<VirtioPciNotifyCap> {
|
||||
Ok(VirtioPciNotifyCap {
|
||||
cap: read_pci_cap(pci, offset)?,
|
||||
notify_off_multiplier: pci
|
||||
.read_config_dword(offset as u64 + 16)
|
||||
.map_err(pci_error)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn pci_error(error: redox_driver_sys::DriverError) -> DriverError {
|
||||
DriverError::Pci(error.to_string())
|
||||
}
|
||||
|
||||
fn map_cap_region(
|
||||
info: &PciDeviceInfo,
|
||||
cap: &VirtioPciCap,
|
||||
name: &str,
|
||||
required_size: usize,
|
||||
) -> Result<MmioRegion> {
|
||||
let bar = info
|
||||
.find_memory_bar(cap.bar as usize)
|
||||
.ok_or_else(|| DriverError::Pci(format!("VirtIO {name} BAR{} is missing", cap.bar)))?;
|
||||
if cap.length == 0 {
|
||||
return Err(DriverError::Pci(format!(
|
||||
"VirtIO {name} capability has zero length"
|
||||
)));
|
||||
}
|
||||
if cap.length < required_size as u32 {
|
||||
return Err(DriverError::Pci(format!(
|
||||
"VirtIO {name} capability too small: {} < {} bytes",
|
||||
cap.length, required_size
|
||||
)));
|
||||
}
|
||||
|
||||
let cap_end = u64::from(cap.offset)
|
||||
.checked_add(u64::from(cap.length))
|
||||
.ok_or_else(|| DriverError::Pci(format!("VirtIO {name} capability range overflow")))?;
|
||||
if cap_end > bar.size {
|
||||
return Err(DriverError::Pci(format!(
|
||||
"VirtIO {name} capability range [{:#x}, {:#x}) exceeds BAR{} size {:#x}",
|
||||
cap.offset, cap_end, cap.bar, bar.size
|
||||
)));
|
||||
}
|
||||
|
||||
let phys = bar
|
||||
.addr
|
||||
.checked_add(cap.offset as u64)
|
||||
.ok_or_else(|| DriverError::Pci(format!("VirtIO {name} MMIO address overflow")))?;
|
||||
MmioRegion::map(
|
||||
phys,
|
||||
cap.length as usize,
|
||||
CacheType::DeviceMemory,
|
||||
MmioProt::READ_WRITE,
|
||||
)
|
||||
.map_err(|e| DriverError::Mmio(format!("failed to map VirtIO {name}: {e}")))
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::atomic::{fence, Ordering};
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use redox_driver_sys::dma::DmaBuffer;
|
||||
|
||||
use crate::driver::{DriverError, Result};
|
||||
|
||||
use super::transport::VirtioModernPciTransport;
|
||||
|
||||
const VIRTQ_DESC_F_NEXT: u16 = 1;
|
||||
const VIRTQ_DESC_F_WRITE: u16 = 2;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct VirtqDesc {
|
||||
addr: u64,
|
||||
len: u32,
|
||||
flags: u16,
|
||||
next: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct VirtqUsedElem {
|
||||
id: u32,
|
||||
len: u32,
|
||||
}
|
||||
|
||||
pub struct Virtqueue {
|
||||
index: u16,
|
||||
size: u16,
|
||||
notify_off: u16,
|
||||
desc: DmaBuffer,
|
||||
avail: DmaBuffer,
|
||||
used: DmaBuffer,
|
||||
free_list: Vec<u16>,
|
||||
pending: BTreeMap<u16, Vec<u16>>,
|
||||
last_used_idx: u16,
|
||||
failed: bool,
|
||||
}
|
||||
|
||||
impl Virtqueue {
|
||||
pub fn new(index: u16, size: u16, notify_off: u16) -> Result<Self> {
|
||||
let desc_bytes = usize::from(size) * core::mem::size_of::<VirtqDesc>();
|
||||
let avail_bytes = 6 + usize::from(size) * 2;
|
||||
let used_bytes = 6 + usize::from(size) * core::mem::size_of::<VirtqUsedElem>();
|
||||
|
||||
let desc = DmaBuffer::allocate(desc_bytes, 16)
|
||||
.map_err(|e| DriverError::Buffer(format!("VirtIO desc allocation failed: {e}")))?;
|
||||
let avail = DmaBuffer::allocate(avail_bytes, 2)
|
||||
.map_err(|e| DriverError::Buffer(format!("VirtIO avail allocation failed: {e}")))?;
|
||||
let used = DmaBuffer::allocate(used_bytes, 4)
|
||||
.map_err(|e| DriverError::Buffer(format!("VirtIO used allocation failed: {e}")))?;
|
||||
|
||||
Ok(Self {
|
||||
index,
|
||||
size,
|
||||
notify_off,
|
||||
desc,
|
||||
avail,
|
||||
used,
|
||||
free_list: (0..size).rev().collect(),
|
||||
pending: BTreeMap::new(),
|
||||
last_used_idx: 0,
|
||||
failed: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u16 {
|
||||
self.index
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u16 {
|
||||
self.size
|
||||
}
|
||||
|
||||
pub fn notify_off(&self) -> u16 {
|
||||
self.notify_off
|
||||
}
|
||||
|
||||
pub fn desc_addr(&self) -> u64 {
|
||||
self.desc.physical_address() as u64
|
||||
}
|
||||
|
||||
pub fn avail_addr(&self) -> u64 {
|
||||
self.avail.physical_address() as u64
|
||||
}
|
||||
|
||||
pub fn used_addr(&self) -> u64 {
|
||||
self.used.physical_address() as u64
|
||||
}
|
||||
|
||||
pub fn submit_request(
|
||||
&mut self,
|
||||
transport: &VirtioModernPciTransport,
|
||||
request_addr: u64,
|
||||
request_len: u32,
|
||||
response_addr: u64,
|
||||
response_len: u32,
|
||||
timeout: Duration,
|
||||
) -> Result<()> {
|
||||
if self.failed {
|
||||
return Err(DriverError::Io(format!(
|
||||
"VirtIO queue {} is failed and cannot accept more requests",
|
||||
self.index
|
||||
)));
|
||||
}
|
||||
|
||||
let head = self.alloc_desc()?;
|
||||
let tail = self.alloc_desc()?;
|
||||
self.write_desc(
|
||||
head,
|
||||
VirtqDesc {
|
||||
addr: request_addr,
|
||||
len: request_len,
|
||||
flags: VIRTQ_DESC_F_NEXT,
|
||||
next: tail,
|
||||
},
|
||||
);
|
||||
self.write_desc(
|
||||
tail,
|
||||
VirtqDesc {
|
||||
addr: response_addr,
|
||||
len: response_len,
|
||||
flags: VIRTQ_DESC_F_WRITE,
|
||||
next: 0,
|
||||
},
|
||||
);
|
||||
|
||||
self.pending.insert(head, vec![head, tail]);
|
||||
|
||||
// VirtIO spec 2.6.8.1: The driver MUST perform a memory barrier before
|
||||
// updating the available index to ensure the descriptor table writes and
|
||||
// the available ring entry are visible to the device.
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
self.push_avail(head);
|
||||
if let Err(error) = transport.notify_queue(self.index, self.notify_off) {
|
||||
self.reclaim_pending_chain(head);
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
if let Err(error) = self.wait_used(head, timeout) {
|
||||
self.failed = true;
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn alloc_desc(&mut self) -> Result<u16> {
|
||||
self.free_list.pop().ok_or_else(|| {
|
||||
DriverError::Buffer(format!(
|
||||
"VirtIO queue {} ran out of descriptors",
|
||||
self.index
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn push_avail(&mut self, head: u16) {
|
||||
let avail_idx = self.read_avail_idx();
|
||||
let slot = usize::from(avail_idx % self.size);
|
||||
self.write_avail_ring(slot, head);
|
||||
fence(Ordering::Release);
|
||||
self.write_avail_idx(avail_idx.wrapping_add(1));
|
||||
}
|
||||
|
||||
fn wait_used(&mut self, expected_head: u16, timeout: Duration) -> Result<()> {
|
||||
let deadline = Instant::now() + timeout;
|
||||
loop {
|
||||
let used_idx = self.read_used_idx();
|
||||
if used_idx != self.last_used_idx {
|
||||
let slot = usize::from(self.last_used_idx % self.size);
|
||||
let elem = self.read_used_elem(slot);
|
||||
self.last_used_idx = self.last_used_idx.wrapping_add(1);
|
||||
self.free_chain(elem.id as u16)?;
|
||||
if elem.id as u16 != expected_head {
|
||||
self.failed = true;
|
||||
return Err(DriverError::Io(format!(
|
||||
"VirtIO queue {} completed descriptor head {} while waiting for {}",
|
||||
self.index, elem.id, expected_head
|
||||
)));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if Instant::now() >= deadline {
|
||||
return Err(DriverError::Io(format!(
|
||||
"VirtIO queue {} timed out waiting for descriptor head {}",
|
||||
self.index, expected_head
|
||||
)));
|
||||
}
|
||||
|
||||
thread::yield_now();
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
}
|
||||
|
||||
fn free_chain(&mut self, head: u16) -> Result<()> {
|
||||
let chain = self.pending.remove(&head).ok_or_else(|| {
|
||||
DriverError::Io(format!(
|
||||
"VirtIO queue {} completed unknown descriptor head {}",
|
||||
self.index, head
|
||||
))
|
||||
})?;
|
||||
for index in chain {
|
||||
self.free_list.push(index);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reclaim_pending_chain(&mut self, head: u16) {
|
||||
if let Some(chain) = self.pending.remove(&head) {
|
||||
for index in chain {
|
||||
self.free_list.push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_desc(&mut self, index: u16, desc: VirtqDesc) {
|
||||
let ptr = self.desc.as_mut_ptr() as *mut VirtqDesc;
|
||||
// SAFETY: The DMA buffer is sized for `size` descriptors, and callers only pass indices < size.
|
||||
unsafe { ptr.add(index as usize).write(desc) };
|
||||
}
|
||||
|
||||
fn read_used_elem(&self, slot: usize) -> VirtqUsedElem {
|
||||
let offset = 4 + slot * core::mem::size_of::<VirtqUsedElem>();
|
||||
let ptr = self.used.as_ptr().wrapping_add(offset) as *const VirtqUsedElem;
|
||||
// SAFETY: The used ring allocation covers all ring entries; read_unaligned handles packed access.
|
||||
unsafe { ptr.read_unaligned() }
|
||||
}
|
||||
|
||||
fn read_avail_idx(&self) -> u16 {
|
||||
let ptr = self.avail.as_ptr().wrapping_add(2) as *const u16;
|
||||
// SAFETY: Offset 2 is the avail.idx field in the DMA allocation.
|
||||
unsafe { ptr.read_unaligned() }
|
||||
}
|
||||
|
||||
fn write_avail_idx(&mut self, value: u16) {
|
||||
let ptr = self.avail.as_mut_ptr().wrapping_add(2) as *mut u16;
|
||||
// SAFETY: Offset 2 is the avail.idx field in the DMA allocation.
|
||||
unsafe { ptr.write_unaligned(value) };
|
||||
}
|
||||
|
||||
fn write_avail_ring(&mut self, slot: usize, value: u16) {
|
||||
let offset = 4 + slot * core::mem::size_of::<u16>();
|
||||
let ptr = self.avail.as_mut_ptr().wrapping_add(offset) as *mut u16;
|
||||
// SAFETY: The avail ring allocation is sized for `size` u16 entries.
|
||||
unsafe { ptr.write_unaligned(value) };
|
||||
}
|
||||
|
||||
fn read_used_idx(&self) -> u16 {
|
||||
let ptr = self.used.as_ptr().wrapping_add(2) as *const u16;
|
||||
// SAFETY: Offset 2 is the used.idx field in the DMA allocation.
|
||||
unsafe { ptr.read_unaligned() }
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ fn run(daemon: daemon::Daemon) -> Result<()> {
|
||||
std::thread::sleep(std::time::Duration::from_millis(16));
|
||||
});
|
||||
|
||||
let drm_scheme = Arc::new(Mutex::new(DrmScheme::new(driver)));
|
||||
let drm_scheme = Arc::new(Mutex::new(DrmScheme::new(driver, info)));
|
||||
let event_scheme = drm_scheme.clone();
|
||||
|
||||
std::thread::spawn(move || loop {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -57,7 +57,7 @@ add_subdirectory(src)
|
||||
|
||||
# Enable unit testing
|
||||
if (BUILD_TESTING)
|
||||
################################# add_subdirectory(autotests)
|
||||
################################################### add_subdirectory(autotests)
|
||||
add_subdirectory(tests)
|
||||
endif ()
|
||||
|
||||
|
||||
@@ -117,6 +117,24 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
|
||||
# shall we use DBus?
|
||||
# enabled per default on Linux & BSD systems
|
||||
|
||||
@@ -95,6 +95,24 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
|
||||
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
||||
|
||||
|
||||
@@ -86,6 +86,24 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
|
||||
find_package(KF6Codecs ${KF_DEP_VERSION} REQUIRED)
|
||||
find_package(KF6Config ${KF_DEP_VERSION} REQUIRED)
|
||||
|
||||
@@ -31,7 +31,7 @@ cmake "${COOKBOOK_SOURCE}" \
|
||||
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
||||
-DBUILD_TESTING=OFF \
|
||||
-DBUILD_QCH=OFF \
|
||||
-DKCONFIG_USE_QML=OFF \
|
||||
-DKCONFIG_USE_QML=ON \
|
||||
-DUSE_DBUS=OFF \
|
||||
-Wno-dev
|
||||
|
||||
|
||||
@@ -89,6 +89,24 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
|
||||
# shall we use DBus?
|
||||
# enabled per default on Linux & BSD systems
|
||||
|
||||
@@ -32,7 +32,7 @@ find_package(KF6GuiAddons ${KF_DEP_VERSION} REQUIRED)
|
||||
|
||||
|
||||
if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT REDOX)
|
||||
######################################################## find_package(KF6GlobalAccel ${KF_DEP_VERSION} REQUIRED)
|
||||
########################################################################## find_package(KF6GlobalAccel ${KF_DEP_VERSION} REQUIRED)
|
||||
set(HAVE_KGLOBALACCEL TRUE)
|
||||
else()
|
||||
set(HAVE_KGLOBALACCEL FALSE)
|
||||
|
||||
@@ -108,6 +108,25 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6Svg ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
|
||||
|
||||
# shall we use DBus?
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#TODO: KIdleTime — idle time detection. Used by KWin for screen dimming/locking.
|
||||
# KIdleTime — idle time detection. Used by KWin for screen dimming/locking.
|
||||
[source]
|
||||
tar = "https://invent.kde.org/frameworks/kidletime/-/archive/v6.10.0/kidletime-v6.10.0.tar.gz"
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ set_package_properties(Qt6Qml PROPERTIES
|
||||
)
|
||||
|
||||
if (TARGET Qt6::Qml)
|
||||
################################### include(ECMQmlModule)
|
||||
##################################################### include(ECMQmlModule)
|
||||
endif()
|
||||
|
||||
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
add_subdirectory(core)
|
||||
if (TARGET Qt6::Qml)
|
||||
################################## add_subdirectory(qml)
|
||||
#################################################### add_subdirectory(qml)
|
||||
endif()
|
||||
|
||||
ecm_qt_install_logging_categories(
|
||||
|
||||
@@ -73,6 +73,25 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
|
||||
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
||||
|
||||
|
||||
@@ -75,6 +75,24 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
|
||||
if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT HAIKU)
|
||||
option(WITH_X11 "Build with support for QX11Info::appUserTime()" ON)
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#TODO: KSvg — SVG rendering library with theme re-coloring and disk caching
|
||||
[source]
|
||||
tar = "https://invent.kde.org/frameworks/ksvg/-/archive/v6.10.0/ksvg-v6.10.0.tar.gz"
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
dependencies = [
|
||||
"qtbase",
|
||||
"kf6-extra-cmake-modules",
|
||||
"kf6-karchive",
|
||||
"kf6-kconfig",
|
||||
"kf6-kcolorscheme",
|
||||
"kf6-kcoreaddons",
|
||||
"kf6-kguiaddons",
|
||||
]
|
||||
script = """
|
||||
DYNAMIC_INIT
|
||||
|
||||
HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build"
|
||||
source "${COOKBOOK_ROOT}/local/scripts/lib/qt-sysroot.sh"
|
||||
|
||||
redbear_qt_link_sysroot_dirs "${COOKBOOK_SYSROOT}" plugins mkspecs metatypes modules
|
||||
|
||||
sed -i "s/^ecm_install_po_files_as_qm/#ecm_install_po_files_as_qm/" \
|
||||
"${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
|
||||
sed -i 's/^ki18n_install(po)/#ki18n_install(po)/' \
|
||||
"${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
|
||||
|
||||
# Disable QML/Quick declarative imports — not needed without Qt6Quick
|
||||
sed -i 's/^include(ECMGenerateQmlTypes)/#include(ECMGenerateQmlTypes)/' \
|
||||
"${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
|
||||
sed -i 's/^include(ECMQmlModule)/#include(ECMQmlModule)/' \
|
||||
"${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
|
||||
|
||||
rm -f CMakeCache.txt
|
||||
rm -rf CMakeFiles
|
||||
|
||||
cmake "${COOKBOOK_SOURCE}" \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
||||
-DQT_HOST_PATH="${HOST_BUILD}" \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
||||
-DBUILD_TESTING=OFF \
|
||||
-DBUILD_QCH=OFF \
|
||||
-DBUILD_TOOLS=OFF \
|
||||
-DUSE_DBUS=OFF \
|
||||
-Wno-dev
|
||||
|
||||
cmake --build . -j${COOKBOOK_MAKE_JOBS}
|
||||
cmake --install . --prefix "${COOKBOOK_STAGE}/usr"
|
||||
|
||||
for lib in "${COOKBOOK_STAGE}/usr/lib/"libKF6*.so.*; do
|
||||
[ -f "${lib}" ] || continue
|
||||
patchelf --remove-rpath "${lib}" 2>/dev/null || true
|
||||
done
|
||||
"""
|
||||
@@ -24,10 +24,10 @@ include(ECMSetupVersion)
|
||||
include(ECMQtDeclareLoggingCategory)
|
||||
include(ECMAddQch)
|
||||
include(KDEPackageAppTemplates)
|
||||
include(ECMGenerateQmlTypes)
|
||||
#include(ECMGenerateQmlTypes)
|
||||
include(ECMMarkNonGuiExecutable)
|
||||
include(ECMDeprecationSettings)
|
||||
include(ECMQmlModule)
|
||||
#include(ECMQmlModule)
|
||||
|
||||
option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF)
|
||||
add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)")
|
||||
|
||||
@@ -90,6 +90,24 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
|
||||
if (WITH_TEXT_TO_SPEECH)
|
||||
find_package(Qt6 ${REQUIRED_QT_VERSION} CONFIG REQUIRED TextToSpeech)
|
||||
|
||||
@@ -93,6 +93,25 @@ find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||
set_package_properties(Wayland PROPERTIES
|
||||
TYPE REQUIRED
|
||||
)
|
||||
|
||||
@@ -74,10 +74,10 @@ void initializeLanguages()
|
||||
// Ideally setting the LANGUAGE would change the default QLocale too
|
||||
// but unfortunately this is too late since the QCoreApplication constructor
|
||||
// already created a QLocale at this stage so we need to set the reset it
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // by triggering the creation and destruction of a QSystemLocale
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // by triggering the creation and destruction of a QSystemLocale
|
||||
// this is highly dependent on Qt internals, so may break, but oh well
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QSystemLocale *dummy = new QSystemLocale();
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// delete dummy;
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QSystemLocale *dummy = new QSystemLocale();
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// delete dummy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,9 +65,9 @@ ecm_set_disabled_deprecation_versions(
|
||||
)
|
||||
|
||||
add_subdirectory( src )
|
||||
######if (BUILD_TESTING)
|
||||
###### add_subdirectory( autotests )
|
||||
######endif()
|
||||
########################if (BUILD_TESTING)
|
||||
######################## add_subdirectory( autotests )
|
||||
########################endif()
|
||||
|
||||
if (BUILD_QCH)
|
||||
ecm_install_qch_export(
|
||||
|
||||
@@ -78,7 +78,7 @@ set_package_properties(PList PROPERTIES
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES Linux)
|
||||
# Used by the UDisks backend on Linux
|
||||
#########################################################################find_package(LibMount)
|
||||
###########################################################################################find_package(LibMount)
|
||||
set_package_properties(LibMount PROPERTIES
|
||||
TYPE REQUIRED)
|
||||
endif()
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
#TODO: KWin — Qt6::Sensors now available (2026-04-30). Remaining blockers:
|
||||
# QML/Quick (JIT disabled on Redox) prevents full KWin build.
|
||||
# Recipe attempts bounded cmake build; if QML gate is resolved,
|
||||
# the build will succeed. Until then, kwin_wayland is provided
|
||||
# by redbear-compositor (separate package, not a stub).
|
||||
#TODO: KWin — full build with Qt6Quick/QML. Effect frames, scripted effects, OSD overlay, outline overlay all enabled.
|
||||
[source]
|
||||
tar = "https://invent.kde.org/plasma/kwin/-/archive/v6.3.4/kwin-v6.3.4.tar.gz"
|
||||
blake3 = "2aa1e234a75b0aa94f0da3a74d93e2a8e49b30a3afb12dc24b2ecd3abaa94e7f"
|
||||
@@ -13,30 +9,47 @@ dependencies = [
|
||||
"qtbase",
|
||||
"qtdeclarative",
|
||||
"qt5compat",
|
||||
"qt6-sensors",
|
||||
"kf6-extra-cmake-modules",
|
||||
"kf6-kcoreaddons",
|
||||
"kf6-ki18n",
|
||||
"kf6-kauth",
|
||||
"kf6-kcolorscheme",
|
||||
"kf6-kcompletion",
|
||||
"kf6-kconfig",
|
||||
"kf6-kconfigwidgets",
|
||||
"kf6-kcoreaddons",
|
||||
"kf6-kcrash",
|
||||
"kf6-kdbusaddons",
|
||||
"kf6-kservice",
|
||||
"kf6-kwayland",
|
||||
"kf6-kwindowsystem",
|
||||
"kf6-kconfigwidgets",
|
||||
"kf6-kglobalaccel",
|
||||
"kf6-kguiaddons",
|
||||
"kf6-ki18n",
|
||||
"kf6-kidletime",
|
||||
"kf6-knotifications",
|
||||
"kf6-kpackage",
|
||||
"kf6-kdeclarative",
|
||||
"kf6-kio",
|
||||
"kdecoration",
|
||||
"kf6-kservice",
|
||||
"kf6-ksvg",
|
||||
"kf6-kwayland",
|
||||
"kf6-kwidgetsaddons",
|
||||
"kf6-kwindowsystem",
|
||||
"kf6-kxmlgui",
|
||||
"kf6-kiconthemes",
|
||||
"kf6-kcmutils",
|
||||
"kf6-kdeclarative",
|
||||
"kf6-knotifications",
|
||||
"kf6-kio",
|
||||
"kf6-kitemmodels",
|
||||
"kf6-kitemviews",
|
||||
"kf6-kjobwidgets",
|
||||
"kf6-ktextwidgets",
|
||||
"kdecoration",
|
||||
"plasma-wayland-protocols",
|
||||
"libepoxy-stub",
|
||||
"libudev-stub",
|
||||
"libepoxy",
|
||||
"libdisplay-info",
|
||||
"libxcvt",
|
||||
"lcms2",
|
||||
"wayland-protocols",
|
||||
"redbear-compositor",
|
||||
"libxkbcommon",
|
||||
"libinput",
|
||||
"libudev-stub",
|
||||
"libxcb",
|
||||
]
|
||||
script = """
|
||||
DYNAMIC_INIT
|
||||
@@ -44,84 +57,128 @@ DYNAMIC_INIT
|
||||
HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build"
|
||||
STAGE="${COOKBOOK_STAGE}/usr"
|
||||
|
||||
for qtdir in plugins mkspecs metatypes modules; do
|
||||
if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then
|
||||
ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}"
|
||||
source "${COOKBOOK_ROOT}/local/scripts/lib/qt-sysroot.sh"
|
||||
redbear_qt_link_sysroot_dirs "${COOKBOOK_SYSROOT}" plugins mkspecs metatypes modules
|
||||
|
||||
if [ -d "${COOKBOOK_SYSROOT}/plugins" ] && [ ! -L "${COOKBOOK_SYSROOT}/plugins" ]; then
|
||||
if [ -d "${COOKBOOK_SYSROOT}/usr/plugins" ]; then
|
||||
cp -an "${COOKBOOK_SYSROOT}/plugins/." "${COOKBOOK_SYSROOT}/usr/plugins/" 2>/dev/null || true
|
||||
rm -rf "${COOKBOOK_SYSROOT}/plugins"
|
||||
ln -s usr/plugins "${COOKBOOK_SYSROOT}/plugins"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Attempt real cmake build with Redox-viable feature set
|
||||
sed -i "s/^ecm_install_po_files_as_qm/#ecm_install_po_files_as_qm/" \
|
||||
"${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null
|
||||
sed -i 's/^ki18n_install(po)/#ki18n_install(po)/' \
|
||||
"${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null
|
||||
sed -i '/include(ECMQmlModule)/s/^/#/' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null
|
||||
cp -a "${COOKBOOK_ROOT}/recipes/libs/freetype2/target/x86_64-unknown-redox/stage/usr/." "${COOKBOOK_SYSROOT}/" 2>/dev/null || true
|
||||
cp -a "${COOKBOOK_ROOT}/recipes/dev/fontconfig/target/x86_64-unknown-redox/stage/usr/." "${COOKBOOK_SYSROOT}/" 2>/dev/null || true
|
||||
cp -rn "${HOST_BUILD}/include/QtGui/6.11.0/QtGui/private/." "${COOKBOOK_SYSROOT}/include/QtGui/6.11.0/QtGui/private/" 2>/dev/null || true
|
||||
|
||||
# Remove Qt components not available in cross-build
|
||||
sed -i '/^ UiTools$/d' "${COOKBOOK_SOURCE}/CMakeLists.txt"
|
||||
sed -i '/^ Sensors$/d' "${COOKBOOK_SOURCE}/CMakeLists.txt"
|
||||
FT2_DIR="${COOKBOOK_ROOT}/recipes/libs/freetype2/target/x86_64-unknown-redox/stage/usr"
|
||||
FC_DIR="${COOKBOOK_ROOT}/recipes/dev/fontconfig/target/x86_64-unknown-redox/stage/usr"
|
||||
CROSS_PKGCONFIG="${COOKBOOK_ROOT}/bin/x86_64-unknown-redox-pkg-config"
|
||||
|
||||
# Remove KF6::Svg references (not available in cross-build)
|
||||
find "${COOKBOOK_SOURCE}" -name "CMakeLists.txt" -exec sed -i '/KF6::Svg/d' {} +
|
||||
sed -i 's/Canberra::Canberra/$<IF:$<BOOL:${Canberra_FOUND}>,Canberra::Canberra,>/' \
|
||||
"${COOKBOOK_SOURCE}/src/plugins/systembell/CMakeLists.txt" 2>/dev/null || true
|
||||
|
||||
sed -i '/^[[:space:]]*UiTools$/d' "${COOKBOOK_SOURCE}/CMakeLists.txt"
|
||||
sed -i 's/find_package(Canberra REQUIRED)/find_package(Canberra QUIET)/' "${COOKBOOK_SOURCE}/CMakeLists.txt"
|
||||
sed -i 's/add_subdirectory(killer)/#add_subdirectory(killer) # disabled: X11-only/' "${COOKBOOK_SOURCE}/src/helpers/CMakeLists.txt"
|
||||
|
||||
printf '%s\n' '#ifndef SUN_LEN' '#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))' '#endif' | cat - "${COOKBOOK_SOURCE}/src/helpers/wayland_wrapper/wl-socket.c" > /tmp/wlsocket.tmp && mv /tmp/wlsocket.tmp "${COOKBOOK_SOURCE}/src/helpers/wayland_wrapper/wl-socket.c"
|
||||
|
||||
python3 -c "
|
||||
path = '$COOKBOOK_SOURCE/src/cursor.cpp'
|
||||
with open(path) as f: t = f.read()
|
||||
t = t.replace('#include <xcb/xcb_cursor.h>', '')
|
||||
with open(path, 'w') as f: f.write(t)
|
||||
"
|
||||
|
||||
sed -i 's/message(FATAL_ERROR "Compiler does not support C++20 ranges")/message(WARNING "C++20 ranges test skipped — strtold toolchain issue")/' "${COOKBOOK_SOURCE}/CMakeLists.txt"
|
||||
|
||||
sed -i '/#include "tabletmodemanager.h"/a #include <libudev.h>' \
|
||||
"${COOKBOOK_SOURCE}/src/tabletmodemanager.cpp" 2>/dev/null || true
|
||||
|
||||
sed -i '/#include "device.h"/a #include <libudev.h>' \
|
||||
"${COOKBOOK_SOURCE}/src/backends/libinput/device.cpp" 2>/dev/null || true
|
||||
|
||||
sed -i 's/accept4(socketDescriptor, nullptr, nullptr, SOCK_CLOEXEC)/accept(socketDescriptor, nullptr, nullptr)/' \
|
||||
"${COOKBOOK_SOURCE}/src/wayland/display.cpp" 2>/dev/null || true
|
||||
|
||||
echo '#ifndef F_ADD_SEALS
|
||||
#define F_ADD_SEALS 1033
|
||||
#define F_GET_SEALS 1034
|
||||
#define F_SEAL_SEAL 0x0001
|
||||
#define F_SEAL_SHRINK 0x0002
|
||||
#define F_SEAL_GROW 0x0004
|
||||
#define F_SEAL_WRITE 0x0008
|
||||
#endif' >> "${COOKBOOK_SYSROOT}/include/fcntl.h"
|
||||
|
||||
printf '%s\n' '#define F_ADD_SEALS 1033' '#define F_GET_SEALS 1034' '#define F_SEAL_SEAL 0x0001' '#define F_SEAL_SHRINK 0x0002' '#define F_SEAL_GROW 0x0004' '#define F_SEAL_WRITE 0x0008' | cat - "${COOKBOOK_SOURCE}/src/utils/ramfile.cpp" > /tmp/ramfile.tmp && mv /tmp/ramfile.tmp "${COOKBOOK_SOURCE}/src/utils/ramfile.cpp"
|
||||
|
||||
rm -f CMakeCache.txt
|
||||
rm -rf CMakeFiles
|
||||
|
||||
# Qt cmake Imported targets check plugins/ for .so files, but qtwayland and other
|
||||
# modules install them under usr/plugins/. Mirror any missing files across.
|
||||
# Also remove QmlPlugins dirs which reference host paths from QML module.
|
||||
find "${COOKBOOK_SYSROOT}" -type d -name "QmlPlugins" -exec rm -rf {} + 2>/dev/null || true
|
||||
if [ -d "${COOKBOOK_SYSROOT}/usr/plugins" ] && [ -d "${COOKBOOK_SYSROOT}/plugins" ]; then
|
||||
for src in $(find "${COOKBOOK_SYSROOT}/usr/plugins" -name "*.so"); do
|
||||
dst="${COOKBOOK_SYSROOT}/plugins/${src#${COOKBOOK_SYSROOT}/usr/plugins/}"
|
||||
if [ ! -e "$dst" ]; then
|
||||
mkdir -p "$(dirname "$dst")"
|
||||
ln -sf "$src" "$dst" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
fi
|
||||
rm -rf "${COOKBOOK_SOURCE}/po"
|
||||
|
||||
# Stub missing KF6 packages needed by dependencies
|
||||
mkdir -p "${COOKBOOK_SYSROOT}/lib/cmake/KF6Svg"
|
||||
cat > "${COOKBOOK_SYSROOT}/lib/cmake/KF6Svg/KF6SvgConfig.cmake" << 'KF6EOF'
|
||||
set(KF6Svg_FOUND TRUE)
|
||||
KF6EOF
|
||||
HOST_SCANNER="${COOKBOOK_ROOT}/local/recipes/kde/kwin/target/qtwaylandscanner-host/qtwaylandscanner_kde"
|
||||
|
||||
# Stub missing KF6 packages needed by dependencies
|
||||
mkdir -p "${COOKBOOK_SYSROOT}/lib/cmake/KF6Svg"
|
||||
cat > "${COOKBOOK_SYSROOT}/lib/cmake/KF6Svg/KF6SvgConfig.cmake" << 'KF6EOF'
|
||||
set(KF6Svg_FOUND TRUE)
|
||||
KF6EOF
|
||||
HOST_SCANNER_BUILD="${COOKBOOK_ROOT}/local/recipes/kde/kwin/target/qtwaylandscanner-host"
|
||||
mkdir -p "${HOST_SCANNER_BUILD}"
|
||||
env -u LDFLAGS -u LIBRARY_PATH -u PKG_CONFIG_PATH -u PKG_CONFIG_LIBDIR \
|
||||
/usr/bin/pkg-config --exists Qt6Core 2>/dev/null && \
|
||||
env -u LDFLAGS -u LIBRARY_PATH -u PKG_CONFIG_PATH -u PKG_CONFIG_LIBDIR \
|
||||
g++ -std=c++20 -fPIC -o "${HOST_SCANNER_BUILD}/qtwaylandscanner_kde" \
|
||||
"${COOKBOOK_SOURCE}/src/wayland/tools/qtwaylandscanner.cpp" \
|
||||
$(env -u LDFLAGS -u LIBRARY_PATH -u PKG_CONFIG_PATH -u PKG_CONFIG_LIBDIR /usr/bin/pkg-config --cflags --libs Qt6Core) || \
|
||||
env -u LDFLAGS -u LIBRARY_PATH g++ -std=c++20 -fPIC -o "${HOST_SCANNER_BUILD}/qtwaylandscanner_kde" \
|
||||
"${COOKBOOK_SOURCE}/src/wayland/tools/qtwaylandscanner.cpp" \
|
||||
-I"${HOST_BUILD}/include" -I"${HOST_BUILD}/include/QtCore" \
|
||||
-DQT_CORE_LIB -L"${HOST_BUILD}/lib" -lQt6Core -Wl,-rpath,"${HOST_BUILD}/lib"
|
||||
|
||||
cp -f /usr/bin/wayland-scanner "${COOKBOOK_SYSROOT}/bin/wayland-scanner"
|
||||
|
||||
grep -q "strtold" "${COOKBOOK_SYSROOT}/include/stdlib.h" 2>/dev/null || sed -i '/^#endif/ i long double strtold(const char *nptr, char **endptr);' "${COOKBOOK_SYSROOT}/include/stdlib.h"
|
||||
|
||||
cmake "${COOKBOOK_SOURCE}" \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
||||
-DQT_HOST_PATH="${HOST_BUILD}" \
|
||||
-DKF6_HOST_TOOLING="${HOST_BUILD}/lib/cmake" \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
||||
-DPKG_CONFIG_EXECUTABLE="${CROSS_PKGCONFIG}" \
|
||||
-DFREETYPE_LIBRARY="${FT2_DIR}/lib/libfreetype.so" \
|
||||
-DFREETYPE_INCLUDE_DIRS="${FT2_DIR}/include/freetype2" \
|
||||
-DFontconfig_LIBRARY="${FC_DIR}/lib/libfontconfig.so" \
|
||||
-DFontconfig_INCLUDE_DIR="${FC_DIR}/include" \
|
||||
-DBUILD_TESTING=OFF \
|
||||
-DBUILD_QCH=OFF \
|
||||
-DKWIN_BUILD_AUTO_ROTATION=OFF \
|
||||
-DKWIN_BUILD_X11=OFF \
|
||||
-DKWIN_BUILD_X11_BACKEND=OFF \
|
||||
-DKWIN_BUILD_KCMS=OFF \
|
||||
-DKWIN_BUILD_SCREENLOCKER=OFF \
|
||||
-DKWIN_BUILD_TABBOX=OFF \
|
||||
-DKWIN_BUILD_GLOBALSHORTCUTS=OFF \
|
||||
-DKWIN_BUILD_RUNNERS=OFF \
|
||||
-DKWIN_BUILD_NOTIFICATIONS=OFF \
|
||||
-Wno-dev 2>&1
|
||||
cmake --build . -j${COOKBOOK_MAKE_JOBS} 2>&1
|
||||
cmake --install . --prefix "${STAGE}" 2>&1
|
||||
-DKWIN_BUILD_ACTIVITIES=OFF \
|
||||
-DKWIN_BUILD_EIS=OFF \
|
||||
-DQt6GuiPrivate_DIR="${COOKBOOK_SYSROOT}/lib/cmake/Qt6GuiPrivate" \
|
||||
-DQt6CorePrivate_DIR="${COOKBOOK_SYSROOT}/lib/cmake/Qt6CorePrivate" \
|
||||
-DQT_FIND_PRIVATE_MODULES=ON \
|
||||
-DQTWAYLANDSCANNER_KDE_EXECUTABLE="${HOST_SCANNER}" \
|
||||
-DPLASMA_WAYLAND_PROTOCOLS_DIR="${COOKBOOK_SYSROOT}/usr/share/plasma-wayland-protocols" \
|
||||
-DCMAKE_BUILD_WITH_INSTALL_RPATH=TRUE \
|
||||
-DCMAKE_INSTALL_RPATH="/usr/lib" \
|
||||
-Wno-dev
|
||||
|
||||
# Downstream cmake configs for KF6WindowSystem/KF6Config (needed by plasma-framework, plasma-workspace, plasma-desktop — not KWin itself)
|
||||
mkdir -p "${STAGE}/lib/cmake/KF6WindowSystem" "${STAGE}/lib/cmake/KF6Config"
|
||||
cat > "${STAGE}/lib/cmake/KF6WindowSystem/KF6WindowSystemConfig.cmake" << 'EOFCMAKE'
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
||||
set(KF6WindowSystem_LIBRARIES Qt6::Gui)
|
||||
EOFCMAKE
|
||||
cat > "${STAGE}/lib/cmake/KF6Config/KF6ConfigConfig.cmake" << 'EOFCMAKE'
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core)
|
||||
set(KF6Config_LIBRARIES Qt6::Core)
|
||||
EOFCMAKE
|
||||
cmake --build . -j${COOKBOOK_MAKE_JOBS}
|
||||
cmake --install . --prefix "${STAGE}"
|
||||
|
||||
for bin in "${STAGE}/bin/"* "${STAGE}/lib/"lib*.so.*; do
|
||||
[ -f "${bin}" ] || continue
|
||||
patchelf --set-rpath "/usr/lib" "${bin}" 2>/dev/null || true
|
||||
done
|
||||
"""
|
||||
|
||||
[package]
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[source]
|
||||
tar = "https://sourceforge.net/projects/freetype/files/freetype2/2.13.3/freetype-2.13.3.tar.xz/download"
|
||||
blake3 = "07a01894ccdb584943ce817b57341a8595ce9a92bfaa77c602ec4757dfabd5e2"
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
dependencies = [
|
||||
"libpng",
|
||||
"zlib"
|
||||
]
|
||||
script = """
|
||||
DYNAMIC_INIT
|
||||
|
||||
cookbook_meson -Dpng=disabled
|
||||
"""
|
||||
@@ -0,0 +1,30 @@
|
||||
[source]
|
||||
tar = "https://github.com/mm2/Little-CMS/archive/refs/tags/lcms2.19.tar.gz"
|
||||
blake3 = "730f873079fc24b195f87557c872814206805242e977960ee9e8aff8cd6ab28b"
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
script = """
|
||||
DYNAMIC_INIT
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
cmake "${COOKBOOK_SOURCE}" \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DHCMS2_BUILD_TESTS=OFF \
|
||||
-DHCMS2_BUILD_TOOLS=OFF \
|
||||
-Wno-dev
|
||||
|
||||
cmake --build . -j${COOKBOOK_MAKE_JOBS}
|
||||
cmake --install . --prefix "${COOKBOOK_STAGE}/usr"
|
||||
|
||||
for lib in "${COOKBOOK_STAGE}/usr/lib/"lib*.so.*; do
|
||||
[ -f "${lib}" ] || continue
|
||||
patchelf --remove-rpath "${lib}" 2>/dev/null || true
|
||||
done
|
||||
"""
|
||||
@@ -0,0 +1,52 @@
|
||||
[source]
|
||||
path = "source"
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
script = '''
|
||||
DYNAMIC_INIT
|
||||
|
||||
python3 - <<'PY' "${COOKBOOK_SOURCE}"
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
source_root = Path(sys.argv[1])
|
||||
meson_build = source_root / "meson.build"
|
||||
|
||||
if not meson_build.exists():
|
||||
meson_build.write_text(
|
||||
"""project('libdisplay-info', 'c',
|
||||
version: '0.2.0',
|
||||
meson_version: '>= 0.54.0',
|
||||
default_options: ['warning_level=1', 'buildtype=debugoptimized'])
|
||||
|
||||
inc = include_directories('include')
|
||||
|
||||
libdisplay_info = shared_library('display-info',
|
||||
'di.c',
|
||||
include_directories: inc,
|
||||
version: meson.project_version(),
|
||||
install: true)
|
||||
|
||||
install_headers(
|
||||
'include/libdisplay-info/cta.h',
|
||||
'include/libdisplay-info/displayid.h',
|
||||
'include/libdisplay-info/edid.h',
|
||||
'include/libdisplay-info/info.h',
|
||||
subdir: 'libdisplay-info',
|
||||
)
|
||||
|
||||
pkg = import('pkgconfig')
|
||||
pkg.generate(
|
||||
libdisplay_info,
|
||||
name: 'libdisplay-info',
|
||||
description: 'Display identification data parsing library',
|
||||
filebase: 'libdisplay-info',
|
||||
version: meson.project_version(),
|
||||
)
|
||||
"""
|
||||
)
|
||||
PY
|
||||
|
||||
cookbook_meson
|
||||
'''
|
||||
@@ -0,0 +1,29 @@
|
||||
project('libdisplay-info', 'c',
|
||||
version: '0.2.0',
|
||||
meson_version: '>= 0.54.0',
|
||||
default_options: ['warning_level=1', 'buildtype=debugoptimized'])
|
||||
|
||||
inc = include_directories('include')
|
||||
|
||||
libdisplay_info = shared_library('display-info',
|
||||
'di.c',
|
||||
include_directories: inc,
|
||||
version: meson.project_version(),
|
||||
install: true)
|
||||
|
||||
install_headers(
|
||||
'include/libdisplay-info/cta.h',
|
||||
'include/libdisplay-info/displayid.h',
|
||||
'include/libdisplay-info/edid.h',
|
||||
'include/libdisplay-info/info.h',
|
||||
subdir: 'libdisplay-info',
|
||||
)
|
||||
|
||||
pkg = import('pkgconfig')
|
||||
pkg.generate(
|
||||
libdisplay_info,
|
||||
name: 'libdisplay-info',
|
||||
description: 'Display identification data parsing library',
|
||||
filebase: 'libdisplay-info',
|
||||
version: meson.project_version(),
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -50,6 +50,7 @@
|
||||
#include "libdrm_macros.h"
|
||||
#include "xf86drmMode.h"
|
||||
#include "xf86drm.h"
|
||||
#include "xf86drm_redox.h"
|
||||
#include <drm.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include <string.h>
|
||||
@@ -68,6 +69,212 @@ static inline int DRM_IOCTL(int fd, unsigned long cmd, void *arg)
|
||||
return ret < 0 ? -errno : ret;
|
||||
}
|
||||
|
||||
static void* drmAllocCpy(char *array, int count, int entry_size);
|
||||
|
||||
#if defined(__redox__)
|
||||
static void redox_drm_mode_from_wire(drmModeModeInfoPtr mode,
|
||||
const struct redox_drm_mode_wire *wire,
|
||||
const char *name)
|
||||
{
|
||||
memclear(*mode);
|
||||
mode->clock = wire->clock;
|
||||
mode->hdisplay = wire->hdisplay;
|
||||
mode->hsync_start = wire->hsync_start;
|
||||
mode->hsync_end = wire->hsync_end;
|
||||
mode->htotal = wire->htotal;
|
||||
mode->hskew = wire->hskew;
|
||||
mode->vdisplay = wire->vdisplay;
|
||||
mode->vsync_start = wire->vsync_start;
|
||||
mode->vsync_end = wire->vsync_end;
|
||||
mode->vtotal = wire->vtotal;
|
||||
mode->vscan = wire->vscan;
|
||||
mode->vrefresh = wire->vrefresh;
|
||||
mode->flags = wire->flags;
|
||||
mode->type = wire->type;
|
||||
if (name) {
|
||||
size_t name_len = strlen(name);
|
||||
if (name_len >= DRM_DISPLAY_MODE_LEN)
|
||||
name_len = DRM_DISPLAY_MODE_LEN - 1;
|
||||
memcpy(mode->name, name, name_len);
|
||||
}
|
||||
}
|
||||
|
||||
static void redox_drm_mode_to_wire(struct redox_drm_mode_wire *wire,
|
||||
const drmModeModeInfo *mode)
|
||||
{
|
||||
memclear(*wire);
|
||||
wire->clock = mode->clock;
|
||||
wire->hdisplay = mode->hdisplay;
|
||||
wire->hsync_start = mode->hsync_start;
|
||||
wire->hsync_end = mode->hsync_end;
|
||||
wire->htotal = mode->htotal;
|
||||
wire->hskew = mode->hskew;
|
||||
wire->vdisplay = mode->vdisplay;
|
||||
wire->vsync_start = mode->vsync_start;
|
||||
wire->vsync_end = mode->vsync_end;
|
||||
wire->vtotal = mode->vtotal;
|
||||
wire->vscan = mode->vscan;
|
||||
wire->vrefresh = mode->vrefresh;
|
||||
wire->flags = mode->flags;
|
||||
wire->type = mode->type;
|
||||
}
|
||||
|
||||
static int redox_drm_fetch_resources(int fd,
|
||||
struct redox_drm_resources_wire *header,
|
||||
uint32_t **connector_ids)
|
||||
{
|
||||
void *response = NULL;
|
||||
size_t response_size = 0;
|
||||
size_t expected_size;
|
||||
|
||||
if (redox_drm_exchange_alloc(fd, REDOX_DRM_IOCTL_MODE_GETRESOURCES,
|
||||
NULL, 0,
|
||||
&response, &response_size) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (response_size < sizeof(*header)) {
|
||||
drmFree(response);
|
||||
errno = EPROTO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(header, response, sizeof(*header));
|
||||
expected_size = sizeof(*header) +
|
||||
((size_t)header->connector_count * sizeof(uint32_t));
|
||||
if (response_size < expected_size) {
|
||||
drmFree(response);
|
||||
errno = EPROTO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*connector_ids = NULL;
|
||||
if (header->connector_count != 0) {
|
||||
*connector_ids = drmAllocCpy((char *)response + sizeof(*header),
|
||||
header->connector_count, sizeof(uint32_t));
|
||||
if (!*connector_ids) {
|
||||
drmFree(response);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
drmFree(response);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int redox_drm_parse_connector_response(const void *blob, size_t blob_size,
|
||||
struct redox_drm_connector_wire *header,
|
||||
drmModeModeInfoPtr *modes_out)
|
||||
{
|
||||
const uint8_t *bytes = blob;
|
||||
drmModeModeInfoPtr modes = NULL;
|
||||
size_t offset = sizeof(*header);
|
||||
uint32_t i;
|
||||
|
||||
if (blob_size < sizeof(*header)) {
|
||||
errno = EPROTO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(header, bytes, sizeof(*header));
|
||||
if (header->mode_count != 0) {
|
||||
modes = drmMalloc(header->mode_count * sizeof(*modes));
|
||||
if (!modes) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < header->mode_count; ++i) {
|
||||
struct redox_drm_mode_wire mode_wire;
|
||||
const char *name;
|
||||
size_t name_len;
|
||||
|
||||
if (blob_size - offset < sizeof(mode_wire)) {
|
||||
drmFree(modes);
|
||||
errno = EPROTO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&mode_wire, bytes + offset, sizeof(mode_wire));
|
||||
offset += sizeof(mode_wire);
|
||||
name = (const char *)bytes + offset;
|
||||
name_len = strnlen(name, blob_size - offset);
|
||||
if (offset + name_len >= blob_size) {
|
||||
drmFree(modes);
|
||||
errno = EPROTO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
redox_drm_mode_from_wire(&modes[i], &mode_wire, name);
|
||||
offset += name_len + 1;
|
||||
}
|
||||
|
||||
*modes_out = modes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int redox_drm_fetch_connector(int fd, uint32_t connector_id,
|
||||
struct redox_drm_connector_wire *header,
|
||||
drmModeModeInfoPtr *modes_out)
|
||||
{
|
||||
void *response = NULL;
|
||||
size_t response_size = 0;
|
||||
int ret;
|
||||
|
||||
if (redox_drm_exchange_alloc(fd, REDOX_DRM_IOCTL_MODE_GETCONNECTOR,
|
||||
&connector_id, sizeof(connector_id),
|
||||
&response, &response_size) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = redox_drm_parse_connector_response(response, response_size,
|
||||
header, modes_out);
|
||||
drmFree(response);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t redox_drm_connector_type_id(int fd, uint32_t connector_id,
|
||||
uint32_t connector_type)
|
||||
{
|
||||
struct redox_drm_resources_wire resources;
|
||||
uint32_t *connector_ids = NULL;
|
||||
uint32_t ordinal = 0;
|
||||
uint32_t i;
|
||||
|
||||
if (redox_drm_fetch_resources(fd, &resources, &connector_ids) != 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < resources.connector_count; ++i) {
|
||||
struct redox_drm_connector_wire header;
|
||||
drmModeModeInfoPtr modes = NULL;
|
||||
|
||||
if (redox_drm_fetch_connector(fd, connector_ids[i], &header, &modes) != 0)
|
||||
break;
|
||||
drmFree(modes);
|
||||
|
||||
if (header.connector_type == connector_type)
|
||||
++ordinal;
|
||||
if (header.connector_id == connector_id)
|
||||
break;
|
||||
}
|
||||
|
||||
drmFree(connector_ids);
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
static int redox_drm_page_flip_unsupported(uint32_t flags)
|
||||
{
|
||||
if (flags & (DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_TARGET)) {
|
||||
errno = EOPNOTSUPP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Util functions
|
||||
*/
|
||||
@@ -153,16 +360,104 @@ drm_public void drmModeFreeEncoder(drmModeEncoderPtr ptr)
|
||||
|
||||
drm_public int drmIsKMS(int fd)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
struct redox_drm_resources_wire resources;
|
||||
uint32_t *connector_ids = NULL;
|
||||
int is_kms;
|
||||
|
||||
if (redox_drm_fetch_resources(fd, &resources, &connector_ids) != 0)
|
||||
return 0;
|
||||
|
||||
is_kms = resources.crtc_count > 0 &&
|
||||
resources.connector_count > 0 &&
|
||||
resources.encoder_count > 0;
|
||||
drmFree(connector_ids);
|
||||
return is_kms;
|
||||
#else
|
||||
struct drm_mode_card_res res = {0};
|
||||
|
||||
if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) != 0)
|
||||
return 0;
|
||||
|
||||
return res.count_crtcs > 0 && res.count_connectors > 0 && res.count_encoders > 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
drm_public drmModeResPtr drmModeGetResources(int fd)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
struct redox_drm_resources_wire resources;
|
||||
uint32_t *connector_ids = NULL;
|
||||
drmModeResPtr r = NULL;
|
||||
uint32_t i;
|
||||
|
||||
if (redox_drm_fetch_resources(fd, &resources, &connector_ids) != 0)
|
||||
return NULL;
|
||||
|
||||
r = drmMalloc(sizeof(*r));
|
||||
if (!r) {
|
||||
drmFree(connector_ids);
|
||||
return NULL;
|
||||
}
|
||||
memclear(*r);
|
||||
|
||||
r->count_connectors = resources.connector_count;
|
||||
r->count_crtcs = resources.crtc_count;
|
||||
r->count_encoders = resources.encoder_count;
|
||||
r->connectors = connector_ids;
|
||||
|
||||
if (resources.crtc_count != 0) {
|
||||
r->crtcs = drmMalloc(resources.crtc_count * sizeof(*r->crtcs));
|
||||
if (!r->crtcs)
|
||||
goto err_allocs;
|
||||
for (i = 0; i < resources.crtc_count; ++i)
|
||||
r->crtcs[i] = i + 1;
|
||||
}
|
||||
|
||||
if (resources.encoder_count != 0) {
|
||||
r->encoders = drmMalloc(resources.encoder_count * sizeof(*r->encoders));
|
||||
if (!r->encoders)
|
||||
goto err_allocs;
|
||||
memset(r->encoders, 0, resources.encoder_count * sizeof(*r->encoders));
|
||||
}
|
||||
|
||||
for (i = 0; i < resources.connector_count; ++i) {
|
||||
struct redox_drm_connector_wire connector;
|
||||
drmModeModeInfoPtr modes = NULL;
|
||||
uint32_t j;
|
||||
|
||||
if (redox_drm_fetch_connector(fd, connector_ids[i], &connector, &modes) != 0)
|
||||
goto err_allocs;
|
||||
|
||||
if (i < resources.encoder_count)
|
||||
r->encoders[i] = connector.encoder_id;
|
||||
|
||||
for (j = 0; j < connector.mode_count; ++j) {
|
||||
uint32_t width = modes[j].hdisplay;
|
||||
uint32_t height = modes[j].vdisplay;
|
||||
if (r->min_width == 0 || width < r->min_width)
|
||||
r->min_width = width;
|
||||
if (width > r->max_width)
|
||||
r->max_width = width;
|
||||
if (r->min_height == 0 || height < r->min_height)
|
||||
r->min_height = height;
|
||||
if (height > r->max_height)
|
||||
r->max_height = height;
|
||||
}
|
||||
drmFree(modes);
|
||||
}
|
||||
|
||||
if (resources.encoder_count > resources.connector_count) {
|
||||
for (i = resources.connector_count; i < resources.encoder_count; ++i)
|
||||
r->encoders[i] = 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
err_allocs:
|
||||
drmModeFreeResources(r);
|
||||
return NULL;
|
||||
#else
|
||||
struct drm_mode_card_res res, counts;
|
||||
drmModeResPtr r = 0;
|
||||
|
||||
@@ -253,6 +548,7 @@ err_allocs:
|
||||
drmFree(U642VOID(res.encoder_id_ptr));
|
||||
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -260,6 +556,26 @@ drm_public int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t dep
|
||||
uint8_t bpp, uint32_t pitch, uint32_t bo_handle,
|
||||
uint32_t *buf_id)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
struct redox_drm_add_fb_wire wire;
|
||||
|
||||
memclear(wire);
|
||||
wire.width = width;
|
||||
wire.height = height;
|
||||
wire.pitch = pitch;
|
||||
wire.bpp = bpp;
|
||||
wire.depth = depth;
|
||||
wire.handle = bo_handle;
|
||||
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_ADDFB,
|
||||
&wire, sizeof(wire),
|
||||
&wire, sizeof(wire),
|
||||
NULL) != 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
*buf_id = wire.fb_id;
|
||||
return 0;
|
||||
#else
|
||||
struct drm_mode_fb_cmd f;
|
||||
int ret;
|
||||
|
||||
@@ -276,6 +592,7 @@ drm_public int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t dep
|
||||
|
||||
*buf_id = f.fb_id;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
drm_public int drmModeAddFB2WithModifiers(int fd, uint32_t width,
|
||||
@@ -317,7 +634,22 @@ drm_public int drmModeAddFB2(int fd, uint32_t width, uint32_t height,
|
||||
|
||||
drm_public int drmModeRmFB(int fd, uint32_t bufferId)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
struct redox_drm_rm_fb_wire wire;
|
||||
|
||||
memclear(wire);
|
||||
wire.fb_id = bufferId;
|
||||
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_RMFB,
|
||||
&wire, sizeof(wire),
|
||||
NULL, 0,
|
||||
NULL) != 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return DRM_IOCTL(fd, DRM_IOCTL_MODE_RMFB, &bufferId);
|
||||
#endif
|
||||
}
|
||||
|
||||
drm_public int drmModeCloseFB(int fd, uint32_t buffer_id)
|
||||
@@ -374,6 +706,40 @@ drm_public int drmModeDirtyFB(int fd, uint32_t bufferId,
|
||||
|
||||
drm_public drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
struct redox_drm_get_crtc_wire wire;
|
||||
drmModeCrtcPtr r;
|
||||
|
||||
memclear(wire);
|
||||
wire.crtc_id = crtcId;
|
||||
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_GETCRTC,
|
||||
&wire, sizeof(wire),
|
||||
&wire, sizeof(wire),
|
||||
NULL) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = drmMalloc(sizeof(*r));
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
memclear(*r);
|
||||
r->crtc_id = wire.crtc_id;
|
||||
r->x = wire.x;
|
||||
r->y = wire.y;
|
||||
r->mode_valid = wire.mode_valid;
|
||||
if (r->mode_valid) {
|
||||
char mode_name[DRM_DISPLAY_MODE_LEN];
|
||||
snprintf(mode_name, sizeof(mode_name), "%ux%u@%u",
|
||||
wire.mode.hdisplay, wire.mode.vdisplay, wire.mode.vrefresh);
|
||||
redox_drm_mode_from_wire(&r->mode, &wire.mode, mode_name);
|
||||
r->width = wire.mode.hdisplay;
|
||||
r->height = wire.mode.vdisplay;
|
||||
}
|
||||
r->buffer_id = wire.fb_id;
|
||||
r->gamma_size = 0;
|
||||
return r;
|
||||
#else
|
||||
struct drm_mode_crtc crtc;
|
||||
drmModeCrtcPtr r;
|
||||
|
||||
@@ -402,12 +768,47 @@ drm_public drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId)
|
||||
r->buffer_id = crtc.fb_id;
|
||||
r->gamma_size = crtc.gamma_size;
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
drm_public int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
|
||||
uint32_t x, uint32_t y, uint32_t *connectors, int count,
|
||||
drmModeModeInfoPtr mode)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
struct redox_drm_set_crtc_wire wire;
|
||||
|
||||
if ((x != 0 || y != 0) && bufferId != 0) {
|
||||
errno = EOPNOTSUPP;
|
||||
return -errno;
|
||||
}
|
||||
if (count < 0 || count > 8) {
|
||||
errno = EINVAL;
|
||||
return -errno;
|
||||
}
|
||||
if (bufferId != 0 && !mode) {
|
||||
errno = EINVAL;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memclear(wire);
|
||||
wire.crtc_id = crtcId;
|
||||
wire.fb_handle = bufferId;
|
||||
wire.connector_count = count;
|
||||
if (count > 0)
|
||||
memcpy(wire.connectors, connectors, count * sizeof(*connectors));
|
||||
if (mode)
|
||||
redox_drm_mode_to_wire(&wire.mode, mode);
|
||||
|
||||
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_SETCRTC,
|
||||
&wire, sizeof(wire),
|
||||
NULL, 0,
|
||||
NULL) != 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
struct drm_mode_crtc crtc;
|
||||
|
||||
memclear(crtc);
|
||||
@@ -423,6 +824,7 @@ drm_public int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
|
||||
}
|
||||
|
||||
return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETCRTC, &crtc);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -480,6 +882,30 @@ drm_public int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y)
|
||||
*/
|
||||
drm_public drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
struct drm_mode_get_encoder enc;
|
||||
drmModeEncoderPtr r = NULL;
|
||||
|
||||
memclear(enc);
|
||||
enc.encoder_id = encoder_id;
|
||||
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_GETENCODER,
|
||||
&enc, sizeof(enc),
|
||||
&enc, sizeof(enc),
|
||||
NULL) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = drmMalloc(sizeof(*r));
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
r->encoder_id = enc.encoder_id;
|
||||
r->crtc_id = enc.crtc_id;
|
||||
r->encoder_type = enc.encoder_type;
|
||||
r->possible_crtcs = enc.possible_crtcs;
|
||||
r->possible_clones = enc.possible_clones;
|
||||
return r;
|
||||
#else
|
||||
struct drm_mode_get_encoder enc;
|
||||
drmModeEncoderPtr r = NULL;
|
||||
|
||||
@@ -499,6 +925,7 @@ drm_public drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
|
||||
r->possible_clones = enc.possible_clones;
|
||||
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -507,6 +934,50 @@ drm_public drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
|
||||
static drmModeConnectorPtr
|
||||
_drmModeGetConnector(int fd, uint32_t connector_id, int probe)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
struct redox_drm_connector_wire conn;
|
||||
drmModeConnectorPtr r = NULL;
|
||||
drmModeModeInfoPtr modes = NULL;
|
||||
|
||||
(void)probe;
|
||||
if (redox_drm_fetch_connector(fd, connector_id, &conn, &modes) != 0)
|
||||
return NULL;
|
||||
|
||||
r = drmMalloc(sizeof(*r));
|
||||
if (!r)
|
||||
goto err_allocs;
|
||||
memclear(*r);
|
||||
|
||||
r->connector_id = conn.connector_id;
|
||||
r->encoder_id = conn.encoder_id;
|
||||
r->connection = conn.connection;
|
||||
r->mmWidth = conn.mm_width;
|
||||
r->mmHeight = conn.mm_height;
|
||||
r->subpixel = DRM_MODE_SUBPIXEL_UNKNOWN;
|
||||
r->count_modes = conn.mode_count;
|
||||
r->modes = modes;
|
||||
r->count_props = 0;
|
||||
r->props = NULL;
|
||||
r->prop_values = NULL;
|
||||
r->count_encoders = conn.encoder_id ? 1 : 0;
|
||||
if (r->count_encoders != 0) {
|
||||
r->encoders = drmMalloc(sizeof(*r->encoders));
|
||||
if (!r->encoders)
|
||||
goto err_allocs;
|
||||
r->encoders[0] = conn.encoder_id;
|
||||
}
|
||||
r->connector_type = conn.connector_type;
|
||||
r->connector_type_id = redox_drm_connector_type_id(fd,
|
||||
conn.connector_id,
|
||||
conn.connector_type);
|
||||
return r;
|
||||
|
||||
err_allocs:
|
||||
drmFree(modes);
|
||||
drmFree(r ? r->encoders : NULL);
|
||||
drmFree(r);
|
||||
return NULL;
|
||||
#else
|
||||
struct drm_mode_get_connector conn, counts;
|
||||
drmModeConnectorPtr r = NULL;
|
||||
struct drm_mode_modeinfo stack_mode;
|
||||
@@ -608,6 +1079,7 @@ err_allocs:
|
||||
drmFree(U642VOID(conn.encoders_ptr));
|
||||
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
drm_public drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id)
|
||||
@@ -1100,6 +1572,27 @@ drm_public int drmHandleEvent(int fd, drmEventContextPtr evctx)
|
||||
drm_public int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id,
|
||||
uint32_t flags, void *user_data)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
struct redox_drm_page_flip_wire flip;
|
||||
uint64_t sequence;
|
||||
|
||||
(void)user_data;
|
||||
if (redox_drm_page_flip_unsupported(flags) != 0)
|
||||
return -errno;
|
||||
|
||||
memclear(flip);
|
||||
flip.fb_handle = fb_id;
|
||||
flip.crtc_id = crtc_id;
|
||||
flip.flags = flags;
|
||||
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_PAGE_FLIP,
|
||||
&flip, sizeof(flip),
|
||||
&sequence, sizeof(sequence),
|
||||
NULL) != 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
struct drm_mode_crtc_page_flip flip;
|
||||
|
||||
memclear(flip);
|
||||
@@ -1109,12 +1602,23 @@ drm_public int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id,
|
||||
flip.flags = flags;
|
||||
|
||||
return DRM_IOCTL(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip);
|
||||
#endif
|
||||
}
|
||||
|
||||
drm_public int drmModePageFlipTarget(int fd, uint32_t crtc_id, uint32_t fb_id,
|
||||
uint32_t flags, void *user_data,
|
||||
uint32_t target_vblank)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
(void)fd;
|
||||
(void)crtc_id;
|
||||
(void)fb_id;
|
||||
(void)flags;
|
||||
(void)user_data;
|
||||
(void)target_vblank;
|
||||
errno = EOPNOTSUPP;
|
||||
return -errno;
|
||||
#else
|
||||
struct drm_mode_crtc_page_flip_target flip_target;
|
||||
|
||||
memclear(flip_target);
|
||||
@@ -1125,6 +1629,7 @@ drm_public int drmModePageFlipTarget(int fd, uint32_t crtc_id, uint32_t fb_id,
|
||||
flip_target.sequence = target_vblank;
|
||||
|
||||
return DRM_IOCTL(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip_target);
|
||||
#endif
|
||||
}
|
||||
|
||||
drm_public int drmModeSetPlane(int fd, uint32_t plane_id, uint32_t crtc_id,
|
||||
@@ -1839,6 +2344,26 @@ drmModeCreateDumbBuffer(int fd, uint32_t width, uint32_t height, uint32_t bpp,
|
||||
uint32_t flags, uint32_t *handle, uint32_t *pitch,
|
||||
uint64_t *size)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
struct redox_drm_create_dumb_wire create;
|
||||
|
||||
memclear(create);
|
||||
create.width = width;
|
||||
create.height = height;
|
||||
create.bpp = bpp;
|
||||
create.flags = flags;
|
||||
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_CREATE_DUMB,
|
||||
&create, sizeof(create),
|
||||
&create, sizeof(create),
|
||||
NULL) != 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
*handle = create.handle;
|
||||
*pitch = create.pitch;
|
||||
*size = create.size;
|
||||
return 0;
|
||||
#else
|
||||
int ret;
|
||||
struct drm_mode_create_dumb create = {
|
||||
.width = width,
|
||||
@@ -1855,21 +2380,52 @@ drmModeCreateDumbBuffer(int fd, uint32_t width, uint32_t height, uint32_t bpp,
|
||||
*pitch = create.pitch;
|
||||
*size = create.size;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
drm_public int
|
||||
drmModeDestroyDumbBuffer(int fd, uint32_t handle)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
struct redox_drm_destroy_dumb_wire destroy;
|
||||
|
||||
memclear(destroy);
|
||||
destroy.handle = handle;
|
||||
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_DESTROY_DUMB,
|
||||
&destroy, sizeof(destroy),
|
||||
NULL, 0,
|
||||
NULL) != 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
struct drm_mode_destroy_dumb destroy = {
|
||||
.handle = handle,
|
||||
};
|
||||
|
||||
return DRM_IOCTL(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
|
||||
#endif
|
||||
}
|
||||
|
||||
drm_public int
|
||||
drmModeMapDumbBuffer(int fd, uint32_t handle, uint64_t *offset)
|
||||
{
|
||||
#if defined(__redox__)
|
||||
struct redox_drm_map_dumb_wire map;
|
||||
|
||||
memclear(map);
|
||||
map.handle = handle;
|
||||
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_MAP_DUMB,
|
||||
&map, sizeof(map),
|
||||
&map, sizeof(map),
|
||||
NULL) != 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
*offset = map.offset;
|
||||
return 0;
|
||||
#else
|
||||
int ret;
|
||||
struct drm_mode_map_dumb map = {
|
||||
.handle = handle,
|
||||
@@ -1881,4 +2437,5 @@ drmModeMapDumbBuffer(int fd, uint32_t handle, uint64_t *offset)
|
||||
|
||||
*offset = map.offset;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
#ifndef _XF86DRM_REDOX_H_
|
||||
#define _XF86DRM_REDOX_H_
|
||||
|
||||
#if defined(__redox__)
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define REDOX_DRM_IOCTL_BASE 0x00A0UL
|
||||
|
||||
#define REDOX_LINUX_IOCTL_NR(request) ((unsigned long)((request) & 0xffUL))
|
||||
#define REDOX_LINUX_IOCTL_SIZE(request) (((unsigned long)(request) >> 16) & 0x3fffUL)
|
||||
|
||||
#define REDOX_DRM_IOCTL_MODE_GETRESOURCES (REDOX_DRM_IOCTL_BASE + 0)
|
||||
#define REDOX_DRM_IOCTL_MODE_SETCRTC (REDOX_DRM_IOCTL_BASE + 2)
|
||||
#define REDOX_DRM_IOCTL_MODE_GETCRTC (REDOX_DRM_IOCTL_BASE + 3)
|
||||
#define REDOX_DRM_IOCTL_MODE_GETENCODER (REDOX_DRM_IOCTL_BASE + 6)
|
||||
#define REDOX_DRM_IOCTL_MODE_GETCONNECTOR (REDOX_DRM_IOCTL_BASE + 7)
|
||||
#define REDOX_DRM_IOCTL_MODE_PAGE_FLIP (REDOX_DRM_IOCTL_BASE + 16)
|
||||
#define REDOX_DRM_IOCTL_MODE_CREATE_DUMB (REDOX_DRM_IOCTL_BASE + 18)
|
||||
#define REDOX_DRM_IOCTL_MODE_MAP_DUMB (REDOX_DRM_IOCTL_BASE + 19)
|
||||
#define REDOX_DRM_IOCTL_MODE_DESTROY_DUMB (REDOX_DRM_IOCTL_BASE + 20)
|
||||
#define REDOX_DRM_IOCTL_MODE_ADDFB (REDOX_DRM_IOCTL_BASE + 21)
|
||||
#define REDOX_DRM_IOCTL_MODE_RMFB (REDOX_DRM_IOCTL_BASE + 22)
|
||||
#define REDOX_DRM_IOCTL_GET_CAP (REDOX_DRM_IOCTL_BASE + 23)
|
||||
#define REDOX_DRM_IOCTL_SET_CLIENT_CAP (REDOX_DRM_IOCTL_BASE + 24)
|
||||
#define REDOX_DRM_IOCTL_VERSION (REDOX_DRM_IOCTL_BASE + 25)
|
||||
#define REDOX_DRM_IOCTL_GEM_CLOSE (REDOX_DRM_IOCTL_BASE + 27)
|
||||
#define REDOX_DRM_IOCTL_GEM_FLINK (REDOX_DRM_IOCTL_BASE + 10)
|
||||
#define REDOX_DRM_IOCTL_GEM_OPEN (REDOX_DRM_IOCTL_BASE + 11)
|
||||
#define REDOX_DRM_IOCTL_PRIME_HANDLE_TO_FD (REDOX_DRM_IOCTL_BASE + 29)
|
||||
#define REDOX_DRM_IOCTL_PRIME_FD_TO_HANDLE (REDOX_DRM_IOCTL_BASE + 30)
|
||||
#define REDOX_DRM_IOCTL_GET_MAGIC (REDOX_DRM_IOCTL_BASE + 33)
|
||||
#define REDOX_DRM_IOCTL_AUTH_MAGIC (REDOX_DRM_IOCTL_BASE + 34)
|
||||
#define REDOX_DRM_IOCTL_SET_MASTER (REDOX_DRM_IOCTL_BASE + 35)
|
||||
#define REDOX_DRM_IOCTL_DROP_MASTER (REDOX_DRM_IOCTL_BASE + 36)
|
||||
#define REDOX_DRM_IOCTL_MODE_OBJ_GETPROPERTIES (REDOX_DRM_IOCTL_BASE + 0x50)
|
||||
#define REDOX_DRM_IOCTL_MODE_GETPROPERTY (REDOX_DRM_IOCTL_BASE + 0x51)
|
||||
#define REDOX_DRM_IOCTL_MODE_GETPROPBLOB (REDOX_DRM_IOCTL_BASE + 0x52)
|
||||
#define REDOX_DRM_IOCTL_MODE_CREATE_LEASE (REDOX_DRM_IOCTL_BASE + 0x53)
|
||||
#define REDOX_DRM_IOCTL_MODE_LIST_LESSEES (REDOX_DRM_IOCTL_BASE + 0x54)
|
||||
#define REDOX_DRM_IOCTL_MODE_OBJ_SETPROPERTY (REDOX_DRM_IOCTL_BASE + 0x55)
|
||||
#define REDOX_DRM_IOCTL_MODE_GETPLANERESOURCES (REDOX_DRM_IOCTL_BASE + 0x56)
|
||||
#define REDOX_DRM_IOCTL_MODE_GETPLANE (REDOX_DRM_IOCTL_BASE + 0x57)
|
||||
#define REDOX_DRM_IOCTL_MODE_SETPLANE (REDOX_DRM_IOCTL_BASE + 0x58)
|
||||
#define REDOX_DRM_IOCTL_MODE_ADDFB2 (REDOX_DRM_IOCTL_BASE + 0x59)
|
||||
#define REDOX_DRM_IOCTL_GET_PCI_INFO (REDOX_DRM_IOCTL_BASE + 0x60)
|
||||
|
||||
struct redox_drm_pci_info_wire {
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint16_t subvendor_id;
|
||||
uint16_t subdevice_id;
|
||||
uint8_t revision_id;
|
||||
uint8_t pad[3];
|
||||
uint16_t domain;
|
||||
uint8_t bus;
|
||||
uint8_t dev;
|
||||
uint8_t func;
|
||||
uint8_t pad2[3];
|
||||
};
|
||||
|
||||
struct redox_drm_resources_wire {
|
||||
uint32_t connector_count;
|
||||
uint32_t crtc_count;
|
||||
uint32_t encoder_count;
|
||||
};
|
||||
|
||||
struct redox_drm_connector_wire {
|
||||
uint32_t connector_id;
|
||||
uint32_t connection;
|
||||
uint32_t connector_type;
|
||||
uint32_t mm_width;
|
||||
uint32_t mm_height;
|
||||
uint32_t encoder_id;
|
||||
uint32_t mode_count;
|
||||
};
|
||||
|
||||
struct redox_drm_mode_wire {
|
||||
uint32_t clock;
|
||||
uint16_t hdisplay;
|
||||
uint16_t hsync_start;
|
||||
uint16_t hsync_end;
|
||||
uint16_t htotal;
|
||||
uint16_t hskew;
|
||||
uint16_t vdisplay;
|
||||
uint16_t vsync_start;
|
||||
uint16_t vsync_end;
|
||||
uint16_t vtotal;
|
||||
uint16_t vscan;
|
||||
uint32_t vrefresh;
|
||||
uint32_t flags;
|
||||
uint32_t type;
|
||||
};
|
||||
|
||||
struct redox_drm_set_crtc_wire {
|
||||
uint32_t crtc_id;
|
||||
uint32_t fb_handle;
|
||||
uint32_t connector_count;
|
||||
uint32_t connectors[8];
|
||||
struct redox_drm_mode_wire mode;
|
||||
};
|
||||
|
||||
struct redox_drm_page_flip_wire {
|
||||
uint32_t crtc_id;
|
||||
uint32_t fb_handle;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct redox_drm_create_dumb_wire {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t bpp;
|
||||
uint32_t flags;
|
||||
uint32_t pitch;
|
||||
uint32_t reserved0;
|
||||
uint64_t size;
|
||||
uint32_t handle;
|
||||
uint32_t reserved1;
|
||||
};
|
||||
|
||||
struct redox_drm_map_dumb_wire {
|
||||
uint32_t handle;
|
||||
uint32_t pad;
|
||||
uint64_t offset;
|
||||
};
|
||||
|
||||
struct redox_drm_destroy_dumb_wire {
|
||||
uint32_t handle;
|
||||
};
|
||||
|
||||
struct redox_drm_add_fb_wire {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t pitch;
|
||||
uint32_t bpp;
|
||||
uint32_t depth;
|
||||
uint32_t handle;
|
||||
uint32_t fb_id;
|
||||
};
|
||||
|
||||
struct redox_drm_rm_fb_wire {
|
||||
uint32_t fb_id;
|
||||
};
|
||||
|
||||
struct redox_drm_get_crtc_wire {
|
||||
uint32_t crtc_id;
|
||||
uint32_t fb_id;
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
uint32_t mode_valid;
|
||||
struct redox_drm_mode_wire mode;
|
||||
};
|
||||
|
||||
struct redox_drm_version_wire {
|
||||
int32_t major;
|
||||
int32_t minor;
|
||||
int32_t patch;
|
||||
char name[64];
|
||||
};
|
||||
|
||||
struct redox_drm_prime_handle_to_fd_wire {
|
||||
uint32_t handle;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct redox_drm_prime_handle_to_fd_response_wire {
|
||||
int32_t fd;
|
||||
uint32_t pad;
|
||||
};
|
||||
|
||||
struct redox_drm_prime_fd_to_handle_wire {
|
||||
int32_t fd;
|
||||
uint32_t pad;
|
||||
};
|
||||
|
||||
struct redox_drm_prime_fd_to_handle_response_wire {
|
||||
uint32_t handle;
|
||||
uint32_t pad;
|
||||
};
|
||||
|
||||
unsigned long redox_translate_request(unsigned long linux_nr);
|
||||
int redox_drm_simple_ioctl(int fd, unsigned long request, void *arg);
|
||||
int redox_drm_exchange(int fd, unsigned long request_code,
|
||||
const void *payload, size_t payload_size,
|
||||
void *response, size_t response_capacity,
|
||||
size_t *response_size);
|
||||
int redox_drm_exchange_alloc(int fd, unsigned long request_code,
|
||||
const void *payload, size_t payload_size,
|
||||
void **response, size_t *response_size);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
[source]
|
||||
path = "source"
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
dependencies = [
|
||||
"mesa",
|
||||
]
|
||||
script = """
|
||||
DYNAMIC_INIT
|
||||
|
||||
cookbook_meson \
|
||||
-Ddocs=false \
|
||||
-Dtests=false \
|
||||
-Dglx=no \
|
||||
-Dx11=false \
|
||||
-Degl=yes
|
||||
"""
|
||||
@@ -0,0 +1,10 @@
|
||||
[source]
|
||||
path = "source"
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
script = """
|
||||
DYNAMIC_INIT
|
||||
|
||||
cookbook_meson
|
||||
"""
|
||||
@@ -0,0 +1 @@
|
||||
../../../../local/patches/qtbase/futex-redox-support.patch
|
||||
@@ -4,9 +4,11 @@
|
||||
# Redox platform detection and syscall adaptations in redox.patch
|
||||
[source]
|
||||
tar = "https://download.qt.io/official_releases/qt/6.8/6.8.2/submodules/qtbase-everywhere-src-6.8.2.tar.xz"
|
||||
blake3 = "6e9a81b44a2f6a12ce36b77a990a1e18586afe2ab2b140113b4ec59c6ba5d3c6"
|
||||
# TODO: qtwayland-empty-cursor-guards and qtwaylandscanner-null-guard-listeners
|
||||
# were for Qt 6.8.2; changes applied directly to v6.11-patched source tree.
|
||||
# Regenerate proper patches for 6.11.0 and restore to this list.
|
||||
patches = [
|
||||
"qtwaylandscanner-null-guard-listeners.patch",
|
||||
"qtwayland-empty-cursor-guards.patch",
|
||||
]
|
||||
|
||||
[build]
|
||||
@@ -650,10 +652,10 @@ fi
|
||||
cmake "${COOKBOOK_SOURCE}" \
|
||||
-GNinja \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="-lc -lffi -lredbear-qt-strtold-compat" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-lc -lffi -lredbear-qt-strtold-compat" \
|
||||
-DCMAKE_C_STANDARD_LIBRARIES="-lffi -lredbear-qt-strtold-compat" \
|
||||
-DCMAKE_CXX_STANDARD_LIBRARIES="-lffi -lredbear-qt-strtold-compat" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="-lc -lffi -lgcc -lredbear-qt-strtold-compat" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-lc -lffi -lgcc -lredbear-qt-strtold-compat" \
|
||||
-DCMAKE_C_STANDARD_LIBRARIES="-lffi -lgcc -lredbear-qt-strtold-compat" \
|
||||
-DCMAKE_CXX_STANDARD_LIBRARIES="-lffi -lgcc -lredbear-qt-strtold-compat" \
|
||||
-DQT_HOST_PATH="${HOST_BUILD}" \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
|
||||
@@ -275,12 +275,4 @@ redbear_qt_copy_stage_qt6_cmake_to_sysroot "${STAGE_USR}" "${SYSROOT}"
|
||||
redbear_qt_copy_optional_stage_dir_to_sysroot "${STAGE_USR}" "${SYSROOT}" metatypes
|
||||
redbear_qt_copy_optional_stage_dir_to_sysroot "${STAGE_USR}" "${SYSROOT}" plugins
|
||||
redbear_qt_copy_optional_stage_dir_to_sysroot "${STAGE_USR}" "${SYSROOT}" qml
|
||||
|
||||
for stdlib in \
|
||||
"${COOKBOOK_STAGE}/usr/include/stdlib.h" \
|
||||
"${SYSROOT}/include/stdlib.h" \
|
||||
"${SYSROOT}/usr/include/stdlib.h"; do
|
||||
[ -f "$stdlib" ] || continue
|
||||
sed -i '/strtold[[:space:]]*(/d' "$stdlib" 2>/dev/null || true
|
||||
done
|
||||
"""
|
||||
|
||||
@@ -208,7 +208,7 @@ impl Driver for DriverConfig {
|
||||
};
|
||||
|
||||
if !std::path::Path::new(&actual_path).exists() {
|
||||
return ProbeResult::Deferred {
|
||||
return ProbeResult::Fatal {
|
||||
reason: format!("driver binary not found: {}", actual_path),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
@@ -10,6 +10,11 @@ use redox_driver_core::manager::ProbeEvent;
|
||||
|
||||
use crate::scheme::{DriverManagerScheme, notify_bind, notify_unbind};
|
||||
|
||||
/// Maximum times a single deferred device+driver pair is retried before
|
||||
/// permanently abandoning it. Deferred means a dependency scheme (e.g.
|
||||
/// scheme:firmware) is not yet ready, so the device may bind later.
|
||||
const MAX_DEFERRED_RETRIES: u32 = 5;
|
||||
|
||||
pub fn run_hotplug_loop(
|
||||
manager: Arc<Mutex<DeviceManager>>,
|
||||
scheme: Arc<DriverManagerScheme>,
|
||||
@@ -20,6 +25,9 @@ pub fn run_hotplug_loop(
|
||||
poll_interval_ms
|
||||
);
|
||||
|
||||
let mut deferred_retries: BTreeMap<(String, String), u32> = BTreeMap::new();
|
||||
let mut permanently_fatal: BTreeSet<(String, String)> = BTreeSet::new();
|
||||
|
||||
loop {
|
||||
thread::sleep(Duration::from_millis(poll_interval_ms));
|
||||
|
||||
@@ -57,18 +65,41 @@ pub fn run_hotplug_loop(
|
||||
result,
|
||||
} => {
|
||||
track_pci_device(device, &mut seen_pci_devices);
|
||||
let key = (device.path.clone(), driver_name.clone());
|
||||
|
||||
// Skip devices that were permanently fatal in a previous cycle.
|
||||
// enumerate() re-probes all unbound devices each poll, but a Fatal
|
||||
// result means the driver binary is genuinely absent (e.g. ided on
|
||||
// a live ISO that doesn't ship it) — no amount of re-probing will
|
||||
// change the outcome.
|
||||
if permanently_fatal.contains(&key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match result {
|
||||
ProbeResult::Bound => {
|
||||
log::info!("hotplug: bound {} -> {}", device.path, driver_name);
|
||||
notify_bound_device(scheme.as_ref(), device, driver_name);
|
||||
}
|
||||
ProbeResult::Deferred { reason } => {
|
||||
let retries = deferred_retries.entry(key).or_insert(0);
|
||||
*retries += 1;
|
||||
if *retries <= MAX_DEFERRED_RETRIES {
|
||||
log::info!(
|
||||
"hotplug: deferred {} -> {} ({})",
|
||||
device.path,
|
||||
driver_name,
|
||||
reason
|
||||
);
|
||||
} else if *retries == MAX_DEFERRED_RETRIES + 1 {
|
||||
log::warn!(
|
||||
"hotplug: giving up on {} -> {} after {} retries: {}",
|
||||
device.path,
|
||||
driver_name,
|
||||
MAX_DEFERRED_RETRIES,
|
||||
reason
|
||||
);
|
||||
}
|
||||
}
|
||||
ProbeResult::Fatal { reason } => {
|
||||
log::error!(
|
||||
@@ -77,6 +108,7 @@ pub fn run_hotplug_loop(
|
||||
driver_name,
|
||||
reason
|
||||
);
|
||||
permanently_fatal.insert(key);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,13 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
export DISPLAY=""
|
||||
export WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-0}"
|
||||
# NOTE: Neither WAYLAND_DISPLAY nor DISPLAY may be exported when starting
|
||||
# KWin. KWin uses their presence (even as empty strings) to select the
|
||||
# nested Wayland or X11 backend instead of the bare-metal DRM/KMS backend.
|
||||
# Clients (the greeter UI, KDE session apps) receive WAYLAND_DISPLAY from
|
||||
# redbear-session-launch instead.
|
||||
unset DISPLAY
|
||||
unset WAYLAND_DISPLAY
|
||||
export XDG_SESSION_TYPE=wayland
|
||||
export LIBSEAT_BACKEND=seatd
|
||||
export SEATD_SOCK=/run/seatd.sock
|
||||
@@ -13,6 +18,10 @@ export QML2_IMPORT_PATH="${QML2_IMPORT_PATH:-/usr/qml}"
|
||||
export QT_WAYLAND_SHELL_INTEGRATION="${QT_WAYLAND_SHELL_INTEGRATION:-xdg-shell}"
|
||||
export XCURSOR_THEME="${XCURSOR_THEME:-Pop}"
|
||||
export XKB_CONFIG_ROOT="${XKB_CONFIG_ROOT:-/usr/share/X11/xkb}"
|
||||
export KWIN_COMPOSE="${KWIN_COMPOSE:-O2}"
|
||||
export QT_DEBUG_PLUGINS=1
|
||||
export QT_LOGGING_RULES="${QT_LOGGING_RULES:-*.debug=true}"
|
||||
export MESA_LOADER_DRIVER_OVERRIDE="${MESA_LOADER_DRIVER_OVERRIDE:-virtio_gpu}"
|
||||
|
||||
if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
|
||||
export XDG_RUNTIME_DIR="/tmp/run/redbear-greeter"
|
||||
@@ -20,53 +29,27 @@ fi
|
||||
|
||||
mkdir -p "$XDG_RUNTIME_DIR"
|
||||
|
||||
parse_drm_devices() {
|
||||
local devices="${1:-}"
|
||||
local part=""
|
||||
|
||||
IFS=':' read -r -a parts <<< "$devices"
|
||||
for part in "${parts[@]}"; do
|
||||
if [ -n "$part" ]; then
|
||||
printf '%s\n' "$part"
|
||||
fi
|
||||
done
|
||||
drm_scheme_ready() {
|
||||
# Try to open /scheme/drm/card0 via read (head -c 1).
|
||||
# On Redox, stat and test -e are unreliable for scheme paths,
|
||||
# but opening the scheme file descriptor works.
|
||||
( head -c 1 "/scheme/drm/card0" ) >/dev/null 2>&1
|
||||
}
|
||||
|
||||
drm_devices_ready() {
|
||||
local devices="${1:-}"
|
||||
local device=""
|
||||
|
||||
for device in $(parse_drm_devices "$devices"); do
|
||||
if [ ! -e "$device" ]; then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
wait_for_drm_devices() {
|
||||
local devices="${1:-}"
|
||||
local wait_seconds="${REDBEAR_DRM_WAIT_SECONDS:-2}"
|
||||
wait_for_drm_scheme() {
|
||||
local wait_seconds="${REDBEAR_DRM_WAIT_SECONDS:-10}"
|
||||
local attempts=0
|
||||
|
||||
if [ -z "$devices" ]; then
|
||||
return 1
|
||||
if drm_scheme_ready; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$wait_seconds" -le 0 ] 2>/dev/null; then
|
||||
drm_devices_ready "$devices"
|
||||
return $?
|
||||
fi
|
||||
echo "redbear-greeter-compositor: waiting up to ${wait_seconds}s for DRM device(s): /scheme/drm:/scheme/drm/card0" >&2
|
||||
|
||||
attempts=$((wait_seconds * 2))
|
||||
|
||||
if ! drm_devices_ready "$devices"; then
|
||||
echo "redbear-greeter-compositor: waiting up to ${wait_seconds}s for DRM device(s): ${devices}" >&2
|
||||
fi
|
||||
|
||||
while [ "$attempts" -gt 0 ]; do
|
||||
if drm_devices_ready "$devices"; then
|
||||
if drm_scheme_ready; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -74,13 +57,9 @@ wait_for_drm_devices() {
|
||||
attempts=$((attempts - 1))
|
||||
done
|
||||
|
||||
drm_devices_ready "$devices"
|
||||
drm_scheme_ready
|
||||
}
|
||||
|
||||
if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ] && command -v dbus-launch >/dev/null 2>&1; then
|
||||
eval "$(dbus-launch --sh-syntax)"
|
||||
fi
|
||||
|
||||
# Prefer kwin_wayland (real KWin compositor). Fall back to redbear-compositor only if
|
||||
# KWin is not installed (e.g. text-only target accidentally includes greeter).
|
||||
COMPOSITOR=""
|
||||
@@ -97,27 +76,28 @@ else
|
||||
fi
|
||||
|
||||
# pcid-spawner is intentionally async in Red Bear OS, so service ordering only guarantees that
|
||||
# spawning has started. Wait for the DRM scheme root first, then the selected card node.
|
||||
if [ -n "${KWIN_DRM_DEVICES:-}" ]; then
|
||||
desired_drm_devices="/scheme/drm:${KWIN_DRM_DEVICES}"
|
||||
else
|
||||
desired_drm_devices="/scheme/drm:/scheme/drm/card0"
|
||||
fi
|
||||
|
||||
if wait_for_drm_devices "$desired_drm_devices"; then
|
||||
# spawning has started. Wait for the DRM scheme to appear.
|
||||
if wait_for_drm_scheme; then
|
||||
export KWIN_DRM_DEVICES="${KWIN_DRM_DEVICES:-/scheme/drm/card0}"
|
||||
echo "redbear-greeter-compositor: using DRM compositor backend (KWIN_DRM_DEVICES=${KWIN_DRM_DEVICES})" >&2
|
||||
if [ "$COMPOSITOR" = "kwin_wayland" ] || [ "$COMPOSITOR" = "/usr/bin/kwin_wayland" ]; then
|
||||
exec "$COMPOSITOR" --drm
|
||||
else
|
||||
exec "$COMPOSITOR" --drm
|
||||
|
||||
# NOTE: QDBusConnection::sessionBus() blocks on Redox when attempting Unix socket
|
||||
# connect(), even when dbus-daemon is running. All KWin sessionBus() calls in the
|
||||
# startup path have been bypassed in the KWin source. We do NOT set
|
||||
# DBUS_SESSION_BUS_ADDRESS so that remaining sessionBus() calls with env-var guards
|
||||
# skip gracefully. If a future change re-enables D-Bus session bus, the Qt/Redox
|
||||
# Unix socket connect() issue must be resolved first.
|
||||
|
||||
echo "redbear-greeter-compositor: env DBUS_SESSION_BUS_ADDRESS=${DBUS_SESSION_BUS_ADDRESS:-unset} DBUS_SYSTEM_BUS_ADDRESS=${DBUS_SYSTEM_BUS_ADDRESS:-unset}" >&2
|
||||
echo "redbear-greeter-compositor: launching $COMPOSITOR" >&2
|
||||
QT_QPA_PLATFORM=offscreen "$COMPOSITOR"
|
||||
EXIT_CODE=$?
|
||||
echo "redbear-greeter-compositor: compositor exited with code $EXIT_CODE" >&2
|
||||
exit $EXIT_CODE
|
||||
fi
|
||||
else
|
||||
|
||||
# No DRM at all — fall back to virtual backend.
|
||||
unset KWIN_DRM_DEVICES
|
||||
echo "redbear-greeter-compositor: DRM device not ready after wait, using virtual compositor backend" >&2
|
||||
if [ "$COMPOSITOR" = "kwin_wayland" ] || [ "$COMPOSITOR" = "/usr/bin/kwin_wayland" ]; then
|
||||
exec "$COMPOSITOR" --virtual
|
||||
else
|
||||
exec "$COMPOSITOR" --virtual
|
||||
fi
|
||||
fi
|
||||
echo "redbear-greeter-compositor: no DRM device found, using virtual compositor backend" >&2
|
||||
"$COMPOSITOR"
|
||||
exit $?
|
||||
|
||||
@@ -28,6 +28,7 @@ export XDG_SESSION_DESKTOP="${XDG_SESSION_DESKTOP:-KDE}"
|
||||
export XDG_SESSION_ID="${XDG_SESSION_ID:-c1}"
|
||||
export XDG_SESSION_TYPE="${XDG_SESSION_TYPE:-wayland}"
|
||||
export XKB_CONFIG_ROOT="${XKB_CONFIG_ROOT:-/usr/share/X11/xkb}"
|
||||
export KWIN_COMPOSE="${KWIN_COMPOSE:-Q}"
|
||||
|
||||
if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
|
||||
export XDG_RUNTIME_DIR="/tmp/run/user/$(id -u)"
|
||||
@@ -89,12 +90,18 @@ trap cleanup EXIT INT TERM
|
||||
|
||||
kwin_mode="virtual"
|
||||
|
||||
# Redox scheme paths do not respond to stat()/test -e, but opening and
|
||||
# reading a byte works. Use the same probe the compositor wrapper uses.
|
||||
drm_scheme_ready() {
|
||||
( head -c 1 "/scheme/drm/card0" ) >/dev/null 2>&1
|
||||
}
|
||||
|
||||
set_kwin_mode() {
|
||||
local requested="${REDBEAR_KDE_SESSION_BACKEND:-auto}"
|
||||
|
||||
case "$requested" in
|
||||
drm)
|
||||
if [ -z "${KWIN_DRM_DEVICES:-}" ] && [ -e /scheme/drm/card0 ]; then
|
||||
if [ -z "${KWIN_DRM_DEVICES:-}" ] && drm_scheme_ready; then
|
||||
export KWIN_DRM_DEVICES=/scheme/drm/card0
|
||||
fi
|
||||
if [ -n "${KWIN_DRM_DEVICES:-}" ]; then
|
||||
@@ -109,7 +116,7 @@ set_kwin_mode() {
|
||||
auto|"")
|
||||
if [ -n "${KWIN_DRM_DEVICES:-}" ]; then
|
||||
kwin_mode="drm"
|
||||
elif [ -e /scheme/drm/card0 ]; then
|
||||
elif drm_scheme_ready; then
|
||||
export KWIN_DRM_DEVICES=/scheme/drm/card0
|
||||
kwin_mode="drm"
|
||||
else
|
||||
@@ -257,6 +264,11 @@ kwin_pid=$!
|
||||
|
||||
if ! wait_for_wayland_socket; then
|
||||
printf '%s\n' "redbear-kde-session: $COMPOSITOR failed to expose $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" >&2
|
||||
printf '%s\n' "redbear-kde-session: kwin_pid=$kwin_pid, kwin_alive=$(kill -0 $kwin_pid 2>/dev/null && echo yes || echo no)" >&2
|
||||
printf '%s\n' "redbear-kde-session: XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR" >&2
|
||||
printf '%s\n' "redbear-kde-session: WAYLAND_DISPLAY=$WAYLAND_DISPLAY" >&2
|
||||
printf '%s\n' "redbear-kde-session: contents of $XDG_RUNTIME_DIR:" >&2
|
||||
ls -la "$XDG_RUNTIME_DIR" 2>/dev/null >&2 || echo "(dir not found)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ patches = [
|
||||
"P4-acpi-shutdown-hardening.patch",
|
||||
"P4-acpi-s3-sleep.patch",
|
||||
"P4-pcid-public-client-channel.patch",
|
||||
"P4-pcid-config-scheme.patch",
|
||||
"P4-pcid-spawner-pci-coordinate-env.patch",
|
||||
"P4-initfs-usb-drm-services.patch",
|
||||
"P4-initfs-release-virtio-gpu.patch",
|
||||
|
||||
@@ -83,6 +83,10 @@ patches = [
|
||||
"P3-ipc-tests.patch",
|
||||
# ST_RDONLY/ST_NOSUID constants for statvfs (POSIX)
|
||||
"P3-statvfs-constants.patch",
|
||||
"P3-ld-so-usr-lib-search-path.patch",
|
||||
# Remove relibc's DRM ioctl handler — libdrm's redox_drm_simple_ioctl()
|
||||
# handles all DRM ioctls with its own scheme:drm wire format.
|
||||
"P5-remove-drm-ioctl-intercept.patch",
|
||||
]
|
||||
|
||||
[build]
|
||||
|
||||
@@ -9,6 +9,6 @@ dependencies = [
|
||||
"zlib"
|
||||
]
|
||||
script = """
|
||||
DYNAMIC_STATIC_INIT
|
||||
cookbook_meson
|
||||
DYNAMIC_INIT
|
||||
cookbook_meson -Dpng=disabled
|
||||
"""
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../local/recipes/system/dbus
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
qemu-system-x86_64 -m 8G -drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd -drive file=/home/kellito/Builds/rbos/build/x86_64/redbear-full.iso,format=raw -device virtio-gpu-pci -serial mon:stdio
|
||||
qemu-system-x86_64 -m 8G -drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd -drive file=/home/kellito/Builds/rbos/build/x86_64/redbear-full.iso,format=raw -device virtio-gpu-pci -enable-kvm -serial mon:stdio
|
||||
|
||||
Reference in New Issue
Block a user