devices/virtio/console/
control.rs1use std::collections::VecDeque;
8use std::io::Write;
9
10use anyhow::anyhow;
11use anyhow::Context;
12use base::debug;
13use base::error;
14use zerocopy::IntoBytes;
15
16use crate::virtio::console::worker::WorkerPort;
17use crate::virtio::device_constants::console::virtio_console_control;
18use crate::virtio::device_constants::console::VIRTIO_CONSOLE_CONSOLE_PORT;
19use crate::virtio::device_constants::console::VIRTIO_CONSOLE_DEVICE_ADD;
20use crate::virtio::device_constants::console::VIRTIO_CONSOLE_DEVICE_READY;
21use crate::virtio::device_constants::console::VIRTIO_CONSOLE_PORT_NAME;
22use crate::virtio::device_constants::console::VIRTIO_CONSOLE_PORT_OPEN;
23use crate::virtio::device_constants::console::VIRTIO_CONSOLE_PORT_READY;
24use crate::virtio::Queue;
25use crate::virtio::Reader;
26
27pub type ControlMsgBytes = Box<[u8]>;
28
29fn control_msg(id: u32, event: u16, value: u16, extra_bytes: &[u8]) -> ControlMsgBytes {
30 virtio_console_control {
31 id: id.into(),
32 event: event.into(),
33 value: value.into(),
34 }
35 .as_bytes()
36 .iter()
37 .chain(extra_bytes.iter())
38 .copied()
39 .collect()
40}
41
42fn process_control_msg(
43 reader: &mut Reader,
44 ports: &[WorkerPort],
45 pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>,
46) -> anyhow::Result<()> {
47 let ctrl_msg: virtio_console_control =
48 reader.read_obj().context("failed to read from reader")?;
49 let id = ctrl_msg.id.to_native();
50 let event = ctrl_msg.event.to_native();
51 let value = ctrl_msg.value.to_native();
52
53 match event {
54 VIRTIO_CONSOLE_DEVICE_READY => {
55 if value != 1 {
57 return Err(anyhow!("console device ready failure ({value})"));
58 }
59
60 for (index, port) in ports.iter().enumerate() {
61 let port_id = index as u32;
62 pending_receive_control_msgs.push_back(control_msg(
64 port_id,
65 VIRTIO_CONSOLE_DEVICE_ADD,
66 0,
67 &[],
68 ));
69
70 if let Some(name) = port.name() {
71 pending_receive_control_msgs.push_back(control_msg(
72 port_id,
73 VIRTIO_CONSOLE_PORT_NAME,
74 0,
75 name.as_bytes(),
76 ));
77 }
78 }
79 Ok(())
80 }
81 VIRTIO_CONSOLE_PORT_READY => {
82 if value != 1 {
84 return Err(anyhow!("console port{id} ready failure ({value})"));
85 }
86
87 let port = ports
88 .get(id as usize)
89 .with_context(|| format!("invalid port id {id}"))?;
90
91 pending_receive_control_msgs.push_back(control_msg(
92 id,
93 VIRTIO_CONSOLE_PORT_OPEN,
94 1,
95 &[],
96 ));
97
98 if port.is_console() {
99 pending_receive_control_msgs.push_back(control_msg(
100 id,
101 VIRTIO_CONSOLE_CONSOLE_PORT,
102 1,
103 &[],
104 ));
105 }
106 Ok(())
107 }
108 VIRTIO_CONSOLE_PORT_OPEN => {
109 match value {
110 0 => debug!("console port{id} close"),
113 1 => debug!("console port{id} open"),
114 _ => error!("console port{id} unknown value {value}"),
115 }
116 Ok(())
117 }
118 _ => Err(anyhow!("unexpected control event {}", event)),
119 }
120}
121
122pub fn process_control_transmit_queue(
123 queue: &mut Queue,
124 ports: &[WorkerPort],
125 pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>,
126) {
127 let mut needs_interrupt = false;
128
129 while let Some(mut avail_desc) = queue.pop() {
130 if let Err(e) =
131 process_control_msg(&mut avail_desc.reader, ports, pending_receive_control_msgs)
132 {
133 error!("failed to handle control msg: {:#}", e);
134 }
135
136 queue.add_used(avail_desc);
137 needs_interrupt = true;
138 }
139
140 if needs_interrupt {
141 queue.trigger_interrupt();
142 }
143}
144
145pub fn process_control_receive_queue(
146 queue: &mut Queue,
147 pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>,
148) {
149 let mut needs_interrupt = false;
150
151 while !pending_receive_control_msgs.is_empty() {
152 let Some(mut avail_desc) = queue.pop() else {
153 break;
154 };
155
156 let reply = pending_receive_control_msgs
159 .pop_front()
160 .expect("missing reply");
161
162 let len = match avail_desc.writer.write_all(&reply) {
163 Ok(()) => avail_desc.writer.bytes_written() as u32,
164 Err(e) => {
165 error!("failed to write control receiveq reply: {}", e);
166 0
167 }
168 };
169
170 queue.add_used_with_bytes_written(avail_desc, len);
171 needs_interrupt = true;
172 }
173
174 if needs_interrupt {
175 queue.trigger_interrupt();
176 }
177}