mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-12 05:41:36 +00:00
parent
f2188bd397
commit
85c8bd7d7e
@ -1,7 +1,11 @@
|
||||
base-hw
|
||||
base
|
||||
block_session
|
||||
gui_session
|
||||
framebuffer_session
|
||||
input_session
|
||||
nic_session
|
||||
os
|
||||
terminal_session
|
||||
timer_session
|
||||
blit
|
||||
|
@ -70,9 +70,10 @@ For each virtio_device node the following attributes need to be set:
|
||||
A unique name denoting the device.
|
||||
|
||||
:type:
|
||||
The Virtio type of device. One can decide in between "console", "net",
|
||||
The Virtio type of device. One can decide in between "console", "net", "gpu",
|
||||
and "block". The "console" type gets mapped to a Genode Terminal session,
|
||||
"net" is mapped to a Nic session, and "block" to a Block session.
|
||||
"net" is mapped to a Nic session, "gpu" is mapped to a Gui session,
|
||||
and "block" to a Block session.
|
||||
|
||||
|
||||
Additional devices
|
||||
|
@ -38,7 +38,7 @@ class Vmm::Config
|
||||
|
||||
struct Virtio_device : List_model<Virtio_device>::Element
|
||||
{
|
||||
enum Type { INVALID, CONSOLE, NET, BLOCK };
|
||||
enum Type { INVALID, CONSOLE, NET, BLOCK, GPU };
|
||||
|
||||
enum { MMIO_SIZE = 0x200 };
|
||||
|
||||
@ -94,6 +94,7 @@ class Vmm::Config
|
||||
if (type == "console") t = Virtio_device::CONSOLE;
|
||||
if (type == "net") t = Virtio_device::NET;
|
||||
if (type == "block") t = Virtio_device::BLOCK;
|
||||
if (type == "gpu") t = Virtio_device::GPU;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
TARGET = vmm
|
||||
REQUIRES = hw
|
||||
LIBS = base
|
||||
LIBS = base blit
|
||||
SRC_CC += address_space.cc
|
||||
SRC_CC += cpu_base.cc
|
||||
SRC_CC += config.cc
|
||||
@ -11,6 +11,7 @@ SRC_CC += main.cc
|
||||
SRC_CC += mmio.cc
|
||||
SRC_CC += pl011.cc
|
||||
SRC_CC += vm.cc
|
||||
SRC_CC += virtio_gpu.cc
|
||||
INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR)
|
||||
|
||||
vpath %.cc $(PRG_DIR)/../..
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <util/mmio.h>
|
||||
#include <util/reconstructible.h>
|
||||
|
||||
#include <cpu.h>
|
||||
#include <gic.h>
|
||||
#include <ram.h>
|
||||
#include <mmio.h>
|
||||
|
265
repos/os/src/server/vmm/virtio_gpu.cc
Normal file
265
repos/os/src/server/vmm/virtio_gpu.cc
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* \brief Virtio GPU implementation
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2021-02-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <blit/blit.h>
|
||||
#include <virtio_gpu.h>
|
||||
|
||||
|
||||
void Vmm::Virtio_gpu_queue::notify(Virtio_gpu_device & dev)
|
||||
{
|
||||
memory_barrier();
|
||||
bool inform = false;
|
||||
for (Ring_index avail_idx = _avail.current();
|
||||
_cur_idx != avail_idx; _cur_idx.inc()) {
|
||||
try {
|
||||
Index idx = _avail.get(_cur_idx);
|
||||
Virtio_gpu_control_request
|
||||
request(idx, _descriptors, _ram, dev);
|
||||
_used.add(_cur_idx.idx(), idx, request.size());
|
||||
} catch (Exception & e) {
|
||||
error(e);
|
||||
}
|
||||
inform = true;
|
||||
}
|
||||
|
||||
if (!inform)
|
||||
return;
|
||||
|
||||
_used.write<Used_queue::Idx>(_cur_idx.idx());
|
||||
memory_barrier();
|
||||
if (_avail.inject_irq()) dev.assert_irq();
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Virtio_gpu_control_request::_get_display_info()
|
||||
{
|
||||
Display_info_response dir { _desc_addr(1) };
|
||||
memset((void*)dir.base(), 0, Display_info_response::SIZE);
|
||||
dir.write<Control_header::Type>(Control_header::Type::OK_DISPLAY_INFO);
|
||||
|
||||
dir.write<Display_info_response::X>(0);
|
||||
dir.write<Display_info_response::Y>(0);
|
||||
dir.write<Display_info_response::Width>(_device._fb_mode.area.w());
|
||||
dir.write<Display_info_response::Height>(_device._fb_mode.area.h());
|
||||
dir.write<Display_info_response::Enabled>(1);
|
||||
dir.write<Display_info_response::Flags>(0);
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Virtio_gpu_control_request::_resource_create_2d()
|
||||
{
|
||||
Resource_create_2d c2d { _desc_addr(0) };
|
||||
Control_header response { _desc_addr(1) };
|
||||
|
||||
if (c2d.read<Resource_create_2d::Format>() !=
|
||||
Resource_create_2d::Format::B8G8R8X8) {
|
||||
warning("Unsupported pixel fomat (id=",
|
||||
c2d.read<Resource_create_2d::Format>(), ")!");
|
||||
response.write<Control_header::Type>(Control_header::Type::ERR_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
using Resource = Virtio_gpu_device::Resource;
|
||||
|
||||
try {
|
||||
new (_device._heap)
|
||||
Resource(_device,
|
||||
c2d.read<Resource_create_2d::Resource_id>(),
|
||||
c2d.read<Resource_create_2d::Width>(),
|
||||
c2d.read<Resource_create_2d::Height>());
|
||||
response.write<Control_header::Type>(Control_header::Type::OK_NO_DATA);
|
||||
} catch(...) {
|
||||
response.write<Control_header::Type>(Control_header::Type::ERR_OUT_OF_MEMORY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Virtio_gpu_control_request::_resource_delete()
|
||||
{
|
||||
using Resource = Virtio_gpu_device::Resource;
|
||||
using Scanout = Resource::Scanout;
|
||||
|
||||
Resource_unref rur { _desc_addr(0) };
|
||||
Control_header response { _desc_addr(1) };
|
||||
|
||||
response.write<Control_header::Type>(Control_header::Type::ERR_INVALID_RESOURCE_ID);
|
||||
uint32_t id = rur.read<Resource_unref::Resource_id>();
|
||||
|
||||
_device._resources.for_each([&] (Resource & res) {
|
||||
if (res.id != id)
|
||||
return;
|
||||
|
||||
res.scanouts.for_each([&] (Scanout & sc) {
|
||||
destroy(_device._heap, &sc); });
|
||||
destroy(_device._heap, &res);
|
||||
response.write<Control_header::Type>(Control_header::Type::OK_NO_DATA);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Virtio_gpu_control_request::_resource_attach_backing()
|
||||
{
|
||||
using Resource = Virtio_gpu_device::Resource;
|
||||
using Entry = Resource_attach_backing::Memory_entry;
|
||||
|
||||
Resource_attach_backing rab { _desc_addr(0) };
|
||||
addr_t entry_base { _desc_addr(1) };
|
||||
Control_header response { _desc_addr(2) };
|
||||
|
||||
response.write<Control_header::Type>(Control_header::Type::ERR_INVALID_RESOURCE_ID);
|
||||
uint32_t id = rab.read<Resource_attach_backing::Resource_id>();
|
||||
unsigned nr = rab.read<Resource_attach_backing::Nr_entries>();
|
||||
|
||||
_device._resources.for_each([&] (Resource & res) {
|
||||
if (res.id != id)
|
||||
return;
|
||||
|
||||
try {
|
||||
for (unsigned i = 0; i < nr; i++) {
|
||||
Entry entry(entry_base+i*Entry::SIZE);
|
||||
size_t sz = entry.read<Entry::Length>();
|
||||
addr_t off = _device._ram.local_address(entry.read<Entry::Address>(), sz)
|
||||
- _device._ram.local();
|
||||
res.attach(off, sz);
|
||||
}
|
||||
response.write<Control_header::Type>(Control_header::Type::OK_NO_DATA);
|
||||
} catch (Exception &) {
|
||||
response.write<Control_header::Type>(Control_header::Type::ERR_INVALID_PARAMETER);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Virtio_gpu_control_request::_set_scanout()
|
||||
{
|
||||
Set_scanout scr { _desc_addr(0) };
|
||||
Control_header response { _desc_addr(1) };
|
||||
|
||||
uint32_t id = scr.read<Set_scanout::Resource_id>();
|
||||
uint32_t sid = scr.read<Set_scanout::Scanout_id>();
|
||||
response.write<Control_header::Type>(id ? Control_header::Type::ERR_INVALID_RESOURCE_ID
|
||||
: Control_header::Type::OK_NO_DATA);
|
||||
|
||||
using Resource = Virtio_gpu_device::Resource;
|
||||
using Scanout = Resource::Scanout;
|
||||
_device._resources.for_each([&] (Resource & res) {
|
||||
if (!id || id == res.id)
|
||||
res.scanouts.for_each([&] (Scanout & sc) {
|
||||
if (sc.id == sid) destroy(_device._heap, &sc); });
|
||||
|
||||
if (res.id != id)
|
||||
return;
|
||||
|
||||
try {
|
||||
new (_device._heap) Scanout(res.scanouts, sid,
|
||||
scr.read<Set_scanout::X>(),
|
||||
scr.read<Set_scanout::Y>(),
|
||||
scr.read<Set_scanout::Width>(),
|
||||
scr.read<Set_scanout::Height>());
|
||||
response.write<Control_header::Type>(Control_header::Type::OK_NO_DATA);
|
||||
} catch(...) {
|
||||
response.write<Control_header::Type>(Control_header::Type::ERR_OUT_OF_MEMORY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Virtio_gpu_control_request::_resource_flush()
|
||||
{
|
||||
Resource_flush rf { _desc_addr(0) };
|
||||
Control_header response { _desc_addr(1) };
|
||||
|
||||
uint32_t id = rf.read<Resource_flush::Resource_id>();
|
||||
response.write<Control_header::Type>(Control_header::Type::ERR_INVALID_RESOURCE_ID);
|
||||
|
||||
using Resource = Virtio_gpu_device::Resource;
|
||||
_device._resources.for_each([&] (Resource & res) {
|
||||
if (res.id != id)
|
||||
return;
|
||||
|
||||
uint32_t x = rf.read<Resource_flush::X>();
|
||||
uint32_t y = rf.read<Resource_flush::Y>();
|
||||
uint32_t w = rf.read<Resource_flush::Width>();
|
||||
uint32_t h = rf.read<Resource_flush::Height>();
|
||||
|
||||
if (x > res.area.w() ||
|
||||
y > res.area.h() ||
|
||||
w > res.area.w() ||
|
||||
h > res.area.h() ||
|
||||
x + w > res.area.w() ||
|
||||
y + h > res.area.h()) {
|
||||
response.write<Control_header::Type>(Control_header::Type::ERR_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
enum { BYTES_PER_PIXEL = Virtio_gpu_device::BYTES_PER_PIXEL };
|
||||
response.write<Control_header::Type>(Control_header::Type::OK_NO_DATA);
|
||||
|
||||
void * src =
|
||||
(void*)((addr_t)res.dst_ds.local_addr<void>() +
|
||||
(res.area.w() * y + x) * BYTES_PER_PIXEL);
|
||||
void * dst =
|
||||
(void*)((addr_t)_device._fb_ds->local_addr<void>() +
|
||||
(_device._fb_mode.area.w() * y + x) * BYTES_PER_PIXEL);
|
||||
size_t line = res.area.w() * BYTES_PER_PIXEL;
|
||||
|
||||
blit(src, line, dst, line, w*BYTES_PER_PIXEL, h);
|
||||
|
||||
_device._gui.framebuffer()->refresh(x, y, w, h);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Virtio_gpu_control_request::_transfer_to_host_2d()
|
||||
{
|
||||
Transfer_to_host_2d tth { _desc_addr(0) };
|
||||
Control_header response { _desc_addr(1) };
|
||||
|
||||
uint32_t id = tth.read<Transfer_to_host_2d::Resource_id>();
|
||||
response.write<Control_header::Type>(Control_header::Type::ERR_INVALID_RESOURCE_ID);
|
||||
|
||||
enum { BYTES_PER_PIXEL = Virtio_gpu_device::BYTES_PER_PIXEL };
|
||||
|
||||
using Resource = Virtio_gpu_device::Resource;
|
||||
_device._resources.for_each([&] (Resource & res)
|
||||
{
|
||||
if (res.id != id)
|
||||
return;
|
||||
|
||||
uint32_t x = tth.read<Transfer_to_host_2d::X>();
|
||||
uint32_t y = tth.read<Transfer_to_host_2d::Y>();
|
||||
uint32_t w = tth.read<Transfer_to_host_2d::Width>();
|
||||
uint32_t h = tth.read<Transfer_to_host_2d::Height>();
|
||||
addr_t off = tth.read<Transfer_to_host_2d::Offset>();
|
||||
|
||||
if (x + w > res.area.w() || y + h > res.area.h()) {
|
||||
response.write<Control_header::Type>(Control_header::Type::ERR_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
void * src = (void*)((addr_t)res.src_ds.local_addr<void>() + off);
|
||||
void * dst = (void*)((addr_t)res.dst_ds.local_addr<void>() +
|
||||
(y * res.area.w() + x) * BYTES_PER_PIXEL);
|
||||
size_t line = res.area.w() * BYTES_PER_PIXEL;
|
||||
|
||||
blit(src, line, dst, line, w*BYTES_PER_PIXEL, h);
|
||||
|
||||
response.write<Control_header::Type>(Control_header::Type::OK_NO_DATA);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Virtio_gpu_control_request::_update_cursor() { }
|
||||
|
||||
|
||||
void Vmm::Virtio_gpu_control_request::_move_cursor() { }
|
469
repos/os/src/server/vmm/virtio_gpu.h
Normal file
469
repos/os/src/server/vmm/virtio_gpu.h
Normal file
@ -0,0 +1,469 @@
|
||||
/*
|
||||
* \brief Virtio GPU implementation
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2021-02-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIRTIO_GPU_H_
|
||||
#define _VIRTIO_GPU_H_
|
||||
|
||||
#include <base/attached_ram_dataspace.h>
|
||||
#include <rm_session/connection.h>
|
||||
#include <region_map/client.h>
|
||||
#include <base/registry.h>
|
||||
|
||||
#include <gui_session/connection.h>
|
||||
#include <virtio_device.h>
|
||||
|
||||
namespace Vmm {
|
||||
class Virtio_gpu_queue;
|
||||
class Virtio_gpu_control_request;
|
||||
class Virtio_gpu_device;
|
||||
using namespace Genode;
|
||||
}
|
||||
|
||||
|
||||
class Vmm::Virtio_gpu_queue : public Virtio_split_queue
|
||||
{
|
||||
private:
|
||||
|
||||
Ring_index _used_idx;
|
||||
|
||||
friend class Virtio_gpu_control_request;
|
||||
|
||||
public:
|
||||
|
||||
enum { CONTROL, CURSOR, QUEUE_COUNT };
|
||||
|
||||
using Virtio_split_queue::Virtio_split_queue;
|
||||
|
||||
void notify(Virtio_gpu_device &);
|
||||
};
|
||||
|
||||
|
||||
class Vmm::Virtio_gpu_control_request
|
||||
{
|
||||
private:
|
||||
|
||||
using Index = Virtio_gpu_queue::Descriptor_index;
|
||||
using Descriptor = Virtio_gpu_queue::Descriptor;
|
||||
using Descriptor_array = Virtio_gpu_queue::Descriptor_array;
|
||||
|
||||
struct Control_header : Mmio
|
||||
{
|
||||
enum { SIZE = 24 };
|
||||
|
||||
struct Type : Register<0, 32>
|
||||
{
|
||||
enum Commands {
|
||||
/* 2D commands */
|
||||
GET_DISPLAY_INFO = 0x0100,
|
||||
RESOURCE_CREATE_2D,
|
||||
RESOURCE_UNREF,
|
||||
SET_SCANOUT,
|
||||
RESOURCE_FLUSH,
|
||||
TRANSFER_TO_HOST_2D,
|
||||
RESOURCE_ATTACH_BACKING,
|
||||
RESOURCE_DETACH_BACKING,
|
||||
GET_CAPSET_INFO,
|
||||
GET_CAPSET,
|
||||
GET_EDID,
|
||||
|
||||
/* cursor commands */
|
||||
UPDATE_CURSOR = 0x0300,
|
||||
MOVE_CURSOR,
|
||||
};
|
||||
|
||||
enum Responses {
|
||||
OK_NO_DATA = 0x1100,
|
||||
OK_DISPLAY_INFO,
|
||||
OK_CAPSET_INFO,
|
||||
OK_CAPSET,
|
||||
OK_EDID,
|
||||
ERR_UNSPEC = 0x1200,
|
||||
ERR_OUT_OF_MEMORY,
|
||||
ERR_INVALID_SCANOUT_ID,
|
||||
ERR_INVALID_RESOURCE_ID,
|
||||
ERR_INVALID_CONTEXT_ID,
|
||||
ERR_INVALID_PARAMETER,
|
||||
};
|
||||
};
|
||||
struct Flags : Register<0x4, 32> {};
|
||||
struct Fence_id : Register<0x8, 64> {};
|
||||
struct Ctx_id : Register<0x10, 32> {};
|
||||
|
||||
using Mmio::Mmio;
|
||||
};
|
||||
|
||||
struct Display_info_response : Control_header
|
||||
{
|
||||
enum { SIZE = Control_header::SIZE + 24*16 };
|
||||
|
||||
struct X : Register<0x18, 32> {};
|
||||
struct Y : Register<0x1c, 32> {};
|
||||
struct Width : Register<0x20, 32> {};
|
||||
struct Height : Register<0x24, 32> {};
|
||||
struct Enabled : Register<0x28, 32> {};
|
||||
struct Flags : Register<0x2c, 32> {};
|
||||
|
||||
using Control_header::Control_header;
|
||||
};
|
||||
|
||||
struct Resource_create_2d : Control_header
|
||||
{
|
||||
enum { SIZE = Control_header::SIZE + 16 };
|
||||
|
||||
struct Resource_id : Register<0x18, 32> {};
|
||||
|
||||
struct Format : Register<0x1c, 32>
|
||||
{
|
||||
enum {
|
||||
B8G8R8A8 = 1,
|
||||
B8G8R8X8 = 2,
|
||||
A8R8G8B8 = 3,
|
||||
X8R8G8B8 = 4,
|
||||
R8G8B8A8 = 67,
|
||||
X8B8G8R8 = 68,
|
||||
A8B8G8R8 = 121,
|
||||
R8G8B8X8 = 134,
|
||||
};
|
||||
};
|
||||
|
||||
struct Width : Register<0x20, 32> {};
|
||||
struct Height : Register<0x24, 32> {};
|
||||
|
||||
using Control_header::Control_header;
|
||||
};
|
||||
|
||||
struct Resource_unref : Control_header
|
||||
{
|
||||
enum { SIZE = Control_header::SIZE + 8 };
|
||||
|
||||
struct Resource_id : Register<0x18, 32> {};
|
||||
|
||||
using Control_header::Control_header;
|
||||
};
|
||||
|
||||
struct Resource_attach_backing : Control_header
|
||||
{
|
||||
enum { SIZE = Control_header::SIZE + 8 };
|
||||
|
||||
struct Resource_id : Register<0x18, 32> {};
|
||||
struct Nr_entries : Register<0x1c, 32> {};
|
||||
|
||||
struct Memory_entry : Mmio
|
||||
{
|
||||
enum { SIZE = 16 };
|
||||
|
||||
struct Address : Register<0x0, 64> {};
|
||||
struct Length : Register<0x8, 32> {};
|
||||
|
||||
using Mmio::Mmio;
|
||||
};
|
||||
|
||||
using Control_header::Control_header;
|
||||
};
|
||||
|
||||
struct Set_scanout : Control_header
|
||||
{
|
||||
enum { SIZE = Control_header::SIZE + 24 };
|
||||
|
||||
struct X : Register<0x18, 32> {};
|
||||
struct Y : Register<0x1c, 32> {};
|
||||
struct Width : Register<0x20, 32> {};
|
||||
struct Height : Register<0x24, 32> {};
|
||||
struct Scanout_id : Register<0x28, 32> {};
|
||||
struct Resource_id : Register<0x2c, 32> {};
|
||||
|
||||
using Control_header::Control_header;
|
||||
};
|
||||
|
||||
struct Resource_flush : Control_header
|
||||
{
|
||||
enum { SIZE = Control_header::SIZE + 24 };
|
||||
|
||||
struct X : Register<0x18, 32> {};
|
||||
struct Y : Register<0x1c, 32> {};
|
||||
struct Width : Register<0x20, 32> {};
|
||||
struct Height : Register<0x24, 32> {};
|
||||
struct Resource_id : Register<0x28, 32> {};
|
||||
|
||||
using Control_header::Control_header;
|
||||
};
|
||||
|
||||
struct Transfer_to_host_2d :Control_header
|
||||
{
|
||||
enum { SIZE = Control_header::SIZE + 32 };
|
||||
|
||||
struct X : Register<0x18, 32> {};
|
||||
struct Y : Register<0x1c, 32> {};
|
||||
struct Width : Register<0x20, 32> {};
|
||||
struct Height : Register<0x24, 32> {};
|
||||
struct Offset : Register<0x28, 64> {};
|
||||
struct Resource_id : Register<0x30, 32> {};
|
||||
|
||||
using Control_header::Control_header;
|
||||
};
|
||||
|
||||
Descriptor_array & _array;
|
||||
Ram & _ram;
|
||||
Virtio_gpu_device & _device;
|
||||
Index _idx;
|
||||
|
||||
Index _next(Descriptor desc)
|
||||
{
|
||||
if (!Descriptor::Flags::Next::get(desc.flags()))
|
||||
throw Exception("Invalid request, no next descriptor");
|
||||
return desc.next();
|
||||
}
|
||||
|
||||
Descriptor _desc(unsigned i)
|
||||
{
|
||||
Index idx = _idx;
|
||||
for (; i; i--)
|
||||
idx = _next(_array.get(idx));
|
||||
return _array.get(idx);
|
||||
}
|
||||
|
||||
addr_t _desc_addr(unsigned i)
|
||||
{
|
||||
Descriptor d = _desc(i);
|
||||
return _ram.local_address(d.address(), d.length());
|
||||
}
|
||||
|
||||
Control_header _ctrl_hdr { _desc_addr(0) };
|
||||
|
||||
void _get_display_info();
|
||||
void _resource_create_2d();
|
||||
void _resource_delete();
|
||||
void _resource_attach_backing();
|
||||
void _set_scanout();
|
||||
void _resource_flush();
|
||||
void _transfer_to_host_2d();
|
||||
void _update_cursor();
|
||||
void _move_cursor();
|
||||
|
||||
public:
|
||||
|
||||
Virtio_gpu_control_request(Index id,
|
||||
Descriptor_array & array,
|
||||
Ram & ram,
|
||||
Virtio_gpu_device & device)
|
||||
: _array(array), _ram(ram), _device(device), _idx(id)
|
||||
{
|
||||
switch (_ctrl_hdr.read<Control_header::Type>()) {
|
||||
case Control_header::Type::GET_DISPLAY_INFO:
|
||||
_get_display_info();
|
||||
break;
|
||||
case Control_header::Type::RESOURCE_CREATE_2D:
|
||||
_resource_create_2d();
|
||||
break;
|
||||
case Control_header::Type::RESOURCE_UNREF:
|
||||
_resource_delete();
|
||||
break;
|
||||
case Control_header::Type::RESOURCE_ATTACH_BACKING:
|
||||
_resource_attach_backing();
|
||||
break;
|
||||
case Control_header::Type::SET_SCANOUT:
|
||||
_set_scanout();
|
||||
break;
|
||||
case Control_header::Type::RESOURCE_FLUSH:
|
||||
_resource_flush();
|
||||
break;
|
||||
case Control_header::Type::TRANSFER_TO_HOST_2D:
|
||||
_transfer_to_host_2d();
|
||||
break;
|
||||
case Control_header::Type::UPDATE_CURSOR:
|
||||
_update_cursor();
|
||||
break;
|
||||
case Control_header::Type::MOVE_CURSOR:
|
||||
_move_cursor();
|
||||
break;
|
||||
default:
|
||||
error("Unknown control request ",
|
||||
_ctrl_hdr.read<Control_header::Type>());
|
||||
};
|
||||
}
|
||||
|
||||
size_t size() { return Control_header::SIZE; }
|
||||
};
|
||||
|
||||
|
||||
class Vmm::Virtio_gpu_device : public Virtio_device<Virtio_gpu_queue, 2>
|
||||
{
|
||||
private:
|
||||
|
||||
friend class Virtio_gpu_control_request;
|
||||
|
||||
Env & _env;
|
||||
Heap & _heap;
|
||||
Attached_ram_dataspace & _ram_ds;
|
||||
Gui::Connection _gui { _env };
|
||||
Cpu::Signal_handler<Virtio_gpu_device> _handler;
|
||||
Constructible<Attached_dataspace> _fb_ds { };
|
||||
Framebuffer::Mode _fb_mode { _gui.mode() };
|
||||
Gui::Session::View_handle _view = _gui.create_view();
|
||||
bool _mode_changed { true };
|
||||
|
||||
using Area = Genode::Area<>;
|
||||
using Rect = Genode::Rect<>;
|
||||
|
||||
enum { BYTES_PER_PIXEL = 4 };
|
||||
|
||||
struct Resource : Registry<Resource>::Element
|
||||
{
|
||||
struct Scanout : Registry<Scanout>::Element, Rect
|
||||
{
|
||||
uint32_t id;
|
||||
|
||||
Scanout(Registry<Scanout> & registry,
|
||||
uint32_t id,
|
||||
uint32_t x, uint32_t y,
|
||||
uint32_t w, uint32_t h)
|
||||
:
|
||||
Registry<Scanout>::Element(registry, *this),
|
||||
Rect(Point((int)x,(int)y), Area((int)w,(int)h)) { }
|
||||
|
||||
using Rect::Rect;
|
||||
};
|
||||
|
||||
Virtio_gpu_device & device;
|
||||
uint32_t id;
|
||||
Area area;
|
||||
|
||||
size_t _size() const {
|
||||
return align_addr(area.w() * area.h() * BYTES_PER_PIXEL, 12); }
|
||||
|
||||
addr_t attach_off { 0UL };
|
||||
Rm_connection rm { device._env };
|
||||
Region_map_client region_map { rm.create(_size()) };
|
||||
Attached_dataspace src_ds { device._env.rm(),
|
||||
region_map.dataspace() };
|
||||
Attached_ram_dataspace dst_ds { device._env.ram(),
|
||||
device._env.rm(), _size() };
|
||||
Registry<Scanout> scanouts;
|
||||
|
||||
Resource(Virtio_gpu_device & dev,
|
||||
uint32_t id,
|
||||
uint32_t w,
|
||||
uint32_t h)
|
||||
:
|
||||
Registry<Resource>::Element(dev._resources, *this),
|
||||
device(dev),
|
||||
id(id),
|
||||
area((int)w, (int)h) {}
|
||||
|
||||
void attach(addr_t off, size_t sz)
|
||||
{
|
||||
if (attach_off + sz > _size())
|
||||
return;
|
||||
|
||||
retry<Out_of_ram>(
|
||||
[&] () {
|
||||
retry<Out_of_caps>(
|
||||
[&] {
|
||||
region_map.attach(device._ram_ds.cap(),
|
||||
sz, off, true, attach_off);
|
||||
attach_off += sz;
|
||||
},
|
||||
[&] { rm.upgrade_caps(2); });
|
||||
},
|
||||
[&] () { rm.upgrade_ram(8*1024); });
|
||||
}
|
||||
};
|
||||
|
||||
Registry<Resource> _resources {};
|
||||
|
||||
struct Configuration_area : Mmio_register
|
||||
{
|
||||
Virtio_gpu_device & dev;
|
||||
|
||||
enum {
|
||||
EVENTS_READ = 0,
|
||||
EVENTS_CLEAR = 4,
|
||||
SCANOUTS = 8 };
|
||||
|
||||
Register read(Address_range & range, Cpu&) override
|
||||
{
|
||||
if (range.start == EVENTS_READ && range.size == 4)
|
||||
return dev._mode_changed ? 1 : 0;
|
||||
|
||||
/* we support no multi-head, just return 1 */
|
||||
if (range.start == SCANOUTS && range.size == 4)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void write(Address_range & range, Cpu&, Register v) override
|
||||
{
|
||||
if (range.start == EVENTS_CLEAR && range.size == 4 && v == 1)
|
||||
dev._mode_changed = false;
|
||||
}
|
||||
|
||||
Configuration_area(Virtio_gpu_device & device)
|
||||
: Mmio_register("GPU config area", Mmio_register::RO, 0x100, 16),
|
||||
dev(device) { device.add(*this); }
|
||||
} _config_area{ *this };
|
||||
|
||||
void _mode_change()
|
||||
{
|
||||
Genode::Mutex::Guard guard(_mutex);
|
||||
|
||||
_fb_mode = _gui.mode();
|
||||
|
||||
_gui.buffer(_fb_mode, false);
|
||||
|
||||
if (_fb_mode.area.count() > 0)
|
||||
_fb_ds.construct(_env.rm(),
|
||||
_gui.framebuffer()->dataspace());
|
||||
|
||||
using Command = Gui::Session::Command;
|
||||
_gui.enqueue<Command::Geometry>(_view, Rect(Point(0, 0), _fb_mode.area));
|
||||
_gui.enqueue<Command::To_front>(_view, Gui::Session::View_handle());
|
||||
_gui.execute();
|
||||
|
||||
_mode_changed = true;
|
||||
}
|
||||
|
||||
void _notify(unsigned idx) override
|
||||
{
|
||||
if (idx < Virtio_gpu_queue::QUEUE_COUNT)
|
||||
_queue[idx]->notify(*this);
|
||||
}
|
||||
|
||||
enum Device_id { GPU = 16 };
|
||||
|
||||
public:
|
||||
|
||||
Virtio_gpu_device(const char * const name,
|
||||
const uint64_t addr,
|
||||
const uint64_t size,
|
||||
unsigned irq,
|
||||
Cpu & cpu,
|
||||
Mmio_bus & bus,
|
||||
Ram & ram,
|
||||
Env & env,
|
||||
Heap & heap,
|
||||
Attached_ram_dataspace & ram_ds)
|
||||
:
|
||||
Virtio_device<Virtio_gpu_queue, 2>(name, addr, size,
|
||||
irq, cpu, bus, ram, GPU),
|
||||
_env(env), _heap(heap), _ram_ds(ram_ds),
|
||||
_handler(cpu, env.ep(), *this, &Virtio_gpu_device::_mode_change)
|
||||
{
|
||||
_gui.mode_sigh(_handler);
|
||||
_mode_change();
|
||||
}
|
||||
|
||||
void assert_irq() { _assert_irq(); }
|
||||
};
|
||||
|
||||
#endif /* _VIRTIO_GPU_H_ */
|
@ -13,6 +13,10 @@
|
||||
|
||||
#include <fdt.h>
|
||||
#include <vm.h>
|
||||
#include <virtio_console.h>
|
||||
#include <virtio_net.h>
|
||||
#include <virtio_block.h>
|
||||
#include <virtio_gpu.h>
|
||||
|
||||
using Vmm::Vm;
|
||||
|
||||
@ -112,12 +116,18 @@ Vm::Vm(Genode::Env & env, Heap & heap, Config & config)
|
||||
Virtio_net(dev.name.string(), (uint64_t)dev.mmio_start,
|
||||
dev.mmio_size, dev.irq, boot_cpu(), _bus, _ram,
|
||||
env));
|
||||
return;
|
||||
return;
|
||||
case Config::Virtio_device::BLOCK:
|
||||
_device_list.insert(new (_heap)
|
||||
Virtio_block_device(dev.name.string(), (uint64_t)dev.mmio_start,
|
||||
dev.mmio_size, dev.irq, boot_cpu(),
|
||||
_bus, _ram, env, heap));
|
||||
return;
|
||||
case Config::Virtio_device::GPU:
|
||||
_device_list.insert(new (_heap)
|
||||
Virtio_gpu_device(dev.name.string(), (uint64_t)dev.mmio_start,
|
||||
dev.mmio_size, dev.irq, boot_cpu(),
|
||||
_bus, _ram, env, heap, _vm_ram));
|
||||
default:
|
||||
return;
|
||||
};
|
||||
|
@ -21,9 +21,7 @@
|
||||
#include <cpu.h>
|
||||
#include <gic.h>
|
||||
#include <pl011.h>
|
||||
#include <virtio_console.h>
|
||||
#include <virtio_net.h>
|
||||
#include <virtio_block.h>
|
||||
#include <virtio_device.h>
|
||||
|
||||
#include <base/attached_ram_dataspace.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
|
Loading…
x
Reference in New Issue
Block a user