Path: blob/21.2-virgl/src/asahi/lib/agx_device.c
4560 views
/*1* Copyright (C) 2021 Alyssa Rosenzweig <[email protected]>2* Copyright 2019 Collabora, Ltd.3*4* Permission is hereby granted, free of charge, to any person obtaining a5* copy of this software and associated documentation files (the "Software"),6* to deal in the Software without restriction, including without limitation7* the rights to use, copy, modify, merge, publish, distribute, sublicense,8* and/or sell copies of the Software, and to permit persons to whom the9* Software is furnished to do so, subject to the following conditions:10*11* The above copyright notice and this permission notice (including the next12* paragraph) shall be included in all copies or substantial portions of the13* Software.14*15* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL18* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER19* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,20* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE21* SOFTWARE.22*/2324#include <inttypes.h>25#include "agx_device.h"26#include "agx_bo.h"27#include "decode.h"2829unsigned AGX_FAKE_HANDLE = 0;30uint64_t AGX_FAKE_LO = 0;31uint64_t AGX_FAKE_HI = (1ull << 32);3233static void34agx_bo_free(struct agx_device *dev, struct agx_bo *bo)35{36#if __APPLE__37const uint64_t handle = bo->handle;3839kern_return_t ret = IOConnectCallScalarMethod(dev->fd,40AGX_SELECTOR_FREE_MEM,41&handle, 1, NULL, NULL);4243if (ret)44fprintf(stderr, "error freeing BO mem: %u\n", ret);45#else46free(bo->ptr.cpu);47#endif4849/* Reset the handle */50memset(bo, 0, sizeof(*bo));51}5253void54agx_shmem_free(struct agx_device *dev, unsigned handle)55{56#if __APPLE__57const uint64_t input = handle;58kern_return_t ret = IOConnectCallScalarMethod(dev->fd,59AGX_SELECTOR_FREE_SHMEM,60&input, 1, NULL, NULL);6162if (ret)63fprintf(stderr, "error freeing shmem: %u\n", ret);64#else65#endif66}6768struct agx_bo69agx_shmem_alloc(struct agx_device *dev, size_t size, bool cmdbuf)70{71struct agx_bo bo;7273#if __APPLE__74struct agx_create_shmem_resp out = {};75size_t out_sz = sizeof(out);7677uint64_t inputs[2] = {78size,79cmdbuf ? 1 : 0 // 2 - error reporting, 1 - no error reporting80};8182kern_return_t ret = IOConnectCallMethod(dev->fd,83AGX_SELECTOR_CREATE_SHMEM, inputs, 2, NULL, 0, NULL,84NULL, &out, &out_sz);8586assert(ret == 0);87assert(out_sz == sizeof(out));88assert(out.size == size);89assert(out.map != 0);9091bo = (struct agx_bo) {92.type = cmdbuf ? AGX_ALLOC_CMDBUF : AGX_ALLOC_MEMMAP,93.handle = out.id,94.ptr.cpu = out.map,95.size = out.size,96.guid = 0, /* TODO? */97};98#else99bo = (struct agx_bo) {100.type = cmdbuf ? AGX_ALLOC_CMDBUF : AGX_ALLOC_MEMMAP,101.handle = AGX_FAKE_HANDLE++,102.ptr.cpu = calloc(1, size),103.size = size,104.guid = 0, /* TODO? */105};106#endif107108if (dev->debug & AGX_DBG_TRACE)109agxdecode_track_alloc(&bo);110111return bo;112}113114static struct agx_bo *115agx_bo_alloc(struct agx_device *dev, size_t size,116uint32_t flags)117{118struct agx_bo *bo;119unsigned handle = 0;120121#if __APPLE__122bool write_combine = false;123uint32_t mode = 0x430; // shared, ?124125uint32_t args_in[24] = { 0 };126args_in[1] = write_combine ? 0x400 : 0x0;127args_in[2] = 0x2580320; //0x18000; // unk128args_in[3] = 0x1; // unk;129args_in[4] = 0x4000101; //0x1000101; // unk130args_in[5] = mode;131args_in[16] = size;132args_in[20] = flags;133args_in[21] = 0x3;134135uint64_t out[10] = { 0 };136size_t out_sz = sizeof(out);137138kern_return_t ret = IOConnectCallMethod(dev->fd,139AGX_SELECTOR_ALLOCATE_MEM, NULL, 0, args_in,140sizeof(args_in), NULL, 0, out, &out_sz);141142assert(ret == 0);143assert(out_sz == sizeof(out));144handle = (out[3] >> 32ull);145#else146/* Faked software path until we have a DRM driver */147handle = (++AGX_FAKE_HANDLE);148#endif149150pthread_mutex_lock(&dev->bo_map_lock);151bo = agx_lookup_bo(dev, handle);152pthread_mutex_unlock(&dev->bo_map_lock);153154/* Fresh handle */155assert(!memcmp(bo, &((struct agx_bo) {}), sizeof(*bo)));156157bo->type = AGX_ALLOC_REGULAR;158bo->size = size;159bo->flags = flags;160bo->dev = dev;161bo->handle = handle;162163ASSERTED bool lo = (flags & 0x08000000);164165#if __APPLE__166bo->ptr.gpu = out[0];167bo->ptr.cpu = (void *) out[1];168bo->guid = out[5];169#else170if (lo) {171bo->ptr.gpu = AGX_FAKE_LO;172AGX_FAKE_LO += bo->size;173} else {174bo->ptr.gpu = AGX_FAKE_HI;175AGX_FAKE_HI += bo->size;176}177178bo->ptr.gpu = (((uint64_t) bo->handle) << (lo ? 16 : 24));179bo->ptr.cpu = calloc(1, bo->size);180#endif181182assert(bo->ptr.gpu < (1ull << (lo ? 32 : 40)));183184return bo;185}186187void188agx_bo_reference(struct agx_bo *bo)189{190if (bo) {191ASSERTED int count = p_atomic_inc_return(&bo->refcnt);192assert(count != 1);193}194}195196void197agx_bo_unreference(struct agx_bo *bo)198{199if (!bo)200return;201202/* Don't return to cache if there are still references */203if (p_atomic_dec_return(&bo->refcnt))204return;205206struct agx_device *dev = bo->dev;207208pthread_mutex_lock(&dev->bo_map_lock);209210/* Someone might have imported this BO while we were waiting for the211* lock, let's make sure it's still not referenced before freeing it.212*/213if (p_atomic_read(&bo->refcnt) == 0) {214if (dev->debug & AGX_DBG_TRACE)215agxdecode_track_free(bo);216217/* TODO: cache */218agx_bo_free(dev, bo);219220}221pthread_mutex_unlock(&dev->bo_map_lock);222}223224struct agx_bo *225agx_bo_create(struct agx_device *dev, unsigned size, unsigned flags)226{227struct agx_bo *bo;228assert(size > 0);229230/* To maximize BO cache usage, don't allocate tiny BOs */231size = ALIGN_POT(size, 4096);232233/* TODO: Cache fetch */234bo = agx_bo_alloc(dev, size, flags);235236if (!bo) {237fprintf(stderr, "BO creation failed\n");238return NULL;239}240241p_atomic_set(&bo->refcnt, 1);242243if (dev->debug & AGX_DBG_TRACE)244agxdecode_track_alloc(bo);245246return bo;247}248249static void250agx_get_global_ids(struct agx_device *dev)251{252#if __APPLE__253uint64_t out[2] = {};254size_t out_sz = sizeof(out);255256ASSERTED kern_return_t ret = IOConnectCallStructMethod(dev->fd,257AGX_SELECTOR_GET_GLOBAL_IDS,258NULL, 0, &out, &out_sz);259260assert(ret == 0);261assert(out_sz == sizeof(out));262assert(out[1] > out[0]);263264dev->next_global_id = out[0];265dev->last_global_id = out[1];266#else267dev->next_global_id = 0;268dev->last_global_id = 0x1000000;269#endif270}271272uint64_t273agx_get_global_id(struct agx_device *dev)274{275if (unlikely(dev->next_global_id >= dev->last_global_id)) {276agx_get_global_ids(dev);277}278279return dev->next_global_id++;280}281282/* Tries to open an AGX device, returns true if successful */283284bool285agx_open_device(void *memctx, struct agx_device *dev)286{287#if __APPLE__288kern_return_t ret;289290/* TODO: Support other models */291CFDictionaryRef matching = IOServiceNameMatching("AGXAcceleratorG13G_B0");292293io_service_t service =294IOServiceGetMatchingService(kIOMasterPortDefault, matching);295296if (!service)297return false;298299ret = IOServiceOpen(service, mach_task_self(), AGX_SERVICE_TYPE, &dev->fd);300301if (ret)302return false;303304const char *api = "Equestria";305char in[16] = { 0 };306assert(strlen(api) < sizeof(in));307memcpy(in, api, strlen(api));308309ret = IOConnectCallStructMethod(dev->fd, AGX_SELECTOR_SET_API, in,310sizeof(in), NULL, NULL);311312/* Oddly, the return codes are flipped for SET_API */313if (ret != 1)314return false;315#else316/* Only open a fake AGX device on other operating systems if forced */317if (!getenv("AGX_FAKE_DEVICE"))318return false;319#endif320321dev->memctx = memctx;322util_sparse_array_init(&dev->bo_map, sizeof(struct agx_bo), 512);323324/* XXX: why do BO ids below 6 mess things up..? */325for (unsigned i = 0; i < 6; ++i)326agx_bo_alloc(dev, 4096, AGX_MEMORY_TYPE_FRAMEBUFFER);327328dev->queue = agx_create_command_queue(dev);329dev->cmdbuf = agx_shmem_alloc(dev, 0x4000, true); // length becomes kernelCommandDataSize330dev->memmap = agx_shmem_alloc(dev, 0x4000, false);331agx_get_global_ids(dev);332333return true;334}335336void337agx_close_device(struct agx_device *dev)338{339util_sparse_array_finish(&dev->bo_map);340341#if __APPLE__342kern_return_t ret = IOServiceClose(dev->fd);343344if (ret)345fprintf(stderr, "Error from IOServiceClose: %u\n", ret);346#endif347}348349#if __APPLE__350static struct agx_notification_queue351agx_create_notification_queue(mach_port_t connection)352{353struct agx_create_notification_queue_resp resp;354size_t resp_size = sizeof(resp);355assert(resp_size == 0x10);356357ASSERTED kern_return_t ret = IOConnectCallStructMethod(connection,358AGX_SELECTOR_CREATE_NOTIFICATION_QUEUE,359NULL, 0, &resp, &resp_size);360361assert(resp_size == sizeof(resp));362assert(ret == 0);363364mach_port_t notif_port = IODataQueueAllocateNotificationPort();365IOConnectSetNotificationPort(connection, 0, notif_port, resp.unk2);366367return (struct agx_notification_queue) {368.port = notif_port,369.queue = resp.queue,370.id = resp.unk2371};372}373#endif374375struct agx_command_queue376agx_create_command_queue(struct agx_device *dev)377{378#if __APPLE__379struct agx_command_queue queue = {};380381{382uint8_t buffer[1024 + 8] = { 0 };383const char *path = "/tmp/a.out";384assert(strlen(path) < 1022);385memcpy(buffer + 0, path, strlen(path));386387/* Copy to the end */388unsigned END_LEN = MIN2(strlen(path), 1024 - strlen(path));389unsigned SKIP = strlen(path) - END_LEN;390unsigned OFFS = 1024 - END_LEN;391memcpy(buffer + OFFS, path + SKIP, END_LEN);392393buffer[1024] = 0x2;394395struct agx_create_command_queue_resp out = {};396size_t out_sz = sizeof(out);397398ASSERTED kern_return_t ret = IOConnectCallStructMethod(dev->fd,399AGX_SELECTOR_CREATE_COMMAND_QUEUE,400buffer, sizeof(buffer),401&out, &out_sz);402403assert(ret == 0);404assert(out_sz == sizeof(out));405406queue.id = out.id;407assert(queue.id);408}409410queue.notif = agx_create_notification_queue(dev->fd);411412{413uint64_t scalars[2] = {414queue.id,415queue.notif.id416};417418ASSERTED kern_return_t ret = IOConnectCallScalarMethod(dev->fd,4190x1D,420scalars, 2, NULL, NULL);421422assert(ret == 0);423}424425{426uint64_t scalars[2] = {427queue.id,4280x1ffffffffull429};430431ASSERTED kern_return_t ret = IOConnectCallScalarMethod(dev->fd,4320x29,433scalars, 2, NULL, NULL);434435assert(ret == 0);436}437438return queue;439#else440return (struct agx_command_queue) {4410442};443#endif444}445446void447agx_submit_cmdbuf(struct agx_device *dev, unsigned cmdbuf, unsigned mappings, uint64_t scalar)448{449#if __APPLE__450struct agx_submit_cmdbuf_req req = {451.unk0 = 0x10,452.unk1 = 0x1,453.cmdbuf = cmdbuf,454.mappings = mappings,455.user_0 = (void *) ((uintptr_t) 0xABCD), // Passed in the notif queue456.user_1 = (void *) ((uintptr_t) 0x1234), // Maybe pick better457.unk2 = 0x0,458.unk3 = 0x1,459};460461assert(sizeof(req) == 40);462463ASSERTED kern_return_t ret = IOConnectCallMethod(dev->fd,464AGX_SELECTOR_SUBMIT_COMMAND_BUFFERS,465&scalar, 1,466&req, sizeof(req),467NULL, 0, NULL, 0);468assert(ret == 0);469return;470#endif471}472473void474agx_wait_queue(struct agx_command_queue queue)475{476#if __APPLE__477IOReturn ret = IODataQueueWaitForAvailableData(queue.notif.queue, queue.notif.port);478479uint64_t data[4];480unsigned sz = sizeof(data);481ret = IODataQueueDequeue(queue.notif.queue, data, &sz);482assert(sz == sizeof(data));483assert(data[0] == 0xABCD);484485ret = IODataQueueWaitForAvailableData(queue.notif.queue, queue.notif.port);486ret = IODataQueueDequeue(queue.notif.queue, data, &sz);487assert(sz == sizeof(data));488assert(data[0] == 0x1234);489490assert(!IODataQueueDataAvailable(queue.notif.queue));491#endif492}493494495