Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kern: add IRQ_STATUS syscall #1661

Merged
merged 23 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion app/demo-stm32g0-nucleo/app-g031.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ board = "stm32g031-nucleo"

[kernel]
name = "demo-stm32g0-nucleo"
requires = {flash = 10752, ram = 1296}
requires = {flash = 11104, ram = 1296}
features = ["g031"]
stacksize = 640

Expand Down
2 changes: 1 addition & 1 deletion app/demo-stm32g0-nucleo/app-g070.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ stacksize = 944

[kernel]
name = "demo-stm32g0-nucleo"
requires = {flash = 18448, ram = 1632}
requires = {flash = 19040, ram = 1632}
features = ["g070"]
stacksize = 640

Expand Down
2 changes: 1 addition & 1 deletion app/demo-stm32h7-nucleo/app-h753.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ stacksize = 896

[kernel]
name = "demo-stm32h7-nucleo"
requires = {flash = 24000, ram = 5120}
requires = {flash = 24320, ram = 5120}
features = ["h753", "dump"]

[tasks.jefe]
Expand Down
2 changes: 1 addition & 1 deletion app/donglet/app-g031-i2c.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ board = "donglet-g031"

[kernel]
name = "app-donglet"
requires = {flash = 18144, ram = 1616}
requires = {flash = 18720, ram = 1616}
features = ["g031"]
stacksize = 936

Expand Down
2 changes: 1 addition & 1 deletion app/donglet/app-g031.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ board = "donglet-g031"

[kernel]
name = "app-donglet"
requires = {flash = 18272, ram = 1820}
requires = {flash = 18944, ram = 1820}
features = ["g031"]
stacksize = 936

Expand Down
4 changes: 2 additions & 2 deletions app/lpc55xpresso/app-sprot.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fwid = true
[kernel]
name = "lpc55xpresso"
features = ["dice-self"]
requires = {flash = 53248, ram = 4096}
requires = {flash = 53312, ram = 4096}

[caboose]
region = "flash"
Expand Down Expand Up @@ -183,7 +183,7 @@ task-slots = ["swd"]
[tasks.sprot]
name = "drv-lpc55-sprot-server"
priority = 6
max-sizes = {flash = 47360, ram = 32768}
max-sizes = {flash = 48512, ram = 32768}
uses = ["flexcomm8", "bootrom"]
features = ["spi0"]
start = true
Expand Down
2 changes: 1 addition & 1 deletion app/lpc55xpresso/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fwid = true
[kernel]
name = "lpc55xpresso"
features = ["dump", "dice-self"]
requires = {flash = 54116, ram = 4096}
requires = {flash = 54336, ram = 4096}

[caboose]
region = "flash"
Expand Down
2 changes: 1 addition & 1 deletion app/oxide-rot-1/app-dev.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ fwid = true

[kernel]
name = "oxide-rot-1"
requires = {flash = 52512, ram = 4096}
requires = {flash = 61124, ram = 4096}
features = ["dice-self"]

[caboose]
Expand Down
2 changes: 1 addition & 1 deletion app/oxide-rot-1/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ fwid = true

[kernel]
name = "oxide-rot-1"
requires = {flash = 60644, ram = 2696}
requires = {flash = 61124, ram = 2696}
features = ["dice-mfg"]

[caboose]
Expand Down
2 changes: 1 addition & 1 deletion app/rot-carrier/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fwid = true
[kernel]
name = "rot-carrier"
features = ["dice-self"]
requires = {flash = 53000, ram = 4096}
requires = {flash = 53408, ram = 4096}

[caboose]
tasks = ["caboose_reader", "sprot"]
Expand Down
2 changes: 1 addition & 1 deletion app/sidecar/base.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ fwid = true

[kernel]
name = "sidecar"
requires = {flash = 25384, ram = 6256}
requires = {flash = 25792, ram = 6256}
features = ["dump"]

[caboose]
Expand Down
40 changes: 40 additions & 0 deletions doc/syscalls.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -659,3 +659,43 @@ return registers 0 and 1 for future compatibility.
Like `REPLY`, this syscall just silently ignores replies to the wrong
generation, under the assumption that the task got restarted for some reason
while we were processing its request. (It can happen.)

