Skip to content

Commit 83924ea

Browse files
cleanup: Ioctl derive proc macro
Signed-off-by: Anhad Singh <[email protected]>
1 parent ee33d34 commit 83924ea

File tree

6 files changed

+171
-46
lines changed

6 files changed

+171
-46
lines changed

src/aero_kernel/src/arch/x86_64/user_copy.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
// You should have received a copy of the GNU General Public License
1616
// along with Aero. If not, see <https://www.gnu.org/licenses/>.
1717

18+
use core::fmt::{Debug, Display};
1819
use core::mem::MaybeUninit;
1920
use core::ops::{Deref, DerefMut};
2021

2122
use crate::interrupts::exceptions::PF_RESUME;
2223
use crate::mem::paging::VirtAddr;
24+
use crate::syscall::SysArg;
2325

2426
use super::task::user_access_ok;
2527

@@ -131,6 +133,13 @@ impl<T> UserRef<T> {
131133
val: unsafe { val.assume_init() },
132134
}
133135
}
136+
137+
pub fn take(self) -> T
138+
where
139+
T: Clone,
140+
{
141+
self.val.clone()
142+
}
134143
}
135144

136145
impl<T> Deref for UserRef<T> {
@@ -152,3 +161,22 @@ impl<T> Drop for UserRef<T> {
152161
assert!(copy_to_user(self.ptr, &self.val));
153162
}
154163
}
164+
165+
impl<T: Debug> Display for UserRef<T> {
166+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
167+
self.val.fmt(f)
168+
}
169+
}
170+
171+
impl<T: Debug> Debug for UserRef<T> {
172+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
173+
write!(f, "UserRef({:?})", self.val)
174+
}
175+
}
176+
177+
impl<T: Debug> SysArg for UserRef<T> {
178+
fn from_usize(value: usize) -> Self {
179+
// TODO: SAFETY
180+
unsafe { Self::new(VirtAddr::new(value.try_into().unwrap())) }
181+
}
182+
}

src/aero_kernel/src/drivers/pty.rs

Lines changed: 75 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
use core::sync::atomic::{AtomicU32, Ordering};
1919

20+
use aero_syscall as libc;
2021
use aero_syscall::{Termios, WinSize};
22+
2123
use alloc::collections::BTreeMap;
2224
use alloc::string::ToString;
2325
use alloc::sync::{Arc, Weak};
@@ -26,6 +28,7 @@ use spin::{Once, RwLock};
2628

2729
use uapi::pty::*;
2830

31+
use crate::arch::user_copy::UserRef;
2932
use crate::fs::cache::*;
3033
use crate::fs::devfs::DEV_FILESYSTEM;
3134
use crate::fs::inode::{DirEntry, FileType, INodeInterface, PollFlags};
@@ -45,6 +48,55 @@ lazy_static::lazy_static! {
4548
static PTS_FS: Once<Arc<PtsFs>> = Once::new();
4649
static PTY_ID: AtomicU32 = AtomicU32::new(0);
4750

51+
#[derive(Debug, Ioctl)]
52+
pub enum TermiosCmd {
53+
/// Get window size.
54+
#[command(libc::TIOCGWINSZ)]
55+
GetWinSize(UserRef<WinSize>),
56+
57+
/// Set window size.
58+
#[command(libc::TIOCSWINSZ)]
59+
SetWinSize(UserRef<WinSize>),
60+
61+
/// Get the current serial port settings.
62+
///
63+
/// Equivalent to `tcgetattr(fd, argp)`.
64+
#[command(libc::TCGETS)]
65+
TcGets(UserRef<Termios>),
66+
67+
/// Allow the output buffer to drain, discard pending input, and set the current serial
68+
/// port settings.
69+
///
70+
/// Equivalent to `tcsetattr(fd, TCSAFLUSH, argp)`.
71+
#[command(libc::TCSETSF)]
72+
TcSetsf(UserRef<Termios>),
73+
74+
/// Allow the output buffer to drain, and set the current serial port settings.
75+
///
76+
/// Equivalent to `tcsetattr(fd, TCSADRAIN, argp)`.
77+
#[command(libc::TCSETSW)]
78+
TcSetsw(UserRef<Termios>),
79+
80+
/// Make the given terminal the controlling terminal of the calling process. The calling
81+
/// process must be a session leader and not have a controlling terminal already. For this
82+
/// case, arg should be specified as zero.
83+
///
84+
/// If this terminal is already the controlling terminal of a different session group, then the
85+
/// ioctl fails with EPERM, unless the caller has the CAP_SYS_ADMIN capability and arg equals
86+
/// 1, in which case the terminal is stolen, and all processes that had it as controlling
87+
/// terminal lose it.
88+
// FIXME: argument usize
89+
#[command(libc::TIOCSCTTY)]
90+
SetCtrlTerm,
91+
92+
/// Get the process group ID of the foreground process group on this terminal.
93+
///
94+
/// When successful, equivalent to `*argp = tcgetpgrp(fd)`.
95+
// FIXME: argument usize
96+
#[command(libc::TIOCGPGRP)]
97+
GetProcGroupId,
98+
}
99+
48100
struct Master {
49101
id: u32,
50102
wq: WaitQueue,
@@ -65,6 +117,16 @@ impl Master {
65117
discipline: LineDiscipline::new(),
66118
}
67119
}
120+
121+
#[inline]
122+
fn set_window_size(&self, size: WinSize) {
123+
*self.window_size.lock_irq() = size;
124+
}
125+
126+
#[inline]
127+
fn get_window_size(&self) -> WinSize {
128+
*self.window_size.lock_irq()
129+
}
68130
}
69131

