Serenity Operating System
at master 241 lines 9.2 kB view raw
1/* 2 * Copyright (c) 2021, the SerenityOS developers. 3 * Copyright (c) 2021, Kyle Pereira <hey@xylepereira.me> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <Kernel/Bus/VirtIO/Console.h> 9#include <Kernel/Devices/DeviceManagement.h> 10#include <Kernel/Sections.h> 11#include <Kernel/WorkQueue.h> 12 13namespace Kernel::VirtIO { 14 15unsigned Console::next_device_id = 0; 16 17UNMAP_AFTER_INIT NonnullLockRefPtr<Console> Console::must_create(PCI::DeviceIdentifier const& pci_device_identifier) 18{ 19 return adopt_lock_ref_if_nonnull(new Console(pci_device_identifier)).release_nonnull(); 20} 21 22UNMAP_AFTER_INIT void Console::initialize() 23{ 24 Device::initialize(); 25 if (auto const* cfg = get_config(ConfigurationType::Device)) { 26 bool success = negotiate_features([&](u64 supported_features) { 27 u64 negotiated = 0; 28 if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_SIZE)) 29 dbgln("VirtIO::Console: Console size is not yet supported!"); 30 if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_MULTIPORT)) 31 negotiated |= VIRTIO_CONSOLE_F_MULTIPORT; 32 return negotiated; 33 }); 34 if (success) { 35 u32 max_nr_ports = 0; 36 u16 cols = 0, rows = 0; 37 read_config_atomic([&]() { 38 if (is_feature_accepted(VIRTIO_CONSOLE_F_SIZE)) { 39 cols = config_read16(*cfg, 0x0); 40 rows = config_read16(*cfg, 0x2); 41 } 42 if (is_feature_accepted(VIRTIO_CONSOLE_F_MULTIPORT)) { 43 max_nr_ports = config_read32(*cfg, 0x4); 44 m_ports.resize(max_nr_ports); 45 } 46 }); 47 dbgln("VirtIO::Console: cols: {}, rows: {}, max nr ports {}", cols, rows, max_nr_ports); 48 // Base receiveq/transmitq for port0 + optional control queues and 2 per every additional port 49 success = setup_queues(2 + max_nr_ports > 0 ? 2 + 2 * max_nr_ports : 0); 50 } 51 if (success) { 52 finish_init(); 53 54 if (is_feature_accepted(VIRTIO_CONSOLE_F_MULTIPORT)) { 55 setup_multiport(); 56 } else { 57 auto port = MUST(DeviceManagement::the().try_create_device<VirtIO::ConsolePort>(0u, *this)); 58 port->init_receive_buffer({}); 59 m_ports.append(port); 60 } 61 } 62 } 63} 64 65UNMAP_AFTER_INIT Console::Console(PCI::DeviceIdentifier const& pci_device_identifier) 66 : VirtIO::Device(pci_device_identifier) 67 , m_device_id(next_device_id++) 68{ 69} 70 71bool Console::handle_device_config_change() 72{ 73 dbgln("VirtIO::Console: Handle device config change"); 74 return true; 75} 76 77void Console::handle_queue_update(u16 queue_index) 78{ 79 dbgln_if(VIRTIO_DEBUG, "VirtIO::Console: Handle queue update {}", queue_index); 80 81 if (queue_index == CONTROL_RECEIVEQ) { 82 SpinlockLocker ringbuffer_lock(m_control_receive_buffer->lock()); 83 auto& queue = get_queue(CONTROL_RECEIVEQ); 84 SpinlockLocker queue_lock(queue.lock()); 85 size_t used; 86 QueueChain popped_chain = queue.pop_used_buffer_chain(used); 87 88 while (!popped_chain.is_empty()) { 89 popped_chain.for_each([&](auto addr, auto) { 90 auto offset = addr.as_ptr() - m_control_receive_buffer->start_of_region().as_ptr(); 91 auto* message = reinterpret_cast<ControlMessage*>(m_control_receive_buffer->vaddr().offset(offset).as_ptr()); 92 process_control_message(*message); 93 }); 94 95 supply_chain_and_notify(CONTROL_RECEIVEQ, popped_chain); 96 popped_chain = queue.pop_used_buffer_chain(used); 97 } 98 } else if (queue_index == CONTROL_TRANSMITQ) { 99 SpinlockLocker ringbuffer_lock(m_control_transmit_buffer->lock()); 100 auto& queue = get_queue(CONTROL_TRANSMITQ); 101 SpinlockLocker queue_lock(queue.lock()); 102 size_t used; 103 QueueChain popped_chain = queue.pop_used_buffer_chain(used); 104 auto number_of_messages = 0; 105 do { 106 popped_chain.for_each([this](PhysicalAddress address, size_t length) { 107 m_control_transmit_buffer->reclaim_space(address, length); 108 }); 109 popped_chain.release_buffer_slots_to_queue(); 110 popped_chain = queue.pop_used_buffer_chain(used); 111 number_of_messages++; 112 } while (!popped_chain.is_empty()); 113 m_control_wait_queue.wake_n(number_of_messages); 114 } else { 115 u32 port_index = queue_index < 2 ? 0 : (queue_index - 2) / 2; 116 if (port_index >= m_ports.size() || !m_ports.at(port_index)) { 117 dbgln("Invalid queue_index {}", queue_index); 118 return; 119 } 120 m_ports.at(port_index)->handle_queue_update({}, queue_index); 121 } 122} 123 124void Console::setup_multiport() 125{ 126 m_control_receive_buffer = Memory::RingBuffer::try_create("VirtIOConsole control receive queue"sv, CONTROL_BUFFER_SIZE).release_value_but_fixme_should_propagate_errors(); 127 m_control_transmit_buffer = Memory::RingBuffer::try_create("VirtIOConsole control transmit queue"sv, CONTROL_BUFFER_SIZE).release_value_but_fixme_should_propagate_errors(); 128 129 auto& queue = get_queue(CONTROL_RECEIVEQ); 130 SpinlockLocker queue_lock(queue.lock()); 131 QueueChain chain(queue); 132 auto offset = 0ul; 133 134 while (offset < CONTROL_BUFFER_SIZE) { 135 auto buffer_start = m_control_receive_buffer->start_of_region().offset(offset); 136 auto did_add_buffer = chain.add_buffer_to_chain(buffer_start, CONTROL_MESSAGE_SIZE, BufferType::DeviceWritable); 137 VERIFY(did_add_buffer); 138 offset += CONTROL_MESSAGE_SIZE; 139 supply_chain_and_notify(CONTROL_RECEIVEQ, chain); 140 } 141 142 ControlMessage ready_event { 143 .id = 0, // Unused 144 .event = (u16)ControlEvent::DeviceReady, 145 .value = (u16)ControlMessage::Status::Success 146 }; 147 write_control_message(ready_event); 148} 149 150void Console::process_control_message(ControlMessage message) 151{ 152 switch (message.event) { 153 case (u16)ControlEvent::DeviceAdd: { 154 // FIXME: Do something sanely here if we can't allocate a work queue? 155 MUST(g_io_work->try_queue([message, this]() -> void { 156 u32 id = message.id; 157 if (id >= m_ports.size()) { 158 dbgln("Device provided an invalid port number {}. max_nr_ports: {}", id, m_ports.size()); 159 return; 160 } 161 if (!m_ports.at(id).is_null()) { 162 dbgln("Device tried to add port {} which was already added!", id); 163 return; 164 } 165 166 auto port = MUST(DeviceManagement::the().try_create_device<VirtIO::ConsolePort>(id, *this)); 167 port->init_receive_buffer({}); 168 m_ports.at(id) = port; 169 170 ControlMessage ready_event { 171 .id = static_cast<u32>(id), 172 .event = (u16)ControlEvent::PortReady, 173 .value = (u16)ControlMessage::Status::Success 174 }; 175 write_control_message(ready_event); 176 })); 177 178 break; 179 } 180 case (u16)ControlEvent::ConsolePort: 181 case (u16)ControlEvent::PortOpen: { 182 if (message.id >= m_ports.size()) { 183 dbgln("Device provided an invalid port number {}. max_nr_ports: {}", message.id, m_ports.size()); 184 return; 185 } 186 if (m_ports.at(message.id).is_null()) { 187 dbgln("Device tried to open port {} which was not added!", message.id); 188 return; 189 } 190 191 if (message.value == (u16)ControlMessage::PortStatus::Open) { 192 auto is_open = m_ports.at(message.id)->is_open(); 193 if (!is_open) { 194 m_ports.at(message.id)->set_open({}, true); 195 send_open_control_message(message.id, true); 196 } 197 } else if (message.value == (u16)ControlMessage::PortStatus::Close) { 198 m_ports.at(message.id)->set_open({}, false); 199 } else { 200 dbgln("Device specified invalid value {}. Must be 0 or 1.", message.value); 201 } 202 break; 203 } 204 default: 205 dbgln("Unhandled message event {}!", message.event); 206 } 207} 208void Console::write_control_message(ControlMessage message) 209{ 210 SpinlockLocker ringbuffer_lock(m_control_transmit_buffer->lock()); 211 212 PhysicalAddress start_of_chunk; 213 size_t length_of_chunk; 214 215 auto data = UserOrKernelBuffer::for_kernel_buffer((u8*)&message); 216 while (!m_control_transmit_buffer->copy_data_in(data, 0, sizeof(message), start_of_chunk, length_of_chunk)) { 217 ringbuffer_lock.unlock(); 218 m_control_wait_queue.wait_forever(); 219 ringbuffer_lock.lock(); 220 } 221 222 auto& queue = get_queue(CONTROL_TRANSMITQ); 223 SpinlockLocker queue_lock(queue.lock()); 224 QueueChain chain(queue); 225 226 bool did_add_buffer = chain.add_buffer_to_chain(start_of_chunk, length_of_chunk, BufferType::DeviceReadable); 227 VERIFY(did_add_buffer); 228 229 supply_chain_and_notify(CONTROL_TRANSMITQ, chain); 230} 231 232void Console::send_open_control_message(unsigned port_number, bool open) 233{ 234 ControlMessage port_open { 235 .id = static_cast<u32>(port_number), 236 .event = (u16)ControlEvent::PortOpen, 237 .value = open 238 }; 239 write_control_message(port_open); 240} 241}