0ec7bd46bb
ROLLUP of all Phase 1-3 work on branch 0.3.0, targeting a production-ready console + full graphical desktop under Intel and Virgl/VirtIO-GPU. === Phase 1 — Stability === - fbcond: Enter key handler (scancode 0x1C→\n), display map buffering, control-char filter in all 7 keymaps, write_event assert - build-redbear.sh: auto-rebuild-prefix when fork timestamps are newer than prefix/x86_64-unknown-redox/sysroot (was warning-only). Added configurable REDBEAR_SKIP_PREFIX_REBUILD guard. - build-redbear.sh: set explicit keymap '-K us' in console activation - config/redbear-device-services.toml: remove spurious init.d service files for redbear-acmd/ecmd/usbaudiod. These USB device daemons are spawned dynamically by pcid-spawner, not as boot-time init services. Starting them without args panicked the boot flow. - relibc: grantpt/unlockpt/ptsname (then deduplicated against stdlib) - userutils: cherry-pick upstream getty commit2834434(standard C ptsname/grantpt/unlockpt) - base fork: Russian (ЙЦУКЕН) keymap + inputd control-char filter (K_ESC/K_BKSP/K_ENTER → \0, commit73e44d81in submodule/base) === Phase 2 — Login & Console === - login.rs: restored to 0.2.5-known-good liner-based prompt - redbear-upower: removed tokio full/signal features → protection fault fix - redbear-power: excluded temporarily (being fixed in other session) - tlc version: updated to 0.3.0 === Phase 3 — GPU/3D Drivers (commit0898332f7a) === Intel i915: FULLY implemented — real ring buffer + MMIO command submission via GEM DMA → GGTT → i915 render ring, with hardware head-pointer polling (2M iterations, 50µs backoff). Zero stubs. VirtIO GPU/Virgl (VirtioTransport): 11 VIRTGPU ioctls (GETPARAM, GET_CAPS, RESOURCE_CREATE, RESOURCE_INFO, CONTEXT_INIT, EXECBUFFER, WAIT, TRANSFER_TO_HOST, TRANSFER_FROM_HOST, MAP, CREATE_BLOB) ported from Linux 7.1 virtgpu_ioctl.c and virtgpu_vq.c. - driver.rs: 8 virgl_* trait methods (default Unsupported) - scheme.rs: 11 ioctl constants + 8 wire structures + dispatch - virtio/transport.rs: PCI capability discovery, feature negotiation, control+cursor virtqueue setup, vring descriptor building - virtio/mod.rs: real implementations for all 8 virgl_* methods - intel/mod.rs: explicit virgl Unsupported stubs Other changes from active sessions: Cargo.toml version bumps, linux-kpi headers, libpciaccess recipe, mesa/recipe.toml, kf6 patches, expat, driver-manager, redbear-sessiond, redbear-compositor, cub, tlc, and many other local recipes.
273 lines
7.5 KiB
C
273 lines
7.5 KiB
C
#define _DEFAULT_SOURCE
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <pciaccess.h>
|
|
|
|
static struct pci_device *pci_devices = NULL;
|
|
static int pci_device_count = 0;
|
|
|
|
static uint32_t read_dword(const uint8_t *data, size_t offset) {
|
|
return (uint32_t)data[offset]
|
|
| ((uint32_t)data[offset + 1] << 8)
|
|
| ((uint32_t)data[offset + 2] << 16)
|
|
| ((uint32_t)data[offset + 3] << 24);
|
|
}
|
|
|
|
static uint16_t read_word(const uint8_t *data, size_t offset) {
|
|
return (uint16_t)data[offset]
|
|
| ((uint16_t)data[offset + 1] << 8);
|
|
}
|
|
|
|
static int read_config(const char *path, uint8_t *data, size_t len) {
|
|
int fd = open(path, O_RDONLY);
|
|
if (fd < 0) {
|
|
return -1;
|
|
}
|
|
size_t total = 0;
|
|
while (total < len) {
|
|
ssize_t n = read(fd, data + total, len - total);
|
|
if (n < 0) {
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
if (n == 0) {
|
|
break;
|
|
}
|
|
total += n;
|
|
}
|
|
close(fd);
|
|
return total == len ? 0 : -1;
|
|
}
|
|
|
|
static int probe_bar_size(int fd, unsigned int offset, int is_io, uint64_t *size) {
|
|
uint32_t original, inverted;
|
|
uint32_t mask = is_io ? 0xFFFFFFFC : 0xFFFFFFF0;
|
|
|
|
if (lseek(fd, offset, SEEK_SET) < 0) {
|
|
return -1;
|
|
}
|
|
if (read(fd, &original, sizeof(original)) != (ssize_t)sizeof(original)) {
|
|
return -1;
|
|
}
|
|
|
|
uint32_t probe = 0xFFFFFFFF;
|
|
if (lseek(fd, offset, SEEK_SET) < 0) {
|
|
return -1;
|
|
}
|
|
if (write(fd, &probe, sizeof(probe)) != (ssize_t)sizeof(probe)) {
|
|
lseek(fd, offset, SEEK_SET);
|
|
write(fd, &original, sizeof(original));
|
|
return -1;
|
|
}
|
|
|
|
if (lseek(fd, offset, SEEK_SET) < 0) {
|
|
lseek(fd, offset, SEEK_SET);
|
|
write(fd, &original, sizeof(original));
|
|
return -1;
|
|
}
|
|
if (read(fd, &inverted, sizeof(inverted)) != (ssize_t)sizeof(inverted)) {
|
|
lseek(fd, offset, SEEK_SET);
|
|
write(fd, &original, sizeof(original));
|
|
return -1;
|
|
}
|
|
|
|
lseek(fd, offset, SEEK_SET);
|
|
write(fd, &original, sizeof(original));
|
|
|
|
if ((inverted & mask) == 0) {
|
|
*size = 0;
|
|
return 0;
|
|
}
|
|
|
|
*size = ((uint64_t)(~(inverted & mask) & mask)) + 1;
|
|
return 0;
|
|
}
|
|
|
|
static int parse_bars(int config_fd, const uint8_t *config, struct pci_device *dev) {
|
|
unsigned int bar_idx = 0;
|
|
unsigned int offset = 0x10;
|
|
|
|
memset(dev->regions, 0, sizeof(dev->regions));
|
|
|
|
while (bar_idx < 6 && offset <= 0x24) {
|
|
uint32_t val = read_dword(config, offset);
|
|
int is_io = (val & 0x1) != 0;
|
|
|
|
if (is_io) {
|
|
dev->regions[bar_idx].type = PCIREGION_TYPE_IO;
|
|
dev->regions[bar_idx].is_IO = 1;
|
|
dev->regions[bar_idx].bus_addr = (pciaddr_t)(val & 0xFFFFFFFC);
|
|
if (probe_bar_size(config_fd, offset, 1, &dev->regions[bar_idx].size) != 0) {
|
|
dev->regions[bar_idx].size = 0;
|
|
}
|
|
bar_idx += 1;
|
|
offset += 4;
|
|
} else {
|
|
int is_64 = ((val >> 1) & 0x3) == 2;
|
|
dev->regions[bar_idx].is_prefetchable = ((val >> 3) & 0x1) != 0;
|
|
dev->regions[bar_idx].type = PCIREGION_TYPE_MEMORY;
|
|
dev->regions[bar_idx].is_IO = 0;
|
|
|
|
if (is_64) {
|
|
if (offset + 4 > 0x24) {
|
|
break;
|
|
}
|
|
dev->regions[bar_idx].is_64 = 1;
|
|
uint32_t val_hi = read_dword(config, offset + 4);
|
|
dev->regions[bar_idx].bus_addr = ((pciaddr_t)val_hi << 32) | (val & 0xFFFFFFF0);
|
|
|
|
uint64_t size_lo = 0, size_hi = 0;
|
|
if (probe_bar_size(config_fd, offset, 0, &size_lo) == 0) {
|
|
probe_bar_size(config_fd, offset + 4, 0, &size_hi);
|
|
}
|
|
dev->regions[bar_idx].size = (size_hi << 32) | size_lo;
|
|
bar_idx += 2;
|
|
offset += 8;
|
|
} else {
|
|
dev->regions[bar_idx].bus_addr = (pciaddr_t)(val & 0xFFFFFFF0);
|
|
if (probe_bar_size(config_fd, offset, 0, &dev->regions[bar_idx].size) != 0) {
|
|
dev->regions[bar_idx].size = 0;
|
|
}
|
|
bar_idx += 1;
|
|
offset += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int parse_device(const char *name, struct pci_device *dev) {
|
|
unsigned int domain, bus, device, func;
|
|
if (sscanf(name, "%x--%x--%x.%x", &domain, &bus, &device, &func) != 4) {
|
|
return -1;
|
|
}
|
|
if (device > 0x1F || func > 0x7) {
|
|
return -1;
|
|
}
|
|
|
|
char config_path[256];
|
|
snprintf(config_path, sizeof(config_path), "/scheme/pci/%s/config", name);
|
|
|
|
uint8_t config[256];
|
|
if (read_config(config_path, config, sizeof(config)) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
dev->domain = domain;
|
|
dev->bus = bus;
|
|
dev->dev = device;
|
|
dev->func = func;
|
|
dev->vendor_id = read_word(config, 0x00);
|
|
dev->device_id = read_word(config, 0x02);
|
|
dev->revision = config[0x08];
|
|
dev->class_code = config[0x0B];
|
|
dev->subclass = config[0x0A];
|
|
dev->prog_if = config[0x09];
|
|
|
|
int config_fd = open(config_path, O_RDWR);
|
|
if (config_fd < 0) {
|
|
config_fd = open(config_path, O_RDONLY);
|
|
}
|
|
if (config_fd >= 0) {
|
|
parse_bars(config_fd, config, dev);
|
|
close(config_fd);
|
|
}
|
|
|
|
dev->_internal = NULL;
|
|
return 0;
|
|
}
|
|
|
|
int pci_system_init(void) {
|
|
if (pci_devices) {
|
|
return 0;
|
|
}
|
|
|
|
DIR *dir = opendir("/scheme/pci");
|
|
if (!dir) {
|
|
return -1;
|
|
}
|
|
|
|
int capacity = 0;
|
|
struct dirent *entry;
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
if (entry->d_name[0] == '.') {
|
|
continue;
|
|
}
|
|
|
|
struct pci_device probe;
|
|
memset(&probe, 0, sizeof(probe));
|
|
if (parse_device(entry->d_name, &probe) != 0) {
|
|
continue;
|
|
}
|
|
|
|
if (pci_device_count >= capacity) {
|
|
capacity = capacity ? capacity * 2 : 8;
|
|
struct pci_device *new_devices = realloc(pci_devices, capacity * sizeof(struct pci_device));
|
|
if (!new_devices) {
|
|
closedir(dir);
|
|
return -1;
|
|
}
|
|
pci_devices = new_devices;
|
|
}
|
|
|
|
pci_devices[pci_device_count++] = probe;
|
|
}
|
|
|
|
closedir(dir);
|
|
return 0;
|
|
}
|
|
|
|
void pci_system_cleanup(void) {
|
|
if (pci_devices) {
|
|
free(pci_devices);
|
|
pci_devices = NULL;
|
|
pci_device_count = 0;
|
|
}
|
|
}
|
|
|
|
struct pci_device *pci_device_find_by_slot(unsigned int domain,
|
|
unsigned int bus,
|
|
unsigned int dev,
|
|
unsigned int func) {
|
|
for (int i = 0; i < pci_device_count; i++) {
|
|
if (pci_devices[i].domain == domain &&
|
|
pci_devices[i].bus == bus &&
|
|
pci_devices[i].dev == dev &&
|
|
pci_devices[i].func == func) {
|
|
return &pci_devices[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int pci_device_probe(struct pci_device *dev) {
|
|
char config_path[256];
|
|
snprintf(config_path, sizeof(config_path),
|
|
"/scheme/pci/%04x--%02x--%02x.%x/config",
|
|
dev->domain, dev->bus, dev->dev, dev->func);
|
|
|
|
uint8_t config[256];
|
|
if (read_config(config_path, config, sizeof(config)) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
int config_fd = open(config_path, O_RDWR);
|
|
if (config_fd < 0) {
|
|
config_fd = open(config_path, O_RDONLY);
|
|
}
|
|
if (config_fd >= 0) {
|
|
parse_bars(config_fd, config, dev);
|
|
close(config_fd);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|