Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions chips/x86_q35/src/chip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use x86::registers::bits32::paging::{PD, PT};
use x86::support;
use x86::{Boundary, InterruptPoller};

use crate::keyboard::Keyboard;
use crate::pic::PIC1_OFFSET;
use crate::pit::{Pit, RELOAD_1KHZ};
use crate::serial::{SerialPort, SerialPortComponent, COM1_BASE, COM2_BASE, COM3_BASE, COM4_BASE};
Expand Down Expand Up @@ -81,6 +82,9 @@ pub struct Pc<'a, I: InterruptService + 'a, const PR: u16 = RELOAD_1KHZ> {
/// PS/2 Controller
pub ps2: &'a crate::ps2::Ps2Controller,

/// Keyboard device
pub keyboard: &'a Keyboard<'a>,

/// System call context
syscall: Boundary,
paging: PagingMPU<'a>,
Expand Down Expand Up @@ -240,6 +244,7 @@ impl<I: InterruptService + 'static> Component for PcComponent<'static, I> {
<SerialPortComponent as Component>::StaticInput,
<SerialPortComponent as Component>::StaticInput,
&'static mut MaybeUninit<Pc<'static, I>>,
&'static mut MaybeUninit<crate::keyboard::Keyboard<'static>>,
);
type Output = &'static Pc<'static, I>;

Expand Down Expand Up @@ -291,6 +296,17 @@ impl<I: InterruptService + 'static> Component for PcComponent<'static, I> {
// controller bring-up owned by the chip
let _ = ps2.init_early();

// keyboard device
let keyboard = s.5.write(Keyboard::new(ps2));
// connect keyboard as the ps/2 client, controller will call `receive_scancode`
ps2.set_client(keyboard);
keyboard.init_device();

// allow IRQ1 to fire
unsafe {
crate::pic::unmask(interrupt::KEYBOARD);
}

let pc = s.4.write(Pc {
com1,
com2,
Expand All @@ -299,6 +315,7 @@ impl<I: InterruptService + 'static> Component for PcComponent<'static, I> {
pit,
vga,
ps2,
keyboard,
syscall,
paging,
int_svc: self.int_svc,
Expand All @@ -318,6 +335,7 @@ macro_rules! x86_q35_component_static {
$crate::serial_port_component_static!(),
$crate::serial_port_component_static!(),
kernel::static_buf!($crate::Pc<'static, $isr_ty>),
kernel::static_buf!($crate::keyboard::Keyboard<'static>),
)
};};
}
97 changes: 97 additions & 0 deletions chips/x86_q35/src/cmd_fifo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2025.

//! Minimal, non-alloc ring FIFO for small fixed-size queues.

// Why `FifoItem::EMPTY` and const init?
// We need to create the FIFO in static memory (no heap). That means the backing
// array `[T; N]` must be initialized in a `const` context. I believe we can't directly
// call `T::default()` in `const fn`, so `[T::default(); N]` won’t work.
// By requiring `T: FifoItem` with a `const EMPTY`, we can do `[T::EMPTY; N]`
// and safely build the FIFO at compile time

use core::cell::Cell;

pub(crate) trait FifoItem: Copy {
/// Const zero/empty value used to initialize the backing array.
const EMPTY: Self;
}

pub struct Fifo<T: Copy + FifoItem, const N: usize> {
buf: [Cell<T>; N],
head: Cell<usize>,
tail: Cell<usize>,
len: Cell<usize>,
}

impl<T: Copy + FifoItem, const N: usize> Fifo<T, N> {
pub(crate) const fn new() -> Self {
Self {
buf: [const { Cell::new(T::EMPTY) }; N],
head: Cell::new(0),
tail: Cell::new(0),
len: Cell::new(0),
}
}

#[inline]
pub(crate) fn is_empty(&self) -> bool {
self.len.get() == 0
}

#[inline]
pub(crate) fn is_full(&self) -> bool {
self.len.get() == N
}

/// Push at head; returns Err(item) if full
#[inline]
pub(crate) fn push(&self, item: T) -> Result<(), T> {
if self.is_full() {
return Err(item);
}
let h = self.head.get();
self.buf[h].set(item);
self.head.set((h + 1) % N);
self.len.set(self.len.get() + 1);
Ok(())
}

/// Copy the current head entry (None if empty).
#[inline]
pub(crate) fn peek_copy(&self) -> Option<T> {
if self.is_empty() {
None
} else {
Some(self.buf[self.tail.get()].get())
}
}

/// Read-modify-write the current head entry; returns closure result.
#[inline]
pub(crate) fn peek_update<R>(&self, f: impl FnOnce(&mut T) -> R) -> Option<R> {
if self.is_empty() {
None
} else {
let t = self.tail.get();
let mut v = self.buf[t].get();
let r = f(&mut v);
self.buf[t].set(v);
Some(r)
}
}

/// Pop from tail; returns a copy of the removed item.
#[inline]
pub(crate) fn pop(&self) -> Option<T> {
if self.is_empty() {
return None;
}
let t = self.tail.get();
let item = self.buf[t].get();
self.tail.set((t + 1) % N);
self.len.set(self.len.get() - 1);
Some(item)
}
}
Loading