/* * Copyright 2020 Google LLC * SPDX-License-Identifier: MIT */ #include "vkr_transport.h" #include "venus-protocol/vn_protocol_renderer_dispatches.h" #include "venus-protocol/vn_protocol_renderer_transport.h" #include "vrend_iov.h" #include "vkr_context.h" #include "vkr_ring.h" static void vkr_dispatch_vkSetReplyCommandStreamMESA( struct vn_dispatch_context *dispatch, struct vn_command_vkSetReplyCommandStreamMESA *args) { struct vkr_context *ctx = dispatch->data; struct vkr_resource_attachment *att; if (!args->pStream) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } att = util_hash_table_get(ctx->resource_table, uintptr_to_pointer(args->pStream->resourceId)); if (!att) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } vkr_cs_encoder_set_stream(&ctx->encoder, att->resource->iov, att->resource->iov_count, args->pStream->offset, args->pStream->size); } static void vkr_dispatch_vkSeekReplyCommandStreamMESA( struct vn_dispatch_context *dispatch, struct vn_command_vkSeekReplyCommandStreamMESA *args) { struct vkr_context *ctx = dispatch->data; vkr_cs_encoder_seek_stream(&ctx->encoder, args->position); } static void * copy_command_stream(struct vkr_context *ctx, const VkCommandStreamDescriptionMESA *stream) { struct vkr_resource_attachment *att; struct virgl_resource *res; att = util_hash_table_get(ctx->resource_table, uintptr_to_pointer(stream->resourceId)); if (!att) return NULL; res = att->resource; /* seek to offset */ size_t iov_offset = stream->offset; const struct iovec *iov = NULL; for (int i = 0; i < res->iov_count; i++) { if (iov_offset < res->iov[i].iov_len) { iov = &res->iov[i]; break; } iov_offset -= res->iov[i].iov_len; } if (!iov) return NULL; /* XXX until the decoder supports scatter-gather and is robust enough, * always make a copy in case the caller modifies the commands while we * parse */ uint8_t *data = malloc(stream->size); if (!data) return NULL; uint32_t copied = 0; while (true) { const size_t s = MIN2(stream->size - copied, iov->iov_len - iov_offset); memcpy(data + copied, (const uint8_t *)iov->iov_base + iov_offset, s); copied += s; if (copied == stream->size) { break; } else if (iov == &res->iov[res->iov_count - 1]) { free(data); return NULL; } iov++; iov_offset = 0; } return data; } static void vkr_dispatch_vkExecuteCommandStreamsMESA( struct vn_dispatch_context *dispatch, struct vn_command_vkExecuteCommandStreamsMESA *args) { struct vkr_context *ctx = dispatch->data; if (!args->streamCount || !args->pStreams) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } /* note that nested vkExecuteCommandStreamsMESA is not allowed */ if (!vkr_cs_decoder_push_state(&ctx->decoder)) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } for (uint32_t i = 0; i < args->streamCount; i++) { const VkCommandStreamDescriptionMESA *stream = &args->pStreams[i]; if (args->pReplyPositions) vkr_cs_encoder_seek_stream(&ctx->encoder, args->pReplyPositions[i]); if (!stream->size) continue; void *data = copy_command_stream(ctx, stream); if (!data) { vkr_cs_decoder_set_fatal(&ctx->decoder); break; } vkr_cs_decoder_set_stream(&ctx->decoder, data, stream->size); while (vkr_cs_decoder_has_command(&ctx->decoder)) { vn_dispatch_command(&ctx->dispatch); if (vkr_cs_decoder_get_fatal(&ctx->decoder)) break; } free(data); if (vkr_cs_decoder_get_fatal(&ctx->decoder)) break; } vkr_cs_decoder_pop_state(&ctx->decoder); } static struct vkr_ring * lookup_ring(struct vkr_context *ctx, uint64_t ring_id) { struct vkr_ring *ring; LIST_FOR_EACH_ENTRY (ring, &ctx->rings, head) { if (ring->id == ring_id) return ring; } return NULL; } static void vkr_dispatch_vkCreateRingMESA(struct vn_dispatch_context *dispatch, struct vn_command_vkCreateRingMESA *args) { struct vkr_context *ctx = dispatch->data; const VkRingCreateInfoMESA *info = args->pCreateInfo; const struct vkr_resource_attachment *att; uint8_t *shared; size_t size; struct vkr_ring *ring; if (!info) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } att = util_hash_table_get(ctx->resource_table, uintptr_to_pointer(info->resourceId)); if (!att) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } /* TODO support scatter-gather or require logically contiguous resources */ if (att->resource->iov_count != 1) { vkr_log("no scatter-gather support for ring buffers (TODO)"); vkr_cs_decoder_set_fatal(&ctx->decoder); return; } shared = att->resource->iov[0].iov_base; size = att->resource->iov[0].iov_len; if (info->offset > size || info->size > size - info->offset) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } shared += info->offset; size = info->size; if (info->headOffset > size || info->tailOffset > size || info->statusOffset > size || info->bufferOffset > size || info->extraOffset > size) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } if (sizeof(uint32_t) > size - info->headOffset || sizeof(uint32_t) > size - info->tailOffset || sizeof(uint32_t) > size - info->statusOffset || info->bufferSize > size - info->bufferOffset || info->extraSize > size - info->extraOffset) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } if (!info->bufferSize || !util_is_power_of_two(info->bufferSize)) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } const struct vkr_ring_layout layout = { .head_offset = info->headOffset, .tail_offset = info->tailOffset, .status_offset = info->statusOffset, .buffer_offset = info->bufferOffset, .buffer_size = info->bufferSize, .extra_offset = info->extraOffset, .extra_size = info->extraSize, }; ring = vkr_ring_create(&layout, shared, &ctx->base, info->idleTimeout); if (!ring) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } ring->id = args->ring; list_addtail(&ring->head, &ctx->rings); vkr_ring_start(ring); } static void vkr_dispatch_vkDestroyRingMESA(struct vn_dispatch_context *dispatch, struct vn_command_vkDestroyRingMESA *args) { struct vkr_context *ctx = dispatch->data; struct vkr_ring *ring = lookup_ring(ctx, args->ring); if (!ring || !vkr_ring_stop(ring)) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } list_del(&ring->head); vkr_ring_destroy(ring); } static void vkr_dispatch_vkNotifyRingMESA(struct vn_dispatch_context *dispatch, struct vn_command_vkNotifyRingMESA *args) { struct vkr_context *ctx = dispatch->data; struct vkr_ring *ring = lookup_ring(ctx, args->ring); if (!ring) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } vkr_ring_notify(ring); } static void vkr_dispatch_vkWriteRingExtraMESA(struct vn_dispatch_context *dispatch, struct vn_command_vkWriteRingExtraMESA *args) { struct vkr_context *ctx = dispatch->data; struct vkr_ring *ring = lookup_ring(ctx, args->ring); if (!ring) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } if (!vkr_ring_write_extra(ring, args->offset, args->value)) vkr_cs_decoder_set_fatal(&ctx->decoder); } static void vkr_dispatch_vkGetVenusExperimentalFeatureData100000MESA( struct vn_dispatch_context *dispatch, struct vn_command_vkGetVenusExperimentalFeatureData100000MESA *args) { struct vkr_context *ctx = dispatch->data; if (!args->pDataSize) { vkr_cs_decoder_set_fatal(&ctx->decoder); return; } const VkVenusExperimentalFeatures100000MESA features = { .memoryResourceAllocationSize = VK_TRUE, }; vn_replace_vkGetVenusExperimentalFeatureData100000MESA_args_handle(args); if (!args->pData) { *args->pDataSize = sizeof(features); return; } *args->pDataSize = MIN2(*args->pDataSize, sizeof(features)); memcpy(args->pData, &features, *args->pDataSize); } void vkr_context_init_transport_dispatch(struct vkr_context *ctx) { struct vn_dispatch_context *dispatch = &ctx->dispatch; dispatch->dispatch_vkSetReplyCommandStreamMESA = vkr_dispatch_vkSetReplyCommandStreamMESA; dispatch->dispatch_vkSeekReplyCommandStreamMESA = vkr_dispatch_vkSeekReplyCommandStreamMESA; dispatch->dispatch_vkExecuteCommandStreamsMESA = vkr_dispatch_vkExecuteCommandStreamsMESA; dispatch->dispatch_vkCreateRingMESA = vkr_dispatch_vkCreateRingMESA; dispatch->dispatch_vkDestroyRingMESA = vkr_dispatch_vkDestroyRingMESA; dispatch->dispatch_vkNotifyRingMESA = vkr_dispatch_vkNotifyRingMESA; dispatch->dispatch_vkWriteRingExtraMESA = vkr_dispatch_vkWriteRingExtraMESA; dispatch->dispatch_vkGetVenusExperimentalFeatureData100000MESA = vkr_dispatch_vkGetVenusExperimentalFeatureData100000MESA; }