1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
use super::*;
impl DeviceManager {
/// Polls the bridge (vmnet) host fd for inbound frames and injects
/// them into the bridge VirtioNet RX queue. Thin shim that reads the
/// device's current MMIO-transport queue configuration, hands it to
/// `VirtioNet::poll_rx`, and returns whether any frame was injected.
/// Caller fires the used-ring interrupt on `true`.
pub fn poll_bridge_rx(&self) -> bool {
let Some(bridge_arc) = self.bridge_net.as_ref() else {
return false;
};
let Some(bridge_id) = self.bridge_net_device_id else {
return false;
};
let Some(device) = self.devices.get(&bridge_id) else {
return false;
};
let Some(mmio_arc) = device.mmio_state.as_ref() else {
return false;
};
// Build a snapshot of the RX queue (idx 0) from MMIO state.
let rx_qcfg = {
let Ok(mmio) = mmio_arc.read() else {
return false;
};
let qi = 0usize;
if !mmio.queue_ready[qi] || mmio.queue_num[qi] == 0 {
return false;
}
QueueConfig {
desc_addr: mmio.queue_desc[qi],
avail_addr: mmio.queue_driver[qi],
used_addr: mmio.queue_device[qi],
size: mmio.queue_num[qi],
ready: true,
gpa_base: self.guest_ram_gpa,
}
};
let Ok(dev) = bridge_arc.lock() else {
return false;
};
dev.poll_rx(&rx_qcfg)
}
/// Called from the vCPU run loop during WFI (guest idle). Returns
/// true if any data was injected (caller should trigger interrupt).
/// Thin shim that builds RX + TX `QueueConfig` snapshots from the
/// vsock device's MMIO state and forwards to
/// `VirtioVsock::poll_rx_injection`. The 400-line body that was here
/// previously now lives on the device.
pub fn poll_vsock_rx(&self) -> bool {
let Some(vsock_arc) = self.vsock.as_ref() else {
return false;
};
let Some(device) = self
.devices
.values()
.find(|d| d.info.device_type == DeviceType::VirtioVsock)
else {
return false;
};
let Some(mmio_arc) = device.mmio_state.as_ref() else {
return false;
};
let (rx_qcfg, tx_qcfg) = {
let Ok(mmio) = mmio_arc.read() else {
return false;
};
let rxi = 0usize;
if !mmio.queue_ready[rxi] || mmio.queue_num[rxi] == 0 {
return false;
}
let rx = QueueConfig {
desc_addr: mmio.queue_desc[rxi],
avail_addr: mmio.queue_driver[rxi],
used_addr: mmio.queue_device[rxi],
size: mmio.queue_num[rxi],
ready: true,
gpa_base: self.guest_ram_gpa,
};
let txi = 1usize;
let tx = if mmio.queue_ready[txi] && mmio.queue_num[txi] > 0 {
Some(QueueConfig {
desc_addr: mmio.queue_desc[txi],
avail_addr: mmio.queue_driver[txi],
used_addr: mmio.queue_device[txi],
size: mmio.queue_num[txi],
ready: true,
gpa_base: self.guest_ram_gpa,
})
} else {
None
};
(rx, tx)
};
let Ok(mut dev) = vsock_arc.lock() else {
return false;
};
dev.poll_rx_injection(&rx_qcfg, tx_qcfg.as_ref())
}
/// Queues host operator input (`data`) onto the virtio-console and injects
/// it into the guest RX queue (queue 0). Returns `true` if any descriptor
/// was filled, in which case the caller raises `INT_VRING` for the console.
///
/// Drives the interactive debug console: the `console_rx_worker` reads
/// operator keystrokes from the Unix socket and forwards them here. Passing
/// empty `data` just flushes any input buffered from a previous call.
pub fn console_inject_input(&self, data: &[u8]) -> bool {
let Some(console_arc) = self.console.as_ref() else {
return false;
};
let Some(device) = self
.devices
.values()
.find(|d| d.info.device_type == DeviceType::VirtioConsole)
else {
return false;
};
let Some(mmio_arc) = device.mmio_state.as_ref() else {
return false;
};
// RX is queue 0 for virtio-console.
let rx_qcfg = {
let Ok(mmio) = mmio_arc.read() else {
return false;
};
let rxi = 0usize;
if !mmio.queue_ready[rxi] || mmio.queue_num[rxi] == 0 {
if !data.is_empty() {
// ABX-388: operator typed before the guest set up the console
// RX queue — the bytes are dropped here, not buffered.
tracing::debug!(
ready = mmio.queue_ready[rxi],
num = mmio.queue_num[rxi],
dropped = data.len(),
"debug-console: RX queue 0 not ready, dropping operator input"
);
}
return false;
}
QueueConfig {
desc_addr: mmio.queue_desc[rxi],
avail_addr: mmio.queue_driver[rxi],
used_addr: mmio.queue_device[rxi],
size: mmio.queue_num[rxi],
ready: true,
gpa_base: self.guest_ram_gpa,
}
};
let Some(ram_base) = self.guest_ram_base else {
return false;
};
if self.guest_ram_size == 0 {
return false;
}
// SAFETY: `ram_base` is the platform hypervisor's guest-RAM mapping,
// valid for `guest_ram_size` bytes — same contract as the other
// guest-memory slices built in this module.
let guest_mem = unsafe { std::slice::from_raw_parts_mut(ram_base, self.guest_ram_size) };
let Ok(mut dev) = console_arc.lock() else {
return false;
};
if !data.is_empty() {
let _ = dev.queue_input(data);
}
dev.process_rx_queue(guest_mem, &rx_qcfg).unwrap_or(false)
}
}