feat: docs & ARCH 2.2, 2.3, 2.4

This commit is contained in:
Faynot
2026-07-07 16:40:41 +03:00
parent c092a81331
commit 1bfde637d0
50 changed files with 5113 additions and 1823 deletions

View 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**: если очередь отзыва переполнена — паника.
Это критический сбой подсистемы ресурсов.

View 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`).

View 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);
```

View 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 | — |