feat: docs & ARCH 2.2, 2.3, 2.4
This commit is contained in:
110
kernel/docs/debug/serial.md
Normal file
110
kernel/docs/debug/serial.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Serial Port драйвер: `serial.rs`
|
||||
|
||||
## Назначение
|
||||
|
||||
Драйвер последовательного порта (UART 16550, COM1) для отладочного
|
||||
вывода. Позволяет видеть сообщения ядра через QEMU serial console,
|
||||
minicom, screen и т.д.
|
||||
|
||||
## Аппаратная модель
|
||||
|
||||
COM1 расположен по портам ввода-вывода `0x3F8`-`0x3FF`:
|
||||
|
||||
| Порт | Регистр | Назначение |
|
||||
|------|---------|------------|
|
||||
| 0x3F8 | DATA | Чтение/запись данных |
|
||||
| 0x3F9 | IER | Interrupt Enable |
|
||||
| 0x3FA | IIR/FCR | Interrupt ID / FIFO Control |
|
||||
| 0x3FB | LCR | Line Control |
|
||||
| 0x3FC | MCR | Modem Control |
|
||||
| 0x3FD | LSR | Line Status |
|
||||
| 0x3FE | MSR | Modem Status |
|
||||
|
||||
## Инициализация
|
||||
|
||||
```rust
|
||||
pub unsafe fn init() -> Self {
|
||||
let port = Self::COM1; // 0x3F8
|
||||
outb(port + 1, 0x00); // IER = 0 (disable interrupts)
|
||||
outb(port + 3, 0x80); // LCR DLAB=1 (enable baud rate programming)
|
||||
outb(port + 0, 0x03); // Divisor LSB = 3 (38400 baud)
|
||||
outb(port + 1, 0x00); // Divisor MSB = 0
|
||||
outb(port + 3, 0x03); // LCR = 8N1 (8 bits, No parity, 1 stop)
|
||||
outb(port + 2, 0xC7); // FCR = enable FIFO, clear, 14-byte threshold
|
||||
outb(port + 4, 0x0B); // MCR = DTR+RTS+OUT2 (enable IRQ + handshake)
|
||||
SerialPort(port)
|
||||
}
|
||||
```
|
||||
|
||||
### Детали конфигурации
|
||||
|
||||
1. **IER = 0**: отключаем прерывания UART (TODO: включить для RX).
|
||||
2. **DLAB = 1**: разрешаем программирование делителя бода.
|
||||
3. **Divisor = 3**: при тактовой 1.8432 MHz → 115200 / 3 = 38400 бод.
|
||||
4. **LCR = 0x03**: 8N1 — 8 бит данных, нет чётности, 1 стоп-бит.
|
||||
5. **FCR = 0xC7**: enable FIFO, clear both FIFOs, trigger at 14 bytes.
|
||||
6. **MCR = 0x0B**: DTR=1, RTS=1, OUT2=1 (необходимо для IRQ на ISA шине).
|
||||
|
||||
## Отправка байта
|
||||
|
||||
```rust
|
||||
fn is_transmit_empty(&self) -> bool {
|
||||
unsafe { (inb(self.0 + 5) & 0x20) != 0 } // LSR bit 5 = Transmitter Holding Register Empty
|
||||
}
|
||||
|
||||
pub fn send(&self, data: u8) {
|
||||
while !self.is_transmit_empty() {} // Ждём, пока UART готов
|
||||
unsafe { outb(self.0, data); }
|
||||
}
|
||||
```
|
||||
|
||||
## fmt::Write реализация
|
||||
|
||||
```rust
|
||||
impl core::fmt::Write for SerialPort {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
for byte in s.bytes() { self.send(byte); }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Глобальный экземпляр
|
||||
|
||||
```rust
|
||||
static SERIAL_PORT: Locked<Option<SerialPort>> = Locked::new(None);
|
||||
|
||||
pub fn init_global() {
|
||||
let mut guard = SERIAL_PORT.lock();
|
||||
*guard = Some(unsafe { SerialPort::init() });
|
||||
}
|
||||
|
||||
pub fn write_global(args: core::fmt::Arguments) {
|
||||
let mut guard = SERIAL_PORT.lock();
|
||||
if let Some(ref mut sp) = *guard {
|
||||
let _ = sp.write_fmt(args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`init_global()` вызывается из `kmain()` после инициализации TTY.
|
||||
`write_global()` используется:
|
||||
- В `log!()` макросе (через debug.rs).
|
||||
- В `rust_panic()` обработчике.
|
||||
- В обработчиках исключений (GPF, Double Fault, Early Exception).
|
||||
|
||||
## Низкоуровневый I/O
|
||||
|
||||
```rust
|
||||
unsafe fn outb(port: u16, val: u8) {
|
||||
asm!("out dx, al", in("dx") port, in("al") val,
|
||||
options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
unsafe fn inb(port: u16) -> u8 {
|
||||
let res: u8;
|
||||
asm!("in al, dx", out("al") res, in("dx") port,
|
||||
options(nomem, nostack, preserves_flags));
|
||||
res
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user