[#sys_irq_status]
=== `IRQ_STATUS` (13)

Returns the current status of interrupts mapped to the calling task.

==== Arguments

- 0: notification bitmask corresponding to the interrupt(s) to query

==== Return values

- 0: an `IrqStatus` (see the `abi` crate) describing the status of the
interrupts in the notification mask. Currently, the following bits in
`IrqStatus` are significant:
** `0b0001`: set if any interrupt in the mask is enabled
** `0b0010`: set if an IRQ is pending for any interrupt in the mask
** `0b0100`: set if a notification has been posted to the caller but
not yet consumed

==== Faults

|===
| Condition | Fault taken

| The given notification bitmask is not mapped to an interrupt in this task.
| `NoIrq`

|===

==== Notes

As discussed in the notes for the <<sys_irq_control, IRQ_CONTROL>> syscall,
tasks refer to interrupts using their notification bits.

If the provided notification mask is zero, the syscall will return a `NoIrq`
fault. If the provided notification mask has multiple bits set, the returned
`IrqStatus` value will be the boolean OR of the status of all interrupts in the
map (e.g. if any interrupt in the mask is pending, the `PENDING` bit will be
set, and so on).
20 changes: 19 additions & 1 deletion sys/abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ pub struct ULease {
pub length: u32,
}

#[derive(Copy, Clone, Debug, FromBytes)]
#[derive(Copy, Clone, Debug, FromBytes, PartialEq, Eq)]
#[repr(transparent)]
pub struct LeaseAttributes(u32);

Expand Down Expand Up @@ -446,6 +446,7 @@ pub enum Sysnum {
RefreshTaskId = 10,
Post = 11,
ReplyFault = 12,
IrqStatus = 13,
}

/// We're using an explicit `TryFrom` impl for `Sysnum` instead of
Expand All @@ -469,6 +470,7 @@ impl core::convert::TryFrom<u32> for Sysnum {
10 => Ok(Self::RefreshTaskId),
11 => Ok(Self::Post),
12 => Ok(Self::ReplyFault),
13 => Ok(Self::IrqStatus),
_ => Err(()),
}
}
Expand All @@ -490,6 +492,7 @@ pub enum Kipcnum {
Reset = 5,
GetTaskDumpRegion = 6,
ReadTaskDumpRegion = 7,
SoftwareIrq = 8,
}

impl core::convert::TryFrom<u16> for Kipcnum {
Expand All @@ -504,6 +507,7 @@ impl core::convert::TryFrom<u16> for Kipcnum {
5 => Ok(Self::Reset),
6 => Ok(Self::GetTaskDumpRegion),
7 => Ok(Self::ReadTaskDumpRegion),
8 => Ok(Self::SoftwareIrq),
_ => Err(()),
}
}
Expand Down Expand Up @@ -532,3 +536,17 @@ pub struct ImageVectors {
pub sp: u32,
pub entry: u32,
}

bitflags::bitflags! {
/// A set of bitflags representing the status of the interrupts mapped to a
/// notification mask.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct IrqStatus: u32 {
/// If 1, this interrupt is enabled.
const ENABLED = 1 << 0;
/// If 1, an IRQ is currently pending for this interrupt.
const PENDING = 1 << 1;
///If 1, a notification has been posted for this interrupt.
const POSTED = 1 << 2;
}
}
34 changes: 33 additions & 1 deletion sys/kern/src/arch/arm_m.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ use crate::startup::with_task_table;
use crate::task;
use crate::time::Timestamp;
use crate::umem::USlice;
use abi::FaultInfo;
#[cfg(any(armv7m, armv8m))]
use abi::FaultSource;
use abi::{FaultInfo, InterruptNum};
#[cfg(armv8m)]
use armv8_m_mpu::{disable_mpu, enable_mpu};
use unwrap_lite::UnwrapLite;
Expand Down Expand Up @@ -1159,6 +1159,38 @@ pub fn enable_irq(n: u32) {
}
}

/// Looks up an interrupt in the NVIC and returns a cross-platform
/// representation of that interrupt's status.
pub fn irq_status(n: u32) -> abi::IrqStatus {
let mut status = abi::IrqStatus::empty();

let nvic = unsafe { &*cortex_m::peripheral::NVIC::PTR };
let reg_num = (n / 32) as usize;
let bit_mask = 1 << (n % 32);

// See if the interrupt is enabled by checking the bit in the Interrupt Set
// Enable Register.
let enabled = nvic.iser[reg_num].read() & bit_mask == bit_mask;
status.set(abi::IrqStatus::ENABLED, enabled);

// See if the interrupt is pending by checking the bit in the Interrupt
// Set Pending Register (ISPR).
let pending = nvic.ispr[reg_num].read() & bit_mask == bit_mask;
status.set(abi::IrqStatus::PENDING, pending);

status
}

pub fn pend_software_irq(InterruptNum(n): InterruptNum) {
let nvic = unsafe { &*cortex_m::peripheral::NVIC::PTR };
let reg_num = (n / 32) as usize;
let bit_mask = 1 << (n % 32);

// Pend the IRQ by poking the corresponding bit in the Interrupt Set Pending
// Register (ISPR).
unsafe { nvic.ispr[reg_num].write(bit_mask) };
}

