Kernel: Add support for reading from VirtIOConsole

This allows two-way communication with the host through a VirtIOConsole.
This is necessary for features like clipboard sharing.
This commit is contained in:
x-yl 2021-06-20 17:46:04 +04:00 committed by Ali Mohammad Pur
parent 1c06d77262
commit 1492bb2fd6
Notes: sideshowbarker 2024-07-18 10:02:01 +09:00
5 changed files with 74 additions and 8 deletions

View file

@ -53,9 +53,6 @@ set(KERNEL_SOURCES
Devices/RandomDevice.cpp
Devices/SB16.cpp
Devices/SerialDevice.cpp
VirtIO/VirtIO.cpp
VirtIO/VirtIOQueue.cpp
VirtIO/VirtIOConsole.cpp
Devices/VMWareBackdoor.cpp
Devices/ZeroDevice.cpp
Devices/HID/I8042Controller.cpp

View file

@ -30,6 +30,25 @@ bool RingBuffer::copy_data_in(const UserOrKernelBuffer& buffer, size_t offset, s
return false;
}
KResultOr<size_t> RingBuffer::copy_data_out(size_t size, UserOrKernelBuffer& buffer) const
{
auto start = m_start_of_used % m_capacity_in_bytes;
auto num_bytes = min(min(m_num_used_bytes, size), m_capacity_in_bytes - start);
if (!buffer.write(m_region->vaddr().offset(start).as_ptr(), num_bytes))
return EIO;
return num_bytes;
}
KResultOr<PhysicalAddress> RingBuffer::reserve_space(size_t size)
{
if (m_capacity_in_bytes < m_num_used_bytes + size)
return ENOSPC;
size_t start_of_free_area = (m_start_of_used + m_num_used_bytes) % m_capacity_in_bytes;
m_num_used_bytes += size;
PhysicalAddress start_of_reserved_space = m_region->physical_page(start_of_free_area / PAGE_SIZE)->paddr().offset(start_of_free_area % PAGE_SIZE);
return start_of_reserved_space;
}
void RingBuffer::reclaim_space(PhysicalAddress chunk_start, size_t chunk_size)
{
VERIFY(start_of_used() == chunk_start);

View file

@ -17,10 +17,14 @@ public:
bool has_space() const { return m_num_used_bytes < m_capacity_in_bytes; }
bool copy_data_in(const UserOrKernelBuffer& buffer, size_t offset, size_t length, PhysicalAddress& start_of_copied_data, size_t& bytes_copied);
KResultOr<size_t> copy_data_out(size_t size, UserOrKernelBuffer& buffer) const;
KResultOr<PhysicalAddress> reserve_space(size_t size);
void reclaim_space(PhysicalAddress chunk_start, size_t chunk_size);
PhysicalAddress start_of_used() const;
SpinLock<u8>& lock() { return m_lock; }
size_t used_bytes() const { return m_num_used_bytes; }
PhysicalAddress start_of_region() const { return m_region->physical_page(0)->paddr(); }
private:
OwnPtr<Region> m_region;

View file

@ -43,6 +43,8 @@ UNMAP_AFTER_INIT VirtIOConsole::VirtIOConsole(PCI::Address address)
finish_init();
m_receive_buffer = make<RingBuffer>("VirtIOConsole Receive", RINGBUFFER_SIZE);
m_transmit_buffer = make<RingBuffer>("VirtIOConsole Transmit", RINGBUFFER_SIZE);
init_receive_buffer();
}
}
}
@ -51,6 +53,18 @@ VirtIOConsole::~VirtIOConsole()
{
}
void VirtIOConsole::init_receive_buffer()
{
auto& queue = get_queue(RECEIVEQ);
ScopedSpinLock queue_lock(queue.lock());
VirtIOQueueChain chain(queue);
auto buffer_start = m_receive_buffer->start_of_region();
auto did_add_buffer = chain.add_buffer_to_chain(buffer_start, RINGBUFFER_SIZE, BufferType::DeviceWritable);
VERIFY(did_add_buffer);
supply_chain_and_notify(RECEIVEQ, chain);
}
bool VirtIOConsole::handle_device_config_change()
{
dbgln("VirtIOConsole: Handle device config change");
@ -63,8 +77,27 @@ void VirtIOConsole::handle_queue_update(u16 queue_index)
VERIFY(queue_index <= TRANSMITQ);
switch (queue_index) {
case RECEIVEQ: {
ScopedSpinLock lock(get_queue(RECEIVEQ).lock());
get_queue(RECEIVEQ).discard_used_buffers(); // TODO: do something with incoming data (users writing into qemu console) instead of just clearing
auto& queue = get_queue(RECEIVEQ);
ScopedSpinLock queue_lock(queue.lock());
size_t used;
VirtIOQueueChain popped_chain = queue.pop_used_buffer_chain(used);
ScopedSpinLock ringbuffer_lock(m_receive_buffer->lock());
auto used_space = m_receive_buffer->reserve_space(used).value();
auto remaining_space = RINGBUFFER_SIZE - used;
// Our algorithm always has only one buffer in the queue.
VERIFY(!queue.new_data_available());
popped_chain.release_buffer_slots_to_queue();
VirtIOQueueChain new_chain(queue);
if (remaining_space != 0) {
new_chain.add_buffer_to_chain(used_space.offset(used), remaining_space, BufferType::DeviceWritable);
supply_chain_and_notify(RECEIVEQ, new_chain);
}
evaluate_block_conditions();
break;
}
case TRANSMITQ: {
@ -91,12 +124,23 @@ void VirtIOConsole::handle_queue_update(u16 queue_index)
bool VirtIOConsole::can_read(const FileDescription&, size_t) const
{
return true;
return m_receive_buffer->used_bytes() > 0;
}
KResultOr<size_t> VirtIOConsole::read(FileDescription&, u64, [[maybe_unused]] UserOrKernelBuffer& data, size_t)
KResultOr<size_t> VirtIOConsole::read(FileDescription& desc, u64, UserOrKernelBuffer& buffer, size_t size)
{
return ENOTSUP;
if (!size)
return 0;
if (!can_read(desc, size))
return EAGAIN;
ScopedSpinLock ringbuffer_lock(m_receive_buffer->lock());
auto bytes_copied = m_receive_buffer->copy_data_out(size, buffer);
m_receive_buffer->reclaim_space(m_receive_buffer->start_of_used(), bytes_copied.value());
return bytes_copied;
}
bool VirtIOConsole::can_write(const FileDescription&, size_t) const

View file

@ -42,6 +42,8 @@ private:
virtual String device_name() const override { return String::formatted("hvc{}", minor()); }
virtual void handle_queue_update(u16 queue_index) override;
void init_receive_buffer();
OwnPtr<RingBuffer> m_receive_buffer;
OwnPtr<RingBuffer> m_transmit_buffer;