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

Binding panic handlers #27

Open
adamgreig opened this issue Dec 20, 2019 · 1 comment
Open

Binding panic handlers #27

adamgreig opened this issue Dec 20, 2019 · 1 comment

Comments

@adamgreig
Copy link

Apologies if this is the wrong repository for the issue.

It would be really useful to be able to bind a task to the panic handler, allowing shared resources to be used during a panic. Maybe this is really hard to make sound, I'm not sure, but it would make it much easier to handle making systems safe and emitting diagnostics at panic. I appreciate that for a lot of people the various panic handler crates are used (panic-halt, panic-semihosting, etc) which wouldn't really tie in to RTFM, but I often need a custom panic handler to ensure hardware safety, which would be easier if it could integrate into RTFM.

@japaric
Copy link
Contributor

japaric commented Dec 23, 2019

Letting the panic handler use (shared) resources would be very weird, I think.

In the first place, you can call the panic handler from the NMI (Non-Maskable-Interrupt) or HardFault. These are highest priority, unmaskable exceptions: meaning that priority based masking can't protect the data.

The other issue is that the panic handler always can be called from anywhere. Because of this we cannot uphold the "once a resource is locked, all tasks that may access the resource cannot start" property, which is required for deadlock freedom. For example, this code would deadlock:

#[task(resources = [x])]
fn foo(cx: foo::Context) {
    x.lock(|x| {
        panic!();
    });
}

#[panic(resources = [x])]
fn panic(cx: panic::Context) -> ! {
    x.lock(|x| {
        // deadlock? this results in a nested lock
    });
}

What would be OK, I think, is letting the panic handler use owned resources, e.g. late-initialized data. Never mind, this is also unsound because NMI and HardFault cannot be masked; see below:

#[rtfm::app]
const APP: () = {
    struct Resources { x: u64 }

    #[panic(resources = [x])]
    fn panic(cx: panic::Context) -> ! {
        // exclusive access
        let x: &mut u64 = cx.x;

        // trigger NMI or a HardFault here
    }
}

// NOTE: not controlled by RTFM
#[exception]
fn NMI() {
    panic!(); // results in two exclusive references (`&mut-`) to the resource `x`
}

TL;DR because, in general, no-std panics can be nested the only shared data they can safely access are plain static variables, which must be Sync. So things like AtomicUsize or spin::Mutex can be safely access from panic handlers (and from HardFault and NMI).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants