feat: docs & ARCH 2.2, 2.3, 2.4
This commit is contained in:
146
kernel/docs/capability/cnode.md
Normal file
146
kernel/docs/capability/cnode.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# CNode: таблица capability: `mod.rs`
|
||||
|
||||
## Концептуальная модель
|
||||
|
||||
**CNode** (Capability Node) — это массив слотов, каждый из которых
|
||||
может хранить один capability. Это аналог файловой таблицы в Unix,
|
||||
но для capabilities.
|
||||
|
||||
```
|
||||
CNode {
|
||||
slots: Vec<Locked<CNodeSlot>>
|
||||
}
|
||||
|
||||
CNodeSlot {
|
||||
cap: Capability,
|
||||
parent_idx: Option<usize>, // индекс родительского слота
|
||||
}
|
||||
```
|
||||
|
||||
## Инициализация
|
||||
|
||||
```rust
|
||||
pub fn new(size: usize) -> Self {
|
||||
let mut slots = Vec::with_capacity(size);
|
||||
for _ in 0..size {
|
||||
slots.push(Locked::new(CNodeSlot {
|
||||
cap: Capability::empty(),
|
||||
parent_idx: None,
|
||||
}));
|
||||
}
|
||||
Self { slots }
|
||||
}
|
||||
```
|
||||
|
||||
Все слоты изначально пустые (`Capability::empty()`).
|
||||
|
||||
## Операции
|
||||
|
||||
### insert(slot, cap) — вставка
|
||||
|
||||
```rust
|
||||
pub fn insert(&self, slot: usize, cap: Capability) -> Result<(), &'static str> {
|
||||
if slot >= self.slots.len() { return Err("Index out of bounds"); }
|
||||
let mut s = self.slots[slot].lock();
|
||||
s.cap = cap;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
Простая вставка без проверки (перезаписывает существующий).
|
||||
|
||||
### mint(src, dest, relation, rights) — создание потомка
|
||||
|
||||
```rust
|
||||
pub fn mint(&self, src: usize, dest: usize, relation: Relation, rights: CapRights) -> Result<(), &'static str>
|
||||
```
|
||||
|
||||
**Валидация:**
|
||||
- src и dest в пределах массива.
|
||||
- src != dest (дедлок не имеет смысла, но блокировка была бы корректна).
|
||||
- src содержит валидный capability.
|
||||
- src имеет GRANT.
|
||||
|
||||
**Процесс:**
|
||||
1. Захват блокировок в порядке возрастания индекса (lock ranking).
|
||||
2. Вычисление `final_rights = src.rights & rights`.
|
||||
3. Копирование Capability в dest с новыми правами и relation.
|
||||
4. Установка `parent_idx = Some(src)`.
|
||||
|
||||
### revoke(slot_idx) — отзыв
|
||||
|
||||
```rust
|
||||
pub fn revoke(&self, slot_idx: usize) -> Result<(), &'static str>
|
||||
```
|
||||
|
||||
Каскадное удаление:
|
||||
|
||||
```
|
||||
revoke_internal(slot_idx):
|
||||
│
|
||||
├── 1. Поиск потомков:
|
||||
│ for i in 0..slots.len():
|
||||
│ if slots[i].parent_idx == Some(slot_idx):
|
||||
│ revoke_internal(i) ← рекурсивно!
|
||||
│
|
||||
├── 2. Уничтожение себя:
|
||||
│ cap = Capability::empty()
|
||||
│ parent_idx = None
|
||||
│ token = old_token_sig
|
||||
│
|
||||
└── 3. Отправка token в очередь:
|
||||
if token != 0 && token != 0xDEAD_BEEF:
|
||||
MMU_REVOCATION_QUEUE.push(token)
|
||||
```
|
||||
|
||||
**Почему нет блокировок при рекурсии?**
|
||||
- На каждом шаге проверка `is_child` захватывает и отпускает блокировку.
|
||||
- Рекурсивный вызов происходит **после** освобождения блокировки.
|
||||
- Это предотвращает взаимоблокировки.
|
||||
|
||||
**Фильтр токенов:**
|
||||
- `token == 0`: пустой/невалидный capability.
|
||||
- `token == 0xDEAD_BEEF`: сырой Untyped (для тестов/отладки).
|
||||
- Эти токены не отправляются в очередь (бессмысленно).
|
||||
|
||||
### get_cap(slot) — чтение
|
||||
|
||||
```rust
|
||||
pub fn get_cap(&self, slot: usize) -> Option<Capability> {
|
||||
self.slots.get(slot).map(|s| s.lock().cap)
|
||||
}
|
||||
```
|
||||
|
||||
## Lock Ranking — детали
|
||||
|
||||
```rust
|
||||
let (src_slot, dest_slot) = if src < dest {
|
||||
_guard_low = self.slots[src].lock(); // меньший → первый
|
||||
_guard_high = self.slots[dest].lock(); // больший → второй
|
||||
} else {
|
||||
_guard_low = self.slots[dest].lock();
|
||||
_guard_high = self.slots[src].lock();
|
||||
};
|
||||
```
|
||||
|
||||
**Причина:** если поток A делает mint(5, 10), а поток B делает mint(10, 5),
|
||||
без lock ranking они могут взаимно заблокироваться:
|
||||
- A: lock(5) → ждёт lock(10)
|
||||
- B: lock(10) → ждёт lock(5)
|
||||
|
||||
С lock ranking:
|
||||
- A: lock(5) → lock(10)
|
||||
- B: lock(5) → ждёт... → дождался → lock(10)
|
||||
|
||||
## Интеграция с событиями
|
||||
|
||||
После `revoke()`, токен попадает в `MMU_REVOCATION_QUEUE` (см.
|
||||
`events.rs`). VMM обрабатывает очередь в `process_pending_revocations()`.
|
||||
|
||||
## Особенности реализации
|
||||
|
||||
1. **Vec<Locked<CNodeSlot>>**: каждый слот — отдельная spinlock-ячейка.
|
||||
Это позволяет параллельно читать разные слоты.
|
||||
2. **`pub slots`**: прямой доступ к слоту возможен (для тестов).
|
||||
3. **panic на overflow**: если очередь отзыва переполнена — паника.
|
||||
Это критический сбой подсистемы ресурсов.
|
||||
115
kernel/docs/capability/descriptors.md
Normal file
115
kernel/docs/capability/descriptors.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Дескрипторы capability: `descriptor.rs`
|
||||
|
||||
## Назначение
|
||||
|
||||
Файл определяет базовые типы capability-системы:
|
||||
что такое capability, какие бывают объекты, права и отношения.
|
||||
|
||||
## CapObject — что представляет capability
|
||||
|
||||
```rust
|
||||
pub enum CapObject {
|
||||
Empty, // Пустой слот
|
||||
Memory { phys: PhysAddr, size_pages: usize }, // Фрейм памяти
|
||||
CNode { phys: PhysAddr, slots: usize }, // Другой CNode
|
||||
PMActor { id: u64 }, // PM Actor
|
||||
}
|
||||
```
|
||||
|
||||
### Memory
|
||||
|
||||
Capability на физическую память:
|
||||
- `phys` — физический адрес начала.
|
||||
- `size_pages` — размер в страницах.
|
||||
|
||||
### CNode
|
||||
|
||||
Capability на другой CNode:
|
||||
- `phys` — физический адрес CNode.
|
||||
- `slots` — количество слотов.
|
||||
|
||||
### PMActor
|
||||
|
||||
Capability на PMActor:
|
||||
- `id` — уникальный идентификатор актора.
|
||||
|
||||
### Empty
|
||||
|
||||
Слот пуст. `is_valid()` возвращает `false`.
|
||||
|
||||
## CapRights — права доступа
|
||||
|
||||
```rust
|
||||
bitflags! {
|
||||
pub struct CapRights: u8 {
|
||||
const READ = 1 << 0; // 0x01 — чтение
|
||||
const WRITE = 1 << 1; // 0x02 — запись
|
||||
const EXECUTE = 1 << 2; // 0x04 — исполнение
|
||||
const GRANT = 1 << 3; // 0x08 — разрешение на mint
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Правила:**
|
||||
- Права дочернего capability = `родительские_права & запрошенные_права`.
|
||||
- Нельзя расширить права: если родитель не имеет GRANT, mint невозможен.
|
||||
- `CapRights::all()` = R | W | X | G = 0x0F.
|
||||
|
||||
## Relation — тип связи
|
||||
|
||||
```rust
|
||||
pub enum Relation {
|
||||
Strong, // Владелец — сильная ссылка (объект жив, пока есть Strong)
|
||||
Borrow, // Заёмщик — временный доступ
|
||||
Transfer, // Передача — владение переходит без возможности отзыва
|
||||
}
|
||||
```
|
||||
|
||||
- **Strong**: capability владеет объектом. При revoke, объект может
|
||||
быть уничтожен или возвращён пулу.
|
||||
- **Borrow**: capability предоставляет временный доступ.
|
||||
При revoke родителя, borrow-потомки тоже отзываются.
|
||||
- **Transfer**: полная передача владения. Используется при IPC.
|
||||
|
||||
## Capability — полный дескриптор
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Capability {
|
||||
pub object: CapObject, // Целевой объект
|
||||
pub rights: CapRights, // Права доступа
|
||||
pub relation: Relation, // Тип связи
|
||||
pub token_sig: u64, // Уникальный подписывающий токен
|
||||
}
|
||||
```
|
||||
|
||||
### token_sig — назначение
|
||||
|
||||
- Уникальный 64-битный идентификатор capability.
|
||||
- Используется для:
|
||||
1. **Отзыва**: при revoke, token_sig отправляется в MMU_REVOCATION_QUEUE.
|
||||
2. **Идентификации в VMM**: VMA хранят `cap_token == token_sig`.
|
||||
3. **Отладки**: каждый capability можно однозначно отследить.
|
||||
|
||||
### Методы
|
||||
|
||||
```rust
|
||||
impl Capability {
|
||||
pub const fn empty() -> Self {
|
||||
Self {
|
||||
object: CapObject::Empty,
|
||||
rights: CapRights::empty(),
|
||||
relation: Relation::Borrow,
|
||||
token_sig: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_valid(&self) -> bool {
|
||||
!matches!(self.object, CapObject::Empty)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`token_sig = 0` зарезервирован для пустых/невалидных capability.
|
||||
`token_sig = 0xDEAD_BEEF` используется для сырых Untyped
|
||||
(не отправляется в MMU_REVOCATION_QUEUE, см. `cap/mod.rs`).
|
||||
126
kernel/docs/capability/introduction.md
Normal file
126
kernel/docs/capability/introduction.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Capability-система: концептуальная модель
|
||||
|
||||
## Философия
|
||||
|
||||
**Capability** (дескриптор возможности) — это **неподделываемый токен**,
|
||||
дающий право выполнить определённую операцию над определённым объектом.
|
||||
|
||||
В традиционных ОС (Linux, Windows) доступ контролируется через:
|
||||
- PID + UID/GID + проверка при каждом системном вызове.
|
||||
- MMU: page tables определяют, что отображено, но не кто отобразил.
|
||||
|
||||
В модели capabilities:
|
||||
- **Если у вас нет capability — у вас нет доступа.**
|
||||
- Capability хранятся в CNode — защищённой таблице, доступной только ядру.
|
||||
- Capability можно создавать только от родительского capability
|
||||
(иерархия наследования).
|
||||
- Права можно только **урезать** (mint), но не расширить.
|
||||
- Capability можно **отозвать** (revoke), что уничтожает его
|
||||
и всех его потомков.
|
||||
|
||||
## Основные понятия
|
||||
|
||||
```
|
||||
Capability {
|
||||
object: CapObject, // на что указывает (Memory, CNode, PMActor...)
|
||||
rights: CapRights, // права (R, W, X, G)
|
||||
relation: Relation, // Strong (владеет), Borrow (заём), Transfer
|
||||
token_sig: u64, // уникальный идентификатор (для revoke)
|
||||
}
|
||||
|
||||
Relation {
|
||||
Strong: владеет объектом (capability владеет памятью)
|
||||
Borrow: заём — временный доступ без права распоряжаться
|
||||
Transfer: передача — владение переходит получателю
|
||||
}
|
||||
|
||||
CapRights {
|
||||
READ = 0x1,
|
||||
WRITE = 0x2,
|
||||
EXECUTE = 0x4,
|
||||
GRANT = 0x8, // разрешение создавать дочерние capability
|
||||
}
|
||||
```
|
||||
|
||||
## Иерархия и отзыв
|
||||
|
||||
```
|
||||
CNode (массив слотов)
|
||||
┌────────┬────────┬────────┬────────┐
|
||||
│ slot 0 │ slot 1 │ slot 2 │ slot 3 │ ...
|
||||
├────────┼────────┼────────┼────────┤
|
||||
│ cap │ cap │ cap │ cap │
|
||||
│ parent:│ parent:│ parent:│ parent:│
|
||||
│ None │ Some(0)│ Some(0)│ Some(1)│
|
||||
└────────┴────────┴────────┴────────┘
|
||||
│
|
||||
┌────────┴────────┐
|
||||
▼ ▼
|
||||
slot 1 slot 2
|
||||
(mint from 0) (mint from 0)
|
||||
|
||||
revoke(0) → slot 0 уничтожается
|
||||
→ рекурсивно: slot 1, slot 2 тоже уничтожаются
|
||||
→ token_sig slot 0 отправляется в MMU_REVOCATION_QUEUE
|
||||
→ VMM обработает отзыв при следующем page fault
|
||||
```
|
||||
|
||||
### Mint — создание дочернего capability
|
||||
|
||||
```rust
|
||||
cnode.mint(src, dest, relation, rights)
|
||||
```
|
||||
|
||||
- `src` — исходный слот (должен иметь GRANT).
|
||||
- `dest` — целевой слот (должен быть пустым).
|
||||
- `relation` — как наследник связан с родителем.
|
||||
- `rights` — права наследника (∩ с правами родителя).
|
||||
|
||||
### Revoke — отзыв capability
|
||||
|
||||
```rust
|
||||
cnode.revoke(slot_idx)
|
||||
```
|
||||
|
||||
1. Рекурсивно находит всех потомков и уничтожает их.
|
||||
2. Уничтожает сам capability.
|
||||
3. Отправляет `token_sig` в глобальную `MMU_REVOCATION_QUEUE`.
|
||||
4. VMM при следующем page fault обрабатывает все накопленные отзывы.
|
||||
|
||||
## Lock Ranking — предотвращение дедлоков
|
||||
|
||||
В `mint()` захватываются две блокировки (src и dest). Чтобы избежать
|
||||
инверсии блокировок, используется строгий порядок:
|
||||
|
||||
```rust
|
||||
let (src_slot, dest_slot) = if src < dest {
|
||||
// Захватываем меньший индекс первым
|
||||
_guard_low = self.slots[src].lock();
|
||||
_guard_high = self.slots[dest].lock();
|
||||
} else {
|
||||
_guard_low = self.slots[dest].lock();
|
||||
_guard_high = self.slots[src].lock();
|
||||
};
|
||||
```
|
||||
|
||||
## Интеграция с VMM
|
||||
|
||||
При отзыве capability, VMM должна аннулировать все VMA, связанные
|
||||
с отозванным токеном. Для этого:
|
||||
|
||||
1. `revoke()` пушит `token_sig` в lock-free очередь.
|
||||
2. При page fault: `process_pending_revocations()` дренирует очередь.
|
||||
3. VMA с `cap_token == token_sig` удаляются из AddressSpace.
|
||||
4. TLB flush для синхронизации MMU.
|
||||
|
||||
## Использование в kmain()
|
||||
|
||||
```rust
|
||||
let root_cnode = CNode::new(256);
|
||||
// Вставка capability на фрейм
|
||||
root_cnode.insert(0, mem_cap).unwrap();
|
||||
// Mint с урезанными правами
|
||||
root_cnode.mint(0, 10, Relation::Borrow, R | W).unwrap();
|
||||
// Revoke — отзыв всех потомков
|
||||
root_cnode.revoke(0);
|
||||
```
|
||||
73
kernel/docs/capability/objects.md
Normal file
73
kernel/docs/capability/objects.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Ядерные объекты: `object.rs`
|
||||
|
||||
## Концептуальная модель
|
||||
|
||||
`KernelObject` — это ref-counted представление объекта ядра.
|
||||
В отличие от `Capability` (которая указывает на объект), `KernelObject`
|
||||
— это сам объект с подсчётом ссылок.
|
||||
|
||||
## Структура
|
||||
|
||||
```rust
|
||||
pub struct KernelObject {
|
||||
pub phys_addr: PhysAddr, // физический адрес объекта
|
||||
pub size_bits: u8, // размер в битах (2^size_bits)
|
||||
pub obj_type: ObjectType, // тип объекта
|
||||
pub ref_count: AtomicUsize, // счётчик ссылок
|
||||
pub owner_id: u64, // ID владельца
|
||||
}
|
||||
```
|
||||
|
||||
## ObjectType — классификация объектов
|
||||
|
||||
```rust
|
||||
pub enum ObjectType {
|
||||
Untyped, // Сырая память без типа
|
||||
Frame, // Фрейм (4KiB страница)
|
||||
CNode, // Capability Node
|
||||
ThreadBlock, // Блок управления потоком (TCB)
|
||||
PageTable, // Таблица страниц
|
||||
}
|
||||
```
|
||||
|
||||
## Ref-counting
|
||||
|
||||
```rust
|
||||
impl KernelObject {
|
||||
pub fn add_ref(&self) {
|
||||
self.ref_count.fetch_add(1, Ordering::Relaxed);
|
||||
// ^ Relaxed: нас не волнует порядок других операций
|
||||
// при увеличении счётчика. Только атомарность.
|
||||
}
|
||||
|
||||
pub fn release(&self) -> bool {
|
||||
self.ref_count.fetch_sub(1, Ordering::Release) == 1
|
||||
// ^ Release: все операции до release видны тому,
|
||||
// кто Acquire-читает ref_count.
|
||||
// Возвращает true, если это была последняя ссылка
|
||||
// (объект должен быть уничтожен).
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Текущее состояние
|
||||
|
||||
`KernelObject` определён, но пока не используется активно.
|
||||
Capability-система в текущей версии работает напрямую с `CapObject`
|
||||
и `PhysAddr`, без обёртки в `KernelObject`.
|
||||
|
||||
Планируемое использование:
|
||||
- При выделении памяти через PMActor: создаётся KernelObject.
|
||||
- Capability ссылается на KernelObject через ID/индекс.
|
||||
- Когда последняя Strong capability удалена → KernelObject
|
||||
уничтожается → память возвращается.
|
||||
|
||||
## Отличие CapObject vs KernelObject
|
||||
|
||||
| Характеристика | CapObject | KernelObject |
|
||||
|---------------|-----------|--------------|
|
||||
| Роль | Что capability представляет | Сам объект в памяти ядра |
|
||||
| Ref-count | Нет | AtomicUsize |
|
||||
| Хранение | В CNode slot | В отдельной таблице |
|
||||
| Типы | Memory, CNode, PMActor | Untyped, Frame, CNode, ThreadBlock, PageTable |
|
||||
| Связь | CapObject.Memory.phys = KernelObject.phys_addr | — |
|
||||
Reference in New Issue
Block a user