1+ //! Support for the userspace serial console "driver" (sys-tty).
2+ //!
3+ //! At the moment, once the "driver" is set, it cannot be changed.
4+ //! An important part of this logic is to forward kernel logs to
5+ //! the userspace driver to avoid kernel logs intermixing with userspace
6+ //! output.
7+ use crate :: util:: StaticRef ;
8+
19use super :: sysobject:: SysObject ;
2- use alloc:: { borrow:: ToOwned , sync:: Arc } ;
10+ use alloc:: { borrow:: ToOwned , boxed :: Box , sync:: Arc } ;
311use core:: sync:: atomic:: * ;
4- use moto_sys:: ErrorCode ;
12+ use moto_sys:: { ErrorCode , SysRay } ;
513
614struct SerialConsole {
715 owner_pid : AtomicU64 ,
816 this_object : Arc < SysObject > ,
17+
18+ uspace_log_buf_addr : AtomicUsize ,
19+ uspace_log_buf_offset_addr : AtomicUsize ,
20+ uspace_log_buf_offset : crate :: util:: SpinLock < usize > ,
21+
22+ console_driver_address_space :
23+ crate :: util:: SpinLock < Option < Arc < crate :: mm:: user:: UserAddressSpace > > > ,
924}
1025
1126static CONSOLE : crate :: util:: StaticRef < SerialConsole > = crate :: util:: StaticRef :: default_const ( ) ;
1227
13- pub fn init ( ) {
14- use alloc:: boxed:: Box ;
28+ // We don't (want to) support nested logging, so we protect top-level log
29+ // routines with per cpu bool flags, and log directly to serial port when nested.
30+ static PERCPU_LOG_GUARD : StaticRef < crate :: util:: StaticPerCpu < bool > > = StaticRef :: default_const ( ) ;
1531
32+ pub fn init ( ) {
1633 CONSOLE . set ( Box :: leak ( Box :: new ( SerialConsole {
1734 owner_pid : AtomicU64 :: new ( super :: process:: KERNEL_PID . as_u64 ( ) ) ,
1835 this_object : SysObject :: new ( Arc :: new ( "serial_console" . to_owned ( ) ) ) ,
36+
37+ uspace_log_buf_addr : AtomicUsize :: new ( 0 ) ,
38+ uspace_log_buf_offset_addr : AtomicUsize :: new ( 0 ) ,
39+ uspace_log_buf_offset : crate :: util:: SpinLock :: new ( 0 ) ,
40+
41+ console_driver_address_space : crate :: util:: SpinLock :: new ( None ) ,
1942 } ) ) ) ;
2043}
2144
2245pub ( super ) fn get_for_process (
2346 process : & super :: process:: Process ,
47+ addresses : & str ,
2448) -> Result < Arc < SysObject > , ErrorCode > {
2549 if CONSOLE . owner_pid . load ( Ordering :: Acquire ) != super :: process:: KERNEL_PID . as_u64 ( ) {
2650 // We do not support transferring console ownership for now.
@@ -32,9 +56,35 @@ pub(super) fn get_for_process(
3256 return Err ( moto_rt:: E_NOT_ALLOWED ) ;
3357 }
3458
59+ let Some ( ( buf_addr, offset_addr) ) = addresses. split_once ( ':' ) else {
60+ log:: error!( "Failed to parse serial console handler parameters" ) ;
61+ return Err ( moto_rt:: E_INVALID_ARGUMENT ) ;
62+ } ;
63+
64+ let Ok ( buf_addr) = buf_addr. parse :: < usize > ( ) else {
65+ log:: error!( "Failed to parse serial console handler parameters" ) ;
66+ return Err ( moto_rt:: E_INVALID_ARGUMENT ) ;
67+ } ;
68+
69+ let Ok ( offset_addr) = offset_addr. parse :: < usize > ( ) else {
70+ log:: error!( "Failed to parse serial console handler parameters" ) ;
71+ return Err ( moto_rt:: E_INVALID_ARGUMENT ) ;
72+ } ;
73+
74+ PERCPU_LOG_GUARD . set ( Box :: leak ( Box :: new ( crate :: util:: StaticPerCpu :: init ( ) ) ) ) ;
75+
76+ * CONSOLE . console_driver_address_space . lock ( line ! ( ) ) = Some ( process. address_space ( ) . clone ( ) ) ;
77+
78+ CONSOLE
79+ . uspace_log_buf_addr
80+ . store ( buf_addr, Ordering :: Relaxed ) ;
3581 CONSOLE
3682 . owner_pid
3783 . store ( process. pid ( ) . as_u64 ( ) , Ordering :: Relaxed ) ;
84+ CONSOLE
85+ . uspace_log_buf_offset_addr
86+ . store ( offset_addr, Ordering :: Release ) ;
87+
3888 Ok ( CONSOLE . this_object . clone ( ) )
3989}
4090
@@ -45,3 +95,90 @@ pub fn on_irq() {
4595 }
4696 SysObject :: wake_irq ( & CONSOLE . this_object ) ;
4797}
98+
99+ pub fn logging_to_uspace ( ) -> bool {
100+ CONSOLE . uspace_log_buf_offset_addr . load ( Ordering :: Relaxed ) != 0
101+ }
102+
103+ pub fn log_to_uspace ( msg : & str ) -> bool {
104+ assert ! ( msg. len( ) < SysRay :: CONSOLE_SHARED_BUF_SZ ) ;
105+
106+ let uspace_log_buf_offset_addr = CONSOLE . uspace_log_buf_offset_addr . load ( Ordering :: Relaxed ) ;
107+ if uspace_log_buf_offset_addr == 0 {
108+ return false ;
109+ }
110+
111+ let uspace_log_buf_addr = CONSOLE . uspace_log_buf_addr . load ( Ordering :: Relaxed ) ;
112+ if uspace_log_buf_addr == 0 {
113+ return false ; // Raced with console setup?
114+ }
115+
116+ let bytes = msg. as_bytes ( ) ;
117+
118+ if PERCPU_LOG_GUARD . is_null ( ) {
119+ PERCPU_LOG_GUARD . set_per_cpu ( Box :: leak ( Box :: new ( false ) ) ) ;
120+ }
121+ * PERCPU_LOG_GUARD . get_per_cpu ( ) = true ;
122+
123+ let mut offset = CONSOLE . uspace_log_buf_offset . lock ( line ! ( ) ) ;
124+ let start = ( * offset) & ( SysRay :: CONSOLE_SHARED_BUF_SZ - 1 ) ;
125+ let end = start + bytes. len ( ) ;
126+
127+ let address_space_guard = CONSOLE . console_driver_address_space . lock ( line ! ( ) ) ;
128+ let address_space = address_space_guard. as_ref ( ) . unwrap ( ) ;
129+
130+ if end <= SysRay :: CONSOLE_SHARED_BUF_SZ {
131+ if let Err ( err) = address_space. copy_to_user ( bytes, ( uspace_log_buf_addr + start) as u64 ) {
132+ log:: error!( "Failed to log to userspace: {err:?}." ) ;
133+ * PERCPU_LOG_GUARD . get_per_cpu ( ) = false ;
134+ return false ;
135+ }
136+ } else {
137+ if let Err ( err) = address_space. copy_to_user (
138+ & bytes[ ..( SysRay :: CONSOLE_SHARED_BUF_SZ - start) ] ,
139+ ( uspace_log_buf_addr + start) as u64 ,
140+ ) {
141+ log:: error!( "Failed to log to userspace: {err:?}." ) ;
142+ * PERCPU_LOG_GUARD . get_per_cpu ( ) = false ;
143+ return false ;
144+ }
145+
146+ if let Err ( err) = address_space. copy_to_user (
147+ & bytes[ ( SysRay :: CONSOLE_SHARED_BUF_SZ - start) ..] ,
148+ uspace_log_buf_addr as u64 ,
149+ ) {
150+ log:: error!( "Failed to log to userspace: {err:?}." ) ;
151+ * PERCPU_LOG_GUARD . get_per_cpu ( ) = false ;
152+ return false ;
153+ }
154+ }
155+
156+ * offset += bytes. len ( ) ;
157+ let offset_bytes = offset. to_ne_bytes ( ) ;
158+
159+ if let Err ( err) = address_space. copy_to_user ( & offset_bytes, uspace_log_buf_offset_addr as u64 ) {
160+ log:: error!( "Failed to log to userspace: {err:?}." ) ;
161+ }
162+
163+ core:: mem:: drop ( address_space_guard) ;
164+ core:: mem:: drop ( offset) ;
165+
166+ SysObject :: wake_irq ( & CONSOLE . this_object ) ;
167+ * PERCPU_LOG_GUARD . get_per_cpu ( ) = false ;
168+
169+ true
170+ }
171+
172+ pub fn log_to_uspace_protected ( msg : & str ) -> bool {
173+ if CONSOLE . uspace_log_buf_offset_addr . load ( Ordering :: Relaxed ) == 0 {
174+ return false ;
175+ }
176+ if PERCPU_LOG_GUARD . is_null ( ) {
177+ return false ;
178+ }
179+ if * PERCPU_LOG_GUARD . get_per_cpu ( ) {
180+ return false ;
181+ }
182+
183+ log_to_uspace ( msg)
184+ }
0 commit comments