ConsolePort.cpp 6.0 KB

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