@@ -2,6 +2,7 @@ use std::cmp;
22use std:: collections:: VecDeque ;
33use std:: io;
44use std:: io:: Write ;
5+ use std:: mem:: { size_of, size_of_val} ;
56use std:: os:: unix:: io:: { AsRawFd , RawFd } ;
67use std:: result;
78use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
@@ -16,14 +17,23 @@ use super::super::{
1617 ActivateError , ActivateResult , ConsoleError , DeviceState , Queue as VirtQueue , VirtioDevice ,
1718 VIRTIO_MMIO_INT_CONFIG , VIRTIO_MMIO_INT_VRING ,
1819} ;
19- use super :: { defs, defs:: uapi} ;
20+ use super :: { defs, defs:: control_event , defs :: uapi} ;
2021use crate :: legacy:: Gic ;
22+ use crate :: virtio:: console:: console_control:: {
23+ ConsoleControl , VirtioConsoleControl , VirtioConsoleResize ,
24+ } ;
25+ use crate :: virtio:: console:: defs:: NUM_PORTS ;
2126use crate :: Error as DeviceError ;
2227
2328pub ( crate ) const RXQ_INDEX : usize = 0 ;
2429pub ( crate ) const TXQ_INDEX : usize = 1 ;
25- pub ( crate ) const AVAIL_FEATURES : u64 =
26- 1 << uapi:: VIRTIO_CONSOLE_F_SIZE as u64 | 1 << uapi:: VIRTIO_F_VERSION_1 as u64 ;
30+
31+ pub ( crate ) const CONTROL_RXQ_INDEX : usize = 2 ;
32+ pub ( crate ) const CONTROL_TXQ_INDEX : usize = 3 ;
33+
34+ pub ( crate ) const AVAIL_FEATURES : u64 = 1 << uapi:: VIRTIO_CONSOLE_F_SIZE as u64
35+ | 1 << uapi:: VIRTIO_CONSOLE_F_MULTIPORT as u64
36+ | 1 << uapi:: VIRTIO_F_VERSION_1 as u64 ;
2737
2838pub ( crate ) fn get_win_size ( ) -> ( u16 , u16 ) {
2939 #[ repr( C ) ]
@@ -60,20 +70,16 @@ impl VirtioConsoleConfig {
6070 VirtioConsoleConfig {
6171 cols,
6272 rows,
63- max_nr_ports : 1u32 ,
73+ max_nr_ports : NUM_PORTS as u32 ,
6474 emerg_wr : 0u32 ,
6575 }
6676 }
67-
68- pub fn update_console_size ( & mut self , cols : u16 , rows : u16 ) {
69- self . cols = cols;
70- self . rows = rows;
71- }
7277}
7378
7479pub struct Console {
7580 pub ( crate ) queues : Vec < VirtQueue > ,
7681 pub ( crate ) queue_events : Vec < EventFd > ,
82+ pub ( crate ) control : Arc < ConsoleControl > ,
7783 pub ( crate ) avail_features : u64 ,
7884 pub ( crate ) acked_features : u64 ,
7985 pub ( crate ) interrupt_status : Arc < AtomicUsize > ,
@@ -107,6 +113,7 @@ impl Console {
107113 let config = VirtioConsoleConfig :: new ( cols, rows) ;
108114
109115 Ok ( Console {
116+ control : ConsoleControl :: new ( ) ,
110117 queues,
111118 queue_events,
112119 avail_features : AVAIL_FEATURES ,
@@ -185,9 +192,110 @@ impl Console {
185192 }
186193
187194 pub fn update_console_size ( & mut self , cols : u16 , rows : u16 ) {
188- debug ! ( "update_console_size: {} {}" , cols, rows) ;
189- self . config . update_console_size ( cols, rows) ;
190- self . signal_config_update ( ) . unwrap ( ) ;
195+ log:: debug!( "update_console_size: {} {}" , cols, rows) ;
196+ // Note that we currently only support resizing on the first/main console
197+ self . control
198+ . console_resize ( 0 , VirtioConsoleResize { rows, cols } ) ;
199+ }
200+
201+ pub ( crate ) fn process_control_rx ( & mut self ) -> bool {
202+ log:: trace!( "process_control_rx" ) ;
203+ let DeviceState :: Activated ( ref mem) = self . device_state else {
204+ unreachable ! ( )
205+ } ;
206+ let mut raise_irq = false ;
207+
208+ while let Some ( head) = self . queues [ CONTROL_RXQ_INDEX ] . pop ( mem) {
209+ if let Some ( buf) = self . control . queue_pop ( ) {
210+ match mem. write ( & buf, head. addr ) {
211+ Ok ( n) => {
212+ if n != buf. len ( ) {
213+ log:: error!( "process_control_rx: partial write" ) ;
214+ }
215+ raise_irq = true ;
216+ log:: trace!( "process_control_rx wrote {n}" ) ;
217+ self . queues [ CONTROL_RXQ_INDEX ] . add_used ( mem, head. index , n as u32 ) ;
218+ }
219+ Err ( e) => {
220+ log:: error!( "process_control_rx failed to write: {e}" ) ;
221+ }
222+ }
223+ } else {
224+ self . queues [ CONTROL_RXQ_INDEX ] . undo_pop ( ) ;
225+ break ;
226+ }
227+ }
228+ raise_irq
229+ }
230+
231+ pub ( crate ) fn process_control_tx ( & mut self ) -> bool {
232+ let mem = match self . device_state {
233+ DeviceState :: Activated ( ref mem) => mem,
234+ // This should never happen, it's been already validated in the event handler.
235+ DeviceState :: Inactive => unreachable ! ( ) ,
236+ } ;
237+
238+ let tx_queue = & mut self . queues [ CONTROL_TXQ_INDEX ] ;
239+ let mut raise_irq = false ;
240+
241+ while let Some ( head) = tx_queue. pop ( mem) {
242+ raise_irq = true ;
243+
244+ let cmd: VirtioConsoleControl = match mem. read_obj ( head. addr ) {
245+ Ok ( cmd) => cmd,
246+ Err ( e) => {
247+ log:: error!(
248+ "Failed to read VirtioConsoleControl struct: {e:?}, struct len = {len}, head.len = {head_len}" ,
249+ len = size_of:: <VirtioConsoleControl >( ) ,
250+ head_len = head. len,
251+ ) ;
252+ continue ;
253+ }
254+ } ;
255+ tx_queue. add_used ( mem, head. index , size_of_val ( & cmd) as u32 ) ;
256+
257+ log:: trace!( "VirtioConsoleControl cmd: {cmd:?}" ) ;
258+ match cmd. event {
259+ control_event:: VIRTIO_CONSOLE_DEVICE_READY => {
260+ log:: debug!(
261+ "Device is ready: initialization {}" ,
262+ if cmd. value == 1 { "ok" } else { "failed" }
263+ ) ;
264+ self . control . port_add ( 0 ) ;
265+ }
266+ control_event:: VIRTIO_CONSOLE_PORT_READY => {
267+ if cmd. value != 1 {
268+ log:: error!( "Port initialization failed: {:?}" , cmd) ;
269+ continue ;
270+ }
271+ if cmd. id == 0 {
272+ self . control . mark_console_port ( mem, 0 ) ;
273+ }
274+ }
275+ control_event:: VIRTIO_CONSOLE_PORT_OPEN => {
276+ let opened = match cmd. value {
277+ 0 => false ,
278+ 1 => true ,
279+ _ => {
280+ log:: error!(
281+ "Invalid value ({}) for VIRTIO_CONSOLE_PORT_OPEN on port {}" ,
282+ cmd. value,
283+ cmd. id
284+ ) ;
285+ continue ;
286+ }
287+ } ;
288+
289+ if !opened {
290+ log:: debug!( "Guest closed port {}" , cmd. id) ;
291+ continue ;
292+ }
293+ }
294+ _ => log:: warn!( "Unknown console control event {:x}" , cmd. event) ,
295+ }
296+ }
297+
298+ raise_irq
191299 }
192300
193301 pub ( crate ) fn process_rx ( & mut self ) -> bool {
0 commit comments