ConsolePort.cpp 6.2 KB

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