18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: MIT 28c2ecf20Sopenharmony_ci/* Copyright (C) 2006-2017 Oracle Corporation */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/vbox_err.h> 58c2ecf20Sopenharmony_ci#include "vbox_drv.h" 68c2ecf20Sopenharmony_ci#include "vboxvideo_guest.h" 78c2ecf20Sopenharmony_ci#include "hgsmi_channels.h" 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * There is a hardware ring buffer in the graphics device video RAM, formerly 118c2ecf20Sopenharmony_ci * in the VBox VMMDev PCI memory space. 128c2ecf20Sopenharmony_ci * All graphics commands go there serialized by vbva_buffer_begin_update. 138c2ecf20Sopenharmony_ci * and vbva_buffer_end_update. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * free_offset is writing position. data_offset is reading position. 168c2ecf20Sopenharmony_ci * free_offset == data_offset means buffer is empty. 178c2ecf20Sopenharmony_ci * There must be always gap between data_offset and free_offset when data 188c2ecf20Sopenharmony_ci * are in the buffer. 198c2ecf20Sopenharmony_ci * Guest only changes free_offset, host changes data_offset. 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic u32 vbva_buffer_available(const struct vbva_buffer *vbva) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci s32 diff = vbva->data_offset - vbva->free_offset; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci return diff > 0 ? diff : vbva->data_len + diff; 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void vbva_buffer_place_data_at(struct vbva_buf_ctx *vbva_ctx, 308c2ecf20Sopenharmony_ci const void *p, u32 len, u32 offset) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct vbva_buffer *vbva = vbva_ctx->vbva; 338c2ecf20Sopenharmony_ci u32 bytes_till_boundary = vbva->data_len - offset; 348c2ecf20Sopenharmony_ci u8 *dst = &vbva->data[offset]; 358c2ecf20Sopenharmony_ci s32 diff = len - bytes_till_boundary; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (diff <= 0) { 388c2ecf20Sopenharmony_ci /* Chunk will not cross buffer boundary. */ 398c2ecf20Sopenharmony_ci memcpy(dst, p, len); 408c2ecf20Sopenharmony_ci } else { 418c2ecf20Sopenharmony_ci /* Chunk crosses buffer boundary. */ 428c2ecf20Sopenharmony_ci memcpy(dst, p, bytes_till_boundary); 438c2ecf20Sopenharmony_ci memcpy(&vbva->data[0], (u8 *)p + bytes_till_boundary, diff); 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic void vbva_buffer_flush(struct gen_pool *ctx) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct vbva_flush *p; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_VBVA, VBVA_FLUSH); 528c2ecf20Sopenharmony_ci if (!p) 538c2ecf20Sopenharmony_ci return; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci p->reserved = 0; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci hgsmi_buffer_submit(ctx, p); 588c2ecf20Sopenharmony_ci hgsmi_buffer_free(ctx, p); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cibool vbva_write(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx, 628c2ecf20Sopenharmony_ci const void *p, u32 len) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct vbva_record *record; 658c2ecf20Sopenharmony_ci struct vbva_buffer *vbva; 668c2ecf20Sopenharmony_ci u32 available; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci vbva = vbva_ctx->vbva; 698c2ecf20Sopenharmony_ci record = vbva_ctx->record; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (!vbva || vbva_ctx->buffer_overflow || 728c2ecf20Sopenharmony_ci !record || !(record->len_and_flags & VBVA_F_RECORD_PARTIAL)) 738c2ecf20Sopenharmony_ci return false; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci available = vbva_buffer_available(vbva); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci while (len > 0) { 788c2ecf20Sopenharmony_ci u32 chunk = len; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (chunk >= available) { 818c2ecf20Sopenharmony_ci vbva_buffer_flush(ctx); 828c2ecf20Sopenharmony_ci available = vbva_buffer_available(vbva); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (chunk >= available) { 868c2ecf20Sopenharmony_ci if (WARN_ON(available <= vbva->partial_write_tresh)) { 878c2ecf20Sopenharmony_ci vbva_ctx->buffer_overflow = true; 888c2ecf20Sopenharmony_ci return false; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci chunk = available - vbva->partial_write_tresh; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci vbva_buffer_place_data_at(vbva_ctx, p, chunk, 948c2ecf20Sopenharmony_ci vbva->free_offset); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci vbva->free_offset = (vbva->free_offset + chunk) % 978c2ecf20Sopenharmony_ci vbva->data_len; 988c2ecf20Sopenharmony_ci record->len_and_flags += chunk; 998c2ecf20Sopenharmony_ci available -= chunk; 1008c2ecf20Sopenharmony_ci len -= chunk; 1018c2ecf20Sopenharmony_ci p += chunk; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return true; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic bool vbva_inform_host(struct vbva_buf_ctx *vbva_ctx, 1088c2ecf20Sopenharmony_ci struct gen_pool *ctx, s32 screen, bool enable) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct vbva_enable_ex *p; 1118c2ecf20Sopenharmony_ci bool ret; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_VBVA, VBVA_ENABLE); 1148c2ecf20Sopenharmony_ci if (!p) 1158c2ecf20Sopenharmony_ci return false; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci p->base.flags = enable ? VBVA_F_ENABLE : VBVA_F_DISABLE; 1188c2ecf20Sopenharmony_ci p->base.offset = vbva_ctx->buffer_offset; 1198c2ecf20Sopenharmony_ci p->base.result = VERR_NOT_SUPPORTED; 1208c2ecf20Sopenharmony_ci if (screen >= 0) { 1218c2ecf20Sopenharmony_ci p->base.flags |= VBVA_F_EXTENDED | VBVA_F_ABSOFFSET; 1228c2ecf20Sopenharmony_ci p->screen_id = screen; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci hgsmi_buffer_submit(ctx, p); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (enable) 1288c2ecf20Sopenharmony_ci ret = p->base.result >= 0; 1298c2ecf20Sopenharmony_ci else 1308c2ecf20Sopenharmony_ci ret = true; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci hgsmi_buffer_free(ctx, p); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return ret; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cibool vbva_enable(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx, 1388c2ecf20Sopenharmony_ci struct vbva_buffer *vbva, s32 screen) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci bool ret = false; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci memset(vbva, 0, sizeof(*vbva)); 1438c2ecf20Sopenharmony_ci vbva->partial_write_tresh = 256; 1448c2ecf20Sopenharmony_ci vbva->data_len = vbva_ctx->buffer_length - sizeof(struct vbva_buffer); 1458c2ecf20Sopenharmony_ci vbva_ctx->vbva = vbva; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ret = vbva_inform_host(vbva_ctx, ctx, screen, true); 1488c2ecf20Sopenharmony_ci if (!ret) 1498c2ecf20Sopenharmony_ci vbva_disable(vbva_ctx, ctx, screen); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_civoid vbva_disable(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx, 1558c2ecf20Sopenharmony_ci s32 screen) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci vbva_ctx->buffer_overflow = false; 1588c2ecf20Sopenharmony_ci vbva_ctx->record = NULL; 1598c2ecf20Sopenharmony_ci vbva_ctx->vbva = NULL; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci vbva_inform_host(vbva_ctx, ctx, screen, false); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cibool vbva_buffer_begin_update(struct vbva_buf_ctx *vbva_ctx, 1658c2ecf20Sopenharmony_ci struct gen_pool *ctx) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct vbva_record *record; 1688c2ecf20Sopenharmony_ci u32 next; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (!vbva_ctx->vbva || 1718c2ecf20Sopenharmony_ci !(vbva_ctx->vbva->host_flags.host_events & VBVA_F_MODE_ENABLED)) 1728c2ecf20Sopenharmony_ci return false; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci WARN_ON(vbva_ctx->buffer_overflow || vbva_ctx->record); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci next = (vbva_ctx->vbva->record_free_index + 1) % VBVA_MAX_RECORDS; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Flush if all slots in the records queue are used */ 1798c2ecf20Sopenharmony_ci if (next == vbva_ctx->vbva->record_first_index) 1808c2ecf20Sopenharmony_ci vbva_buffer_flush(ctx); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* If even after flush there is no place then fail the request */ 1838c2ecf20Sopenharmony_ci if (next == vbva_ctx->vbva->record_first_index) 1848c2ecf20Sopenharmony_ci return false; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci record = &vbva_ctx->vbva->records[vbva_ctx->vbva->record_free_index]; 1878c2ecf20Sopenharmony_ci record->len_and_flags = VBVA_F_RECORD_PARTIAL; 1888c2ecf20Sopenharmony_ci vbva_ctx->vbva->record_free_index = next; 1898c2ecf20Sopenharmony_ci /* Remember which record we are using. */ 1908c2ecf20Sopenharmony_ci vbva_ctx->record = record; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return true; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_civoid vbva_buffer_end_update(struct vbva_buf_ctx *vbva_ctx) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct vbva_record *record = vbva_ctx->record; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci WARN_ON(!vbva_ctx->vbva || !record || 2008c2ecf20Sopenharmony_ci !(record->len_and_flags & VBVA_F_RECORD_PARTIAL)); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Mark the record completed. */ 2038c2ecf20Sopenharmony_ci record->len_and_flags &= ~VBVA_F_RECORD_PARTIAL; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci vbva_ctx->buffer_overflow = false; 2068c2ecf20Sopenharmony_ci vbva_ctx->record = NULL; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_civoid vbva_setup_buffer_context(struct vbva_buf_ctx *vbva_ctx, 2108c2ecf20Sopenharmony_ci u32 buffer_offset, u32 buffer_length) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci vbva_ctx->buffer_offset = buffer_offset; 2138c2ecf20Sopenharmony_ci vbva_ctx->buffer_length = buffer_length; 2148c2ecf20Sopenharmony_ci} 215