70132
impl INodeInterface for Master {
@@ -180,58 +242,29 @@ impl INodeInterface for Slave {
180242
}
181243

182244
fn ioctl(&self, command: usize, arg: usize) -> fs::Result<usize> {
183-
match command {
184-
aero_syscall::TIOCGWINSZ => {
185-
let winsize = VirtAddr::new(arg as u64).read_mut::<WinSize>()?;
186-
*winsize = *self.master.window_size.lock_irq();
187-
188-
Ok(0)
189-
}
190-
191-
aero_syscall::TIOCSWINSZ => {
192-
*self.master.window_size.lock_irq() = *VirtAddr::new(arg as u64).read_mut()?;
193-
Ok(0)
194-
}
195-
196-
aero_syscall::TCGETS => {
197-
let termios = VirtAddr::new(arg as u64).read_mut::<Termios>()?;
198-
*termios = self.master.discipline.termios();
199-
200-
Ok(0)
201-
}
202-
203-
aero_syscall::TCSETSF => {
204-
let termios = VirtAddr::new(arg as u64).read_mut::<Termios>()?;
205-
self.master.discipline.set_termios(termios.clone());
206-
207-
Ok(0)
208-
}
209-
210-
aero_syscall::TCSETSW => {
245+
match TermiosCmd::from_command_arg(command, arg) {
246+
TermiosCmd::GetWinSize(mut size) => *size = self.master.get_window_size(),
247+
TermiosCmd::SetWinSize(size) => self.master.set_window_size(*size),
248+
TermiosCmd::TcGets(mut termios) => *termios = self.master.discipline.termios(),
249+
TermiosCmd::TcSetsf(termios) => self.master.discipline.set_termios(*termios),
250+
TermiosCmd::TcSetsw(termios) => {
211251
// TODO: Allow the output buffer to drain and then set the current serial port
212252
// settings.
213-
//
214-
// Equivalent to tcsetattr(fd, TCSADRAIN, argp).
215-
let termios = VirtAddr::new(arg as u64).read_mut::<Termios>()?;
216-
self.master.discipline.set_termios(termios.clone());
217-
218-
Ok(0)
253+
self.master.discipline.set_termios(*termios)
219254
}
220255

221-
aero_syscall::TIOCSCTTY => {
256+
TermiosCmd::SetCtrlTerm => {
222257
let current_task = scheduler::get_scheduler().current_task();
223258
assert!(current_task.is_session_leader());
224259

225260
current_task.attach(self.sref());
226-
Ok(0)
227261
}
228262

229-
// TIOCGPGRP - Get the process group ID of the foreground process group on this
230-
// terminal.
231-
0x540f => Err(FileSystemError::NotSupported),
232-
233-
_ => panic!("pty: unknown ioctl: {command}"),
263+
// FIXME: the following ioctls are not implemented.
264+
TermiosCmd::GetProcGroupId => return Err(FileSystemError::NotSupported),
234265
}
266+
267+
Ok(0)
235268
}
236269

237270
fn poll(&self, table: Option<&mut fs::inode::PollTable>) -> fs::Result<PollFlags> {
@@ -250,9 +283,7 @@ impl INodeInterface for Slave {
250283
}
251284

252285
fn read_at(&self, _offset: usize, buffer: &mut [u8]) -> fs::Result<usize> {
253-
let x = self.master.discipline.read(buffer)?;
254-
dbg!(core::str::from_utf8(&buffer[..x]));
255-
Ok(x)
286+
Ok(self.master.discipline.read(buffer)?)
256287
}
257288

258289
fn write_at(&self, _offset: usize, buffer: &[u8]) -> fs::Result<usize> {

src/aero_kernel/src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@
4949
naked_functions, // https://github.com/rust-lang/rust/issues/32408
5050
cfg_match, // https://github.com/rust-lang/rust/issues/115585
5151
strict_provenance,
52-
offset_of
52+
offset_of,
53+
associated_type_defaults
5354
)]
5455
// TODO(andypython): can we remove the dependency of "prelude_import" and "lang_items"?
5556
// `lang_items` => is currently used for the personality function (`rust_eh_personality`).

src/aero_proc/src/ioctl.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use proc_macro::TokenStream;
2+
use syn::{Data, DeriveInput, Path};
3+
4+
fn make_command_enum(ast: &DeriveInput) -> TokenStream {
5+
let name = &ast.ident;
6+
let variants = match &ast.data {
7+
Data::Enum(data) => &data.variants,
8+
_ => panic!("`Ioctl` derive macro can only be used on enums."),
9+
};
10+
11+
let mut pattern_match = vec![];
12+
13+
for variant in variants {
14+
let attrs = &variant.attrs;
15+
let ident = &variant.ident;
16+
17+
for attr in attrs {
18+
if attr.path.get_ident().unwrap() != "command" {
19+
assert_eq!(attr.path.get_ident().unwrap(), "doc");
20+
continue;
21+
}
22+
23+
let path = attr.parse_args::<Path>().unwrap();
24+
25+
pattern_match.push(match &variant.fields {
26+
syn::Fields::Unit => quote::quote!(#path => Self::#ident),
27+
syn::Fields::Unnamed(fields) => {
28+
assert!(fields.unnamed.len() == 1);
29+
quote::quote!(#path => Self::#ident(crate::syscall::SysArg::from_usize(arg)))
30+
}
31+
32+
_ => panic!("`Ioctl` derive macro can only be used on enums with unit variants."),
33+
});
34+
}
35+
}
36+
37+
// implement Ioctl::from_command_arg for the enum
38+
39+
quote::quote! {
40+
impl #name {
41+
pub fn from_command_arg(cmd: usize, arg: usize) -> Self {
42+
match cmd {
43+
#(#pattern_match,)*
44+
_ => unimplemented!("unknown command: {cmd:#x}")
45+
}
46+
}
47+
}
48+
}
49+
.into()
50+
}
51+
52+
pub fn parse(item: TokenStream) -> TokenStream {
53+
let ast: DeriveInput = syn::parse(item).unwrap();
54+
let cmd_enum = make_command_enum(&ast);
55+
56+
cmd_enum
57+
}

src/aero_proc/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ extern crate proc_macro_error;
2323
mod cpu_local;
2424
mod downcastable;
2525
mod indirect;
26+
mod ioctl;
2627
mod syscall;
2728
mod test;
2829

@@ -71,3 +72,9 @@ pub fn cpu_local(attr: TokenStream, item: TokenStream) -> TokenStream {
7172
pub fn indirect(attr: TokenStream, item: TokenStream) -> TokenStream {
7273
indirect::parse(attr, item)
7374
}
75+
76+
#[proc_macro_derive(Ioctl, attributes(command))]
77+
#[proc_macro_error]
78+
pub fn ioctl(item: TokenStream) -> TokenStream {
79+
ioctl::parse(item)
80+
}

src/aero_syscall/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,9 @@ pub const TCSETSW: usize = 0x5403;
298298
pub const TCSETSF: usize = 0x5404;
299299
pub const TIOCSCTTY: usize = 0x540e;
300300
pub const TIOCNOTTY: usize = 0x5422;
301+
pub const TIOCGPGRP: usize = 0x540f;
301302

302-
#[derive(Default, Copy, Clone)]
303+
#[derive(Default, Debug, Copy, Clone)]
303304
#[repr(C)]
304305
pub struct WinSize {
305306
pub ws_row: u16,

0 commit comments

Comments
 (0)