1 // Copyright 2024 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 //! Virtio console device control queue handling.
6
7 use std::collections::VecDeque;
8 use std::io::Write;
9
10 use anyhow::anyhow;
11 use anyhow::Context;
12 use base::debug;
13 use base::error;
14 use zerocopy::AsBytes;
15
16 use crate::virtio::console::worker::WorkerPort;
17 use crate::virtio::device_constants::console::virtio_console_control;
18 use crate::virtio::device_constants::console::VIRTIO_CONSOLE_CONSOLE_PORT;
19 use crate::virtio::device_constants::console::VIRTIO_CONSOLE_DEVICE_ADD;
20 use crate::virtio::device_constants::console::VIRTIO_CONSOLE_DEVICE_READY;
21 use crate::virtio::device_constants::console::VIRTIO_CONSOLE_PORT_NAME;
22 use crate::virtio::device_constants::console::VIRTIO_CONSOLE_PORT_OPEN;
23 use crate::virtio::device_constants::console::VIRTIO_CONSOLE_PORT_READY;
24 use crate::virtio::Queue;
25 use crate::virtio::Reader;
26
27 pub type ControlMsgBytes = Box<[u8]>;
28
control_msg(id: u32, event: u16, value: u16, extra_bytes: &[u8]) -> ControlMsgBytes29 fn 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
process_control_msg( reader: &mut Reader, ports: &[WorkerPort], pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>, ) -> anyhow::Result<()>42 fn 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 // value of 1 indicates success, and 0 indicates failure
56 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 // TODO(dverkamp): cap the size of `pending_receive_control_msgs` somehow
63 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 // value of 1 indicates success, and 0 indicates failure
83 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 // Currently, port state change is not supported, default is open.
111 // And only print debug info here.
112 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
process_control_transmit_queue( queue: &mut Queue, ports: &[WorkerPort], pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>, )122 pub 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, 0);
137 needs_interrupt = true;
138 }
139
140 if needs_interrupt {
141 queue.trigger_interrupt();
142 }
143 }
144
process_control_receive_queue( queue: &mut Queue, pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>, )145 pub 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 // Get a reply to copy into `avail_desc`. This should never fail since we check that
157 // `pending_receive_control_msgs` is not empty in the loop condition.
158 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(avail_desc, len);
171 needs_interrupt = true;
172 }
173
174 if needs_interrupt {
175 queue.trigger_interrupt();
176 }
177 }
178