VirtIOConsole.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. /*
  2. * Copyright (c) 2021, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <Kernel/Sections.h>
  7. #include <Kernel/VirtIO/VirtIOConsole.h>
  8. namespace Kernel {
  9. unsigned VirtIOConsole::next_device_id = 0;
  10. UNMAP_AFTER_INIT VirtIOConsole::VirtIOConsole(PCI::Address address)
  11. : CharacterDevice(229, next_device_id++)
  12. , VirtIODevice(address, "VirtIOConsole")
  13. {
  14. if (auto cfg = get_config(ConfigurationType::Device)) {
  15. bool success = negotiate_features([&](u64 supported_features) {
  16. u64 negotiated = 0;
  17. if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_SIZE))
  18. dbgln("VirtIOConsole: Console size is not yet supported!");
  19. if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_MULTIPORT))
  20. dbgln("VirtIOConsole: Multi port is not yet supported!");
  21. return negotiated;
  22. });
  23. if (success) {
  24. u32 max_nr_ports = 0;
  25. u16 cols = 0, rows = 0;
  26. read_config_atomic([&]() {
  27. if (is_feature_accepted(VIRTIO_CONSOLE_F_SIZE)) {
  28. cols = config_read16(*cfg, 0x0);
  29. rows = config_read16(*cfg, 0x2);
  30. }
  31. if (is_feature_accepted(VIRTIO_CONSOLE_F_MULTIPORT)) {
  32. max_nr_ports = config_read32(*cfg, 0x4);
  33. }
  34. });
  35. dbgln("VirtIOConsole: cols: {}, rows: {}, max nr ports {}", cols, rows, max_nr_ports);
  36. success = setup_queues(2 + max_nr_ports * 2); // base receiveq/transmitq for port0 + 2 per every additional port
  37. }
  38. if (success) {
  39. finish_init();
  40. m_receive_buffer = make<RingBuffer>("VirtIOConsole Receive", RINGBUFFER_SIZE);
  41. m_transmit_buffer = make<RingBuffer>("VirtIOConsole Transmit", RINGBUFFER_SIZE);
  42. init_receive_buffer();
  43. }
  44. }
  45. }
  46. VirtIOConsole::~VirtIOConsole()
  47. {
  48. }
  49. void VirtIOConsole::init_receive_buffer()
  50. {
  51. auto& queue = get_queue(RECEIVEQ);
  52. ScopedSpinLock queue_lock(queue.lock());
  53. VirtIOQueueChain chain(queue);
  54. auto buffer_start = m_receive_buffer->start_of_region();
  55. auto did_add_buffer = chain.add_buffer_to_chain(buffer_start, RINGBUFFER_SIZE, BufferType::DeviceWritable);
  56. VERIFY(did_add_buffer);
  57. supply_chain_and_notify(RECEIVEQ, chain);
  58. }
  59. bool VirtIOConsole::handle_device_config_change()
  60. {
  61. dbgln("VirtIOConsole: Handle device config change");
  62. return true;
  63. }
  64. void VirtIOConsole::handle_queue_update(u16 queue_index)
  65. {
  66. dbgln_if(VIRTIO_DEBUG, "VirtIOConsole: Handle queue update");
  67. VERIFY(queue_index <= TRANSMITQ);
  68. switch (queue_index) {
  69. case RECEIVEQ: {
  70. auto& queue = get_queue(RECEIVEQ);
  71. ScopedSpinLock queue_lock(queue.lock());
  72. size_t used;
  73. VirtIOQueueChain popped_chain = queue.pop_used_buffer_chain(used);
  74. ScopedSpinLock ringbuffer_lock(m_receive_buffer->lock());
  75. auto used_space = m_receive_buffer->reserve_space(used).value();
  76. auto remaining_space = RINGBUFFER_SIZE - used;
  77. // Our algorithm always has only one buffer in the queue.
  78. VERIFY(!queue.new_data_available());
  79. popped_chain.release_buffer_slots_to_queue();
  80. VirtIOQueueChain new_chain(queue);
  81. if (remaining_space != 0) {
  82. new_chain.add_buffer_to_chain(used_space.offset(used), remaining_space, BufferType::DeviceWritable);
  83. supply_chain_and_notify(RECEIVEQ, new_chain);
  84. }
  85. evaluate_block_conditions();
  86. break;
  87. }
  88. case TRANSMITQ: {
  89. ScopedSpinLock ringbuffer_lock(m_transmit_buffer->lock());
  90. auto& queue = get_queue(TRANSMITQ);
  91. ScopedSpinLock queue_lock(queue.lock());
  92. size_t used;
  93. VirtIOQueueChain popped_chain = queue.pop_used_buffer_chain(used);
  94. do {
  95. popped_chain.for_each([this](PhysicalAddress address, size_t length) {
  96. m_transmit_buffer->reclaim_space(address, length);
  97. });
  98. popped_chain.release_buffer_slots_to_queue();
  99. popped_chain = queue.pop_used_buffer_chain(used);
  100. } while (!popped_chain.is_empty());
  101. // Unblock any IO tasks that were blocked because can_write() returned false
  102. evaluate_block_conditions();
  103. break;
  104. }
  105. default:
  106. VERIFY_NOT_REACHED();
  107. }
  108. }
  109. bool VirtIOConsole::can_read(const FileDescription&, size_t) const
  110. {
  111. return m_receive_buffer->used_bytes() > 0;
  112. }
  113. KResultOr<size_t> VirtIOConsole::read(FileDescription& desc, u64, UserOrKernelBuffer& buffer, size_t size)
  114. {
  115. if (!size)
  116. return 0;
  117. if (!can_read(desc, size))
  118. return EAGAIN;
  119. ScopedSpinLock ringbuffer_lock(m_receive_buffer->lock());
  120. auto bytes_copied = m_receive_buffer->copy_data_out(size, buffer);
  121. m_receive_buffer->reclaim_space(m_receive_buffer->start_of_used(), bytes_copied.value());
  122. return bytes_copied;
  123. }
  124. bool VirtIOConsole::can_write(const FileDescription&, size_t) const
  125. {
  126. return get_queue(TRANSMITQ).has_free_slots() && m_transmit_buffer->has_space();
  127. }
  128. KResultOr<size_t> VirtIOConsole::write(FileDescription& desc, u64, const UserOrKernelBuffer& data, size_t size)
  129. {
  130. if (!size)
  131. return 0;
  132. if (!can_write(desc, size))
  133. return EAGAIN;
  134. ScopedSpinLock ringbuffer_lock(m_transmit_buffer->lock());
  135. auto& queue = get_queue(TRANSMITQ);
  136. ScopedSpinLock queue_lock(queue.lock());
  137. VirtIOQueueChain chain(queue);
  138. size_t total_bytes_copied = 0;
  139. do {
  140. PhysicalAddress start_of_chunk;
  141. size_t length_of_chunk;
  142. if (!m_transmit_buffer->copy_data_in(data, total_bytes_copied, size - total_bytes_copied, start_of_chunk, length_of_chunk)) {
  143. chain.release_buffer_slots_to_queue();
  144. return EINVAL;
  145. }
  146. bool did_add_buffer = chain.add_buffer_to_chain(start_of_chunk, length_of_chunk, BufferType::DeviceReadable);
  147. VERIFY(did_add_buffer);
  148. total_bytes_copied += length_of_chunk;
  149. } while (total_bytes_copied < size && can_write(desc, size - total_bytes_copied));
  150. supply_chain_and_notify(TRANSMITQ, chain);
  151. return total_bytes_copied;
  152. }
  153. }