Контроль доступа

Одна из основ RTIC - контроль доступа. Контроль того, какая часть программы может получить доступ к какой статической переменной - инструмент обеспечения безопасности памяти.

Статические переменные используются для разделения состояний между обработчиками прерываний, или между обработчиком прерывания и нижним контекстом выполнения, main. В обычном Rust коде трудно обеспечить гранулированный контроль за тем, какие функции могут получать доступ к статическим переменным, поскольку к статическим переменным можно получить доступ из любой функции, находящейся в той же области видимости, в которой они определены. Модули дают частичный контроль над доступом к статическим переменным, но они недостаточно гибкие.

Чтобы добиться полного контроля за тем, что задачи могут получить доступ только к статическим переменным (ресурсам), которые им были указаны в RTIC атрибуте, фреймворк RTIC производит трансформацию структуры кода. Эта трансформация состоит из размещения ресурсов (статических переменных), определенных пользователем внутри модуля, а пользовательского кода вне модуля. Это делает невозможным обращение пользовательского кода к статическим переменным.

Затем доступ к ресурсам предоставляется каждой задаче с помощью структуры Resources, чьи поля соответствуют ресурсам, к которым получает доступ задача. Есть лишь одна такая структура на задачу и структура Resources инициализируется либо уникальной ссылкой (&mut-) на статическую переменную, либо с помощью прокси-ресурса (см. раздел критические секции).

Код ниже - пример разных трансформаций структуры кода, происходящих за сценой:


#![allow(unused)]

fn main() {

#[rtic::app(device = ..)]

mod app {

static mut X: u64: 0;

static mut Y: bool: 0;


#[init(resources = [Y])]

fn init(c: init::Context) {

// .. пользовательский код ..

}


#[interrupt(binds = UART0, resources = [X])]

fn foo(c: foo::Context) {

// .. пользовательский код ..

}


#[interrupt(binds = UART1, resources = [X, Y])]

fn bar(c: bar::Context) {

// .. пользовательский код ..

}


// ..

}

}

Фреймворк создает код, подобный этому:

fn init(c: init::Context) {

// .. пользовательский код ..

}


fn foo(c: foo::Context) {

// .. пользовательский код ..

}


fn bar(c: bar::Context) {

// .. пользовательский код ..

}


// Публичное API

pub mod init {

pub struct Context<'a> {

pub resources: Resources<'a>,

// ..

}


pub struct Resources<'a> {

pub Y: &'a mut bool,

}

}


pub mod foo {

pub struct Context<'a> {

pub resources: Resources<'a>,

// ..

}


pub struct Resources<'a> {

pub X: &'a mut u64,

}

}


pub mod bar {

pub struct Context<'a> {

pub resources: Resources<'a>,

// ..

}


pub struct Resources<'a> {

pub X: &'a mut u64,

pub Y: &'a mut bool,

}

}


/// Детали реализации

mod app {

// все, что внутри этого модуля спрятано от пользовательского кода


static mut X: u64 = 0;

static mut Y: bool = 0;


// настоящая точка входа в программу

unsafe fn main() -> ! {

interrupt::disable();


// ..


// вызов пользовательского кода; передача ссылок на статические переменные

init(init::Context {

resources: init::Resources {

X: &mut X,

},

// ..

});


// ..


interrupt::enable();


// ..

}


// обработчик прерывания,с которым связан `foo`

#[no_mangle]

unsafe fn UART0() {

// вызов пользовательского кода; передача ссылок на статические переменные

foo(foo::Context {

resources: foo::Resources {

X: &mut X,

},

// ..

});

}


// обработчик прерывания,с которым связан `bar`

#[no_mangle]

unsafe fn UART1() {

// вызов пользовательского кода; передача ссылок на статические переменные

bar(bar::Context {

resources: bar::Resources {

X: &mut X,

Y: &mut Y,

},

// ..

});

}

}

Загрузка...