From 1492bb2fd6893e2a39d461c3c8a12ec6cf4e7ffd Mon Sep 17 00:00:00 2001 From: x-yl Date: Sun, 20 Jun 2021 17:46:04 +0400 Subject: [PATCH] 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. --- Kernel/CMakeLists.txt | 3 -- Kernel/VM/RingBuffer.cpp | 19 ++++++++++++ Kernel/VM/RingBuffer.h | 4 +++ Kernel/VirtIO/VirtIOConsole.cpp | 54 ++++++++++++++++++++++++++++++--- Kernel/VirtIO/VirtIOConsole.h | 2 ++ 5 files changed, 74 insertions(+), 8 deletions(-) diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index e41f83275f..b9565aa7ff 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -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 diff --git a/Kernel/VM/RingBuffer.cpp b/Kernel/VM/RingBuffer.cpp index e33f40271a..bfaa8da326 100644 --- a/Kernel/VM/RingBuffer.cpp +++ b/Kernel/VM/RingBuffer.cpp @@ -30,6 +30,25 @@ bool RingBuffer::copy_data_in(const UserOrKernelBuffer& buffer, size_t offset, s return false; } +KResultOr 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 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); diff --git a/Kernel/VM/RingBuffer.h b/Kernel/VM/RingBuffer.h index 53258b45cb..8363a43bec 100644 --- a/Kernel/VM/RingBuffer.h +++ b/Kernel/VM/RingBuffer.h @@ -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 copy_data_out(size_t size, UserOrKernelBuffer& buffer) const; + KResultOr reserve_space(size_t size); void reclaim_space(PhysicalAddress chunk_start, size_t chunk_size); PhysicalAddress start_of_used() const; SpinLock& 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 m_region; diff --git a/Kernel/VirtIO/VirtIOConsole.cpp b/Kernel/VirtIO/VirtIOConsole.cpp index 17ccbff8c5..b443d171dd 100644 --- a/Kernel/VirtIO/VirtIOConsole.cpp +++ b/Kernel/VirtIO/VirtIOConsole.cpp @@ -43,6 +43,8 @@ UNMAP_AFTER_INIT VirtIOConsole::VirtIOConsole(PCI::Address address) finish_init(); m_receive_buffer = make("VirtIOConsole Receive", RINGBUFFER_SIZE); m_transmit_buffer = make("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 VirtIOConsole::read(FileDescription&, u64, [[maybe_unused]] UserOrKernelBuffer& data, size_t) +KResultOr 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 diff --git a/Kernel/VirtIO/VirtIOConsole.h b/Kernel/VirtIO/VirtIOConsole.h index d71cfe5209..2514352826 100644 --- a/Kernel/VirtIO/VirtIOConsole.h +++ b/Kernel/VirtIO/VirtIOConsole.h @@ -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 m_receive_buffer; OwnPtr m_transmit_buffer;