mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 19:11:08 +00:00
165 lines
7.7 KiB
C
165 lines
7.7 KiB
C
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
#include "kgsl_compat.h"
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
static unsigned long calls[8];
|
|
static int count, old_error, alloc_error, info_error, free_error;
|
|
static unsigned long address;
|
|
static int mock(int fd, unsigned long request, void *arg) {
|
|
assert(fd == 42 && count < 8);
|
|
calls[count++] = request;
|
|
if (request == IOCTL_KGSL_GPUMEM_ALLOC_ID || request == IOCTL_KGSL_GPUMEM_FREE_ID) {
|
|
if (old_error) { errno = old_error; return -1; }
|
|
return 0;
|
|
}
|
|
if (request == IOCTL_KGSL_GPUOBJ_ALLOC) {
|
|
if (alloc_error) { errno = alloc_error; return -1; }
|
|
struct kgsl_gpuobj_alloc *p = arg;
|
|
assert(p->size == 123 && p->flags == 0x10000 && !p->metadata && !p->metadata_len);
|
|
p->id = 77; p->size = 4096; p->mmapsize = 8192;
|
|
return 0;
|
|
}
|
|
if (request == IOCTL_KGSL_GPUOBJ_INFO) {
|
|
struct kgsl_gpuobj_info *p = arg;
|
|
assert(p->id == 77);
|
|
if (info_error) { errno = info_error; return -1; }
|
|
p->gpuaddr = address; p->flags = 0x20000; p->size = 4096;
|
|
return 0;
|
|
}
|
|
if (request == IOCTL_KGSL_GPUOBJ_FREE) {
|
|
struct kgsl_gpuobj_free *p = arg;
|
|
assert(p->id == 77 && !p->flags && !p->priv && !p->type && !p->len);
|
|
if (free_error) { errno = free_error; return -1; }
|
|
return 0;
|
|
}
|
|
errno = EACCES;
|
|
return -1;
|
|
}
|
|
static struct kgsl_gpumem_alloc_id reset(void) {
|
|
count = old_error = alloc_error = info_error = free_error = 0;
|
|
address = 0x100000;
|
|
return (struct kgsl_gpumem_alloc_id){.size = 123, .flags = 0x10000};
|
|
}
|
|
static int read_error, wait_error, waits;
|
|
static unsigned int waited_for;
|
|
static void (*during_wait)(void);
|
|
static int destroy_error;
|
|
static int timestamp_mock(int fd, unsigned long request, void *arg) {
|
|
assert(fd == 42);
|
|
if (request == IOCTL_KGSL_DRAWCTXT_DESTROY && destroy_error) { errno = destroy_error; return -1; }
|
|
if (request == IOCTL_KGSL_GPU_COMMAND || request == IOCTL_KGSL_DRAWCTXT_DESTROY) return 0;
|
|
if (request == IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_CTXTID) {
|
|
errno = read_error;
|
|
return -1;
|
|
}
|
|
if (request == IOCTL_KGSL_DEVICE_WAITTIMESTAMP_CTXTID) {
|
|
const struct kgsl_device_waittimestamp_ctxtid *w = arg;
|
|
assert(w->timeout == 0);
|
|
waits++;
|
|
waited_for = w->timestamp;
|
|
if (during_wait) during_wait();
|
|
if (wait_error) { errno = wait_error; return -1; }
|
|
return 0;
|
|
}
|
|
errno = EACCES;
|
|
return -1;
|
|
}
|
|
static void submit(unsigned int context, unsigned int timestamp) {
|
|
struct kgsl_gpu_command cmd = {.context_id = context, .timestamp = timestamp};
|
|
assert(quest_kgsl_ioctl(timestamp_mock, 42, IOCTL_KGSL_GPU_COMMAND, &cmd) == 0);
|
|
}
|
|
static int read_ts(unsigned int context, unsigned int type, unsigned int *timestamp) {
|
|
struct kgsl_cmdstream_readtimestamp_ctxtid q = {.context_id = context, .type = type, .timestamp = 0xdead};
|
|
int rc = quest_kgsl_ioctl(timestamp_mock, 42, IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_CTXTID, &q);
|
|
*timestamp = q.timestamp;
|
|
return rc;
|
|
}
|
|
static void submit(unsigned int context, unsigned int timestamp);
|
|
static void recreate_context(void) {
|
|
struct kgsl_drawctxt_destroy gone = {.drawctxt_id = 8};
|
|
during_wait = NULL;
|
|
assert(quest_kgsl_ioctl(timestamp_mock, 42, IOCTL_KGSL_DRAWCTXT_DESTROY, &gone) == 0);
|
|
submit(8, 20);
|
|
}
|
|
static void timestamp_scenarios(void) {
|
|
unsigned int ts;
|
|
read_error = EACCES;
|
|
assert(read_ts(5, KGSL_TIMESTAMP_RETIRED, &ts) == -1 && errno == EACCES && ts == 0xdead);
|
|
submit(5, 10);
|
|
assert(read_ts(5, KGSL_TIMESTAMP_QUEUED, &ts) == 0 && ts == 10 && waits == 0);
|
|
assert(read_ts(5, KGSL_TIMESTAMP_CONSUMED, &ts) == -1 && errno == EACCES && ts == 0xdead);
|
|
wait_error = ETIMEDOUT;
|
|
assert(read_ts(5, KGSL_TIMESTAMP_RETIRED, &ts) == 0 && ts == 0 && waits == 1 && waited_for == 10);
|
|
wait_error = 0;
|
|
assert(read_ts(5, KGSL_TIMESTAMP_RETIRED, &ts) == 0 && ts == 10 && waits == 2);
|
|
assert(read_ts(5, KGSL_TIMESTAMP_RETIRED, &ts) == 0 && ts == 10 && waits == 2);
|
|
submit(5, 11);
|
|
wait_error = ETIMEDOUT;
|
|
assert(read_ts(5, KGSL_TIMESTAMP_RETIRED, &ts) == 0 && ts == 10 && waited_for == 11);
|
|
wait_error = EIO;
|
|
assert(read_ts(5, KGSL_TIMESTAMP_RETIRED, &ts) == -1 && errno == EIO);
|
|
wait_error = 0;
|
|
submit(5, 0xfffffffe);
|
|
submit(5, 1);
|
|
assert(read_ts(5, KGSL_TIMESTAMP_RETIRED, &ts) == 0 && ts == 1);
|
|
struct kgsl_drawctxt_destroy gone = {.drawctxt_id = 5};
|
|
assert(quest_kgsl_ioctl(timestamp_mock, 42, IOCTL_KGSL_DRAWCTXT_DESTROY, &gone) == 0);
|
|
assert(read_ts(5, KGSL_TIMESTAMP_QUEUED, &ts) == -1 && errno == EACCES);
|
|
for (unsigned int c = 100; c < 100 + 129; c++) submit(c, c);
|
|
for (unsigned int c = 100; c < 100 + 129; c++) assert(read_ts(c, KGSL_TIMESTAMP_QUEUED, &ts) == 0 && ts == c);
|
|
submit(8, 5);
|
|
during_wait = recreate_context;
|
|
assert(read_ts(8, KGSL_TIMESTAMP_RETIRED, &ts) == 0 && ts == 5);
|
|
wait_error = ETIMEDOUT;
|
|
assert(read_ts(8, KGSL_TIMESTAMP_RETIRED, &ts) == 0 && ts == 0 && waited_for == 20);
|
|
wait_error = 0;
|
|
assert(read_ts(8, KGSL_TIMESTAMP_RETIRED, &ts) == 0 && ts == 20);
|
|
submit(9, 4);
|
|
struct kgsl_drawctxt_destroy refused = {.drawctxt_id = 9};
|
|
destroy_error = EBUSY;
|
|
assert(quest_kgsl_ioctl(timestamp_mock, 42, IOCTL_KGSL_DRAWCTXT_DESTROY, &refused) == -1 && errno == EBUSY);
|
|
destroy_error = 0;
|
|
assert(read_ts(9, KGSL_TIMESTAMP_QUEUED, &ts) == 0 && ts == 4);
|
|
read_error = EIO;
|
|
submit(6, 3);
|
|
assert(read_ts(6, KGSL_TIMESTAMP_QUEUED, &ts) == -1 && errno == EIO);
|
|
}
|
|
|
|
int main(void) {
|
|
timestamp_scenarios();
|
|
struct kgsl_gpumem_alloc_id p = reset();
|
|
assert(quest_kgsl_ioctl(mock, 42, IOCTL_KGSL_GPUMEM_ALLOC_ID, &p) == 0 && count == 1);
|
|
p = reset(); old_error = EACCES;
|
|
assert(quest_kgsl_ioctl(mock, 42, IOCTL_KGSL_GPUMEM_ALLOC_ID, &p) == 0);
|
|
assert(count == 3 && p.id == 77 && p.size == 4096 && p.mmapsize == 8192);
|
|
assert(p.flags == 0x20000 && p.gpuaddr == address);
|
|
p = reset(); old_error = EPERM; address = 0;
|
|
assert(quest_kgsl_ioctl(mock, 42, IOCTL_KGSL_GPUMEM_ALLOC_ID, &p) == 0 && p.gpuaddr == 0);
|
|
p = reset(); old_error = ENOMEM;
|
|
assert(quest_kgsl_ioctl(mock, 42, IOCTL_KGSL_GPUMEM_ALLOC_ID, &p) == -1 && errno == ENOMEM && count == 1);
|
|
p = reset(); old_error = EACCES; alloc_error = ENOMEM;
|
|
assert(quest_kgsl_ioctl(mock, 42, IOCTL_KGSL_GPUMEM_ALLOC_ID, &p) == -1 && errno == ENOMEM && count == 2);
|
|
assert(p.id == 0 && p.size == 123);
|
|
p = reset(); old_error = EACCES; info_error = EIO;
|
|
assert(quest_kgsl_ioctl(mock, 42, IOCTL_KGSL_GPUMEM_ALLOC_ID, &p) == -1 && errno == EIO);
|
|
assert(count == 4 && calls[3] == IOCTL_KGSL_GPUOBJ_FREE && p.id == 0);
|
|
p = reset(); old_error = EACCES; info_error = EIO; free_error = ENOSPC;
|
|
assert(quest_kgsl_ioctl(mock, 42, IOCTL_KGSL_GPUMEM_ALLOC_ID, &p) == -1 && errno == EIO && count == 4);
|
|
p = reset(); old_error = EACCES;
|
|
assert(quest_kgsl_ioctl(mock, 42, IOCTL_KGSL_GPUMEM_ALLOC_ID, NULL) == -1 && count == 1);
|
|
p = reset(); old_error = EACCES;
|
|
assert(quest_kgsl_ioctl(mock, 42, 0x1234, &p) == -1 && errno == EACCES && count == 1);
|
|
struct kgsl_gpumem_free_id release = {.id = 77};
|
|
reset(); old_error = EACCES;
|
|
assert(quest_kgsl_ioctl(mock, 42, IOCTL_KGSL_GPUMEM_FREE_ID, &release) == 0 && count == 2);
|
|
reset(); old_error = EPERM; free_error = EIO;
|
|
assert(quest_kgsl_ioctl(mock, 42, IOCTL_KGSL_GPUMEM_FREE_ID, &release) == -1 && errno == EIO && count == 2);
|
|
reset();
|
|
assert(quest_kgsl_ioctl(mock, 42, IOCTL_KGSL_GPUMEM_FREE_ID, &release) == 0 && count == 1);
|
|
puts("PASS: 12 GPUOBJ translation, passthrough, failure and cleanup scenarios");
|
|
puts("PASS: denied timestamp reads answered from tracked submissions");
|
|
}
|