The Pedigree Project 0.1
VirtioRng.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "VirtioRng.h"
3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/machine/Device.h"
5#include "pedigree/kernel/machine/Pci.h"
6#include "pedigree/kernel/panic.h"
7#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
8#include "pedigree/kernel/processor/VirtualAddressSpace.h"
9#include "pedigree/kernel/time/Time.h"
10#include "pedigree/kernel/utilities/SecureRandom.h"
11#include "pedigree/kernel/utilities/lib.h"
12
13VirtioRng::VirtioRng(Device* pci)
14 : m_Pci(pci), m_Transport(pci), m_Queue(), m_Data("virtio-rng data"), m_Shutdown(false) {}
15
16VirtioRng::~VirtioRng() {
17 shutdown();
18 if (m_Data.virtualAddress()) {
19 pedigree_random::erase(m_Data.virtualAddress(), PhysicalMemoryManager::getPageSize());
20 }
21}
22
23bool VirtioRng::seedKernel() {
24 if (!m_Pci || m_Pci->getPciVendorId() != 0x1af4 ||
25 (m_Pci->getPciDeviceId() != 0x1044 && m_Pci->getPciDeviceId() != 0x1005) ||
26 !m_Transport.initialise() || !m_Transport.negotiate(0) ||
27 !m_Transport.setupQueue(0, m_Queue)) {
28 return false;
29 }
30
31 auto& memory = PhysicalMemoryManager::instance();
33 if (!memory.allocateRegion(m_Data, 1, PhysicalMemoryManager::continuous, flags)) {
34 return false;
35 }
36 // This one-shot bootstrap runs during module initialization, where IRQ
37 // handler retirement cannot wait for a thread-context dispatch to finish.
38 if (!m_Transport.ready() || !PciBus::instance().updateCommand(m_Pci, 0, 0x400U)) {
39 return false;
40 }
41
42 uint8_t seed[32] = {};
43 size_t received = 0;
44 const auto deadline = Time::getTicks() + 5 * Time::Multiplier::Second;
45 while (received < sizeof(seed) && Time::getTicks() < deadline) {
46 const size_t requested = sizeof(seed) - received;
47 Virtio::Buffer buffer = {m_Data.physicalAddress(), static_cast<uint32_t>(requested), true};
48 if (!m_Queue.submit(&buffer, 1, this)) {
49 break;
50 }
51 m_Transport.notify(0);
52
53 Virtio::Completion completion{};
54 bool completed = false;
55 while (Time::getTicks() < deadline) {
56 if (m_Queue.pop(completion)) {
57 completed = true;
58 break;
59 }
60 Time::delay(Time::Multiplier::Millisecond);
61 }
62 if (!completed) {
63 ERROR("virtio-rng: seed request timed out");
64 break;
65 }
66 if (completion.cookie != this || !completion.length || completion.length > requested) {
67 ERROR("virtio-rng: malformed completion");
68 break;
69 }
70 FENCE();
71 MemoryCopy(seed + received, m_Data.virtualAddress(), completion.length);
72 received += completion.length;
73 }
74
75 const bool seeded = received == sizeof(seed) && secure_random_seed(seed, sizeof(seed));
76 shutdown();
77 pedigree_random::erase(seed, sizeof(seed));
78 if (seeded) {
79 NOTICE("virtio-rng: seeded secure random generator");
80 }
81 return seeded;
82}
83
84void VirtioRng::shutdown() {
85 if (m_Shutdown) {
86 return;
87 }
88 if (!m_Transport.reset()) {
89 panic("virtio-rng: device did not stop DMA");
90 }
91 m_Queue.stop();
92 m_Shutdown = true;
93}
uint16_t getPciDeviceId()
Definition Device.h:228
uint16_t getPciVendorId()
Definition Device.h:224
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
static PhysicalMemoryManager & instance()
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:117