#[repr(u8)]
#[allow(dead_code)]
#[cfg(any(armv7m, armv8m))]
Expand Down
39 changes: 39 additions & 0 deletions sys/kern/src/kipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub fn handle_kernel_message(
Ok(Kipcnum::ReadTaskDumpRegion) => {
read_task_dump_region(tasks, caller, args.message?, args.response?)
}
Ok(Kipcnum::SoftwareIrq) => software_irq(tasks, caller, args.message?),

_ => {
// Task has sent an unknown message to the kernel. That's bad.
Expand Down Expand Up @@ -427,3 +428,41 @@ fn read_task_dump_region(
.set_send_response_and_length(0, response_len);
Ok(NextTask::Same)
}

fn software_irq(
tasks: &mut [Task],
caller: usize,
message: USlice<u8>,
) -> Result<NextTask, UserError> {
if caller != 0 {
return Err(UserError::Unrecoverable(FaultInfo::SyscallUsage(
UsageError::NotSupervisor,
)));
}

let (index, notification): (u32, u32) =
deserialize_message(&tasks[caller], message)?;

if index as usize >= tasks.len() {
return Err(UserError::Unrecoverable(FaultInfo::SyscallUsage(
UsageError::TaskOutOfRange,
)));
}

// Look up which IRQs are mapped to the target task.
let irqs = crate::startup::HUBRIS_TASK_IRQ_LOOKUP
.get(abi::InterruptOwner {
task: index,
notification,
})
.ok_or(UserError::Unrecoverable(FaultInfo::SyscallUsage(
UsageError::NoIrq,
)))?;

for &irq in irqs.iter() {
crate::arch::pend_software_irq(irq);
}

tasks[caller].save_mut().set_send_response_and_length(0, 0);
Ok(NextTask::Same)
}
53 changes: 51 additions & 2 deletions sys/kern/src/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
use core::convert::TryFrom;

use abi::{
FaultInfo, LeaseAttributes, SchedState, Sysnum, TaskId, TaskState, ULease,
UsageError,
FaultInfo, IrqStatus, LeaseAttributes, SchedState, Sysnum, TaskId,
TaskState, ULease, UsageError,
};
use unwrap_lite::UnwrapLite;

Expand Down Expand Up @@ -112,6 +112,7 @@ fn safe_syscall_entry(nr: u32, current: usize, tasks: &mut [Task]) -> NextTask {
Ok(Sysnum::ReplyFault) => {
reply_fault(tasks, current).map_err(UserError::from)
}
Ok(Sysnum::IrqStatus) => irq_status(tasks, current),
Err(_) => {
// Bogus syscall number! That's a fault.
Err(FaultInfo::SyscallUsage(UsageError::BadSyscallNumber).into())
Expand Down Expand Up @@ -819,3 +820,51 @@ fn reply_fault(
// the task using it faults.
Ok(NextTask::Same)
}

/// Implementation of the `IRQ_STATUS` syscall.
///
/// `caller` is a valid task index (i.e. not directly from user code).
///
/// # Syscall arguments
///
/// 0. a notification mask
///
/// # Syscall returns
///
/// 0. An [`IrqStatus`] structure representing the current state of the
/// interrupts mapped to the provided notification mask. If the notification
/// mask contains multiple IRQs, the returned status will be the boolean OR
/// of all the IRQs in the notification mask (e.g.., if *any* of the IRQs in
/// the notification mask is pending, the [`IrqStatus::PENDING`] bit will be
/// set, and so on).
fn irq_status(
tasks: &mut [Task],
caller: usize,
) -> Result<NextTask, UserError> {
let args = tasks[caller].save().as_irq_status_args();

// Look up which IRQs are mapped to the calling task.
let irqs = crate::startup::HUBRIS_TASK_IRQ_LOOKUP
.get(abi::InterruptOwner {
task: caller as u32,
notification: args.notification_bitmask,
})
.ok_or(UserError::Unrecoverable(FaultInfo::SyscallUsage(
UsageError::NoIrq,
)))?;

// Combine the platform-level status of all the IRQs in the notification set.
let mut status = irqs.iter().fold(IrqStatus::empty(), |status, irq| {
status | crate::arch::irq_status(irq.0)
});

// If any bits in the notification mask are set in the caller's notification
// set, then a notification has been posted to the task and not yet consumed.
let posted = tasks[caller].has_notifications(args.notification_bitmask);
status.set(IrqStatus::POSTED, posted);

tasks[caller].save_mut().set_irq_status_result(status);

// Continue running the same task.
Ok(NextTask::Same)
}
Loading
Loading