Serenity Operating System
at master 172 lines 6.4 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/Bus/VirtIO/ConsolePort.h> 10 11namespace Kernel::VirtIO { 12 13unsigned ConsolePort::next_device_id = 0; 14 15ErrorOr<NonnullLockRefPtr<ConsolePort>> ConsolePort::try_create(unsigned port, Console& console) 16{ 17 auto receive_buffer = TRY(Memory::RingBuffer::try_create("VirtIO::ConsolePort Receive"sv, RINGBUFFER_SIZE)); 18 auto transmit_buffer = TRY(Memory::RingBuffer::try_create("VirtIO::ConsolePort Transmit"sv, RINGBUFFER_SIZE)); 19 return adopt_nonnull_lock_ref_or_enomem(new (nothrow) ConsolePort(port, console, move(receive_buffer), move(transmit_buffer))); 20} 21 22ConsolePort::ConsolePort(unsigned port, VirtIO::Console& console, NonnullOwnPtr<Memory::RingBuffer> receive_buffer, NonnullOwnPtr<Memory::RingBuffer> transmit_buffer) 23 : CharacterDevice(229, next_device_id++) 24 , m_receive_buffer(move(receive_buffer)) 25 , m_transmit_buffer(move(transmit_buffer)) 26 , m_console(console) 27 , m_port(port) 28{ 29 m_receive_queue = m_port == 0 ? 0 : m_port * 2 + 2; 30 m_transmit_queue = m_port == 0 ? 1 : m_port * 2 + 3; 31} 32 33void ConsolePort::init_receive_buffer(Badge<VirtIO::Console>) 34{ 35 auto& queue = m_console.get_queue(m_receive_queue); 36 SpinlockLocker queue_lock(queue.lock()); 37 QueueChain chain(queue); 38 39 auto buffer_start = m_receive_buffer->start_of_region(); 40 auto did_add_buffer = chain.add_buffer_to_chain(buffer_start, RINGBUFFER_SIZE, BufferType::DeviceWritable); 41 VERIFY(did_add_buffer); 42 m_console.supply_chain_and_notify(m_receive_queue, chain); 43} 44 45void ConsolePort::handle_queue_update(Badge<VirtIO::Console>, u16 queue_index) 46{ 47 dbgln_if(VIRTIO_DEBUG, "VirtIO::ConsolePort: Handle queue update for port {}", m_port); 48 VERIFY(queue_index == m_transmit_queue || queue_index == m_receive_queue); 49 if (queue_index == m_receive_queue) { 50 auto& queue = m_console.get_queue(m_receive_queue); 51 SpinlockLocker queue_lock(queue.lock()); 52 size_t used; 53 QueueChain popped_chain = queue.pop_used_buffer_chain(used); 54 55 SpinlockLocker ringbuffer_lock(m_receive_buffer->lock()); 56 auto used_space_or_error = m_receive_buffer->reserve_space(used); 57 if (used_space_or_error.is_error()) { 58 TODO(); 59 } 60 auto used_space = used_space_or_error.release_value(); 61 auto remaining_space = m_receive_buffer->bytes_till_end(); 62 63 // Our algorithm always has only one buffer in the queue. 64 VERIFY(popped_chain.length() == 1); 65 VERIFY(!queue.new_data_available()); 66 popped_chain.release_buffer_slots_to_queue(); 67 68 QueueChain new_chain(queue); 69 if (remaining_space != 0) { 70 new_chain.add_buffer_to_chain(used_space.offset(used), remaining_space, BufferType::DeviceWritable); 71 m_console.supply_chain_and_notify(m_receive_queue, new_chain); 72 } else { 73 m_receive_buffer_exhausted = true; 74 } 75 76 evaluate_block_conditions(); 77 } else { 78 SpinlockLocker ringbuffer_lock(m_transmit_buffer->lock()); 79 auto& queue = m_console.get_queue(m_transmit_queue); 80 SpinlockLocker queue_lock(queue.lock()); 81 size_t used; 82 QueueChain popped_chain = queue.pop_used_buffer_chain(used); 83 do { 84 popped_chain.for_each([this](PhysicalAddress address, size_t length) { 85 m_transmit_buffer->reclaim_space(address, length); 86 }); 87 popped_chain.release_buffer_slots_to_queue(); 88 popped_chain = queue.pop_used_buffer_chain(used); 89 } while (!popped_chain.is_empty()); 90 // Unblock any IO tasks that were blocked because can_write() returned false 91 evaluate_block_conditions(); 92 } 93} 94 95bool ConsolePort::can_read(OpenFileDescription const&, u64) const 96{ 97 return m_receive_buffer->used_bytes() > 0; 98} 99 100ErrorOr<size_t> ConsolePort::read(OpenFileDescription& desc, u64, UserOrKernelBuffer& buffer, size_t size) 101{ 102 if (!size) 103 return 0; 104 105 SpinlockLocker ringbuffer_lock(m_receive_buffer->lock()); 106 107 if (!can_read(desc, size)) 108 return EAGAIN; 109 110 auto bytes_copied = TRY(m_receive_buffer->copy_data_out(size, buffer)); 111 m_receive_buffer->reclaim_space(m_receive_buffer->start_of_used(), bytes_copied); 112 113 if (m_receive_buffer_exhausted && m_receive_buffer->used_bytes() == 0) { 114 auto& queue = m_console.get_queue(m_receive_queue); 115 SpinlockLocker queue_lock(queue.lock()); 116 QueueChain new_chain(queue); 117 new_chain.add_buffer_to_chain(m_receive_buffer->start_of_region(), RINGBUFFER_SIZE, BufferType::DeviceWritable); 118 m_console.supply_chain_and_notify(m_receive_queue, new_chain); 119 m_receive_buffer_exhausted = false; 120 } 121 122 return bytes_copied; 123} 124 125bool ConsolePort::can_write(OpenFileDescription const&, u64) const 126{ 127 return m_console.get_queue(m_transmit_queue).has_free_slots() && m_transmit_buffer->has_space(); 128} 129 130ErrorOr<size_t> ConsolePort::write(OpenFileDescription& desc, u64, UserOrKernelBuffer const& data, size_t size) 131{ 132 if (!size) 133 return 0; 134 135 SpinlockLocker ringbuffer_lock(m_transmit_buffer->lock()); 136 auto& queue = m_console.get_queue(m_transmit_queue); 137 SpinlockLocker queue_lock(queue.lock()); 138 139 if (!can_write(desc, size)) 140 return EAGAIN; 141 142 QueueChain chain(queue); 143 144 size_t total_bytes_copied = 0; 145 do { 146 PhysicalAddress start_of_chunk; 147 size_t length_of_chunk; 148 149 if (!m_transmit_buffer->copy_data_in(data, total_bytes_copied, size - total_bytes_copied, start_of_chunk, length_of_chunk)) { 150 chain.release_buffer_slots_to_queue(); 151 return EINVAL; 152 } 153 154 bool did_add_buffer = chain.add_buffer_to_chain(start_of_chunk, length_of_chunk, BufferType::DeviceReadable); 155 VERIFY(did_add_buffer); 156 total_bytes_copied += length_of_chunk; 157 } while (total_bytes_copied < size && can_write(desc, size)); 158 159 m_console.supply_chain_and_notify(m_transmit_queue, chain); 160 161 return total_bytes_copied; 162} 163 164ErrorOr<NonnullRefPtr<OpenFileDescription>> ConsolePort::open(int options) 165{ 166 if (!m_open) 167 m_console.send_open_control_message(m_port, true); 168 169 return Device::open(options); 170} 171 172}