Kernel: Implement a naive version of virtconsole by memcpying to physical page

This patch allocates a physical page for each of the virtqueues and
memcpys to it when receiving a buffer to get a physical, aligned
contiguous buffer as required by the virtio specification.

Co-authored-by: Sahan <sahan.h.fernando@gmail.com>
This commit is contained in:
Idan Horowitz 2021-04-15 19:22:02 +10:00 committed by Andreas Kling
parent 42b1eb5af1
commit ecfa7cb824
Notes: sideshowbarker 2024-07-18 19:31:31 +09:00
8 changed files with 35 additions and 18 deletions

View file

@ -220,6 +220,9 @@ set(KERNEL_SOURCES
TimerQueue.cpp
UBSanitizer.cpp
UserOrKernelBuffer.cpp
VirtIO/VirtIO.cpp
VirtIO/VirtIOConsole.cpp
VirtIO/VirtIOQueue.cpp
VM/AnonymousVMObject.cpp
VM/ContiguousVMObject.cpp
VM/InodeVMObject.cpp

View file

@ -217,6 +217,10 @@ bool VirtIODevice::accept_device_features(u64 device_features, u64 accepted_feat
accepted_features &= ~(VIRTIO_F_RING_PACKED);
}
if (is_feature_set(device_features, VIRTIO_F_IN_ORDER)) {
accepted_features |= VIRTIO_F_IN_ORDER;
}
dbgln_if(VIRTIO_DEBUG, "{}: Device features: {}", m_class_name, device_features);
dbgln_if(VIRTIO_DEBUG, "{}: Accepted features: {}", m_class_name, accepted_features);
@ -342,7 +346,7 @@ bool VirtIODevice::finish_init()
void VirtIODevice::supply_buffer_and_notify(u16 queue_index, const u8* buffer, u32 len, BufferType buffer_type)
{
VERIFY(queue_index < m_queue_count);
if (get_queue(queue_index).supply_buffer(buffer, len, buffer_type))
if (get_queue(queue_index).supply_buffer({}, buffer, len, buffer_type))
notify_queue(queue_index);
}
@ -356,7 +360,6 @@ u8 VirtIODevice::isr_status()
void VirtIODevice::handle_irq(const RegisterState&)
{
u8 isr_type = isr_status();
dbgln_if(VIRTIO_DEBUG, "{}: Handling interrupt with status: {}", m_class_name, isr_type);
if (isr_type & DEVICE_CONFIG_INTERRUPT) {
if (!handle_device_config_change()) {
set_status_bit(DEVICE_STATUS_FAILED);
@ -369,6 +372,8 @@ void VirtIODevice::handle_irq(const RegisterState&)
return;
}
}
if (isr_type & ~(QUEUE_INTERRUPT | DEVICE_CONFIG_INTERRUPT))
dbgln("{}: Handling interrupt with unknown type: {}", m_class_name, isr_type);
}
}

View file

