From ea4c9efbb984e9b2a3e27c98fb53342ec0e60ca8 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Sat, 2 Jan 2021 19:56:36 +0200 Subject: [PATCH] Kernel: Add base support for VirtConsole (VirtIO based consoles) Based on pull #3236 by tomuta, this is still very much WIP but will eventually allow us to switch from the considerably slower method of knocking on port 0xe9 for each character Co-authored-by: Tom Co-authored-by: Sahan --- Kernel/CMakeLists.txt | 1 + Kernel/VirtIO/VirtIO.cpp | 6 ++ Kernel/VirtIO/VirtIOConsole.cpp | 118 ++++++++++++++++++++++++++++++++ Kernel/VirtIO/VirtIOConsole.h | 66 ++++++++++++++++++ 4 files changed, 191 insertions(+) create mode 100644 Kernel/VirtIO/VirtIOConsole.cpp create mode 100644 Kernel/VirtIO/VirtIOConsole.h diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index a0b3417b0a8..3c083ea4e63 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -42,6 +42,7 @@ set(KERNEL_SOURCES Devices/USB/UHCIController.cpp VirtIO/VirtIO.cpp VirtIO/VirtIOQueue.cpp + VirtIO/VirtIOConsole.cpp Devices/VMWareBackdoor.cpp Devices/ZeroDevice.cpp Devices/HID/I8042Controller.cpp diff --git a/Kernel/VirtIO/VirtIO.cpp b/Kernel/VirtIO/VirtIO.cpp index 637b079658b..aebd3740af7 100644 --- a/Kernel/VirtIO/VirtIO.cpp +++ b/Kernel/VirtIO/VirtIO.cpp @@ -25,6 +25,7 @@ */ #include +#include namespace Kernel { @@ -35,6 +36,11 @@ void VirtIO::detect() return; if (id.vendor_id != VIRTIO_PCI_VENDOR_ID) return; + switch (id.device_id) { + case VIRTIO_CONSOLE_PCI_DEVICE_ID: + [[maybe_unused]] auto& unused = adopt(*new VirtIOConsole(address)).leak_ref(); + break; + } }); } diff --git a/Kernel/VirtIO/VirtIOConsole.cpp b/Kernel/VirtIO/VirtIOConsole.cpp new file mode 100644 index 00000000000..39dae6c9661 --- /dev/null +++ b/Kernel/VirtIO/VirtIOConsole.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +namespace Kernel { + +VirtIOConsole::VirtIOConsole(PCI::Address address) + : CharacterDevice(229, 0) + , VirtIODevice(address, "VirtIOConsole") +{ + if (auto* cfg = get_device_config()) { + bool success = negotiate_features([&](u64 supported_features) { + u64 negotiated = 0; + 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!"); + return negotiated; + }); + if (success) { + u32 max_nr_ports = 0; + u16 cols = 0, rows = 0; + read_config_atomic([&]() { + if (is_feature_accepted(VIRTIO_CONSOLE_F_SIZE)) { + cols = config_read16(cfg, 0x0); + rows = config_read16(cfg, 0x2); + } + if (is_feature_accepted(VIRTIO_CONSOLE_F_MULTIPORT)) { + max_nr_ports = config_read32(cfg, 0x4); + } + }); + dbgln("VirtIOConsole: cols: {}, rows: {}, max nr ports {}", cols, rows, max_nr_ports); + set_requested_queue_count(2 + max_nr_ports * 2); // base receiveq/transmitq for port0 + 2 per every additional port + success = finish_init(); + } + if (success) { + m_receive_queue = get_queue(RECEIVEQ); + m_receive_queue->on_data_available = [&]() { + dbgln("VirtIOConsole: receive_queue on_data_available"); + }; + m_send_queue = get_queue(TRANSMITQ); + m_send_queue->on_data_available = [&]() { + dbgln("VirtIOConsole: send_queue on_data_available"); + }; + dbgln("TODO: Populate receive queue with a receive buffer"); + } + } +} + +VirtIOConsole::~VirtIOConsole() +{ +} + +void VirtIOConsole::handle_device_config_change() +{ + dbgln("VirtIOConsole: Handle device config change"); +} + +bool VirtIOConsole::can_read(const FileDescription&, size_t) const +{ + return false; +} + +KResultOr VirtIOConsole::read(FileDescription&, u64, [[maybe_unused]] UserOrKernelBuffer& data, size_t size) +{ + if (!size) + return 0; + + return 1; +} + +bool VirtIOConsole::can_write(const FileDescription&, size_t) const +{ + return true; +} + +KResultOr VirtIOConsole::write(FileDescription&, u64, const UserOrKernelBuffer& data, size_t size) +{ + if (!size) + return 0; + + dbgln("VirtIOConsole: Write with size {}, kernel: {}", size, data.is_kernel_buffer()); + + 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; +} + +} diff --git a/Kernel/VirtIO/VirtIOConsole.h b/Kernel/VirtIO/VirtIOConsole.h new file mode 100644 index 00000000000..e1aa0ceb5d4 --- /dev/null +++ b/Kernel/VirtIO/VirtIOConsole.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +namespace Kernel { + +#define VIRTIO_CONSOLE_PCI_DEVICE_ID 0x1003 + +#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; + +private: + virtual const char* class_name() const override { return m_class_name; } + + virtual bool can_read(const FileDescription&, size_t) const override; + virtual KResultOr read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override; + virtual bool can_write(const FileDescription&, size_t) const override; + virtual KResultOr write(FileDescription&, u64, const UserOrKernelBuffer&, size_t) override; + + virtual mode_t required_mode() const override { return 0666; } + + virtual void 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 }; +}; + +}