blob: 042ffd554b963bb1926c7b4d2628bfe8c7b3b7a3 [file] [log] [blame]
/*
* Copyright © 2016 Google, LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "anv_private.h"
#include "anv_magma.h"
#include <sys/mman.h>
#include <magma_intel_gen_defs.h>
#include <drm-uapi/drm_fourcc.h>
#include "vk_semaphore.h"
#if defined(__linux__)
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif
#if defined(__Fuchsia__)
#include "os/fuchsia.h"
#include <zircon/syscalls.h>
#endif
#include <util/magma/u_magma.h>
#include <util/magma/u_magma_map.h>
#include <util/magma/u_magma_mmap.h>
#define LOG_VERBOSE(...) \
do { \
if (false) \
mesa_logi(__VA_ARGS__); \
} while (0)
static magma_connection_t magma_connection(struct anv_device* device)
{
assert(device);
assert(device->vk.magma_connection);
return device->vk.magma_connection->connection;
}
static inline struct anv_magma_buffer* get_buffer_object(struct u_magma_map* map, uint32_t handle)
{
uint64_t object;
if (!u_magma_map_query(map, handle, &object))
return NULL;
return (struct anv_magma_buffer*) object;
}
static inline uint32_t new_buffer_handle(struct u_magma_map* map,
struct anv_magma_buffer* buffer_object)
{
return u_magma_map_get(map, (uintptr_t) buffer_object);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
int anv_gem_connect(struct anv_device* device)
{
magma_connection_t magma_connection;
magma_status_t status = magma_device_create_connection(
u_magma_device_from_fd(device->fd), &magma_connection);
if (status != MAGMA_STATUS_OK || !magma_connection) {
mesa_logd("magma_create_connection failed: %d", status);
return -1;
}
struct anv_connection* connection = AnvMagmaCreateConnection(magma_connection);
connection->buffer_map = malloc(sizeof(struct u_magma_map));
u_magma_map_init(connection->buffer_map);
device->vk.magma_connection = &connection->vk;
LOG_VERBOSE("created magma connection");
return 0;
}
void anv_gem_disconnect(struct anv_device* device)
{
struct anv_connection* connection = get_anv_connection(device->vk.magma_connection);
u_magma_map_release(connection->buffer_map);
free(connection->buffer_map);
AnvMagmaReleaseConnection(connection);
LOG_VERBOSE("released magma connection");
}
// Return handle, or 0 on failure. Gem handles are never 0.
uint32_t anv_gem_create(struct anv_device* device, uint64_t size)
{
magma_buffer_t buffer;
uint64_t magma_size = size;
magma_buffer_id_t buffer_id;
magma_status_t status = magma_connection_create_buffer(magma_connection(device), magma_size,
&magma_size, &buffer, &buffer_id);
if (status != MAGMA_STATUS_OK) {
mesa_logd("magma_connection_create_buffer failed (%d) size 0x%" PRIx64, status, magma_size);
return 0;
}
struct anv_connection* connection = get_anv_connection(device->vk.magma_connection);
struct anv_magma_buffer* buffer_object = AnvMagmaCreateBuffer(connection, buffer, buffer_id);
uint32_t gem_handle = new_buffer_handle(connection->buffer_map, buffer_object);
LOG_VERBOSE("magma_create_buffer size 0x%" PRIu64 " returning buffer %" PRIu64 " gem_handle %u", magma_size,
buffer_object->id, gem_handle);
return gem_handle;
}
void anv_gem_close(struct anv_device* device, uint32_t gem_handle)
{
LOG_VERBOSE("anv_gem_close gem_handle %u", gem_handle);
struct anv_connection* connection = get_anv_connection(device->vk.magma_connection);
struct anv_magma_buffer* buffer = get_buffer_object(
connection->buffer_map, gem_handle);
if (!buffer) {
mesa_logd("Unknown gem handle: %u", gem_handle);
return;
}
AnvMagmaReleaseBuffer(connection, buffer);
u_magma_map_put(connection->buffer_map, gem_handle);
}
void* anv_gem_mmap(struct anv_device* device, uint32_t gem_handle, uint64_t offset, uint64_t size,
uint32_t flags)
{
assert(flags == 0);
struct anv_magma_buffer* buffer_object = get_buffer_object(
get_anv_connection(device->vk.magma_connection)->buffer_map, gem_handle);
if (!buffer_object) {
mesa_logd("Unknown gem handle: %u", gem_handle);
return MAP_FAILED;
}
magma_buffer_t buffer = buffer_object->buffer;
magma_handle_t handle;
magma_status_t status = magma_buffer_get_handle(buffer, &handle);
if (status != MAGMA_STATUS_OK) {
mesa_logd("magma_get_buffer_handle failed: status %d", status);
return MAP_FAILED;
}
void* addr = u_magma_mmap_handle(handle, offset, size);
u_magma_close_handle(handle);
LOG_VERBOSE("anv_gem_mmap gem_handle %u buffer %" PRIu64 " offset %" PRIu64 " size 0x%" PRIx64 " returning %p",
gem_handle, buffer_object->id, offset, size, addr);
return addr;
}
void anv_gem_munmap(struct anv_device* device, void* addr, uint64_t size)
{
if (!addr)
return;
u_magma_munmap(addr, size);
LOG_VERBOSE("anv_gem_munmap addr %p size %" PRIu64, addr, size);
}
uint32_t anv_gem_userptr(struct anv_device* device, void* mem, size_t size)
{
LOG_VERBOSE("anv_gem_userptr - STUB");
assert(false);
return 0;
}
int anv_gem_set_caching(struct anv_device* device, uint32_t gem_handle, uint32_t caching)
{
LOG_VERBOSE("anv_get_set_caching - STUB");
return 0;
}
int anv_gem_set_domain(struct anv_device* device, uint32_t gem_handle, uint32_t read_domains,
uint32_t write_domain)
{
LOG_VERBOSE("anv_gem_set_domain - STUB");
return 0;
}
/**
* On error, \a timeout_ns holds the remaining time.
*/
int anv_gem_wait(struct anv_device* device, uint32_t gem_handle, int64_t* timeout_ns)
{
struct anv_connection* connection = get_anv_connection(device->vk.magma_connection);
struct anv_magma_buffer* buffer_object = get_buffer_object(
connection->buffer_map, gem_handle);
if (!buffer_object) {
mesa_logi("Unknown gem handle: %u", gem_handle);
return -1;
}
LOG_VERBOSE("anv_gem_wait gem_handle %u buffer_id %" PRIu64 " timeout_ns %" PRIu64, gem_handle,
buffer_object->id, *timeout_ns);
magma_status_t status = AnvMagmaConnectionWait(connection, buffer_object->id, *timeout_ns);
switch (status) {
case MAGMA_STATUS_OK:
break;
case MAGMA_STATUS_TIMED_OUT:
errno = ETIME;
return -1;
default:
return -1;
}
return 0;
}
/**
* Returns 0, 1, or negative to indicate error
*/
int anv_gem_busy(struct anv_device* device, uint32_t gem_handle)
{
LOG_VERBOSE("anv_gem_busy gem_handle %u", gem_handle);
struct anv_connection* connection = get_anv_connection(device->vk.magma_connection);
struct anv_magma_buffer* buffer_object = get_buffer_object(
connection->buffer_map, gem_handle);
if (!buffer_object) {
mesa_logi("Unknown gem handle: %u", gem_handle);
return -1;
}
magma_status_t status = AnvMagmaConnectionWait(connection, buffer_object->id, 0 /*timeout_ns*/);
switch (status) {
case MAGMA_STATUS_TIMED_OUT:
return 1;
case MAGMA_STATUS_OK:
return 0;
default:
return -1;
}
}
int anv_gem_execbuffer(struct anv_device* device, struct drm_i915_gem_execbuffer2* execbuf)
{
LOG_VERBOSE("anv_gem_execbuffer");
struct anv_connection* connection = get_anv_connection(device->vk.magma_connection);
struct drm_i915_gem_exec_object2* exec_objects = (void*)execbuf->buffers_ptr;
struct anv_magma_buffer* buffers[execbuf->buffer_count];
// Translate gem_handles to struct anv_magma_buffer*
for (uint32_t i = 0; i < execbuf->buffer_count; i++) {
uint32_t gem_handle = exec_objects[i].handle;
struct anv_magma_buffer* buffer_object = get_buffer_object(
connection->buffer_map, gem_handle);
if (!buffer_object) {
mesa_logi("Unknown gem handle: %u", gem_handle);
return -1;
}
buffers[i] = buffer_object;
}
return AnvMagmaConnectionExec(connection, device->context_id, execbuf, buffers);
}
int anv_gem_set_tiling(struct anv_device* device, uint32_t gem_handle, uint32_t stride,
uint32_t tiling)
{
LOG_VERBOSE("anv_gem_set_tiling - STUB");
return 0;
}
#if VK_USE_PLATFORM_FUCHSIA
typedef VkResult(VKAPI_PTR* PFN_vkOpenInNamespaceAddr)(const char* pName, uint32_t handle);
PUBLIC VKAPI_ATTR void VKAPI_CALL
vk_icdInitializeOpenInNamespaceCallback(PFN_vkOpenInNamespaceAddr open_in_namespace_addr);
void vk_icdInitializeOpenInNamespaceCallback(PFN_vkOpenInNamespaceAddr open_in_namespace_addr)
{
fuchsia_init(open_in_namespace_addr);
}
#endif // VK_USE_PLATFORM_FUCHSIA
int anv_gem_get_param(int fd, uint32_t param)
{
int tmp;
drm_i915_getparam_t gp = {
.param = param,
.value = &tmp,
};
int ret = intel_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
if (ret == 0)
return tmp;
return 0;
}
int anv_gem_create_context(struct anv_device* device)
{
uint32_t context_id;
magma_connection_create_context(magma_connection(device), &context_id);
LOG_VERBOSE("magma_create_context returned context_id %u", context_id);
return context_id;
}
int anv_gem_destroy_context(struct anv_device* device, int context_id)
{
magma_connection_release_context(magma_connection(device), context_id);
return 0;
}
int anv_gem_handle_to_fd(struct anv_device* device, uint32_t gem_handle)
{
struct anv_magma_buffer* buffer_object = get_buffer_object(
get_anv_connection(device->vk.magma_connection)->buffer_map, gem_handle);
if (!buffer_object) {
mesa_logi("Unknown gem handle: %u", gem_handle);
return -1;
}
uint32_t handle = 0;
magma_status_t result = magma_buffer_export(buffer_object->buffer, &handle);
assert(result == MAGMA_STATUS_OK);
return (int)handle;
}
uint32_t anv_gem_fd_to_handle(struct anv_device* device, int fd)
{
uint32_t handle = (uint32_t)fd;
magma_buffer_t buffer;
uint64_t size;
magma_buffer_id_t buffer_id;
magma_status_t result = magma_connection_import_buffer(magma_connection(device), handle, &size,
&buffer, &buffer_id);
assert(result == MAGMA_STATUS_OK);
struct anv_connection* connection = get_anv_connection(device->vk.magma_connection);
struct anv_magma_buffer* buffer_object = AnvMagmaCreateBuffer(connection, buffer, buffer_id);
uint32_t gem_handle = new_buffer_handle(connection->buffer_map, buffer_object);
return gem_handle;
}
int anv_gem_context_get_reset_stats(int fd, int context, uint32_t* active, uint32_t* pending)
{
LOG_VERBOSE("anv_gem_context_get_reset_stats - STUB");
*active = 0;
*pending = 0;
return 0;
}
int anv_gem_import_fuchsia_buffer(struct anv_device* device, uint32_t handle,
uint32_t* gem_handle_out, uint64_t* size_out)
{
magma_buffer_t buffer;
uint64_t size;
magma_buffer_id_t buffer_id;
magma_status_t status = magma_connection_import_buffer(magma_connection(device), handle, &size,
&buffer, &buffer_id);
if (status != MAGMA_STATUS_OK) {
mesa_logd("magma_connection_import_buffer failed: %d", status);
return -EINVAL;
}
struct anv_connection* connection = get_anv_connection(device->vk.magma_connection);
struct anv_magma_buffer* buffer_object = AnvMagmaCreateBuffer(connection, buffer, buffer_id);
uint32_t gem_handle = new_buffer_handle(connection->buffer_map, buffer_object);
*size_out = size;
*gem_handle_out = gem_handle;
return 0;
}
#if VK_USE_PLATFORM_FUCHSIA
VkResult
anv_GetMemoryZirconHandleFUCHSIA(VkDevice vk_device,
const VkMemoryGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo,
uint32_t* pHandle)
{
ANV_FROM_HANDLE(anv_device, device, vk_device);
ANV_FROM_HANDLE(anv_device_memory, memory, pGetZirconHandleInfo->memory);
assert(pGetZirconHandleInfo->sType ==
VK_STRUCTURE_TYPE_MEMORY_GET_ZIRCON_HANDLE_INFO_FUCHSIA);
assert(pGetZirconHandleInfo->handleType ==
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA);
uint32_t gem_handle = memory->bo->gem_handle;
struct anv_magma_buffer* buffer_object = get_buffer_object(
get_anv_connection(device->vk.magma_connection)->buffer_map, gem_handle);
assert(buffer_object);
magma_status_t result = magma_buffer_export(buffer_object->buffer, pHandle);
assert(result == MAGMA_STATUS_OK);
return VK_SUCCESS;
}
VkResult anv_GetMemoryZirconHandlePropertiesFUCHSIA(
VkDevice vk_device, VkExternalMemoryHandleTypeFlagBitsKHR handleType, uint32_t handle,
VkMemoryZirconHandlePropertiesFUCHSIA* pMemoryZirconHandleProperties)
{
ANV_FROM_HANDLE(anv_device, device, vk_device);
assert(handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA);
assert(pMemoryZirconHandleProperties->sType ==
VK_STRUCTURE_TYPE_MEMORY_ZIRCON_HANDLE_PROPERTIES_FUCHSIA);
zx_handle_t zx_handle = handle;
struct zx_info_handle_basic handle_info;
zx_status_t status = zx_object_get_info(zx_handle, ZX_INFO_HANDLE_BASIC, &handle_info,
sizeof(handle_info), NULL, NULL);
if (status != ZX_OK) {
mesa_logd("zx_object_get_info failed: %d", status);
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
const uint32_t kNeededFlags = ZX_RIGHT_MAP | ZX_RIGHT_READ | ZX_RIGHT_WRITE;
bool is_mappable = (handle_info.rights & kNeededFlags) == kNeededFlags ? true : false;
if (!is_mappable) {
pMemoryZirconHandleProperties->memoryTypeBits = 0;
} else {
// All memory types supported
struct anv_physical_device* pdevice = device->physical;
pMemoryZirconHandleProperties->memoryTypeBits = (1ull << pdevice->memory.type_count) - 1;
}
return VK_SUCCESS;
}
#endif // VK_USE_PLATFORM_FUCHSIA
int anv_gem_reg_read(int fd, uint32_t offset, uint64_t* result)
{
#define TIMESTAMP 0x2358
if (offset == (TIMESTAMP | I915_REG_READ_8B_WA)) {
// Return 0 to enable support for calibrated timestamps, but the implmementation
// should use anv_gem_query_timestamp.
*result = 0;
return 0;
}
// TODO(fxbug.dev/13248)
assert(false);
return 0;
}
int anv_gem_set_context_param(int handle, int context, uint32_t param, uint64_t value)
{
if (param == I915_CONTEXT_PARAM_RECOVERABLE && !value)
return 0;
assert(false);
return -1;
}
bool anv_gem_has_context_priority(int fd, int priority) { return false; }
uint32_t anv_gem_create_image(struct anv_device *device, uint64_t drm_format,
const uint64_t* drm_format_modifiers, uint32_t width, uint32_t height, uint64_t flags)
{
magma_image_create_info_t info = {
.drm_format = drm_format,
.width = width,
.height = height,
.flags = (flags & ANV_CREATE_IMAGE_PRESENTABLE) ? MAGMA_IMAGE_CREATE_FLAGS_PRESENTABLE : 0,
};
const uint64_t* modifier_ptr = drm_format_modifiers;
uint32_t index = 0;
while (index < MAGMA_MAX_DRM_FORMAT_MODIFIERS - 1) {
if (*modifier_ptr == DRM_FORMAT_MOD_INVALID)
break;
info.drm_format_modifiers[index++] = *modifier_ptr++;
}
info.drm_format_modifiers[index] = DRM_FORMAT_MOD_INVALID;
magma_buffer_t image;
uint64_t size;
magma_buffer_id_t buffer_id;
magma_status_t status = magma_virt_connection_create_image(magma_connection(device), &info,
&size, &image, &buffer_id);
if (status != MAGMA_STATUS_OK) {
mesa_logd("magma_virt_connection_create_image failed (%d)", status);
return 0;
}
struct anv_connection* connection = get_anv_connection(device->vk.magma_connection);
struct anv_magma_buffer* buffer_object = AnvMagmaCreateBuffer(connection, image, buffer_id);
uint32_t gem_handle = new_buffer_handle(connection->buffer_map, buffer_object);
LOG_VERBOSE("magma_virt_create_image returning image buffer %" PRIu64 " gem_handle %u",
buffer_object->id, gem_handle);
return gem_handle;
}
int anv_gem_get_image_info(struct anv_device *device, uint32_t gem_handle,
uint64_t* drm_format_modifier_out, uint32_t* bytes_per_row_out, bool* is_cache_coherent_out)
{
struct anv_magma_buffer* buffer_object = get_buffer_object(
get_anv_connection(device->vk.magma_connection)->buffer_map, gem_handle);
if (!buffer_object) {
mesa_logi("Unknown gem handle: %u", gem_handle);
return -EINVAL;
}
magma_buffer_t image = buffer_object->buffer;
magma_image_info_t info;
magma_status_t status = magma_virt_connection_get_image_info(
magma_connection(device), image, &info);
if (status != MAGMA_STATUS_OK) {
mesa_logd("magma_virt_get_image_info failed: %d", status);
return -EINVAL;
}
assert(info.plane_offsets[0] == 0);
*bytes_per_row_out = info.plane_strides[0];
*drm_format_modifier_out = info.drm_format_modifier;
*is_cache_coherent_out = (info.coherency_domain == MAGMA_COHERENCY_DOMAIN_CPU);
return 0;
}
int anv_gem_query_timestamp(int fd, struct anv_timestamp_query* query_out)
{
magma_handle_t handle;
magma_status_t status = magma_device_query(u_magma_device_from_fd(fd),
kMagmaIntelGenQueryTimestamp, &handle, NULL);
if (status != MAGMA_STATUS_OK) {
mesa_logd("magma_device_query_returns_buffer2 failed: %d", status);
return -1;
}
struct magma_intel_gen_timestamp_query* magma_timestamp_query = u_magma_mmap_handle(
handle, 0 /*offset*/, sizeof(struct magma_intel_gen_timestamp_query));
u_magma_close_handle(handle);
if (!magma_timestamp_query) {
mesa_logd("failed to map timestamp query");
return -1;
}
query_out->monotonic_raw_timestamp[0] = magma_timestamp_query->monotonic_raw_timestamp[0];
query_out->monotonic_raw_timestamp[1] = magma_timestamp_query->monotonic_raw_timestamp[1];
query_out->monotonic_timestamp = magma_timestamp_query->monotonic_timestamp;
query_out->device_timestamp = magma_timestamp_query->device_timestamp;
return 0;
}
struct drm_i915_query_engine_info *anv_gem_get_engine_info(int fd)
{
LOG_VERBOSE("anv_gem_get_engine_info - STUB");
return NULL;
}
uint32_t anv_gem_create_regions(struct anv_device *device, uint64_t anv_bo_size,
uint32_t flags, uint32_t num_regions,
struct drm_i915_gem_memory_class_instance *regions)
{
LOG_VERBOSE("anv_gem_create_regions - STUB");
return 0;
}
int anv_gem_get_tiling(struct anv_device *device, uint32_t gem_handle)
{
LOG_VERBOSE("anv_gem_get_tiling - STUB");
return -1;
}