Serenity Operating System
1/*
2 * Copyright (c) 2021, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <Kernel/Bus/VirtIO/RNG.h>
8#include <Kernel/Sections.h>
9
10namespace Kernel::VirtIO {
11
12UNMAP_AFTER_INIT NonnullLockRefPtr<RNG> RNG::must_create(PCI::DeviceIdentifier const& device_identifier)
13{
14 return adopt_lock_ref_if_nonnull(new RNG(device_identifier)).release_nonnull();
15}
16
17UNMAP_AFTER_INIT void RNG::initialize()
18{
19 Device::initialize();
20 bool success = negotiate_features([&](auto) {
21 return 0;
22 });
23 if (success) {
24 success = setup_queues(1);
25 }
26 if (success) {
27 finish_init();
28 m_entropy_buffer = MM.allocate_contiguous_kernel_region(PAGE_SIZE, "VirtIO::RNG"sv, Memory::Region::Access::ReadWrite).release_value();
29 if (m_entropy_buffer) {
30 memset(m_entropy_buffer->vaddr().as_ptr(), 0, m_entropy_buffer->size());
31 request_entropy_from_host();
32 }
33 }
34}
35
36UNMAP_AFTER_INIT RNG::RNG(PCI::DeviceIdentifier const& device_identifier)
37 : VirtIO::Device(device_identifier)
38{
39}
40
41bool RNG::handle_device_config_change()
42{
43 return false; // Device has no config
44}
45
46void RNG::handle_queue_update(u16 queue_index)
47{
48 VERIFY(queue_index == REQUESTQ);
49 size_t available_entropy = 0, used;
50 auto& queue = get_queue(REQUESTQ);
51 {
52 SpinlockLocker lock(queue.lock());
53 auto chain = queue.pop_used_buffer_chain(used);
54 if (chain.is_empty())
55 return;
56 VERIFY(chain.length() == 1);
57 chain.for_each([&available_entropy](PhysicalAddress, size_t length) {
58 available_entropy = length;
59 });
60 chain.release_buffer_slots_to_queue();
61 }
62 dbgln_if(VIRTIO_DEBUG, "VirtIO::RNG: received {} bytes of entropy!", available_entropy);
63 for (auto i = 0u; i < available_entropy; i++) {
64 m_entropy_source.add_random_event(m_entropy_buffer->vaddr().as_ptr()[i]);
65 }
66 // TODO: When should we get some more entropy?
67}
68
69void RNG::request_entropy_from_host()
70{
71 auto& queue = get_queue(REQUESTQ);
72 SpinlockLocker lock(queue.lock());
73 QueueChain chain(queue);
74 chain.add_buffer_to_chain(m_entropy_buffer->physical_page(0)->paddr(), PAGE_SIZE, BufferType::DeviceWritable);
75 supply_chain_and_notify(REQUESTQ, chain);
76}
77
78}