Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/console/control.rs
5394 views
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
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
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
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);
137
needs_interrupt = true;
138
}
139
140
if needs_interrupt {
141
queue.trigger_interrupt();
142
}
143
}
144
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_with_bytes_written(avail_desc, len);
171
needs_interrupt = true;
172
}
173
174
if needs_interrupt {
175
queue.trigger_interrupt();
176
}
177
}
178
179