xref: /aosp_15_r20/external/crosvm/devices/src/virtio/console/control.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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