/src/crosvm/devices/src/virtio/console/control.rs
Line | Count | Source |
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::IntoBytes; |
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 | | |
29 | 0 | fn control_msg(id: u32, event: u16, value: u16, extra_bytes: &[u8]) -> ControlMsgBytes { |
30 | 0 | virtio_console_control { |
31 | 0 | id: id.into(), |
32 | 0 | event: event.into(), |
33 | 0 | value: value.into(), |
34 | 0 | } |
35 | 0 | .as_bytes() |
36 | 0 | .iter() |
37 | 0 | .chain(extra_bytes.iter()) |
38 | 0 | .copied() |
39 | 0 | .collect() |
40 | 0 | } |
41 | | |
42 | 0 | fn process_control_msg( |
43 | 0 | reader: &mut Reader, |
44 | 0 | ports: &[WorkerPort], |
45 | 0 | pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>, |
46 | 0 | ) -> anyhow::Result<()> { |
47 | 0 | let ctrl_msg: virtio_console_control = |
48 | 0 | reader.read_obj().context("failed to read from reader")?; |
49 | 0 | let id = ctrl_msg.id.to_native(); |
50 | 0 | let event = ctrl_msg.event.to_native(); |
51 | 0 | let value = ctrl_msg.value.to_native(); |
52 | | |
53 | 0 | match event { |
54 | | VIRTIO_CONSOLE_DEVICE_READY => { |
55 | | // value of 1 indicates success, and 0 indicates failure |
56 | 0 | if value != 1 { |
57 | 0 | return Err(anyhow!("console device ready failure ({value})")); |
58 | 0 | } |
59 | | |
60 | 0 | for (index, port) in ports.iter().enumerate() { |
61 | 0 | let port_id = index as u32; |
62 | | // TODO(dverkamp): cap the size of `pending_receive_control_msgs` somehow |
63 | 0 | pending_receive_control_msgs.push_back(control_msg( |
64 | 0 | port_id, |
65 | | VIRTIO_CONSOLE_DEVICE_ADD, |
66 | | 0, |
67 | 0 | &[], |
68 | | )); |
69 | | |
70 | 0 | if let Some(name) = port.name() { |
71 | 0 | pending_receive_control_msgs.push_back(control_msg( |
72 | 0 | port_id, |
73 | 0 | VIRTIO_CONSOLE_PORT_NAME, |
74 | 0 | 0, |
75 | 0 | name.as_bytes(), |
76 | 0 | )); |
77 | 0 | } |
78 | | } |
79 | 0 | Ok(()) |
80 | | } |
81 | | VIRTIO_CONSOLE_PORT_READY => { |
82 | | // value of 1 indicates success, and 0 indicates failure |
83 | 0 | if value != 1 { |
84 | 0 | return Err(anyhow!("console port{id} ready failure ({value})")); |
85 | 0 | } |
86 | | |
87 | 0 | let port = ports |
88 | 0 | .get(id as usize) |
89 | 0 | .with_context(|| format!("invalid port id {id}"))?; |
90 | | |
91 | 0 | pending_receive_control_msgs.push_back(control_msg( |
92 | 0 | id, |
93 | | VIRTIO_CONSOLE_PORT_OPEN, |
94 | | 1, |
95 | 0 | &[], |
96 | | )); |
97 | | |
98 | 0 | if port.is_console() { |
99 | 0 | pending_receive_control_msgs.push_back(control_msg( |
100 | 0 | id, |
101 | 0 | VIRTIO_CONSOLE_CONSOLE_PORT, |
102 | 0 | 1, |
103 | 0 | &[], |
104 | 0 | )); |
105 | 0 | } |
106 | 0 | Ok(()) |
107 | | } |
108 | | VIRTIO_CONSOLE_PORT_OPEN => { |
109 | 0 | match value { |
110 | | // Currently, port state change is not supported, default is open. |
111 | | // And only print debug info here. |
112 | 0 | 0 => debug!("console port{id} close"), |
113 | 0 | 1 => debug!("console port{id} open"), |
114 | 0 | _ => error!("console port{id} unknown value {value}"), |
115 | | } |
116 | 0 | Ok(()) |
117 | | } |
118 | 0 | _ => Err(anyhow!("unexpected control event {}", event)), |
119 | | } |
120 | 0 | } |
121 | | |
122 | 0 | pub fn process_control_transmit_queue( |
123 | 0 | queue: &mut Queue, |
124 | 0 | ports: &[WorkerPort], |
125 | 0 | pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>, |
126 | 0 | ) { |
127 | 0 | let mut needs_interrupt = false; |
128 | | |
129 | 0 | while let Some(mut avail_desc) = queue.pop() { |
130 | 0 | if let Err(e) = |
131 | 0 | process_control_msg(&mut avail_desc.reader, ports, pending_receive_control_msgs) |
132 | | { |
133 | 0 | error!("failed to handle control msg: {:#}", e); |
134 | 0 | } |
135 | | |
136 | 0 | queue.add_used(avail_desc); |
137 | 0 | needs_interrupt = true; |
138 | | } |
139 | | |
140 | 0 | if needs_interrupt { |
141 | 0 | queue.trigger_interrupt(); |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | 0 | pub fn process_control_receive_queue( |
146 | 0 | queue: &mut Queue, |
147 | 0 | pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>, |
148 | 0 | ) { |
149 | 0 | let mut needs_interrupt = false; |
150 | | |
151 | 0 | while !pending_receive_control_msgs.is_empty() { |
152 | 0 | let Some(mut avail_desc) = queue.pop() else { |
153 | 0 | 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 | 0 | let reply = pending_receive_control_msgs |
159 | 0 | .pop_front() |
160 | 0 | .expect("missing reply"); |
161 | | |
162 | 0 | let len = match avail_desc.writer.write_all(&reply) { |
163 | 0 | Ok(()) => avail_desc.writer.bytes_written() as u32, |
164 | 0 | Err(e) => { |
165 | 0 | error!("failed to write control receiveq reply: {}", e); |
166 | 0 | 0 |
167 | | } |
168 | | }; |
169 | | |
170 | 0 | queue.add_used_with_bytes_written(avail_desc, len); |
171 | 0 | needs_interrupt = true; |
172 | | } |
173 | | |
174 | 0 | if needs_interrupt { |
175 | 0 | queue.trigger_interrupt(); |
176 | 0 | } |
177 | 0 | } |