diff options
author | x-yl <kylepereira@mail.com> | 2021-06-23 15:19:24 +0400 |
---|---|---|
committer | Ali Mohammad Pur <Ali.mpfard@gmail.com> | 2021-07-09 13:19:21 +0430 |
commit | 1fe08759e3abe40b4a91e244db9d5affb59e3e9f (patch) | |
tree | 3e5ea8ee9b38626914eabc98ccc4377da7d34daa /Kernel/VirtIO | |
parent | 1492bb2fd6893e2a39d461c3c8a12ec6cf4e7ffd (diff) | |
download | serenity-1fe08759e3abe40b4a91e244db9d5affb59e3e9f.zip |
Kernel: Support multiport for VirtIOConsole
This involves refactoring VirtIOConsole into VirtIOConsole and
VirtIOConsolePort. VirtIOConsole is the VirtIODevice, it owns multiple
VirtIOConsolePorts as well as two control queues. Each
VirtIOConsolePort is a CharacterDevice.
Diffstat (limited to 'Kernel/VirtIO')
-rw-r--r-- | Kernel/VirtIO/VirtIOConsole.cpp | 224 | ||||
-rw-r--r-- | Kernel/VirtIO/VirtIOConsole.h | 75 | ||||
-rw-r--r-- | Kernel/VirtIO/VirtIOConsolePort.cpp | 167 | ||||
-rw-r--r-- | Kernel/VirtIO/VirtIOConsolePort.h | 63 |
4 files changed, 409 insertions, 120 deletions
diff --git a/Kernel/VirtIO/VirtIOConsole.cpp b/Kernel/VirtIO/VirtIOConsole.cpp index b443d171dd..b1df8def1b 100644 --- a/Kernel/VirtIO/VirtIOConsole.cpp +++ b/Kernel/VirtIO/VirtIOConsole.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, the SerenityOS developers. + * Copyright (c) 2021, Kyle Pereira <hey@xylepereira.me> * * SPDX-License-Identifier: BSD-2-Clause */ @@ -12,8 +13,8 @@ namespace Kernel { unsigned VirtIOConsole::next_device_id = 0; UNMAP_AFTER_INIT VirtIOConsole::VirtIOConsole(PCI::Address address) - : CharacterDevice(229, next_device_id++) - , VirtIODevice(address, "VirtIOConsole") + : VirtIODevice(address, "VirtIOConsole") + , m_device_id(next_device_id++) { if (auto cfg = get_config(ConfigurationType::Device)) { bool success = negotiate_features([&](u64 supported_features) { @@ -21,7 +22,7 @@ UNMAP_AFTER_INIT VirtIOConsole::VirtIOConsole(PCI::Address address) if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_SIZE)) dbgln("VirtIOConsole: Console size is not yet supported!"); if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_MULTIPORT)) - dbgln("VirtIOConsole: Multi port is not yet supported!"); + negotiated |= VIRTIO_CONSOLE_F_MULTIPORT; return negotiated; }); if (success) { @@ -34,37 +35,24 @@ UNMAP_AFTER_INIT VirtIOConsole::VirtIOConsole(PCI::Address address) } if (is_feature_accepted(VIRTIO_CONSOLE_F_MULTIPORT)) { max_nr_ports = config_read32(*cfg, 0x4); + m_ports.resize(max_nr_ports); } }); dbgln("VirtIOConsole: cols: {}, rows: {}, max nr ports {}", cols, rows, max_nr_ports); - success = setup_queues(2 + max_nr_ports * 2); // base receiveq/transmitq for port0 + 2 per every additional port + // Base receiveq/transmitq for port0 + optional control queues and 2 per every additional port + success = setup_queues(2 + max_nr_ports > 0 ? 2 + 2 * max_nr_ports : 0); } if (success) { finish_init(); - m_receive_buffer = make<RingBuffer>("VirtIOConsole Receive", RINGBUFFER_SIZE); - m_transmit_buffer = make<RingBuffer>("VirtIOConsole Transmit", RINGBUFFER_SIZE); - init_receive_buffer(); + if (is_feature_accepted(VIRTIO_CONSOLE_F_MULTIPORT)) + setup_multiport(); + else + m_ports.append(new VirtIOConsolePort(0u, *this)); } } } -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"); @@ -73,112 +61,158 @@ bool VirtIOConsole::handle_device_config_change() void VirtIOConsole::handle_queue_update(u16 queue_index) { - dbgln_if(VIRTIO_DEBUG, "VirtIOConsole: Handle queue update"); - VERIFY(queue_index <= TRANSMITQ); - switch (queue_index) { - case RECEIVEQ: { - auto& queue = get_queue(RECEIVEQ); + dbgln_if(VIRTIO_DEBUG, "VirtIOConsole: Handle queue update {}", queue_index); + + if (queue_index == CONTROL_RECEIVEQ) { + ScopedSpinLock ringbuffer_lock(m_control_receive_buffer->lock()); + auto& queue = get_queue(CONTROL_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(); + while (!popped_chain.is_empty()) { + popped_chain.for_each([&](auto addr, auto) { + auto offset = addr.as_ptr() - m_control_receive_buffer->start_of_region().as_ptr(); + auto* message = reinterpret_cast<ControlMessage*>(m_control_receive_buffer->vaddr().offset(offset).as_ptr()); + process_control_message(*message); + }); - 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); + supply_chain_and_notify(CONTROL_RECEIVEQ, popped_chain); + popped_chain = queue.pop_used_buffer_chain(used); } - - evaluate_block_conditions(); - break; - } - case TRANSMITQ: { - ScopedSpinLock ringbuffer_lock(m_transmit_buffer->lock()); - auto& queue = get_queue(TRANSMITQ); + } else if (queue_index == CONTROL_TRANSMITQ) { + ScopedSpinLock ringbuffer_lock(m_control_transmit_buffer->lock()); + auto& queue = get_queue(CONTROL_TRANSMITQ); ScopedSpinLock queue_lock(queue.lock()); size_t used; VirtIOQueueChain popped_chain = queue.pop_used_buffer_chain(used); + auto number_of_messages = 0; do { popped_chain.for_each([this](PhysicalAddress address, size_t length) { - m_transmit_buffer->reclaim_space(address, length); + m_control_transmit_buffer->reclaim_space(address, length); }); popped_chain.release_buffer_slots_to_queue(); popped_chain = queue.pop_used_buffer_chain(used); + number_of_messages++; } while (!popped_chain.is_empty()); - // Unblock any IO tasks that were blocked because can_write() returned false - evaluate_block_conditions(); - break; - } - default: - VERIFY_NOT_REACHED(); + m_control_wait_queue.wake_n(number_of_messages); + } else { + u32 port_index = queue_index < 2 ? 0 : (queue_index - 2) / 2; + if (port_index >= m_ports.size() || !m_ports.at(port_index)) { + dbgln("Invalid queue_index {}", queue_index); + return; + } + m_ports.at(port_index)->handle_queue_update({}, queue_index); } } -bool VirtIOConsole::can_read(const FileDescription&, size_t) const +void VirtIOConsole::setup_multiport() { - return m_receive_buffer->used_bytes() > 0; + m_control_receive_buffer = make<RingBuffer>("VirtIOConsole control receive queue", CONTROL_BUFFER_SIZE); + m_control_transmit_buffer = make<RingBuffer>("VirtIOConsole control transmit queue", CONTROL_BUFFER_SIZE); + + auto& queue = get_queue(CONTROL_RECEIVEQ); + ScopedSpinLock queue_lock(queue.lock()); + VirtIOQueueChain chain(queue); + auto offset = 0ul; + + while (offset < CONTROL_BUFFER_SIZE) { + auto buffer_start = m_control_receive_buffer->start_of_region().offset(offset); + auto did_add_buffer = chain.add_buffer_to_chain(buffer_start, CONTROL_MESSAGE_SIZE, BufferType::DeviceWritable); + VERIFY(did_add_buffer); + offset += CONTROL_MESSAGE_SIZE; + supply_chain_and_notify(CONTROL_RECEIVEQ, chain); + } + + ControlMessage ready_event { + .id = 0, // Unused + .event = (u16)ControlEvent::DeviceReady, + .value = (u16)ControlMessage::Status::Success + }; + write_control_message(ready_event); } -KResultOr<size_t> VirtIOConsole::read(FileDescription& desc, u64, UserOrKernelBuffer& buffer, size_t size) +void VirtIOConsole::process_control_message(ControlMessage message) { - if (!size) - return 0; - - if (!can_read(desc, size)) - return EAGAIN; + switch (message.event) { + case (u16)ControlEvent::DeviceAdd: { + u32 id = message.id; + if (id >= m_ports.size()) { + dbgln("Device provided an invalid port number {}. max_nr_ports: {}", id, m_ports.size()); + return; + } else if (!m_ports.at(id).is_null()) { + dbgln("Device tried to add port {} which was already added!", id); + return; + } - ScopedSpinLock ringbuffer_lock(m_receive_buffer->lock()); + m_ports.at(id) = new VirtIOConsolePort(id, *this); + ControlMessage ready_event { + .id = static_cast<u32>(id), + .event = (u16)ControlEvent::PortReady, + .value = (u16)ControlMessage::Status::Success + }; - 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()); + write_control_message(ready_event); + break; + } + case (u16)ControlEvent::ConsolePort: + case (u16)ControlEvent::PortOpen: { + if (message.id >= m_ports.size()) { + dbgln("Device provided an invalid port number {}. max_nr_ports: {}", message.id, m_ports.size()); + return; + } else if (m_ports.at(message.id).is_null()) { + dbgln("Device tried to open port {} which was not added!", message.id); + return; + } - return bytes_copied; + if (message.value == (u16)ControlMessage::PortStatus::Open) { + auto is_open = m_ports.at(message.id)->is_open(); + if (!is_open) { + m_ports.at(message.id)->set_open({}, true); + send_open_control_message(message.id, true); + } + } else if (message.value == (u16)ControlMessage::PortStatus::Close) { + m_ports.at(message.id)->set_open({}, false); + } else { + dbgln("Device specified invalid value {}. Must be 0 or 1.", message.value); + } + break; + } + default: + dbgln("Unhandled message event {}!", message.event); + } } - -bool VirtIOConsole::can_write(const FileDescription&, size_t) const +void VirtIOConsole::write_control_message(ControlMessage message) { - return get_queue(TRANSMITQ).has_free_slots() && m_transmit_buffer->has_space(); -} + ScopedSpinLock ringbuffer_lock(m_control_transmit_buffer->lock()); -KResultOr<size_t> VirtIOConsole::write(FileDescription& desc, u64, const UserOrKernelBuffer& data, size_t size) -{ - if (!size) - return 0; + PhysicalAddress start_of_chunk; + size_t length_of_chunk; - if (!can_write(desc, size)) - return EAGAIN; + auto data = UserOrKernelBuffer::for_kernel_buffer((u8*)&message); + while (!m_control_transmit_buffer->copy_data_in(data, 0, sizeof(message), start_of_chunk, length_of_chunk)) { + ringbuffer_lock.unlock(); + m_control_wait_queue.wait_forever(); + ringbuffer_lock.lock(); + } - ScopedSpinLock ringbuffer_lock(m_transmit_buffer->lock()); - auto& queue = get_queue(TRANSMITQ); + auto& queue = get_queue(CONTROL_TRANSMITQ); ScopedSpinLock queue_lock(queue.lock()); VirtIOQueueChain chain(queue); - size_t total_bytes_copied = 0; - do { - PhysicalAddress start_of_chunk; - size_t length_of_chunk; - - if (!m_transmit_buffer->copy_data_in(data, total_bytes_copied, size - total_bytes_copied, start_of_chunk, length_of_chunk)) { - chain.release_buffer_slots_to_queue(); - return EINVAL; - } - - bool did_add_buffer = chain.add_buffer_to_chain(start_of_chunk, length_of_chunk, BufferType::DeviceReadable); - VERIFY(did_add_buffer); - total_bytes_copied += length_of_chunk; - } while (total_bytes_copied < size && can_write(desc, size - total_bytes_copied)); - - supply_chain_and_notify(TRANSMITQ, chain); + bool did_add_buffer = chain.add_buffer_to_chain(start_of_chunk, length_of_chunk, BufferType::DeviceReadable); + VERIFY(did_add_buffer); - return total_bytes_copied; + supply_chain_and_notify(CONTROL_TRANSMITQ, chain); } +void VirtIOConsole::send_open_control_message(unsigned port_number, bool open) +{ + ControlMessage port_open { + .id = static_cast<u32>(port_number), + .event = (u16)ControlEvent::PortOpen, + .value = open + }; + write_control_message(port_open); +} } diff --git a/Kernel/VirtIO/VirtIOConsole.h b/Kernel/VirtIO/VirtIOConsole.h index 2514352826..267c448b2b 100644 --- a/Kernel/VirtIO/VirtIOConsole.h +++ b/Kernel/VirtIO/VirtIOConsole.h @@ -1,4 +1,5 @@ /* + * Copyright (c) 2021, Kyle Pereira <hey@xylepereira.me> * Copyright (c) 2021, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause @@ -6,48 +7,72 @@ #pragma once -#include <Kernel/Devices/CharacterDevice.h> #include <Kernel/VM/RingBuffer.h> #include <Kernel/VirtIO/VirtIO.h> +#include <Kernel/VirtIO/VirtIOConsolePort.h> namespace Kernel { +class VirtIOConsole + : public VirtIODevice + , public RefCounted<VirtIOConsole> { + friend VirtIOConsolePort; -#define VIRTIO_CONSOLE_F_SIZE (1 << 0) -#define VIRTIO_CONSOLE_F_MULTIPORT (1 << 1) -#define VIRTIO_CONSOLE_F_EMERG_WRITE (1 << 2) - -#define RECEIVEQ 0 -#define TRANSMITQ 1 - -class VirtIOConsole final : public CharacterDevice - , public VirtIODevice { public: VirtIOConsole(PCI::Address); - virtual ~VirtIOConsole() override; + virtual ~VirtIOConsole() override = default; - virtual const char* purpose() const override { return class_name(); } + virtual const char* purpose() const override { return "VirtIOConsole"; } + + unsigned device_id() const + { + return m_device_id; + } private: - constexpr static size_t RINGBUFFER_SIZE = 2 * PAGE_SIZE; - virtual const char* class_name() const override { return m_class_name.characters(); } + enum class ControlEvent : u16 { + DeviceReady = 0, + DeviceAdd = 1, + PortReady = 3, + ConsolePort = 4, + PortOpen = 6, + }; + struct [[gnu::packed]] ControlMessage { + u32 id; + u16 event; + u16 value; + + enum class Status : u16 { + Success = 1, + Failure = 0 + }; - virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr<size_t> read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual bool can_write(const FileDescription&, size_t) const override; - virtual KResultOr<size_t> write(FileDescription&, u64, const UserOrKernelBuffer&, size_t) override; + enum class PortStatus : u16 { + Open = 1, + Close = 0 + }; + }; - virtual mode_t required_mode() const override { return 0666; } + constexpr static u16 CONTROL_RECEIVEQ = 2; + constexpr static u16 CONTROL_TRANSMITQ = 3; + constexpr static size_t CONTROL_MESSAGE_SIZE = sizeof(ControlMessage); + constexpr static size_t CONTROL_BUFFER_SIZE = CONTROL_MESSAGE_SIZE * 32; virtual bool handle_device_config_change() override; - 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; + Vector<RefPtr<VirtIOConsolePort>> m_ports; + void setup_multiport(); + void process_control_message(ControlMessage message); + void write_control_message(ControlMessage message); + void send_open_control_message(unsigned port_number, bool open); + + unsigned m_device_id; + + OwnPtr<RingBuffer> m_control_transmit_buffer; + OwnPtr<RingBuffer> m_control_receive_buffer; + + WaitQueue m_control_wait_queue; static unsigned next_device_id; }; - } diff --git a/Kernel/VirtIO/VirtIOConsolePort.cpp b/Kernel/VirtIO/VirtIOConsolePort.cpp new file mode 100644 index 0000000000..e51fbdb260 --- /dev/null +++ b/Kernel/VirtIO/VirtIOConsolePort.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2021, the SerenityOS developers. + * Copyright (c) 2021, Kyle Pereira <hey@xylepereira.me> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <Kernel/VirtIO/VirtIOConsole.h> +#include <Kernel/VirtIO/VirtIOConsolePort.h> + +namespace Kernel { + +unsigned VirtIOConsolePort::next_device_id = 0; + +VirtIOConsolePort::VirtIOConsolePort(unsigned port, VirtIOConsole& console) + : CharacterDevice(229, next_device_id++) + , m_console(console) + , m_port(port) +{ + m_receive_buffer = make<RingBuffer>("VirtIOConsolePort Receive", RINGBUFFER_SIZE); + m_transmit_buffer = make<RingBuffer>("VirtIOConsolePort Transmit", RINGBUFFER_SIZE); + m_receive_queue = m_port == 0 ? 0 : m_port * 2 + 2; + m_transmit_queue = m_port == 0 ? 1 : m_port * 2 + 3; + init_receive_buffer(); +} + +void VirtIOConsolePort::init_receive_buffer() +{ + auto& queue = m_console.get_queue(m_receive_queue); + 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); + m_console.supply_chain_and_notify(m_receive_queue, chain); +} + +void VirtIOConsolePort::handle_queue_update(Badge<VirtIOConsole>, u16 queue_index) +{ + dbgln_if(VIRTIO_DEBUG, "VirtIOConsolePort: Handle queue update for port {}", m_port); + VERIFY(queue_index == m_transmit_queue || queue_index == m_receive_queue); + if (queue_index == m_receive_queue) { + auto& queue = m_console.get_queue(m_receive_queue); + 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 = m_receive_buffer->bytes_till_end(); + + // Our algorithm always has only one buffer in the queue. + VERIFY(popped_chain.length() == 1); + 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); + m_console.supply_chain_and_notify(m_receive_queue, new_chain); + } else { + m_receive_buffer_exhausted = true; + } + + evaluate_block_conditions(); + } else { + ScopedSpinLock ringbuffer_lock(m_transmit_buffer->lock()); + auto& queue = m_console.get_queue(m_transmit_queue); + ScopedSpinLock queue_lock(queue.lock()); + size_t used; + VirtIOQueueChain popped_chain = queue.pop_used_buffer_chain(used); + do { + popped_chain.for_each([this](PhysicalAddress address, size_t length) { + m_transmit_buffer->reclaim_space(address, length); + }); + popped_chain.release_buffer_slots_to_queue(); + popped_chain = queue.pop_used_buffer_chain(used); + } while (!popped_chain.is_empty()); + // Unblock any IO tasks that were blocked because can_write() returned false + evaluate_block_conditions(); + } +} + +bool VirtIOConsolePort::can_read(const FileDescription&, size_t) const +{ + return m_receive_buffer->used_bytes() > 0; +} + +KResultOr<size_t> VirtIOConsolePort::read(FileDescription& desc, u64, UserOrKernelBuffer& buffer, size_t size) +{ + if (!size) + return 0; + + ScopedSpinLock ringbuffer_lock(m_receive_buffer->lock()); + + if (!can_read(desc, size)) + return EAGAIN; + + auto bytes_copied = m_receive_buffer->copy_data_out(size, buffer).value(); + m_receive_buffer->reclaim_space(m_receive_buffer->start_of_used(), bytes_copied); + + if (m_receive_buffer_exhausted && m_receive_buffer->used_bytes() == 0) { + auto& queue = m_console.get_queue(m_receive_queue); + ScopedSpinLock queue_lock(queue.lock()); + VirtIOQueueChain new_chain(queue); + new_chain.add_buffer_to_chain(m_receive_buffer->start_of_region(), RINGBUFFER_SIZE, BufferType::DeviceWritable); + m_console.supply_chain_and_notify(m_receive_queue, new_chain); + m_receive_buffer_exhausted = false; + } + + return bytes_copied; +} + +bool VirtIOConsolePort::can_write(const FileDescription&, size_t) const +{ + return m_console.get_queue(m_transmit_queue).has_free_slots() && m_transmit_buffer->has_space(); +} + +KResultOr<size_t> VirtIOConsolePort::write(FileDescription& desc, u64, const UserOrKernelBuffer& data, size_t size) +{ + if (!size) + return 0; + + ScopedSpinLock ringbuffer_lock(m_transmit_buffer->lock()); + auto& queue = m_console.get_queue(m_transmit_queue); + ScopedSpinLock queue_lock(queue.lock()); + + if (!can_write(desc, size)) + return EAGAIN; + + VirtIOQueueChain chain(queue); + + size_t total_bytes_copied = 0; + do { + PhysicalAddress start_of_chunk; + size_t length_of_chunk; + + if (!m_transmit_buffer->copy_data_in(data, total_bytes_copied, size - total_bytes_copied, start_of_chunk, length_of_chunk)) { + chain.release_buffer_slots_to_queue(); + return EINVAL; + } + + bool did_add_buffer = chain.add_buffer_to_chain(start_of_chunk, length_of_chunk, BufferType::DeviceReadable); + VERIFY(did_add_buffer); + total_bytes_copied += length_of_chunk; + } while (total_bytes_copied < size && can_write(desc, size)); + + m_console.supply_chain_and_notify(m_transmit_queue, chain); + + return total_bytes_copied; +} + +String VirtIOConsolePort::device_name() const +{ + return String::formatted("hvc{}p{}", m_console.device_id(), m_port); +} + +KResultOr<NonnullRefPtr<FileDescription>> VirtIOConsolePort::open(int options) +{ + if (m_open) + m_console.send_open_control_message(m_port, true); + + return File::open(options); +} + +} diff --git a/Kernel/VirtIO/VirtIOConsolePort.h b/Kernel/VirtIO/VirtIOConsolePort.h new file mode 100644 index 0000000000..6390a4ae77 --- /dev/null +++ b/Kernel/VirtIO/VirtIOConsolePort.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021, the SerenityOS developers. + * Copyright (c) 2021, Kyle Pereira <hey@xylepereira.me> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <Kernel/Devices/CharacterDevice.h> +#include <Kernel/FileSystem/FileDescription.h> +#include <Kernel/VM/RingBuffer.h> +#include <Kernel/VirtIO/VirtIO.h> + +namespace Kernel { + +class VirtIOConsole; + +#define VIRTIO_CONSOLE_F_SIZE (1 << 0) +#define VIRTIO_CONSOLE_F_MULTIPORT (1 << 1) +#define VIRTIO_CONSOLE_F_EMERG_WRITE (1 << 2) + +class VirtIOConsolePort + : public CharacterDevice { +public: + explicit VirtIOConsolePort(unsigned port, VirtIOConsole&); + void handle_queue_update(Badge<VirtIOConsole>, u16 queue_index); + + void set_open(Badge<VirtIOConsole>, bool state) { m_open = state; } + bool is_open() const { return m_open; } + +private: + constexpr static size_t RINGBUFFER_SIZE = 2 * PAGE_SIZE; + + virtual const char* class_name() const override { return "VirtIOConsolePort"; } + + virtual bool can_read(const FileDescription&, size_t) const override; + virtual KResultOr<size_t> read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override; + virtual bool can_write(const FileDescription&, size_t) const override; + virtual KResultOr<size_t> write(FileDescription&, u64, const UserOrKernelBuffer&, size_t) override; + virtual KResultOr<NonnullRefPtr<FileDescription>> open(int options) override; + + mode_t required_mode() const override { return 0666; } + + String device_name() const override; + + void init_receive_buffer(); + + static unsigned next_device_id; + u16 m_receive_queue {}; + u16 m_transmit_queue {}; + + OwnPtr<RingBuffer> m_receive_buffer; + OwnPtr<RingBuffer> m_transmit_buffer; + + VirtIOConsole& m_console; + unsigned m_port; + + bool m_open { false }; + Atomic<bool> m_receive_buffer_exhausted; +}; + +} |