98982cc2fa
R1-R10 audit Gap 15: the pci_*_quirk_flags and redox_pci_set_quirk_flags symbols lived inside redox_stubs.c alongside kmalloc, printk, and other generic glue functions. The 'stub' file name was misleading — the flag word that pci_get_quirk_flags() returned was real, computed by redox-drm (Rust) via redox_driver_sys::quirks::lookup_pci_quirks_full() and pushed across the FFI boundary. This change: - Adds source/redox_quirk_bridge.c containing the three symbols plus a static g_redox_quirk_flags global. The header documents the Rust-to-C data flow and references the audit + the Rust-side caller at display.rs:155. - Removes the three functions and the g_pci_quirk_flags static from source/redox_stubs.c. redox_stubs.c now only contains generic glue (kmalloc, printk, msleep, udelay, firmware_store, etc.) and the file name matches its contents. - Updates recipe.toml Stage 1 to compile the new translation unit alongside redox_stubs.c. Both files are linked into libamdgpu_dc_redox.so. The Rust-side caller in local/recipes/gpu/redox-drm/source/src/drivers/amd/display.rs is unchanged: the FFI symbol name 'redox_pci_set_quirk_flags' is the same, so the linker picks up the new definition without any code change on the Rust side. No caller code in amdgpu_redox_main.c changes either — pci_get_quirk_flags and pci_has_quirk are still declared in redox_glue.h with the same signatures, and the new TU provides the single definition that the linker resolves. The end result is identical behavior (the flag word flows the same way) with cleaner file naming and accurate documentation. The audit's stub-finding is now a non-issue for these symbols: there is no longer a stub; the bridge file is named for what it does.
513 lines
12 KiB
C
513 lines
12 KiB
C
#include "redox_glue.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
unsigned long jiffies;
|
|
|
|
struct redox_mapped_region {
|
|
void *addr;
|
|
size_t size;
|
|
int fd;
|
|
struct redox_mapped_region *next;
|
|
};
|
|
|
|
static pthread_mutex_t g_region_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
static struct redox_mapped_region *g_regions;
|
|
|
|
static void redox_jiffies_advance(unsigned long delta)
|
|
{
|
|
__sync_add_and_fetch(&jiffies, delta);
|
|
}
|
|
|
|
void *kmalloc(size_t size, unsigned int flags)
|
|
{
|
|
(void)flags;
|
|
return malloc(size);
|
|
}
|
|
|
|
void *kzalloc(size_t size, unsigned int flags)
|
|
{
|
|
(void)flags;
|
|
return calloc(1, size);
|
|
}
|
|
|
|
void kfree(const void *ptr)
|
|
{
|
|
free((void *)ptr);
|
|
}
|
|
|
|
void *vmalloc(unsigned long size)
|
|
{
|
|
return malloc((size_t)size);
|
|
}
|
|
|
|
void vfree(const void *addr)
|
|
{
|
|
free((void *)addr);
|
|
}
|
|
|
|
void *krealloc(const void *ptr, size_t new_size, unsigned int flags)
|
|
{
|
|
(void)flags;
|
|
return realloc((void *)ptr, new_size);
|
|
}
|
|
|
|
static void redox_track_region(void *addr, size_t size, int fd)
|
|
{
|
|
struct redox_mapped_region *region = malloc(sizeof(*region));
|
|
if (!region) {
|
|
if (fd >= 0) {
|
|
close(fd);
|
|
}
|
|
return;
|
|
}
|
|
|
|
region->addr = addr;
|
|
region->size = size;
|
|
region->fd = fd;
|
|
|
|
pthread_mutex_lock(&g_region_lock);
|
|
region->next = g_regions;
|
|
g_regions = region;
|
|
pthread_mutex_unlock(&g_region_lock);
|
|
}
|
|
|
|
static struct redox_mapped_region *redox_untrack_region(const void *addr)
|
|
{
|
|
struct redox_mapped_region *prev = NULL;
|
|
struct redox_mapped_region *cur;
|
|
|
|
pthread_mutex_lock(&g_region_lock);
|
|
cur = g_regions;
|
|
while (cur) {
|
|
if (cur->addr == addr) {
|
|
if (prev) {
|
|
prev->next = cur->next;
|
|
} else {
|
|
g_regions = cur->next;
|
|
}
|
|
pthread_mutex_unlock(&g_region_lock);
|
|
return cur;
|
|
}
|
|
prev = cur;
|
|
cur = cur->next;
|
|
}
|
|
pthread_mutex_unlock(&g_region_lock);
|
|
return NULL;
|
|
}
|
|
|
|
void __iomem *redox_ioremap(phys_addr_t offset, size_t size)
|
|
{
|
|
int fd = open("/scheme/memory/physical", O_RDWR);
|
|
void *addr;
|
|
|
|
if (fd >= 0) {
|
|
addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)offset);
|
|
if (addr != MAP_FAILED) {
|
|
redox_track_region(addr, size, fd);
|
|
return addr;
|
|
}
|
|
close(fd);
|
|
}
|
|
|
|
addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
if (addr == MAP_FAILED) {
|
|
pr_err("ioremap fallback failed for %#llx (%zu bytes): %s\n",
|
|
(unsigned long long)offset, size, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
memset(addr, 0, size);
|
|
redox_track_region(addr, size, -1);
|
|
return addr;
|
|
}
|
|
|
|
void redox_iounmap(void __iomem *addr)
|
|
{
|
|
struct redox_mapped_region *region;
|
|
|
|
if (!addr) {
|
|
return;
|
|
}
|
|
|
|
region = redox_untrack_region(addr);
|
|
if (!region) {
|
|
return;
|
|
}
|
|
|
|
munmap(region->addr, region->size);
|
|
if (region->fd >= 0) {
|
|
close(region->fd);
|
|
}
|
|
free(region);
|
|
}
|
|
|
|
void redox_iowrite32(u32 val, void __iomem *addr)
|
|
{
|
|
*(volatile u32 *)addr = val;
|
|
}
|
|
|
|
u32 redox_ioread32(const void __iomem *addr)
|
|
{
|
|
return *(volatile const u32 *)addr;
|
|
}
|
|
|
|
void redox_iowrite16(u16 val, void __iomem *addr)
|
|
{
|
|
*(volatile u16 *)addr = val;
|
|
}
|
|
|
|
u16 redox_ioread16(const void __iomem *addr)
|
|
{
|
|
return *(volatile const u16 *)addr;
|
|
}
|
|
|
|
void redox_iowrite8(u8 val, void __iomem *addr)
|
|
{
|
|
*(volatile u8 *)addr = val;
|
|
}
|
|
|
|
u8 redox_ioread8(const void __iomem *addr)
|
|
{
|
|
return *(volatile const u8 *)addr;
|
|
}
|
|
|
|
void redox_mmio_write32(void *base, u32 offset, u32 val)
|
|
{
|
|
if (!base) {
|
|
return;
|
|
}
|
|
*(volatile u32 *)((u8 *)base + offset) = val;
|
|
}
|
|
|
|
u32 redox_mmio_read32(void *base, u32 offset)
|
|
{
|
|
if (!base) {
|
|
return 0;
|
|
}
|
|
return *(volatile u32 *)((u8 *)base + offset);
|
|
}
|
|
|
|
void *redox_dma_alloc_coherent(size_t size, dma_addr_t *dma_handle)
|
|
{
|
|
void *ptr = NULL;
|
|
|
|
if (posix_memalign(&ptr, PAGE_SIZE, PAGE_ALIGN(size)) != 0) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(ptr, 0, PAGE_ALIGN(size));
|
|
if (dma_handle) {
|
|
*dma_handle = (dma_addr_t)(uintptr_t)ptr;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
void redox_dma_free_coherent(size_t size, void *vaddr, dma_addr_t dma_handle)
|
|
{
|
|
(void)size;
|
|
(void)dma_handle;
|
|
free(vaddr);
|
|
}
|
|
|
|
/*
|
|
* PCI device state — populated by the Rust side via redox_pci_set_device_info()
|
|
* before amdgpu_redox_init() is called. redox_pci_find_amd_gpu() returns a
|
|
* pointer to this struct, or NULL if the device info has not been set yet.
|
|
*/
|
|
static struct pci_dev g_pci_dev;
|
|
static int g_pci_dev_populated;
|
|
|
|
#define REDOX_MAX_FIRMWARE_BYTES (64U * 1024U * 1024U)
|
|
#define REDOX_MAX_STORED_FIRMWARES 32
|
|
|
|
struct redox_stored_firmware {
|
|
char name[128];
|
|
u8 *data;
|
|
size_t size;
|
|
};
|
|
|
|
static struct redox_stored_firmware g_stored_firmware[REDOX_MAX_STORED_FIRMWARES];
|
|
static int g_stored_firmware_count;
|
|
static pthread_mutex_t g_firmware_store_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
void redox_pci_set_device_info(u16 vendor, u16 device,
|
|
u8 bus_number, u8 dev_number,
|
|
u8 func_number, u8 revision, u32 irq,
|
|
u64 bar0_addr, u64 bar0_size,
|
|
u64 bar2_addr, u64 bar2_size)
|
|
{
|
|
memset(&g_pci_dev, 0, sizeof(g_pci_dev));
|
|
g_pci_dev.vendor = vendor;
|
|
g_pci_dev.device = device;
|
|
g_pci_dev.bus_number = bus_number;
|
|
g_pci_dev.dev_number = dev_number;
|
|
g_pci_dev.func_number = func_number;
|
|
g_pci_dev.revision = revision;
|
|
g_pci_dev.irq = irq;
|
|
g_pci_dev.resource_start[0] = (phys_addr_t)bar0_addr;
|
|
g_pci_dev.resource_len[0] = bar0_size;
|
|
g_pci_dev.resource_flags[0] = IORESOURCE_MEM;
|
|
g_pci_dev.resource_start[2] = (phys_addr_t)bar2_addr;
|
|
g_pci_dev.resource_len[2] = bar2_size;
|
|
g_pci_dev.resource_flags[2] = IORESOURCE_MEM;
|
|
g_pci_dev.driver_data = NULL;
|
|
memset(&g_pci_dev.device_obj, 0, sizeof(g_pci_dev.device_obj));
|
|
g_pci_dev.enabled = false;
|
|
g_pci_dev.mmio_base = NULL;
|
|
g_pci_dev.is_amdgpu = 1;
|
|
g_pci_dev_populated = 1;
|
|
|
|
printk("PCI device info set: %02x:%02x.%u vendor=%#06x device=%#06x rev=%#04x irq=%u "
|
|
"bar0=%#llx+%#llx bar2=%#llx+%#llx\n",
|
|
bus_number, dev_number, func_number,
|
|
vendor, device, revision, irq,
|
|
(unsigned long long)bar0_addr, (unsigned long long)bar0_size,
|
|
(unsigned long long)bar2_addr, (unsigned long long)bar2_size);
|
|
}
|
|
|
|
struct pci_dev *redox_pci_find_amd_gpu(void)
|
|
{
|
|
if (!g_pci_dev_populated) {
|
|
pr_err("redox_pci_find_amd_gpu: device info not set — "
|
|
"call redox_pci_set_device_info() first\n");
|
|
return NULL;
|
|
}
|
|
|
|
return &g_pci_dev;
|
|
}
|
|
|
|
void redox_pci_dev_put(struct pci_dev *pdev)
|
|
{
|
|
(void)pdev;
|
|
}
|
|
|
|
int redox_pci_enable_device(struct pci_dev *pdev)
|
|
{
|
|
if (!pdev) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (pdev->enabled) {
|
|
return 0;
|
|
}
|
|
|
|
pdev->enabled = true;
|
|
printk("PCI device %02x:%02x.%u enabled (memory/IO access via Redox pcid)\n",
|
|
pdev->bus_number, pdev->dev_number, pdev->func_number);
|
|
return 0;
|
|
}
|
|
|
|
void redox_pci_set_master(struct pci_dev *pdev)
|
|
{
|
|
if (!pdev) {
|
|
return;
|
|
}
|
|
printk("PCI device %02x:%02x.%u set as bus master\n",
|
|
pdev->bus_number, pdev->dev_number, pdev->func_number);
|
|
}
|
|
|
|
int redox_pci_request_regions(struct pci_dev *pdev, const char *name)
|
|
{
|
|
(void)name;
|
|
return pdev ? 0 : -ENODEV;
|
|
}
|
|
|
|
void redox_pci_release_regions(struct pci_dev *pdev)
|
|
{
|
|
(void)pdev;
|
|
}
|
|
|
|
void redox_firmware_store(const char *name, const u8 *data, size_t size)
|
|
{
|
|
int i;
|
|
|
|
if (!name || !data || size == 0) {
|
|
return;
|
|
}
|
|
|
|
if (size > REDOX_MAX_FIRMWARE_BYTES) {
|
|
pr_err("firmware_store: blob %s too large (%zu bytes, max %u)\n",
|
|
name, size, REDOX_MAX_FIRMWARE_BYTES);
|
|
return;
|
|
}
|
|
|
|
pthread_mutex_lock(&g_firmware_store_lock);
|
|
|
|
for (i = 0; i < g_stored_firmware_count; ++i) {
|
|
if (strcmp(g_stored_firmware[i].name, name) == 0) {
|
|
free(g_stored_firmware[i].data);
|
|
g_stored_firmware[i].data = malloc(size);
|
|
if (!g_stored_firmware[i].data) {
|
|
g_stored_firmware[i].size = 0;
|
|
pthread_mutex_unlock(&g_firmware_store_lock);
|
|
return;
|
|
}
|
|
memcpy(g_stored_firmware[i].data, data, size);
|
|
g_stored_firmware[i].size = size;
|
|
pthread_mutex_unlock(&g_firmware_store_lock);
|
|
printk("firmware_store: replaced %s (%zu bytes)\n", name, size);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (g_stored_firmware_count >= REDOX_MAX_STORED_FIRMWARES) {
|
|
pr_err("firmware_store: store full (%d entries), cannot store %s\n",
|
|
g_stored_firmware_count, name);
|
|
pthread_mutex_unlock(&g_firmware_store_lock);
|
|
return;
|
|
}
|
|
|
|
i = g_stored_firmware_count;
|
|
strncpy(g_stored_firmware[i].name, name, sizeof(g_stored_firmware[i].name) - 1);
|
|
g_stored_firmware[i].name[sizeof(g_stored_firmware[i].name) - 1] = '\0';
|
|
g_stored_firmware[i].data = malloc(size);
|
|
if (!g_stored_firmware[i].data) {
|
|
g_stored_firmware[i].size = 0;
|
|
pthread_mutex_unlock(&g_firmware_store_lock);
|
|
return;
|
|
}
|
|
memcpy(g_stored_firmware[i].data, data, size);
|
|
g_stored_firmware[i].size = size;
|
|
g_stored_firmware_count++;
|
|
pthread_mutex_unlock(&g_firmware_store_lock);
|
|
printk("firmware_store: stored %s (%zu bytes)\n", name, size);
|
|
}
|
|
|
|
int redox_request_firmware(const struct firmware **fw, const char *name, void *dev)
|
|
{
|
|
char path[512];
|
|
int fd;
|
|
struct stat st;
|
|
struct firmware *image;
|
|
u8 *data;
|
|
ssize_t nread;
|
|
|
|
(void)dev;
|
|
if (!fw || !name) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
snprintf(path, sizeof(path), "/scheme/firmware/amdgpu/%s", name);
|
|
fd = open(path, O_RDONLY);
|
|
if (fd < 0) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
if (fstat(fd, &st) != 0 || st.st_size < 0) {
|
|
close(fd);
|
|
return -EIO;
|
|
}
|
|
|
|
if ((unsigned long long)st.st_size > REDOX_MAX_FIRMWARE_BYTES) {
|
|
close(fd);
|
|
return -EFBIG;
|
|
}
|
|
|
|
image = calloc(1, sizeof(*image));
|
|
data = malloc((size_t)st.st_size);
|
|
if (!image || !data) {
|
|
free(image);
|
|
free(data);
|
|
close(fd);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
nread = read(fd, data, (size_t)st.st_size);
|
|
close(fd);
|
|
if (nread != st.st_size) {
|
|
free(image);
|
|
free(data);
|
|
return -EIO;
|
|
}
|
|
|
|
image->size = (size_t)st.st_size;
|
|
image->data = data;
|
|
*fw = image;
|
|
return 0;
|
|
}
|
|
|
|
void redox_release_firmware(const struct firmware *fw)
|
|
{
|
|
struct firmware *owned = (struct firmware *)fw;
|
|
|
|
if (!owned) {
|
|
return;
|
|
}
|
|
|
|
free((void *)owned->data);
|
|
free(owned);
|
|
}
|
|
|
|
int redox_request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
|
|
{
|
|
char path[128];
|
|
int fd;
|
|
|
|
(void)handler;
|
|
(void)flags;
|
|
(void)name;
|
|
(void)dev;
|
|
|
|
snprintf(path, sizeof(path), "/scheme/irq/%u", irq);
|
|
fd = open(path, O_RDWR);
|
|
if (fd < 0) {
|
|
pr_err("redox_request_irq: failed to open %s (errno=%d)\n", path, errno);
|
|
return -ENOENT;
|
|
}
|
|
|
|
printk("redox_request_irq: opened IRQ %u fd=%d\n", irq, fd);
|
|
return fd;
|
|
}
|
|
|
|
void redox_free_irq(unsigned int irq, void *dev_id)
|
|
{
|
|
int fd = (int)(intptr_t)dev_id;
|
|
|
|
(void)irq;
|
|
|
|
if (fd > 0) {
|
|
close(fd);
|
|
printk("redox_free_irq: closed IRQ fd=%d\n", fd);
|
|
}
|
|
}
|
|
|
|
void msleep(unsigned int msecs)
|
|
{
|
|
struct timespec ts;
|
|
|
|
ts.tv_sec = msecs / 1000U;
|
|
ts.tv_nsec = (long)(msecs % 1000U) * 1000000L;
|
|
nanosleep(&ts, NULL);
|
|
redox_jiffies_advance(msecs_to_jiffies(msecs));
|
|
}
|
|
|
|
void udelay(unsigned long usecs)
|
|
{
|
|
struct timespec ts;
|
|
|
|
ts.tv_sec = usecs / 1000000UL;
|
|
ts.tv_nsec = (long)(usecs % 1000000UL) * 1000L;
|
|
nanosleep(&ts, NULL);
|
|
redox_jiffies_advance(usecs_to_jiffies((unsigned int)usecs));
|
|
}
|
|
|
|
void mdelay(unsigned long msecs)
|
|
{
|
|
msleep((unsigned int)msecs);
|
|
}
|
|
|
|
unsigned long msecs_to_jiffies(unsigned int msecs)
|
|
{
|
|
return (unsigned long)msecs;
|
|
}
|
|
|
|
unsigned long usecs_to_jiffies(unsigned int usecs)
|
|
{
|
|
return (unsigned long)DIV_ROUND_UP(usecs, 1000U);
|
|
}
|