183 lines
3.8 KiB
C
183 lines
3.8 KiB
C
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
|
/* Copyright (C) 2026 David (Corey) Herrera */
|
|
/*
|
|
* barswap.c — Userspace library for BAR Swap kernel module v2
|
|
*
|
|
* Opens /dev/bar_swap, issues ioctls (QUERY, ACQUIRE, RELEASE), and
|
|
* provides VRAM mmap via /dev/mem.
|
|
*/
|
|
|
|
#include "barswap.h"
|
|
#include "bar_swap_ko/bar_swap.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
|
|
struct bs_context {
|
|
int fd;
|
|
struct bs_query_info info;
|
|
};
|
|
|
|
bs_context* bs_init(void)
|
|
{
|
|
bs_context *ctx = calloc(1, sizeof(bs_context));
|
|
if (!ctx)
|
|
return NULL;
|
|
|
|
ctx->fd = open("/dev/bar_swap", O_RDWR);
|
|
if (ctx->fd < 0) {
|
|
/* Try read-only as fallback */
|
|
ctx->fd = open("/dev/bar_swap", O_RDONLY);
|
|
if (ctx->fd < 0) {
|
|
free(ctx);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Query current state */
|
|
memset(&ctx->info, 0, sizeof(ctx->info));
|
|
if (ioctl(ctx->fd, BAR_SWAP_QUERY, &ctx->info) < 0) {
|
|
close(ctx->fd);
|
|
free(ctx);
|
|
return NULL;
|
|
}
|
|
|
|
return ctx;
|
|
}
|
|
|
|
void bs_free(bs_context *ctx)
|
|
{
|
|
if (!ctx)
|
|
return;
|
|
if (ctx->fd >= 0)
|
|
close(ctx->fd);
|
|
free(ctx);
|
|
}
|
|
|
|
uint32_t bs_num_dies(const bs_context *ctx)
|
|
{
|
|
if (!ctx) return 0;
|
|
return ctx->info.n_dies;
|
|
}
|
|
|
|
uint32_t bs_num_slots(const bs_context *ctx)
|
|
{
|
|
if (!ctx) return 0;
|
|
return ctx->info.n_slots;
|
|
}
|
|
|
|
int bs_is_mapped(const bs_context *ctx, int die_id)
|
|
{
|
|
if (!ctx) return 0;
|
|
if (die_id < 0 || die_id >= (int)ctx->info.n_dies) return 0;
|
|
if (die_id >= BS_MAX_DIES) return 0;
|
|
return ctx->info.mapped[die_id] ? 1 : 0;
|
|
}
|
|
|
|
bs_gpu* bs_acquire(bs_context *ctx, int die_id)
|
|
{
|
|
struct bs_acquire_req req;
|
|
bs_gpu *gpu;
|
|
|
|
if (!ctx || ctx->fd < 0)
|
|
return NULL;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.die_id = (uint32_t)die_id;
|
|
req.flags = 0;
|
|
|
|
if (ioctl(ctx->fd, BAR_SWAP_ACQUIRE, &req) < 0)
|
|
return NULL;
|
|
|
|
gpu = calloc(1, sizeof(bs_gpu));
|
|
if (!gpu)
|
|
return NULL;
|
|
|
|
gpu->die_id = die_id;
|
|
gpu->bar1_addr = req.bar1_addr;
|
|
gpu->bar1_size = req.bar1_size;
|
|
gpu->vram = NULL;
|
|
gpu->vram_mapped_size = 0;
|
|
gpu->mem_fd = -1;
|
|
|
|
/* mmap VRAM via /dev/mem */
|
|
gpu->mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
|
|
if (gpu->mem_fd >= 0) {
|
|
gpu->vram = mmap(NULL, (size_t)gpu->bar1_size,
|
|
PROT_READ | PROT_WRITE, MAP_SHARED,
|
|
gpu->mem_fd, (off_t)gpu->bar1_addr);
|
|
if (gpu->vram == MAP_FAILED) {
|
|
gpu->vram = NULL;
|
|
} else {
|
|
gpu->vram_mapped_size = (size_t)gpu->bar1_size;
|
|
}
|
|
}
|
|
|
|
/* Refresh cached state */
|
|
bs_refresh(ctx);
|
|
|
|
return gpu;
|
|
}
|
|
|
|
void bs_release(bs_context *ctx, bs_gpu *gpu)
|
|
{
|
|
struct bs_release_req req;
|
|
|
|
if (!gpu)
|
|
return;
|
|
|
|
/* Unmap VRAM */
|
|
if (gpu->vram && gpu->vram_mapped_size > 0)
|
|
munmap(gpu->vram, gpu->vram_mapped_size);
|
|
|
|
/* Close /dev/mem */
|
|
if (gpu->mem_fd >= 0)
|
|
close(gpu->mem_fd);
|
|
|
|
/* Issue RELEASE ioctl */
|
|
if (ctx && ctx->fd >= 0) {
|
|
memset(&req, 0, sizeof(req));
|
|
req.die_id = (uint32_t)gpu->die_id;
|
|
req.flags = 0;
|
|
ioctl(ctx->fd, BAR_SWAP_RELEASE, &req);
|
|
|
|
/* Refresh cached state */
|
|
bs_refresh(ctx);
|
|
}
|
|
|
|
free(gpu);
|
|
}
|
|
|
|
int bs_active_die(bs_context *ctx)
|
|
{
|
|
struct bs_query_info info;
|
|
|
|
if (!ctx || ctx->fd < 0)
|
|
return -1;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
if (ioctl(ctx->fd, BAR_SWAP_QUERY, &info) < 0)
|
|
return -1;
|
|
|
|
if (info.active_die == 0xFFFFFFFF)
|
|
return -1;
|
|
|
|
return (int)info.active_die;
|
|
}
|
|
|
|
int bs_refresh(bs_context *ctx)
|
|
{
|
|
if (!ctx || ctx->fd < 0)
|
|
return -1;
|
|
|
|
memset(&ctx->info, 0, sizeof(ctx->info));
|
|
if (ioctl(ctx->fd, BAR_SWAP_QUERY, &ctx->info) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|