162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT 262306a36Sopenharmony_ci/* Copyright (C) 2006-2017 Oracle Corporation */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/vbox_err.h> 562306a36Sopenharmony_ci#include "vbox_drv.h" 662306a36Sopenharmony_ci#include "vboxvideo_guest.h" 762306a36Sopenharmony_ci#include "hgsmi_channels.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * There is a hardware ring buffer in the graphics device video RAM, formerly 1162306a36Sopenharmony_ci * in the VBox VMMDev PCI memory space. 1262306a36Sopenharmony_ci * All graphics commands go there serialized by vbva_buffer_begin_update. 1362306a36Sopenharmony_ci * and vbva_buffer_end_update. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * free_offset is writing position. data_offset is reading position. 1662306a36Sopenharmony_ci * free_offset == data_offset means buffer is empty. 1762306a36Sopenharmony_ci * There must be always gap between data_offset and free_offset when data 1862306a36Sopenharmony_ci * are in the buffer. 1962306a36Sopenharmony_ci * Guest only changes free_offset, host changes data_offset. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic u32 vbva_buffer_available(const struct vbva_buffer *vbva) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci s32 diff = vbva->data_offset - vbva->free_offset; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci return diff > 0 ? diff : vbva->data_len + diff; 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic void vbva_buffer_place_data_at(struct vbva_buf_ctx *vbva_ctx, 3062306a36Sopenharmony_ci const void *p, u32 len, u32 offset) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct vbva_buffer *vbva = vbva_ctx->vbva; 3362306a36Sopenharmony_ci u32 bytes_till_boundary = vbva->data_len - offset; 3462306a36Sopenharmony_ci u8 *dst = &vbva->data[offset]; 3562306a36Sopenharmony_ci s32 diff = len - bytes_till_boundary; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (diff <= 0) { 3862306a36Sopenharmony_ci /* Chunk will not cross buffer boundary. */ 3962306a36Sopenharmony_ci memcpy(dst, p, len); 4062306a36Sopenharmony_ci } else { 4162306a36Sopenharmony_ci /* Chunk crosses buffer boundary. */ 4262306a36Sopenharmony_ci memcpy(dst, p, bytes_till_boundary); 4362306a36Sopenharmony_ci memcpy(&vbva->data[0], (u8 *)p + bytes_till_boundary, diff); 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void vbva_buffer_flush(struct gen_pool *ctx) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct vbva_flush *p; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_VBVA, VBVA_FLUSH); 5262306a36Sopenharmony_ci if (!p) 5362306a36Sopenharmony_ci return; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci p->reserved = 0; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci hgsmi_buffer_submit(ctx, p); 5862306a36Sopenharmony_ci hgsmi_buffer_free(ctx, p); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cibool vbva_write(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx, 6262306a36Sopenharmony_ci const void *p, u32 len) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct vbva_record *record; 6562306a36Sopenharmony_ci struct vbva_buffer *vbva; 6662306a36Sopenharmony_ci u32 available; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci vbva = vbva_ctx->vbva; 6962306a36Sopenharmony_ci record = vbva_ctx->record; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (!vbva || vbva_ctx->buffer_overflow || 7262306a36Sopenharmony_ci !record || !(record->len_and_flags & VBVA_F_RECORD_PARTIAL)) 7362306a36Sopenharmony_ci return false; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci available = vbva_buffer_available(vbva); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci while (len > 0) { 7862306a36Sopenharmony_ci u32 chunk = len; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (chunk >= available) { 8162306a36Sopenharmony_ci vbva_buffer_flush(ctx); 8262306a36Sopenharmony_ci available = vbva_buffer_available(vbva); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (chunk >= available) { 8662306a36Sopenharmony_ci if (WARN_ON(available <= vbva->partial_write_tresh)) { 8762306a36Sopenharmony_ci vbva_ctx->buffer_overflow = true; 8862306a36Sopenharmony_ci return false; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci chunk = available - vbva->partial_write_tresh; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci vbva_buffer_place_data_at(vbva_ctx, p, chunk, 9462306a36Sopenharmony_ci vbva->free_offset); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci vbva->free_offset = (vbva->free_offset + chunk) % 9762306a36Sopenharmony_ci vbva->data_len; 9862306a36Sopenharmony_ci record->len_and_flags += chunk; 9962306a36Sopenharmony_ci available -= chunk; 10062306a36Sopenharmony_ci len -= chunk; 10162306a36Sopenharmony_ci p += chunk; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return true; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic bool vbva_inform_host(struct vbva_buf_ctx *vbva_ctx, 10862306a36Sopenharmony_ci struct gen_pool *ctx, s32 screen, bool enable) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct vbva_enable_ex *p; 11162306a36Sopenharmony_ci bool ret; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_VBVA, VBVA_ENABLE); 11462306a36Sopenharmony_ci if (!p) 11562306a36Sopenharmony_ci return false; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci p->base.flags = enable ? VBVA_F_ENABLE : VBVA_F_DISABLE; 11862306a36Sopenharmony_ci p->base.offset = vbva_ctx->buffer_offset; 11962306a36Sopenharmony_ci p->base.result = VERR_NOT_SUPPORTED; 12062306a36Sopenharmony_ci if (screen >= 0) { 12162306a36Sopenharmony_ci p->base.flags |= VBVA_F_EXTENDED | VBVA_F_ABSOFFSET; 12262306a36Sopenharmony_ci p->screen_id = screen; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci hgsmi_buffer_submit(ctx, p); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (enable) 12862306a36Sopenharmony_ci ret = p->base.result >= 0; 12962306a36Sopenharmony_ci else 13062306a36Sopenharmony_ci ret = true; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci hgsmi_buffer_free(ctx, p); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return ret; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cibool vbva_enable(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx, 13862306a36Sopenharmony_ci struct vbva_buffer *vbva, s32 screen) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci bool ret = false; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci memset(vbva, 0, sizeof(*vbva)); 14362306a36Sopenharmony_ci vbva->partial_write_tresh = 256; 14462306a36Sopenharmony_ci vbva->data_len = vbva_ctx->buffer_length - sizeof(struct vbva_buffer); 14562306a36Sopenharmony_ci vbva_ctx->vbva = vbva; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ret = vbva_inform_host(vbva_ctx, ctx, screen, true); 14862306a36Sopenharmony_ci if (!ret) 14962306a36Sopenharmony_ci vbva_disable(vbva_ctx, ctx, screen); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_civoid vbva_disable(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx, 15562306a36Sopenharmony_ci s32 screen) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci vbva_ctx->buffer_overflow = false; 15862306a36Sopenharmony_ci vbva_ctx->record = NULL; 15962306a36Sopenharmony_ci vbva_ctx->vbva = NULL; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci vbva_inform_host(vbva_ctx, ctx, screen, false); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cibool vbva_buffer_begin_update(struct vbva_buf_ctx *vbva_ctx, 16562306a36Sopenharmony_ci struct gen_pool *ctx) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct vbva_record *record; 16862306a36Sopenharmony_ci u32 next; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (!vbva_ctx->vbva || 17162306a36Sopenharmony_ci !(vbva_ctx->vbva->host_flags.host_events & VBVA_F_MODE_ENABLED)) 17262306a36Sopenharmony_ci return false; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci WARN_ON(vbva_ctx->buffer_overflow || vbva_ctx->record); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci next = (vbva_ctx->vbva->record_free_index + 1) % VBVA_MAX_RECORDS; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* Flush if all slots in the records queue are used */ 17962306a36Sopenharmony_ci if (next == vbva_ctx->vbva->record_first_index) 18062306a36Sopenharmony_ci vbva_buffer_flush(ctx); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* If even after flush there is no place then fail the request */ 18362306a36Sopenharmony_ci if (next == vbva_ctx->vbva->record_first_index) 18462306a36Sopenharmony_ci return false; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci record = &vbva_ctx->vbva->records[vbva_ctx->vbva->record_free_index]; 18762306a36Sopenharmony_ci record->len_and_flags = VBVA_F_RECORD_PARTIAL; 18862306a36Sopenharmony_ci vbva_ctx->vbva->record_free_index = next; 18962306a36Sopenharmony_ci /* Remember which record we are using. */ 19062306a36Sopenharmony_ci vbva_ctx->record = record; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return true; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_civoid vbva_buffer_end_update(struct vbva_buf_ctx *vbva_ctx) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct vbva_record *record = vbva_ctx->record; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci WARN_ON(!vbva_ctx->vbva || !record || 20062306a36Sopenharmony_ci !(record->len_and_flags & VBVA_F_RECORD_PARTIAL)); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Mark the record completed. */ 20362306a36Sopenharmony_ci record->len_and_flags &= ~VBVA_F_RECORD_PARTIAL; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci vbva_ctx->buffer_overflow = false; 20662306a36Sopenharmony_ci vbva_ctx->record = NULL; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_civoid vbva_setup_buffer_context(struct vbva_buf_ctx *vbva_ctx, 21062306a36Sopenharmony_ci u32 buffer_offset, u32 buffer_length) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci vbva_ctx->buffer_offset = buffer_offset; 21362306a36Sopenharmony_ci vbva_ctx->buffer_length = buffer_length; 21462306a36Sopenharmony_ci} 215