xref: /aosp_15_r20/external/crosvm/devices/src/virtio/console/control.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2024 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker //! Virtio console device control queue handling.
6*bb4ee6a4SAndroid Build Coastguard Worker 
7*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::VecDeque;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Write;
9*bb4ee6a4SAndroid Build Coastguard Worker 
10*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::anyhow;
11*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Context;
12*bb4ee6a4SAndroid Build Coastguard Worker use base::debug;
13*bb4ee6a4SAndroid Build Coastguard Worker use base::error;
14*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::AsBytes;
15*bb4ee6a4SAndroid Build Coastguard Worker 
16*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::console::worker::WorkerPort;
17*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::device_constants::console::virtio_console_control;
18*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::device_constants::console::VIRTIO_CONSOLE_CONSOLE_PORT;
19*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::device_constants::console::VIRTIO_CONSOLE_DEVICE_ADD;
20*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::device_constants::console::VIRTIO_CONSOLE_DEVICE_READY;
21*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::device_constants::console::VIRTIO_CONSOLE_PORT_NAME;
22*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::device_constants::console::VIRTIO_CONSOLE_PORT_OPEN;
23*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::device_constants::console::VIRTIO_CONSOLE_PORT_READY;
24*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Queue;
25*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Reader;
26*bb4ee6a4SAndroid Build Coastguard Worker 
27*bb4ee6a4SAndroid Build Coastguard Worker pub type ControlMsgBytes = Box<[u8]>;
28*bb4ee6a4SAndroid Build Coastguard Worker 
control_msg(id: u32, event: u16, value: u16, extra_bytes: &[u8]) -> ControlMsgBytes29*bb4ee6a4SAndroid Build Coastguard Worker fn control_msg(id: u32, event: u16, value: u16, extra_bytes: &[u8]) -> ControlMsgBytes {
30*bb4ee6a4SAndroid Build Coastguard Worker     virtio_console_control {
31*bb4ee6a4SAndroid Build Coastguard Worker         id: id.into(),
32*bb4ee6a4SAndroid Build Coastguard Worker         event: event.into(),
33*bb4ee6a4SAndroid Build Coastguard Worker         value: value.into(),
34*bb4ee6a4SAndroid Build Coastguard Worker     }
35*bb4ee6a4SAndroid Build Coastguard Worker     .as_bytes()
36*bb4ee6a4SAndroid Build Coastguard Worker     .iter()
37*bb4ee6a4SAndroid Build Coastguard Worker     .chain(extra_bytes.iter())
38*bb4ee6a4SAndroid Build Coastguard Worker     .copied()
39*bb4ee6a4SAndroid Build Coastguard Worker     .collect()
40*bb4ee6a4SAndroid Build Coastguard Worker }
41*bb4ee6a4SAndroid Build Coastguard Worker 
process_control_msg( reader: &mut Reader, ports: &[WorkerPort], pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>, ) -> anyhow::Result<()>42*bb4ee6a4SAndroid Build Coastguard Worker fn process_control_msg(
43*bb4ee6a4SAndroid Build Coastguard Worker     reader: &mut Reader,
44*bb4ee6a4SAndroid Build Coastguard Worker     ports: &[WorkerPort],
45*bb4ee6a4SAndroid Build Coastguard Worker     pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>,
46*bb4ee6a4SAndroid Build Coastguard Worker ) -> anyhow::Result<()> {
47*bb4ee6a4SAndroid Build Coastguard Worker     let ctrl_msg: virtio_console_control =
48*bb4ee6a4SAndroid Build Coastguard Worker         reader.read_obj().context("failed to read from reader")?;
49*bb4ee6a4SAndroid Build Coastguard Worker     let id = ctrl_msg.id.to_native();
50*bb4ee6a4SAndroid Build Coastguard Worker     let event = ctrl_msg.event.to_native();
51*bb4ee6a4SAndroid Build Coastguard Worker     let value = ctrl_msg.value.to_native();
52*bb4ee6a4SAndroid Build Coastguard Worker 
53*bb4ee6a4SAndroid Build Coastguard Worker     match event {
54*bb4ee6a4SAndroid Build Coastguard Worker         VIRTIO_CONSOLE_DEVICE_READY => {
55*bb4ee6a4SAndroid Build Coastguard Worker             // value of 1 indicates success, and 0 indicates failure
56*bb4ee6a4SAndroid Build Coastguard Worker             if value != 1 {
57*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(anyhow!("console device ready failure ({value})"));
58*bb4ee6a4SAndroid Build Coastguard Worker             }
59*bb4ee6a4SAndroid Build Coastguard Worker 
60*bb4ee6a4SAndroid Build Coastguard Worker             for (index, port) in ports.iter().enumerate() {
61*bb4ee6a4SAndroid Build Coastguard Worker                 let port_id = index as u32;
62*bb4ee6a4SAndroid Build Coastguard Worker                 // TODO(dverkamp): cap the size of `pending_receive_control_msgs` somehow
63*bb4ee6a4SAndroid Build Coastguard Worker                 pending_receive_control_msgs.push_back(control_msg(
64*bb4ee6a4SAndroid Build Coastguard Worker                     port_id,
65*bb4ee6a4SAndroid Build Coastguard Worker                     VIRTIO_CONSOLE_DEVICE_ADD,
66*bb4ee6a4SAndroid Build Coastguard Worker                     0,
67*bb4ee6a4SAndroid Build Coastguard Worker                     &[],
68*bb4ee6a4SAndroid Build Coastguard Worker                 ));
69*bb4ee6a4SAndroid Build Coastguard Worker 
70*bb4ee6a4SAndroid Build Coastguard Worker                 if let Some(name) = port.name() {
71*bb4ee6a4SAndroid Build Coastguard Worker                     pending_receive_control_msgs.push_back(control_msg(
72*bb4ee6a4SAndroid Build Coastguard Worker                         port_id,
73*bb4ee6a4SAndroid Build Coastguard Worker                         VIRTIO_CONSOLE_PORT_NAME,
74*bb4ee6a4SAndroid Build Coastguard Worker                         0,
75*bb4ee6a4SAndroid Build Coastguard Worker                         name.as_bytes(),
76*bb4ee6a4SAndroid Build Coastguard Worker                     ));
77*bb4ee6a4SAndroid Build Coastguard Worker                 }
78*bb4ee6a4SAndroid Build Coastguard Worker             }
79*bb4ee6a4SAndroid Build Coastguard Worker             Ok(())
80*bb4ee6a4SAndroid Build Coastguard Worker         }
81*bb4ee6a4SAndroid Build Coastguard Worker         VIRTIO_CONSOLE_PORT_READY => {
82*bb4ee6a4SAndroid Build Coastguard Worker             // value of 1 indicates success, and 0 indicates failure
83*bb4ee6a4SAndroid Build Coastguard Worker             if value != 1 {
84*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(anyhow!("console port{id} ready failure ({value})"));
85*bb4ee6a4SAndroid Build Coastguard Worker             }
86*bb4ee6a4SAndroid Build Coastguard Worker 
87*bb4ee6a4SAndroid Build Coastguard Worker             let port = ports
88*bb4ee6a4SAndroid Build Coastguard Worker                 .get(id as usize)
89*bb4ee6a4SAndroid Build Coastguard Worker                 .with_context(|| format!("invalid port id {id}"))?;
90*bb4ee6a4SAndroid Build Coastguard Worker 
91*bb4ee6a4SAndroid Build Coastguard Worker             pending_receive_control_msgs.push_back(control_msg(
92*bb4ee6a4SAndroid Build Coastguard Worker                 id,
93*bb4ee6a4SAndroid Build Coastguard Worker                 VIRTIO_CONSOLE_PORT_OPEN,
94*bb4ee6a4SAndroid Build Coastguard Worker                 1,
95*bb4ee6a4SAndroid Build Coastguard Worker                 &[],
96*bb4ee6a4SAndroid Build Coastguard Worker             ));
97*bb4ee6a4SAndroid Build Coastguard Worker 
98*bb4ee6a4SAndroid Build Coastguard Worker             if port.is_console() {
99*bb4ee6a4SAndroid Build Coastguard Worker                 pending_receive_control_msgs.push_back(control_msg(
100*bb4ee6a4SAndroid Build Coastguard Worker                     id,
101*bb4ee6a4SAndroid Build Coastguard Worker                     VIRTIO_CONSOLE_CONSOLE_PORT,
102*bb4ee6a4SAndroid Build Coastguard Worker                     1,
103*bb4ee6a4SAndroid Build Coastguard Worker                     &[],
104*bb4ee6a4SAndroid Build Coastguard Worker                 ));
105*bb4ee6a4SAndroid Build Coastguard Worker             }
106*bb4ee6a4SAndroid Build Coastguard Worker             Ok(())
107*bb4ee6a4SAndroid Build Coastguard Worker         }
108*bb4ee6a4SAndroid Build Coastguard Worker         VIRTIO_CONSOLE_PORT_OPEN => {
109*bb4ee6a4SAndroid Build Coastguard Worker             match value {
110*bb4ee6a4SAndroid Build Coastguard Worker                 // Currently, port state change is not supported, default is open.
111*bb4ee6a4SAndroid Build Coastguard Worker                 // And only print debug info here.
112*bb4ee6a4SAndroid Build Coastguard Worker                 0 => debug!("console port{id} close"),
113*bb4ee6a4SAndroid Build Coastguard Worker                 1 => debug!("console port{id} open"),
114*bb4ee6a4SAndroid Build Coastguard Worker                 _ => error!("console port{id} unknown value {value}"),
115*bb4ee6a4SAndroid Build Coastguard Worker             }
116*bb4ee6a4SAndroid Build Coastguard Worker             Ok(())
117*bb4ee6a4SAndroid Build Coastguard Worker         }
118*bb4ee6a4SAndroid Build Coastguard Worker         _ => Err(anyhow!("unexpected control event {}", event)),
119*bb4ee6a4SAndroid Build Coastguard Worker     }
120*bb4ee6a4SAndroid Build Coastguard Worker }
121*bb4ee6a4SAndroid Build Coastguard Worker 
process_control_transmit_queue( queue: &mut Queue, ports: &[WorkerPort], pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>, )122*bb4ee6a4SAndroid Build Coastguard Worker pub fn process_control_transmit_queue(
123*bb4ee6a4SAndroid Build Coastguard Worker     queue: &mut Queue,
124*bb4ee6a4SAndroid Build Coastguard Worker     ports: &[WorkerPort],
125*bb4ee6a4SAndroid Build Coastguard Worker     pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>,
126*bb4ee6a4SAndroid Build Coastguard Worker ) {
127*bb4ee6a4SAndroid Build Coastguard Worker     let mut needs_interrupt = false;
128*bb4ee6a4SAndroid Build Coastguard Worker 
129*bb4ee6a4SAndroid Build Coastguard Worker     while let Some(mut avail_desc) = queue.pop() {
130*bb4ee6a4SAndroid Build Coastguard Worker         if let Err(e) =
131*bb4ee6a4SAndroid Build Coastguard Worker             process_control_msg(&mut avail_desc.reader, ports, pending_receive_control_msgs)
132*bb4ee6a4SAndroid Build Coastguard Worker         {
133*bb4ee6a4SAndroid Build Coastguard Worker             error!("failed to handle control msg: {:#}", e);
134*bb4ee6a4SAndroid Build Coastguard Worker         }
135*bb4ee6a4SAndroid Build Coastguard Worker 
136*bb4ee6a4SAndroid Build Coastguard Worker         queue.add_used(avail_desc, 0);
137*bb4ee6a4SAndroid Build Coastguard Worker         needs_interrupt = true;
138*bb4ee6a4SAndroid Build Coastguard Worker     }
139*bb4ee6a4SAndroid Build Coastguard Worker 
140*bb4ee6a4SAndroid Build Coastguard Worker     if needs_interrupt {
141*bb4ee6a4SAndroid Build Coastguard Worker         queue.trigger_interrupt();
142*bb4ee6a4SAndroid Build Coastguard Worker     }
143*bb4ee6a4SAndroid Build Coastguard Worker }
144*bb4ee6a4SAndroid Build Coastguard Worker 
process_control_receive_queue( queue: &mut Queue, pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>, )145*bb4ee6a4SAndroid Build Coastguard Worker pub fn process_control_receive_queue(
146*bb4ee6a4SAndroid Build Coastguard Worker     queue: &mut Queue,
147*bb4ee6a4SAndroid Build Coastguard Worker     pending_receive_control_msgs: &mut VecDeque<ControlMsgBytes>,
148*bb4ee6a4SAndroid Build Coastguard Worker ) {
149*bb4ee6a4SAndroid Build Coastguard Worker     let mut needs_interrupt = false;
150*bb4ee6a4SAndroid Build Coastguard Worker 
151*bb4ee6a4SAndroid Build Coastguard Worker     while !pending_receive_control_msgs.is_empty() {
152*bb4ee6a4SAndroid Build Coastguard Worker         let Some(mut avail_desc) = queue.pop() else {
153*bb4ee6a4SAndroid Build Coastguard Worker             break;
154*bb4ee6a4SAndroid Build Coastguard Worker         };
155*bb4ee6a4SAndroid Build Coastguard Worker 
156*bb4ee6a4SAndroid Build Coastguard Worker         // Get a reply to copy into `avail_desc`. This should never fail since we check that
157*bb4ee6a4SAndroid Build Coastguard Worker         // `pending_receive_control_msgs` is not empty in the loop condition.
158*bb4ee6a4SAndroid Build Coastguard Worker         let reply = pending_receive_control_msgs
159*bb4ee6a4SAndroid Build Coastguard Worker             .pop_front()
160*bb4ee6a4SAndroid Build Coastguard Worker             .expect("missing reply");
161*bb4ee6a4SAndroid Build Coastguard Worker 
162*bb4ee6a4SAndroid Build Coastguard Worker         let len = match avail_desc.writer.write_all(&reply) {
163*bb4ee6a4SAndroid Build Coastguard Worker             Ok(()) => avail_desc.writer.bytes_written() as u32,
164*bb4ee6a4SAndroid Build Coastguard Worker             Err(e) => {
165*bb4ee6a4SAndroid Build Coastguard Worker                 error!("failed to write control receiveq reply: {}", e);
166*bb4ee6a4SAndroid Build Coastguard Worker                 0
167*bb4ee6a4SAndroid Build Coastguard Worker             }
168*bb4ee6a4SAndroid Build Coastguard Worker         };
169*bb4ee6a4SAndroid Build Coastguard Worker 
170*bb4ee6a4SAndroid Build Coastguard Worker         queue.add_used(avail_desc, len);
171*bb4ee6a4SAndroid Build Coastguard Worker         needs_interrupt = true;
172*bb4ee6a4SAndroid Build Coastguard Worker     }
173*bb4ee6a4SAndroid Build Coastguard Worker 
174*bb4ee6a4SAndroid Build Coastguard Worker     if needs_interrupt {
175*bb4ee6a4SAndroid Build Coastguard Worker         queue.trigger_interrupt();
176*bb4ee6a4SAndroid Build Coastguard Worker     }
177*bb4ee6a4SAndroid Build Coastguard Worker }
178