@ -56,6 +56,7 @@ namespace Kernel {
#define VIRTIO_F_VERSION_1 ((u64)1 << 32)
#define VIRTIO_F_RING_PACKED ((u64)1 << 34)
#define VIRTIO_F_IN_ORDER ((u64)1 << 35)
#define VIRTIO_PCI_CAP_COMMON_CFG 1
#define VIRTIO_PCI_CAP_NOTIFY_CFG 2

View file

@ -61,9 +61,14 @@ VirtIOConsole::VirtIOConsole(PCI::Address address)
get_queue(RECEIVEQ).on_data_available = [&]() {
dbgln("VirtIOConsole: receive_queue on_data_available");
};
m_receive_region = MM.allocate_contiguous_kernel_region(PAGE_SIZE, "VirtIOConsole Receive", Region::Access::Read | Region::Access::Write);
if (m_receive_region) {
supply_buffer_and_notify(RECEIVEQ, m_receive_region->physical_page(0)->paddr().as_ptr(), m_receive_region->size(), BufferType::DeviceWritable);
}
get_queue(TRANSMITQ).on_data_available = [&]() {
dbgln("VirtIOConsole: send_queue on_data_available");
};
m_transmit_region = MM.allocate_contiguous_kernel_region(PAGE_SIZE, "VirtIOConsole Transmit", Region::Access::Read | Region::Access::Write);
dbgln("TODO: Populate receive queue with a receive buffer");
}
}
@ -101,17 +106,14 @@ KResultOr<size_t> VirtIOConsole::write(FileDescription&, u64, const UserOrKernel
{
if (!size)
return 0;
VERIFY(size <= PAGE_SIZE);
dbgln("VirtIOConsole: Write with size {}, kernel: {}", size, data.is_kernel_buffer());
if (!data.read(m_transmit_region->vaddr().as_ptr(), size)) {
return Kernel::KResult((ErrnoCode)-EFAULT);
}
supply_buffer_and_notify(TRANSMITQ, m_transmit_region->physical_page(0)->paddr().as_ptr(), size, BufferType::DeviceReadable);
ssize_t nread = data.read_buffered<256>(size, [&](const u8* bytes, size_t bytes_count) {
supply_buffer_and_notify(TRANSMITQ, bytes, bytes_count, BufferType::DeviceReadable);
return (ssize_t)bytes_count;
});
if (nread < 0)
return Kernel::KResult((ErrnoCode)-nread);
return (size_t)nread;
return size;
}
}

View file

@ -59,8 +59,8 @@ private:
virtual bool handle_device_config_change() override;
virtual String device_name() const override { return String::formatted("hvc{}", minor()); }
VirtIOQueue* m_receive_queue { nullptr };
VirtIOQueue* m_send_queue { nullptr };
OwnPtr<Region> m_receive_region;
OwnPtr<Region> m_transmit_region;
};
}

View file

@ -64,15 +64,15 @@ void VirtIOQueue::disable_interrupts()
m_driver->flags = 1;
}
bool VirtIOQueue::supply_buffer(const u8* buffer, u32 len, BufferType buffer_type)
bool VirtIOQueue::supply_buffer(Badge<VirtIODevice>, const u8* buffer, u32 length, BufferType buffer_type)
{
VERIFY(buffer && len > 0);
VERIFY(buffer && length > 0);
VERIFY(m_free_buffers > 0);
auto descriptor_index = m_free_head;
m_descriptors[descriptor_index].flags = static_cast<u16>(buffer_type);
m_descriptors[descriptor_index].address = reinterpret_cast<u64>(buffer);
m_descriptors[descriptor_index].length = len;
m_descriptors[descriptor_index].length = length;
m_free_buffers--;
m_free_head = (m_free_head + 1) % m_queue_size;

View file

@ -26,6 +26,7 @@
#pragma once
#include <AK/Badge.h>
#include <Kernel/SpinLock.h>
#include <Kernel/VM/MemoryManager.h>
@ -36,6 +37,8 @@ enum class BufferType {
DeviceWritable = 2
};
class VirtIODevice;
class VirtIOQueue {
public:
VirtIOQueue(u16 queue_size, u16 notify_offset);
@ -51,7 +54,7 @@ public:
PhysicalAddress driver_area() const { return to_physical(m_driver.ptr()); }
PhysicalAddress device_area() const { return to_physical(m_device.ptr()); }
bool supply_buffer(const u8* buffer, u32 len, BufferType);
bool supply_buffer(Badge<VirtIODevice>, const u8* buffer, u32 length, BufferType);
bool new_data_available() const;
bool handle_interrupt();

View file

@ -53,7 +53,10 @@ $SERENITY_EXTRA_QEMU_ARGS
-drive file=${SERENITY_DISK_IMAGE},format=raw,index=0,media=disk
-device ich9-ahci
-usb
-debugcon stdio
-device virtio-serial
-chardev stdio,id=stdout,mux=on
-device virtconsole,chardev=stdout
-device isa-debugcon,chardev=stdout
-soundhw pcspk
-device sb16
"