blob: c37791e5c2175ad9bfbea097324e3a9a2326604b [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <lib/magma/magma.h>
#include <lib/magma/util/short_macros.h>
#include <pthread.h>
#include <sys/mman.h>
#include "src/graphics/lib/magma/include/virtio/virtio_magma.h"
#include "src/graphics/lib/magma/src/libmagma_virt/virtmagma_util.h"
static pthread_once_t gOnceFlag = PTHREAD_ONCE_INIT;
static int gDefaultFd;
// Most magma interfaces get their file descriptor from a wrapped parameter (device, connection,
// etc) (or initially from the file descriptor "handle" in magma_device_import), but some interfaces
// don't have any such parameter; for those, we open the default device, and never close it.
static int get_default_fd() {
pthread_once(&gOnceFlag, [] { gDefaultFd = open("/dev/magma0", O_RDWR); });
assert(gDefaultFd >= 0);
return gDefaultFd;
}
magma_status_t magma_poll(magma_poll_item_t* items, uint32_t count, uint64_t timeout_ns) {
#if VIRTMAGMA_DEBUG
printf("%s\n", __PRETTY_FUNCTION__);
#endif
if (count == 0)
return MAGMA_STATUS_OK;
magma_poll_item_t unwrapped_items[count];
int32_t file_descriptor = -1;
for (uint32_t i = 0; i < count; ++i) {
unwrapped_items[i] = items[i];
switch (items[i].type) {
case MAGMA_POLL_TYPE_SEMAPHORE: {
auto semaphore_wrapped = virtmagma_semaphore_t::Get(items[i].semaphore);
unwrapped_items[i].semaphore = semaphore_wrapped->Object();
if (file_descriptor < 0) {
auto semaphore0_parent_wrapped = virtmagma_connection_t::Get(semaphore_wrapped->Parent());
file_descriptor = semaphore0_parent_wrapped->Parent().fd();
}
break;
}
case MAGMA_POLL_TYPE_HANDLE: {
// Handles are not wrapped.
break;
}
}
}
// Ensure host compatibility with 32bit guest
static_assert(sizeof(magma_poll_item_t) % 8 == 0);
virtio_magma_poll_ctrl_t request{};
virtio_magma_poll_resp_t response{};
request.hdr.type = VIRTIO_MAGMA_CMD_POLL;
request.items = reinterpret_cast<uintptr_t>(&unwrapped_items[0]);
// Send byte count so kernel knows how much memory to copy
request.count = count * sizeof(magma_poll_item_t);
request.timeout_ns = timeout_ns;
if (file_descriptor == -1) {
file_descriptor = get_default_fd();
}
if (!virtmagma_send_command(file_descriptor, &request, sizeof(request), &response,
sizeof(response)))
return MAGMA_STATUS_INTERNAL_ERROR;
if (response.hdr.type != VIRTIO_MAGMA_RESP_POLL)
return MAGMA_STATUS_INTERNAL_ERROR;
magma_status_t result_return = static_cast<decltype(result_return)>(response.result_return);
if (result_return != MAGMA_STATUS_OK)
return result_return;
// Update the results
for (uint32_t i = 0; i < count; i++) {
items[i].result = unwrapped_items[i].result;
}
return MAGMA_STATUS_OK;
}
magma_status_t magma_connection_execute_command(magma_connection_t connection, uint32_t context_id,
struct magma_command_descriptor* descriptor) {
#if VIRTMAGMA_DEBUG
printf("%s\n", __PRETTY_FUNCTION__);
#endif
struct WireDescriptor {
uint32_t resource_count;
uint32_t command_buffer_count;
uint32_t wait_semaphore_count;
uint32_t signal_semaphore_count;
uint64_t flags;
};
WireDescriptor wire_descriptor = {.resource_count = descriptor->resource_count,
.command_buffer_count = descriptor->command_buffer_count,
.wait_semaphore_count = descriptor->wait_semaphore_count,
.signal_semaphore_count = descriptor->signal_semaphore_count,
.flags = descriptor->flags};
// Ensure host compatibility with 32bit guest
static_assert(sizeof(magma_exec_command_buffer) % 8 == 0);
static_assert(sizeof(magma_exec_resource) % 8 == 0);
virtmagma_command_descriptor vdesc = {
.descriptor_size = sizeof(wire_descriptor),
.descriptor = reinterpret_cast<uintptr_t>(&wire_descriptor),
.resource_size = sizeof(magma_exec_resource) * descriptor->resource_count,
.resources = reinterpret_cast<uintptr_t>(descriptor->resources),
.command_buffer_size = sizeof(magma_exec_command_buffer) * descriptor->command_buffer_count,
.command_buffers = reinterpret_cast<uintptr_t>(descriptor->command_buffers),
.semaphore_size = sizeof(uint64_t) *
(descriptor->wait_semaphore_count + descriptor->signal_semaphore_count),
.semaphores = reinterpret_cast<uintptr_t>(descriptor->semaphore_ids)};
virtio_magma_connection_execute_command_ctrl request{
.hdr = {.type = VIRTIO_MAGMA_CMD_CONNECTION_EXECUTE_COMMAND}};
virtio_magma_connection_execute_command_resp response{};
auto connection_wrapped = virtmagma_connection_t::Get(connection);
request.connection = reinterpret_cast<uint64_t>(connection_wrapped->Object());
request.context_id = context_id;
request.descriptor = reinterpret_cast<uintptr_t>(&vdesc);
int32_t file_descriptor = connection_wrapped->Parent().fd();
if (!virtmagma_send_command(file_descriptor, &request, sizeof(request), &response,
sizeof(response))) {
assert(false);
}
return static_cast<magma_status_t>(response.result_return);
}
magma_status_t magma_connection_execute_immediate_commands(
magma_connection_t connection, uint32_t context_id, uint64_t command_count,
magma_inline_command_buffer_t* command_buffers) {
#if VIRTMAGMA_DEBUG
printf("%s\n", __PRETTY_FUNCTION__);
#endif
virtmagma_command_descriptor commands[command_count];
for (uint64_t i = 0; i < command_count; i++) {
commands[i] = {
.descriptor_size = 0,
.descriptor = 0,
.resource_size = 0,
.resources = 0,
.command_buffer_size = command_buffers[i].size,
.command_buffers = reinterpret_cast<uintptr_t>(command_buffers[i].data),
.semaphore_size = sizeof(uint64_t) * command_buffers[i].semaphore_count,
.semaphores = reinterpret_cast<uintptr_t>(command_buffers[i].semaphore_ids),
};
}
virtio_magma_connection_execute_immediate_commands_ctrl request{
.hdr = {.type = VIRTIO_MAGMA_CMD_CONNECTION_EXECUTE_IMMEDIATE_COMMANDS}};
virtio_magma_connection_execute_immediate_commands_resp response{};
auto connection_wrapped = virtmagma_connection_t::Get(connection);
request.connection = reinterpret_cast<uint64_t>(connection_wrapped->Object());
request.context_id = context_id;
request.command_count = command_count;
request.command_buffers = reinterpret_cast<uintptr_t>(commands);
int32_t file_descriptor = connection_wrapped->Parent().fd();
if (!virtmagma_send_command(file_descriptor, &request, sizeof(request), &response,
sizeof(response))) {
assert(false);
}
return static_cast<magma_status_t>(response.result_return);
}
magma_status_t magma_buffer_get_info(magma_buffer_t buffer, magma_buffer_info_t* info_out) {
auto buffer_wrapped = virtmagma_buffer_t::Get(buffer);
virtio_magma_buffer_get_info_ctrl_t request{
.hdr = {.type = VIRTIO_MAGMA_CMD_BUFFER_GET_INFO},
.buffer = reinterpret_cast<uint64_t>(buffer_wrapped->Object()),
.info_out = reinterpret_cast<uintptr_t>(info_out),
};
virtio_magma_buffer_get_info_resp_t response{};
auto connection_wrapped = virtmagma_connection_t::Get(buffer_wrapped->Parent());
if (!virtmagma_send_command(connection_wrapped->Parent().fd(), &request, sizeof(request),
&response, sizeof(response))) {
assert(false);
return DRET(MAGMA_STATUS_INTERNAL_ERROR);
}
if (response.hdr.type != VIRTIO_MAGMA_RESP_BUFFER_GET_INFO)
return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR, "Wrong response header: %u", response.hdr.type);
return static_cast<magma_status_t>(response.result_return);
}
magma_status_t magma_buffer_set_name(magma_buffer_t buffer, const char* name) {
auto buffer_wrapped = virtmagma_buffer_t::Get(buffer);
struct virtmagma_buffer_set_name_wrapper wrapper = {
.name_address = reinterpret_cast<uintptr_t>(name),
.name_size = strlen(name) + 1, // include null terminate byte
};
virtio_magma_buffer_set_name_ctrl_t request{
.hdr = {.type = VIRTIO_MAGMA_CMD_BUFFER_SET_NAME},
.buffer = reinterpret_cast<uint64_t>(buffer_wrapped->Object()),
.name = reinterpret_cast<uintptr_t>(&wrapper),
};
virtio_magma_buffer_set_name_resp_t response{};
auto connection_wrapped = virtmagma_connection_t::Get(buffer_wrapped->Parent());
if (!virtmagma_send_command(connection_wrapped->Parent().fd(), &request, sizeof(request),
&response, sizeof(response))) {
assert(false);
return DRET(MAGMA_STATUS_INTERNAL_ERROR);
}
if (response.hdr.type != VIRTIO_MAGMA_RESP_BUFFER_SET_NAME)
return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR, "Wrong response header: %u", response.hdr.type);
return static_cast<magma_status_t>(response.result_return);
}
magma_status_t magma_initialize_tracing(magma_handle_t channel) {
int fd = channel;
close(fd);
return MAGMA_STATUS_OK;
}
magma_status_t magma_initialize_logging(magma_handle_t channel) {
int fd = channel;
close(fd);
return MAGMA_STATUS_OK;
}
magma_status_t magma_virt_connection_create_image(magma_connection_t connection,
magma_image_create_info_t* create_info,
uint64_t* size_out, magma_buffer_t* image_out,
magma_buffer_id_t* buffer_id_out) {
auto connection_wrapped = virtmagma_connection_t::Get(connection);
#if VIRTMAGMA_DEBUG
printf("%s\n", __PRETTY_FUNCTION__);
printf("connection %lu\n", reinterpret_cast<uint64_t>(connection_wrapped->Object()));
printf("create_info %p\n", create_info);
printf("image_out %p\n", image_out);
#endif
// Ensure host compatibility with 32bit guest
static_assert(sizeof(magma_image_create_info_t) % 8 == 0);
struct virtmagma_create_image_wrapper wrapper {
.create_info = reinterpret_cast<uintptr_t>(create_info),
.create_info_size = sizeof(magma_image_create_info_t),
};
virtio_magma_virt_connection_create_image_ctrl_t request{
.hdr = {.type = VIRTIO_MAGMA_CMD_VIRT_CONNECTION_CREATE_IMAGE},
.connection = reinterpret_cast<uint64_t>(connection_wrapped->Object()),
.create_info = reinterpret_cast<uintptr_t>(&wrapper),
};
virtio_magma_virt_connection_create_image_resp_t response{};
if (!virtmagma_send_command(connection_wrapped->Parent().fd(), &request, sizeof(request),
&response, sizeof(response))) {
assert(false);
return DRET(MAGMA_STATUS_INTERNAL_ERROR);
}
if (response.hdr.type != VIRTIO_MAGMA_RESP_VIRT_CONNECTION_CREATE_IMAGE)
return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR, "Wrong response header: %u", response.hdr.type);
magma_status_t result_return = static_cast<decltype(result_return)>(response.result_return);
if (result_return != MAGMA_STATUS_OK)
return DRET(result_return);
*image_out = virtmagma_buffer_t::Create(response.image_out, connection)->Wrap();
*size_out = response.size_out;
*buffer_id_out = response.buffer_id_out;
return MAGMA_STATUS_OK;
}
magma_status_t magma_virt_connection_get_image_info(magma_connection_t connection,
magma_buffer_t image,
magma_image_info_t* image_info_out) {
#if VIRTMAGMA_DEBUG
printf("%s\n", __PRETTY_FUNCTION__);
printf("image = %lu\n", image);
printf("image_info_out = %p\n", image_info_out);
#endif
auto connection_wrapped = virtmagma_connection_t::Get(connection);
auto image_wrapped = virtmagma_buffer_t::Get(image);
// Ensure host compatibility with 32bit guest
static_assert(sizeof(magma_image_info_t) % 8 == 0);
struct virtmagma_get_image_info_wrapper wrapper {
.image_info_out = reinterpret_cast<uintptr_t>(image_info_out),
.image_info_size = sizeof(magma_image_info_t),
};
virtio_magma_virt_connection_get_image_info_ctrl_t request{
.hdr =
{
.type = VIRTIO_MAGMA_CMD_VIRT_CONNECTION_GET_IMAGE_INFO,
},
.connection = reinterpret_cast<uint64_t>(connection_wrapped->Object()),
.image = image_wrapped->Object(),
.image_info_out = reinterpret_cast<uintptr_t>(&wrapper),
};
virtio_magma_virt_connection_get_image_info_resp_t response{};
if (!virtmagma_send_command(connection_wrapped->Parent().fd(), &request, sizeof(request),
&response, sizeof(response))) {
assert(false);
return DRET(MAGMA_STATUS_INTERNAL_ERROR);
}
if (response.hdr.type != VIRTIO_MAGMA_RESP_VIRT_CONNECTION_GET_IMAGE_INFO)
return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR, "Wrong response header: %u", response.hdr.type);
magma_status_t result_return = static_cast<decltype(result_return)>(response.result_return);
if (result_return != MAGMA_STATUS_OK)
return DRET(result_return);
return MAGMA_STATUS_OK;
}
magma_status_t magma_device_query(magma_device_t device, uint64_t id,
magma_handle_t* result_buffer_out, uint64_t* result_out) {
auto device_wrapped = virtmagma_device_t::Get(device);
device = device_wrapped->Object();
int32_t file_descriptor = device_wrapped->Parent().fd();
virtio_magma_device_query_ctrl_t request{
.hdr = {.type = VIRTIO_MAGMA_CMD_DEVICE_QUERY}, .device = device, .id = id};
virtio_magma_device_query_resp_t response{};
bool success = virtmagma_send_command(file_descriptor, &request, sizeof(request), &response,
sizeof(response));
if (!success)
return DRET(MAGMA_STATUS_INTERNAL_ERROR);
magma_status_t status = static_cast<magma_status_t>(response.result_return);
if (status != MAGMA_STATUS_OK)
return DRET(status);
if (response.hdr.type != VIRTIO_MAGMA_RESP_DEVICE_QUERY)
return DRET(MAGMA_STATUS_INTERNAL_ERROR);
int fd = static_cast<int>(response.result_buffer_out);
if (fd < 0) {
if (!result_out)
return DRET(MAGMA_STATUS_INVALID_ARGS);
*result_out = response.result_out;
if (result_buffer_out)
*result_buffer_out = -1;
return MAGMA_STATUS_OK;
}
// If a buffer is present, it's an error to ignore it.
if (!result_buffer_out) {
close(fd);
return DRET(MAGMA_STATUS_INVALID_ARGS);
}
*result_buffer_out = fd;
return MAGMA_STATUS_OK;
}