Record networking and DMA development progress
Add the current OS source tree, build scripts, system image layout, and development notes covering DMA page allocation, e1000 bring-up, net API dispatch, and DHCP work in progress.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
ISO/deshab.img
|
||||
@@ -0,0 +1,154 @@
|
||||
# Deshab 项目上下文
|
||||
|
||||
## 项目概览
|
||||
|
||||
Deshab 当前包含一个内核/系统实验方向,其中启动内核与主内核目标为:
|
||||
|
||||
```text
|
||||
CODE/UTSM -> SYSTEM/boot/utsm.elf
|
||||
CODE/dsk -> SYSTEM/system/deshab64/deshab.elf
|
||||
```
|
||||
|
||||
`utsm.elf` 是当前由 bootloader 直接加载的首阶段内核,负责 UTSM、DKM 驱动加载、平台发现和早期系统服务。当前已实现两阶段 DSK 加载:优先通过 `kernel_api.block` + 内嵌 FAT32 解析从真实块设备读取 `/system/deshab64/deshab.elf`(在 SATA FAT32 盘场景已验证);无块设备时自动回退 Limine boot module 预加载。
|
||||
|
||||
DSK 主内核(`deshab.elf`)当前已实现:
|
||||
- 内嵌 FAT32 只读解析器(256 扇区批量读取 + 内存内文件查找)
|
||||
- PIE ELF loader(PT_LOAD + PT_DYNAMIC + R_X86_64_RELATIVE 重定位)
|
||||
- 旋转加载动画(双缓冲精灵、顺时针、comet-tail 渐变弧)
|
||||
- 首次启动检测:读取 `firstInit.txt`,值为 `0` 则触发首次启动流程
|
||||
- 系统初始化调度:DSK 直接加载并调用 `mouseInit.elf`(PS/2 鼠标安全初始化)
|
||||
- 用户设置向导:DSK 加载并跳转 `FirstInit.elf`(圆角卡片 UI、键盘输入、SHA256 密码、加密配置缓冲)
|
||||
|
||||
FirstInit(`CODE/firstInit`)只负责用户级设置向导:
|
||||
- 淡蓝背景 → 旋转环淡出 → "欢迎使用 Deshab" → 设置提示 → 账户设置卡片
|
||||
- PS/2 键盘轮询输入:计算机名、用户名、密码
|
||||
- SHA256 密码摘要 + XOR 加密配置缓冲(user.conf 格式已定义,写盘待 FAT32 write API)
|
||||
- 18px 黑体中文位图(预渲染嵌入 ELF)
|
||||
|
||||
mouseInit(`CODE/mouse`)是 DSK 直接调度的系统输入初始化器:
|
||||
- 当前为安全 stub(只读 PS/2 状态,不写控制器),避免阻塞首次启动
|
||||
- 后续将实现完整 IRQ12 + 鼠标包解码
|
||||
|
||||
## 当前核心架构
|
||||
|
||||
```text
|
||||
SAS-R0-PCQ
|
||||
单地址空间 Ring0 任务模型
|
||||
Per-CPU O(1) 位图调度器
|
||||
|
||||
DRR
|
||||
Dedicated Recovery Root
|
||||
看门狗 / checkpoint / recovery log / A-B 回滚
|
||||
|
||||
UTSM
|
||||
UUID-Tuned Sealed Memory
|
||||
UUID 调谐封缄内存
|
||||
|
||||
UTRW
|
||||
UTSM Tagged Reader/Writer
|
||||
UTSM 带标记读写器
|
||||
|
||||
DMP
|
||||
Descriptor Meta Page
|
||||
段描述符元页
|
||||
|
||||
PCKC
|
||||
Per-CPU Key Cache
|
||||
每 CPU 密钥缓存
|
||||
```
|
||||
|
||||
## 关键设计约束
|
||||
|
||||
1. 优先考虑极高权限和极高效率。
|
||||
2. 主系统采用单地址空间 Ring0 模型。
|
||||
3. 不追求传统多用户强安全隔离。
|
||||
4. DRR 作为恢复根,应尽量独立于普通调度器。
|
||||
5. UTSM 数据区连续存密文,UUID 不嵌入数据区。
|
||||
6. 调度器热路径必须保持 O(1)。
|
||||
7. checkpoint 只处理 dirty shard / dirty page,不扫描全内存。
|
||||
8. DRR 必须拥有独立 Emergency Pool,普通 OOM 不应阻断 recovery。
|
||||
9. checkpoint metadata 使用 A/B 双槽、CRC 校验和原子 active slot 切换。
|
||||
10. 驱动模块采用 DKM ELF64 .drv ABI,通过 driver_desc / driver_init / driver_exit 暴露入口。
|
||||
11. 驱动默认通过 dkm_kernel_api 调用内核服务,不直接依赖任意内核符号。
|
||||
12. stage0/stage1 驱动必须支持 bootloader 预加载 boot module 方案。
|
||||
|
||||
## 重要文件
|
||||
|
||||
```text
|
||||
SYSTEM/
|
||||
打包为 IMG 后的系统根目录。
|
||||
|
||||
SYSTEM/driver/manifest.json
|
||||
内核启动驱动模块清单,控制 ELF .drv 驱动的分阶段加载。
|
||||
|
||||
CODE/DKM/README.md
|
||||
DKM 驱动模块系统说明,记录驱动加载目标、ABI、loader、状态机和实现清单。
|
||||
|
||||
CODE/UTSM/README.md
|
||||
UTSM 模块设计说明。
|
||||
|
||||
RE/UTSM_算法记录.md
|
||||
UTSM 核心算法记录。
|
||||
|
||||
RE/UTSM_设计架构.md
|
||||
UTSM 架构记录。
|
||||
|
||||
RE/驱动模块ABI设计.md
|
||||
DKM 驱动模块 ABI、kernel_api、ELF relocation、manifest、状态机和 boot module 方案。
|
||||
|
||||
ISO/deshab.img
|
||||
build.ps1 将 SYSTEM 打包生成的 GPT 磁盘镜像,包含一个 FAT32 EFI System Partition。
|
||||
|
||||
ISO/run_qemu.bat
|
||||
使用 QEMU 启动 ISO/deshab.img 的脚本。
|
||||
```
|
||||
|
||||
## 构建相关
|
||||
|
||||
已知说明:
|
||||
|
||||
```text
|
||||
SYSTEM/ 是打包为 IMG 后的系统根目录。
|
||||
SYSTEM/boot/ 存放启动模块,例如 utsm.elf。
|
||||
SYSTEM/driver/ 存放 ELF .drv 驱动模块和 manifest.json。
|
||||
CODE/UTSM/README.md: 编译到 SYSTEM/boot/utsm.elf
|
||||
build.bat: 调用 build.ps1。
|
||||
build.ps1: 编译 UTSM,生成 SYSTEM/boot/utsm.elf,并把 SYSTEM 打包为 ISO/deshab.img。deshab.img 是 GPT 磁盘镜像,包含 FAT32 EFI System Partition。
|
||||
ISO/run_qemu.bat: 用 QEMU + UEFI 固件启动 ISO/deshab.img,串口输出到 stdio。
|
||||
```
|
||||
|
||||
当前 UTSM 已有首阶段内核代码骨架和构建脚本,可生成 utsm.elf 与 GPT + FAT32 ESP 镜像。早期启动要求:进入 C 前 `cli/cld`,early serial 不允许无限等待硬件 ready 位,未启用 FPU/SSE 前编译必须使用 `-mno-sse -mno-sse2 -mno-mmx -msoft-float`。DATA/BSS 段使用 PF_R|PF_W|PF_X(SAS-R0 下驱动模块内存需可执行)。
|
||||
|
||||
DKM/DSM 驱动加载系统已完工:内置 `console_early` + manifest.json 解析 + Limine boot module 预加载 + 4 stage 分阶段加载 + 全部 14 个外部 `.drv` ET_REL 装载(ELF64/SHT_NOBITS/R_X86_64_64/32/32S/PC32 relocation + driver_desc 校验 + driver_init 调用)。当前镜像额外预加载 `driver/test.fat32` 作为 FAT32 驱动测试镜像。
|
||||
|
||||
当前已实现 14 个真实 DKM 驱动:
|
||||
- **timer** (stage0): PIT 校准, 100ms busy-wait demo
|
||||
- **apic** (stage0): CPUID Local APIC 探测, IA32_APIC_BASE MSR, ACPI MADT 枚举, LAPIC MMIO 只读寄存器验证;当前保留 PIC IRQ 路由
|
||||
- **acpi** (stage0): Limine RSDP → XSDT, ACPI 表枚举
|
||||
- **pci** (stage0): PCI config space 扫描, 设备枚举
|
||||
- **console_fb** (stage1): Limine framebuffer, 淡蓝色背景 + 居中四色圆弧加载环(纯整数运算,无浮点依赖)
|
||||
- **ahci** (stage1): PCI AHCI `8086:2922` 探测, BAR5/ABAR HHDM MMIO 映射, HBA CAP/GHC/PI/VS 与 port 枚举;已接入 `kernel_api.dma`,支持 SATA disk IDENTIFY 与 LBA0 READ 最小 DMA 路径;可向 `kernel_api.block` 注册 `ahci0` block provider;默认无 SATA 盘/ATAPI 场景会跳过命令
|
||||
- **nvme** (stage1): PCI NVMe `1b36:0010` 探测, BAR0/BAR1 解析, IRQ line 读取;当前 QEMU BAR0 位于 4G 以上,MMIO register 读取等待高位 PCI MMIO 映射能力
|
||||
- **bootfs** (stage1): Limine boot module 内存文件系统
|
||||
- **vfs** (stage2): 挂载 bootfs, 文件查找/读取
|
||||
- **devfs** (stage2): 暴露 `/dev/version`, `/dev/platform`, `/dev/fb0`, `/dev/modules`, `/dev/boot/`
|
||||
- **fat32** (stage2): 只读 FAT32 BPB/FAT/目录项解析, 通过 `driver/test.fat32` 验证读取 `README.TXT`
|
||||
- **ps2kbd** (stage3): PS/2 IRQ1 注册, 端口 `0x60` 扫描码读取, scan code set 1 → ASCII demo
|
||||
- **e1000** (stage3): PCI `8086:10d3` 探测, BAR0 HHDM MMIO 映射, MAC 读取, IRQ 注册, 基础 link setup
|
||||
- **virtio_net** (stage3): PCI virtio-net `1af4:1001` 探测, BAR 枚举, modern virtio PCI capability 枚举;暂不进行 feature negotiation / virtqueue / 收发包
|
||||
|
||||
当前内核平台能力:IDT 0–47 stub、异常处理、PIC remap 到 `0x20–0x2f`、IRQ handler 注册与 EOI、Limine RSDP/framebuffer/module/HHDM request。
|
||||
|
||||
kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bpp, boot_modules_response, irq_register, hhdm_offset, dma.alloc_pages。
|
||||
|
||||
当前 manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。
|
||||
|
||||
下一阶段优先路线:
|
||||
1. **内存与 MMIO 基础设施**: 实现物理页分配器、页表映射接口、低位/高位 PCI MMIO 映射,解决 NVMe 4G 以上 BAR 和后续 DMA 映射问题。
|
||||
2. **DMA 与块设备数据路径**: 建立 contiguous DMA buffer、cache/屏障约定、PRDT/队列内存管理,然后推进 AHCI IDENTIFY/READ 与 NVMe admin queue/identify。
|
||||
3. **IRQ 后端升级**: 在现有 PIC fallback 稳定基础上扩展 IDT/vector allocator/APIC EOI/IOAPIC redirection,逐步迁移设备 IRQ,最后接入 MSI/MSI-X。
|
||||
4. **网络数据路径**: 在 DMA/IRQ 完成后推进 e1000 RX/TX ring 与 virtio-net feature negotiation/virtqueue/RX-TX。
|
||||
5. **VFS 与真实块设备接入**: 将 FAT32 从测试镜像迁移到 AHCI/NVMe block provider,完善挂载、读取、目录遍历和错误路径。
|
||||
6. **DKM 工程化收尾**: 统一驱动本地 ABI 头、清理 warning、补充自动化 QEMU 场景(e1000/virtio-net/nvme/ahci)。
|
||||
7. **FirstInit 完善**: 修复按键崩溃(fb_text 优化问题)、实现鼠标光标移动、user.conf 写盘(需 block write API)、网络配置界面。
|
||||
8. **鼠标输入驱动**: 从 mouseInit stub 升级为完整 PS/2 鼠标 IRQ12 + 包解码 + 光标移动。
|
||||
@@ -0,0 +1,691 @@
|
||||
# DKM 驱动模块系统
|
||||
|
||||
```text
|
||||
DKM = Deshab Kernel Module
|
||||
DSM = Driver Startup Manager
|
||||
```
|
||||
|
||||
DKM 是 Deshab 内核的驱动模块系统,负责在内核启动时加载 `SYSTEM/driver` 下的 ELF64 `.drv` 驱动模块,并把驱动注册到内核服务体系中。
|
||||
|
||||
当前实现状态:CODE/UTSM 内核已接入完整 DKM/DSM 驱动加载系统。内置 `console_early` + manifest.json 解析 + Limine boot module 预加载 + 4 stage 分阶段加载。全部 14 个外部 `.drv` 已完成 ET_REL 装载全链路:ELF64 校验、符号扫描、section 拷贝到 arena、SHT_NOBITS 清零、R_X86_64_64/32/32S/PC32 重定位、driver_desc magic 校验、外部 driver_init 调用进入 ACTIVE。当前镜像还预加载 `driver/test.fat32` 作为 FAT32 驱动测试镜像。
|
||||
|
||||
Linux/Windows 已编译驱动不能直接作为 DKM 驱动使用。真实驱动必须按本文件定义的 DKM ABI 编译,导出 `driver_desc`、`driver_init`、`driver_exit`,并只通过 `dkm_kernel_api` 调用内核服务。
|
||||
|
||||
当前真实驱动进度:14/14。
|
||||
|
||||
```text
|
||||
stage0: timer, apic, acpi, pci
|
||||
stage1: console_fb, ahci, nvme, bootfs
|
||||
stage2: vfs, devfs, fat32
|
||||
stage3: ps2kbd, e1000, virtio_net
|
||||
```
|
||||
|
||||
manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。当前 IDT/PIC/IRQ 基础设施已在内核侧实现,`apic` 已完成 APIC discovery 并保留 PIC IRQ 路由,`ahci` 已完成 PCI/ABAR/HBA/port discovery,并通过 `kernel_api.dma.alloc_pages` 接入早期低位 DMA buffer,支持 SATA disk IDENTIFY 与 LBA0 READ 最小路径;识别 SATA disk 后可向 `kernel_api.block` 注册 `ahci0` block provider。`fat32` 已优先通过 block provider 读取 FAT32 镜像,无设备或 BPB 无效时回退 boot module。`nvme` 已完成 PCI discovery 并能识别 QEMU NVMe 控制器;由于当前 QEMU BAR0 位于 4G 以上,NVMe MMIO register 读取等待高位 PCI MMIO 映射能力。`ps2kbd` 和 `e1000` 已通过 `kernel_api.irq_register` 验证 IRQ handler 注册路径。`virtio_net` 已完成 PCI discovery 和 modern virtio capability 枚举,尚未进行 feature negotiation / virtqueue / RX-TX。
|
||||
|
||||
下一阶段 DKM/驱动路线:
|
||||
|
||||
```text
|
||||
P0: 物理页分配器 + 页表/MMIO 映射接口
|
||||
P1: DMA buffer/屏障约定 + AHCI/NVMe block 数据路径
|
||||
P2: IDT 256 vectors + vector allocator + APIC/IOAPIC/MSI IRQ backend
|
||||
P3: e1000 与 virtio_net RX/TX 数据路径
|
||||
P4: FAT32 接入真实 block provider,替代 test.fat32 启动镜像
|
||||
P5: 驱动 ABI 公共头、warning 清理、QEMU 场景自动化
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. 模块目标
|
||||
|
||||
DKM 的目标:
|
||||
|
||||
```text
|
||||
1. 从 SYSTEM/driver/manifest.json 读取驱动清单。
|
||||
2. 按 stage 分阶段加载驱动。
|
||||
3. 支持 ELF64 .drv 模块加载和重定位。
|
||||
4. 提供稳定的 driver_desc ABI。
|
||||
5. 通过 dkm_kernel_api 向驱动暴露内核服务。
|
||||
6. 支持 stage0/stage1 boot module 早期加载。
|
||||
7. 将驱动内存纳入 UTSM。
|
||||
8. 将驱动状态和恢复操作纳入 DRR。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 系统目录约定
|
||||
|
||||
打包为 IMG 后,`SYSTEM` 是系统根目录。
|
||||
|
||||
```text
|
||||
SYSTEM/
|
||||
boot/
|
||||
utsm.elf
|
||||
limine.conf
|
||||
|
||||
driver/
|
||||
manifest.json
|
||||
|
||||
platform/
|
||||
timer.drv
|
||||
apic.drv
|
||||
acpi.drv
|
||||
|
||||
bus/
|
||||
pci.drv
|
||||
|
||||
block/
|
||||
ahci.drv
|
||||
nvme.drv
|
||||
|
||||
fs/
|
||||
bootfs.drv
|
||||
vfs.drv
|
||||
fat32.drv
|
||||
devfs.drv
|
||||
|
||||
net/
|
||||
e1000.drv
|
||||
virtio_net.drv
|
||||
|
||||
input/
|
||||
ps2kbd.drv
|
||||
|
||||
console/
|
||||
console_fb.drv
|
||||
```
|
||||
|
||||
当前驱动清单:
|
||||
|
||||
```text
|
||||
SYSTEM/driver/manifest.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 启动阶段
|
||||
|
||||
DKM 使用分阶段加载。
|
||||
|
||||
```text
|
||||
stage0: platform
|
||||
timer / apic / acpi / pci
|
||||
建立平台发现、中断、总线和基础计时能力。
|
||||
|
||||
stage1: boot
|
||||
console / block / bootfs
|
||||
建立启动日志、块设备和启动文件访问能力。
|
||||
|
||||
stage2: filesystem
|
||||
vfs / fat32 / devfs
|
||||
建立完整 VFS 和可挂载文件系统。
|
||||
|
||||
stage3: optional
|
||||
net / input / gpu
|
||||
加载网络、输入、图形等非启动关键驱动。
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
```text
|
||||
1. stage0/stage1 可由 bootloader 预加载为 boot module。
|
||||
2. stage2/stage3 默认从 SYSTEM/driver 加载。
|
||||
3. required=true 的驱动失败时进入 DRR recovery 或 panic。
|
||||
4. required=false 的驱动失败时标记 FAILED 并继续启动。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. driver_desc ABI
|
||||
|
||||
每个 `.drv` 必须导出三个符号:
|
||||
|
||||
```text
|
||||
driver_desc
|
||||
driver_init
|
||||
driver_exit
|
||||
```
|
||||
|
||||
### 4.1 常量
|
||||
|
||||
```c
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1
|
||||
```
|
||||
|
||||
`0x444B4D31` 表示:
|
||||
|
||||
```text
|
||||
DKM1
|
||||
```
|
||||
|
||||
### 4.2 driver_desc 结构
|
||||
|
||||
```c
|
||||
struct dkm_driver_desc {
|
||||
uint32_t magic;
|
||||
uint16_t abi_version;
|
||||
uint16_t desc_size;
|
||||
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
|
||||
uint32_t driver_class;
|
||||
uint32_t stage;
|
||||
uint32_t flags;
|
||||
uint32_t priority;
|
||||
|
||||
const char *const *depends;
|
||||
uint32_t depends_count;
|
||||
|
||||
const char *const *provides;
|
||||
uint32_t provides_count;
|
||||
|
||||
uint64_t min_kernel_abi;
|
||||
uint64_t feature_bits;
|
||||
|
||||
uint64_t reserved0;
|
||||
uint64_t reserved1;
|
||||
};
|
||||
```
|
||||
|
||||
### 4.3 driver_class
|
||||
|
||||
```text
|
||||
DKM_CLASS_PLATFORM
|
||||
DKM_CLASS_BUS
|
||||
DKM_CLASS_INTERRUPT
|
||||
DKM_CLASS_TIMER
|
||||
DKM_CLASS_CONSOLE
|
||||
DKM_CLASS_STORAGE
|
||||
DKM_CLASS_FS
|
||||
DKM_CLASS_NET
|
||||
DKM_CLASS_INPUT
|
||||
DKM_CLASS_GPU
|
||||
DKM_CLASS_MISC
|
||||
```
|
||||
|
||||
### 4.4 flags
|
||||
|
||||
```text
|
||||
DKM_F_REQUIRED
|
||||
当前 stage 中失败则进入 DRR recovery 或 panic。
|
||||
|
||||
DKM_F_BOOT_MODULE
|
||||
可由 bootloader 预加载。
|
||||
|
||||
DKM_F_NO_UNLOAD
|
||||
不允许卸载。
|
||||
|
||||
DKM_F_STRONG_RECOVERY
|
||||
驱动 data/bss 段使用 UTSM 强恢复策略。
|
||||
|
||||
DKM_F_DMA_REQUIRED
|
||||
驱动需要 DMA API。
|
||||
|
||||
DKM_F_EARLY_LOG
|
||||
驱动 init 期间允许使用 early log。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 驱动入口
|
||||
|
||||
```c
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle);
|
||||
|
||||
int driver_exit(struct dkm_driver_handle *handle);
|
||||
```
|
||||
|
||||
约定:
|
||||
|
||||
```text
|
||||
1. driver_init 返回 0 表示成功。
|
||||
2. driver_init 返回负数表示失败。
|
||||
3. driver_exit 仅用于非 DKM_F_NO_UNLOAD 驱动。
|
||||
4. required 驱动失败会触发 DRR recovery 或 panic。
|
||||
5. optional 驱动失败会被标记为 FAILED。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. kernel_api ABI
|
||||
|
||||
驱动默认不直接依赖任意内核符号,而是通过 `dkm_kernel_api` 调用内核服务。
|
||||
|
||||
```c
|
||||
#define DKM_KERNEL_API_VERSION 1
|
||||
|
||||
struct dkm_kernel_api {
|
||||
uint32_t version;
|
||||
uint32_t size;
|
||||
uint64_t feature_bits;
|
||||
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem;
|
||||
const void *utsm;
|
||||
const void *irq;
|
||||
const void *pci;
|
||||
const void *dma;
|
||||
const void *vfs;
|
||||
const void *net;
|
||||
const void *timer;
|
||||
const void *drr;
|
||||
|
||||
const void *rsdp_address;
|
||||
const void *fb_address;
|
||||
uint64_t fb_width;
|
||||
uint64_t fb_height;
|
||||
uint64_t fb_pitch;
|
||||
uint16_t fb_bpp;
|
||||
const void *boot_modules_response;
|
||||
int (*irq_register)(uint8_t irq, void *handler);
|
||||
uint64_t hhdm_offset;
|
||||
};
|
||||
```
|
||||
|
||||
### 6.1 log API
|
||||
|
||||
```c
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
```
|
||||
|
||||
### 6.2 memory API
|
||||
|
||||
```c
|
||||
struct dkm_mem_api {
|
||||
void *(*alloc)(uint64_t size, uint32_t flags);
|
||||
void (*free)(void *ptr);
|
||||
|
||||
void *(*alloc_pages)(uint32_t order, uint32_t flags);
|
||||
void (*free_pages)(void *ptr, uint32_t order);
|
||||
|
||||
void *(*mmio_map)(uint64_t phys, uint64_t size, uint32_t flags);
|
||||
void (*mmio_unmap)(void *virt, uint64_t size);
|
||||
};
|
||||
```
|
||||
|
||||
### 6.3 UTSM API
|
||||
|
||||
```c
|
||||
struct dkm_utsm_api {
|
||||
int (*create_segment)(uint64_t size, uint32_t flags, struct utsm_capability *out);
|
||||
int (*read)(struct utsm_capability cap, uint64_t off, void *dst, uint64_t len);
|
||||
int (*write)(struct utsm_capability cap, uint64_t off, const void *src, uint64_t len);
|
||||
int (*pin)(struct utsm_capability cap, uint64_t off, uint64_t len, void **window);
|
||||
int (*unpin)(void *window, uint32_t flags);
|
||||
};
|
||||
```
|
||||
|
||||
### 6.4 当前平台直通能力
|
||||
|
||||
当前内核已通过 `dkm_kernel_api` 直接暴露早期平台信息和 IRQ/HHDM 能力:
|
||||
|
||||
```text
|
||||
rsdp_address Limine RSDP 地址,用于 ACPI 驱动
|
||||
fb_address/width/height Limine framebuffer 信息,用于 console_fb
|
||||
fb_pitch/fb_bpp framebuffer 行跨度和像素格式
|
||||
boot_modules_response Limine module response,用于 bootfs/fat32 测试镜像
|
||||
irq_register 注册 PIC IRQ0–15 handler,用于 ps2kbd/e1000
|
||||
hhdm_offset physical → virtual direct map,用于 PCI BAR MMIO
|
||||
```
|
||||
|
||||
### 6.5 IRQ / PCI / DMA API
|
||||
|
||||
```c
|
||||
struct dkm_irq_api {
|
||||
int (*register_irq)(uint32_t vector, int (*handler)(void *ctx), void *ctx, uint32_t flags);
|
||||
int (*unregister_irq)(uint32_t vector);
|
||||
void (*ack)(uint32_t vector);
|
||||
};
|
||||
|
||||
struct dkm_pci_api {
|
||||
int (*scan)(int (*cb)(const struct pci_device *dev, void *ctx), void *ctx);
|
||||
int (*enable_device)(const struct pci_device *dev);
|
||||
uint64_t (*bar_phys)(const struct pci_device *dev, uint32_t bar);
|
||||
uint64_t (*bar_size)(const struct pci_device *dev, uint32_t bar);
|
||||
};
|
||||
|
||||
struct dkm_dma_api {
|
||||
int (*alloc)(uint64_t size, uint32_t flags, struct dkm_dma_buffer *out);
|
||||
void (*free)(struct dkm_dma_buffer *buf);
|
||||
int (*sync_for_device)(struct dkm_dma_buffer *buf);
|
||||
int (*sync_for_cpu)(struct dkm_dma_buffer *buf);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. ELF64 .drv Loader
|
||||
|
||||
第一版仅支持 x86_64 ELF64 小型内核模型。
|
||||
|
||||
### 7.1 必须支持的 relocation
|
||||
|
||||
```text
|
||||
R_X86_64_64
|
||||
绝对 64-bit 地址重定位。
|
||||
|
||||
R_X86_64_RELATIVE
|
||||
base + addend。
|
||||
|
||||
R_X86_64_GLOB_DAT
|
||||
GOT 符号地址。
|
||||
|
||||
R_X86_64_JUMP_SLOT
|
||||
PLT 函数地址。
|
||||
```
|
||||
|
||||
### 7.2 建议支持的 relocation
|
||||
|
||||
```text
|
||||
R_X86_64_PC32
|
||||
32-bit PC-relative 调用。
|
||||
|
||||
R_X86_64_PLT32
|
||||
PLT-relative 调用。
|
||||
|
||||
R_X86_64_32 / R_X86_64_32S
|
||||
仅允许确认地址范围安全时使用。
|
||||
```
|
||||
|
||||
### 7.3 第一版禁止
|
||||
|
||||
```text
|
||||
TLS relocation
|
||||
IFUNC
|
||||
lazy binding
|
||||
外部动态库依赖
|
||||
用户态 libc 依赖
|
||||
异常展开表强依赖
|
||||
```
|
||||
|
||||
### 7.4 加载流程
|
||||
|
||||
```text
|
||||
DKM_ELF_LOAD(path_or_boot_module)
|
||||
1. 读取 ELF header。
|
||||
2. 校验 ELFCLASS64 / EM_X86_64 / endian / ABI。
|
||||
3. 读取 program headers 或 section headers。
|
||||
4. 计算模块内存大小和对齐。
|
||||
5. 通过 UTSM 创建 code/data 段。
|
||||
6. 加载 PT_LOAD 或 SHF_ALLOC section。
|
||||
7. 清零 .bss。
|
||||
8. 解析 symbol table。
|
||||
9. 解析 relocation section。
|
||||
10. 解析内核导出符号和 dkm_kernel_api。
|
||||
11. 执行 relocation。
|
||||
12. 查找 driver_desc / driver_init / driver_exit。
|
||||
13. 设置 code 段 READ|EXEC|SEALED。
|
||||
14. 设置 data 段 READ|WRITE|STRONG_RECOVERY。
|
||||
15. 返回 module handle。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. manifest.json 格式
|
||||
|
||||
路径:
|
||||
|
||||
```text
|
||||
SYSTEM/driver/manifest.json
|
||||
```
|
||||
|
||||
顶层结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "deshab.driver.manifest.v1",
|
||||
"root": "SYSTEM",
|
||||
"driverRoot": "SYSTEM/driver",
|
||||
"format": "elf64-drv",
|
||||
"abi": {},
|
||||
"policy": {},
|
||||
"stages": []
|
||||
}
|
||||
```
|
||||
|
||||
驱动项示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "pci",
|
||||
"path": "driver/bus/pci.drv",
|
||||
"class": "bus",
|
||||
"required": true,
|
||||
"depends": ["irq"],
|
||||
"provides": ["pci"]
|
||||
}
|
||||
```
|
||||
|
||||
路径规则:
|
||||
|
||||
```text
|
||||
manifest 中的 path 相对 SYSTEM 根目录。
|
||||
SYSTEM/driver/manifest.json 内的 driver/bus/pci.drv 指向 SYSTEM/driver/bus/pci.drv。
|
||||
stage0/stage1 驱动允许来自 boot module。
|
||||
stage2/stage3 驱动默认从 SYSTEM/driver 加载。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 驱动加载状态机
|
||||
|
||||
正常加载:
|
||||
|
||||
```text
|
||||
DISCOVERED
|
||||
-> QUEUED
|
||||
-> DEP_WAIT
|
||||
-> LOADING
|
||||
-> ELF_CHECKED
|
||||
-> MEMORY_ALLOCATED
|
||||
-> RELOCATED
|
||||
-> ABI_CHECKED
|
||||
-> INITING
|
||||
-> ACTIVE
|
||||
```
|
||||
|
||||
失败状态:
|
||||
|
||||
```text
|
||||
FAILED_ELF
|
||||
FAILED_RELOCATION
|
||||
FAILED_DEPENDENCY
|
||||
FAILED_ABI
|
||||
FAILED_INIT
|
||||
FAILED_REQUIRED
|
||||
```
|
||||
|
||||
卸载状态:
|
||||
|
||||
```text
|
||||
ACTIVE
|
||||
-> QUIESCING
|
||||
-> EXITING
|
||||
-> UNLOADING
|
||||
-> UNLOADED
|
||||
```
|
||||
|
||||
DRR 恢复状态:
|
||||
|
||||
```text
|
||||
ACTIVE
|
||||
-> SUSPENDED_BY_DRR
|
||||
-> RECOVERING
|
||||
-> REINITING
|
||||
-> ACTIVE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. DSM 加载策略
|
||||
|
||||
```text
|
||||
DSM_LOAD_ALL()
|
||||
1. 读取 SYSTEM/driver/manifest.json。
|
||||
2. 校验 schema / abi / policy。
|
||||
3. 按 stage 顺序加载。
|
||||
4. 每个 stage 内按 priority 排序。
|
||||
5. 检查 depends 是否已由 provides 满足。
|
||||
6. 未满足依赖的驱动进入 DEP_WAIT。
|
||||
7. 每轮加载成功后更新 provides set。
|
||||
8. 一轮后仍 DEP_WAIT 的 required 驱动进入 FAILED_REQUIRED。
|
||||
9. optional 驱动失败只记录状态。
|
||||
```
|
||||
|
||||
required 失败策略:
|
||||
|
||||
```text
|
||||
stage0/stage1 required 失败:
|
||||
DRR report_fault -> boot fallback -> panic / system rollback
|
||||
|
||||
stage2 required 失败:
|
||||
DRR recovery -> fallback 到 bootfs 或只读模式
|
||||
|
||||
stage3 optional 失败:
|
||||
mark FAILED -> continue boot
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. stage0/stage1 boot module 方案
|
||||
|
||||
stage0/stage1 不应依赖普通 VFS。
|
||||
|
||||
bootloader 应预加载:
|
||||
|
||||
```text
|
||||
SYSTEM/driver/manifest.json
|
||||
SYSTEM/driver/platform/timer.drv
|
||||
SYSTEM/driver/platform/acpi.drv
|
||||
SYSTEM/driver/platform/apic.drv
|
||||
SYSTEM/driver/bus/pci.drv
|
||||
SYSTEM/driver/console/console_fb.drv
|
||||
SYSTEM/driver/fs/bootfs.drv
|
||||
SYSTEM/driver/test.fat32
|
||||
```
|
||||
|
||||
可选预加载:
|
||||
|
||||
```text
|
||||
SYSTEM/driver/block/ahci.drv
|
||||
SYSTEM/driver/block/nvme.drv
|
||||
SYSTEM/driver/fs/vfs.drv
|
||||
SYSTEM/driver/fs/devfs.drv
|
||||
SYSTEM/driver/fs/fat32.drv
|
||||
SYSTEM/driver/net/e1000.drv
|
||||
SYSTEM/driver/net/virtio_net.drv
|
||||
SYSTEM/driver/input/ps2kbd.drv
|
||||
```
|
||||
|
||||
boot module 表:
|
||||
|
||||
```c
|
||||
struct dkm_boot_module {
|
||||
const char *path;
|
||||
void *base;
|
||||
uint64_t size;
|
||||
uint32_t type;
|
||||
uint32_t flags;
|
||||
uint64_t checksum;
|
||||
};
|
||||
|
||||
struct dkm_boot_module_table {
|
||||
uint32_t count;
|
||||
struct dkm_boot_module modules[];
|
||||
};
|
||||
```
|
||||
|
||||
查找规则:
|
||||
|
||||
```text
|
||||
DSM_LOAD_DRIVER(path)
|
||||
1. 如果 path 在 boot module table 中,直接从内存加载。
|
||||
2. 否则如果 VFS 已可用,从 SYSTEM/path 加载。
|
||||
3. 否则返回 BOOT_MODULE_NOT_FOUND。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 与 UTSM / DRR 的关系
|
||||
|
||||
### 12.1 UTSM 内存策略
|
||||
|
||||
```text
|
||||
驱动 text:READ | EXEC | SEALED
|
||||
驱动 rodata:READ | SEALED
|
||||
驱动 data:READ | WRITE | STRONG_RECOVERY
|
||||
驱动 bss:READ | WRITE | STRONG_RECOVERY
|
||||
驱动 DMA:DMA | PINNED,必要时 CIPHERTEXT_DMA 或 NO_ENCRYPT
|
||||
```
|
||||
|
||||
### 12.2 DRR 记录项
|
||||
|
||||
```text
|
||||
driver name
|
||||
stage
|
||||
state
|
||||
module memory range
|
||||
provided services
|
||||
recovery ops
|
||||
init result
|
||||
last fault
|
||||
```
|
||||
|
||||
### 12.3 recovery ops
|
||||
|
||||
```c
|
||||
struct dkm_driver_recovery_ops {
|
||||
int (*quiesce)(void *ctx);
|
||||
int (*reset)(void *ctx);
|
||||
int (*reinit)(void *ctx);
|
||||
int (*dump_state)(void *ctx, void *buf, uint64_t len);
|
||||
};
|
||||
```
|
||||
|
||||
DRR recovery 顺序:
|
||||
|
||||
```text
|
||||
quiesce -> UTSM rollback -> device reset -> driver reinit -> resume
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. 第一版实现清单
|
||||
|
||||
```text
|
||||
1. manifest.json parser
|
||||
2. boot module table lookup
|
||||
3. ELF64 header 校验
|
||||
4. ELF64 section/program loading
|
||||
5. relocation 处理
|
||||
6. driver_desc 校验
|
||||
7. kernel_api table 初始化
|
||||
8. depends/provides resolver
|
||||
9. staged loader
|
||||
10. driver state table
|
||||
11. DRR driver fault reporting
|
||||
12. UTSM module memory allocation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. 相关文件
|
||||
|
||||
```text
|
||||
SYSTEM/driver/manifest.json
|
||||
驱动启动清单。
|
||||
|
||||
RE/驱动模块ABI设计.md
|
||||
DKM ABI 详细设计。
|
||||
|
||||
CODE/UTSM/README.md
|
||||
UTSM 与驱动加载的整合说明。
|
||||
```
|
||||
@@ -0,0 +1,298 @@
|
||||
/* DKM ACPI Driver - RSDP scan / RSDT parse / table enumeration
|
||||
* Stage 0, optional, provides "acpi".
|
||||
* Scans BIOS memory for RSDP, then walks RSDT/XSDT to list all ACPI tables.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* DKM ABI types
|
||||
* --------------------------------------------------------------- */
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version;
|
||||
u32 size;
|
||||
u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem;
|
||||
const void *utsm;
|
||||
const void *irq;
|
||||
const void *pci;
|
||||
const void *dma;
|
||||
const void *vfs;
|
||||
const void *net;
|
||||
const void *timer;
|
||||
const void *drr;
|
||||
const void *rsdp_address;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
struct dkm_driver_desc {
|
||||
u32 magic;
|
||||
u16 abi_version;
|
||||
u16 desc_size;
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
u32 driver_class;
|
||||
u32 stage;
|
||||
u32 flags;
|
||||
u32 priority;
|
||||
const char *const *depends;
|
||||
u32 depends_count;
|
||||
const char *const *provides;
|
||||
u32 provides_count;
|
||||
u64 min_kernel_abi;
|
||||
u64 feature_bits;
|
||||
u64 reserved0;
|
||||
u64 reserved1;
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* ACPI structures
|
||||
* --------------------------------------------------------------- */
|
||||
typedef struct __attribute__((packed)) {
|
||||
char signature[8];
|
||||
u8 checksum;
|
||||
char oem_id[6];
|
||||
u8 revision;
|
||||
u32 rsdt_addr;
|
||||
} acpi_rsdp_v1;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
/* RSDP v1 fields */
|
||||
char signature[8];
|
||||
u8 checksum;
|
||||
char oem_id[6];
|
||||
u8 revision;
|
||||
u32 rsdt_addr;
|
||||
/* RSDP v2+ fields */
|
||||
u32 length;
|
||||
u64 xsdt_addr;
|
||||
u8 ext_checksum;
|
||||
u8 reserved[3];
|
||||
} acpi_rsdp_v2;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
char signature[4];
|
||||
u32 length;
|
||||
u8 revision;
|
||||
u8 checksum;
|
||||
char oem_id[6];
|
||||
char oem_table_id[8];
|
||||
u32 oem_revision;
|
||||
u32 creator_id;
|
||||
u32 creator_revision;
|
||||
} acpi_sdt_header;
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver descriptor
|
||||
* --------------------------------------------------------------- */
|
||||
static const char *const g_provides[] = { "acpi" };
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(struct dkm_driver_desc),
|
||||
.name = "acpi",
|
||||
.version = "0.1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = 1, /* DKM_CLASS_PLATFORM */
|
||||
.stage = 0,
|
||||
.flags = 0, /* optional */
|
||||
.priority = 0,
|
||||
.depends = NULL,
|
||||
.depends_count = 0,
|
||||
.provides = g_provides,
|
||||
.provides_count = 1,
|
||||
.min_kernel_abi = 1,
|
||||
.feature_bits = 0,
|
||||
.reserved0 = 0,
|
||||
.reserved1 = 0
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Helpers
|
||||
* --------------------------------------------------------------- */
|
||||
static const struct dkm_log_api *g_log;
|
||||
|
||||
static u32 min_u32(u32 a, u32 b) { return a < b ? a : b; }
|
||||
|
||||
static int memneq(const char *a, const char *b, u32 n) {
|
||||
for (u32 i = 0; i < n; i++) {
|
||||
if (a[i] != b[i]) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 checksum8(const u8 *data, u32 len) {
|
||||
u8 sum = 0;
|
||||
for (u32 i = 0; i < len; i++) {
|
||||
sum += data[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static void log_hex(const char *prefix, u64 val) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
char buf[32];
|
||||
u32 pos = 0;
|
||||
while (pos < 30) { buf[pos] = 0; pos++; }
|
||||
pos = 0;
|
||||
buf[pos++] = '0';
|
||||
buf[pos++] = 'x';
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
buf[pos++] = hex[(val >> (i * 4)) & 0xf];
|
||||
}
|
||||
buf[pos] = 0;
|
||||
|
||||
/* manual concat via serial_write-like approach: pass prefix then buf */
|
||||
g_log->info(prefix);
|
||||
g_log->info(buf);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* RSDP scan
|
||||
* --------------------------------------------------------------- */
|
||||
static const acpi_rsdp_v2 *acpi_find_rsdp(const void *limine_rsdp) {
|
||||
/* If Limine gave us the RSDP, use it directly */
|
||||
if (limine_rsdp) {
|
||||
const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)limine_rsdp;
|
||||
if (memneq(rsdp->signature, "RSD PTR ", 8) == 0) {
|
||||
if (checksum8((const u8 *)rsdp, 20) == 0) {
|
||||
return rsdp;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* EBDA: extended BIOS data area, base at 0x40E */
|
||||
{
|
||||
u16 ebda_seg = *(const volatile u16 *)(uintptr_t)0x40E;
|
||||
u64 ebda_base = (u64)ebda_seg << 4;
|
||||
if (ebda_base >= 0x80000ULL && ebda_base < 0x100000ULL) {
|
||||
for (u64 off = 0; off < 1024; off += 16) {
|
||||
const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)(uintptr_t)(ebda_base + off);
|
||||
if (off + sizeof(acpi_rsdp_v1) > 1024) break;
|
||||
if (memneq(rsdp->signature, "RSD PTR ", 8) == 0) {
|
||||
if (checksum8((const u8 *)rsdp, 20) == 0) {
|
||||
return rsdp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* BIOS read-only area: 0x000E0000 - 0x000FFFFF */
|
||||
for (u64 addr = 0x000E0000ULL; addr < 0x00100000ULL; addr += 16) {
|
||||
const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)(uintptr_t)addr;
|
||||
if (memneq(rsdp->signature, "RSD PTR ", 8) == 0) {
|
||||
if (checksum8((const u8 *)rsdp, 20) == 0) {
|
||||
return rsdp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Table enumeration
|
||||
* --------------------------------------------------------------- */
|
||||
static void acpi_enumerate_rsdt(const acpi_sdt_header *rsdt, u32 entry_size) {
|
||||
u32 entry_count = (rsdt->length - sizeof(acpi_sdt_header)) / entry_size;
|
||||
const u8 *entries = (const u8 *)rsdt + sizeof(acpi_sdt_header);
|
||||
|
||||
g_log->info("[acpi] tables found");
|
||||
for (u32 i = 0; i < entry_count; i++) {
|
||||
u64 addr;
|
||||
if (entry_size == 4) {
|
||||
addr = (u64)((const u32 *)entries)[i];
|
||||
} else {
|
||||
addr = ((const u64 *)entries)[i];
|
||||
}
|
||||
if (addr == 0) continue;
|
||||
|
||||
const acpi_sdt_header *hdr = (const acpi_sdt_header *)(uintptr_t)addr;
|
||||
char sig[5];
|
||||
sig[0] = hdr->signature[0];
|
||||
sig[1] = hdr->signature[1];
|
||||
sig[2] = hdr->signature[2];
|
||||
sig[3] = hdr->signature[3];
|
||||
sig[4] = 0;
|
||||
|
||||
g_log->info("[acpi] ");
|
||||
g_log->info(sig);
|
||||
log_hex("[acpi] addr=", addr);
|
||||
log_hex("[acpi] len =", (u64)hdr->length);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver entry points
|
||||
* --------------------------------------------------------------- */
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
|
||||
if (!api || !api->log) {
|
||||
return -1;
|
||||
}
|
||||
g_log = api->log;
|
||||
|
||||
g_log->info("[acpi] scanning for RSDP");
|
||||
|
||||
const acpi_rsdp_v2 *rsdp = acpi_find_rsdp(api->rsdp_address);
|
||||
if (!rsdp) {
|
||||
g_log->error("[acpi] RSDP not found");
|
||||
return 0; /* optional driver, don't fail boot */
|
||||
}
|
||||
|
||||
g_log->info("[acpi] RSDP found");
|
||||
log_hex("[acpi] revision =", (u64)rsdp->revision);
|
||||
g_log->info("[acpi] oem_id");
|
||||
{
|
||||
char oem[7];
|
||||
for (int i = 0; i < 6; i++) oem[i] = rsdp->oem_id[i];
|
||||
oem[6] = 0;
|
||||
g_log->info(oem);
|
||||
}
|
||||
|
||||
if (rsdp->revision >= 2 && rsdp->xsdt_addr) {
|
||||
g_log->info("[acpi] using XSDT");
|
||||
const acpi_sdt_header *xsdt = (const acpi_sdt_header *)(uintptr_t)rsdp->xsdt_addr;
|
||||
acpi_enumerate_rsdt(xsdt, 8);
|
||||
} else if (rsdp->rsdt_addr) {
|
||||
g_log->info("[acpi] using RSDT");
|
||||
const acpi_sdt_header *rsdt = (const acpi_sdt_header *)(uintptr_t)rsdp->rsdt_addr;
|
||||
acpi_enumerate_rsdt(rsdt, 4);
|
||||
} else {
|
||||
g_log->warn("[acpi] no RSDT/XSDT address");
|
||||
}
|
||||
|
||||
g_log->info("[acpi] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,645 @@
|
||||
/* DKM AHCI Driver — PCI AHCI discovery and ABAR read-only enumeration
|
||||
* Stage 1 optional storage driver, depends on "pci" and "irq", provides "block".
|
||||
* This first version does not issue DMA commands; it only discovers the HBA
|
||||
* and ports so later block I/O can be added safely.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_dma_buffer {
|
||||
void *virt;
|
||||
u64 phys;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
struct dkm_dma_api {
|
||||
int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, struct dkm_dma_buffer *out);
|
||||
};
|
||||
|
||||
struct dkm_block_device_desc {
|
||||
const char *name;
|
||||
u64 sector_size;
|
||||
u64 sector_count;
|
||||
void *ctx;
|
||||
int (*read)(void *ctx, u64 lba, u32 count, void *buffer);
|
||||
};
|
||||
|
||||
struct dkm_block_api {
|
||||
int (*register_device)(const struct dkm_block_device_desc *desc);
|
||||
u32 (*device_count)(void);
|
||||
int (*read)(u32 index, u64 lba, u32 count, void *buffer);
|
||||
u64 (*sector_size)(u32 index);
|
||||
const char *(*device_name)(u32 index);
|
||||
};
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version;
|
||||
u32 size;
|
||||
u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem;
|
||||
const void *utsm;
|
||||
const void *irq;
|
||||
const void *pci;
|
||||
const struct dkm_dma_api *dma;
|
||||
const void *vfs;
|
||||
const void *net;
|
||||
const void *timer;
|
||||
const void *drr;
|
||||
const void *rsdp_address;
|
||||
const void *fb_address;
|
||||
u64 fb_width;
|
||||
u64 fb_height;
|
||||
u64 fb_pitch;
|
||||
u16 fb_bpp;
|
||||
const void *boot_modules_response;
|
||||
int (*irq_register)(u8 irq, void *handler);
|
||||
u64 hhdm_offset;
|
||||
const struct dkm_block_api *block;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
struct dkm_driver_desc {
|
||||
u32 magic;
|
||||
u16 abi_version;
|
||||
u16 desc_size;
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
u32 driver_class;
|
||||
u32 stage;
|
||||
u32 flags;
|
||||
u32 priority;
|
||||
const char *const *depends;
|
||||
u32 depends_count;
|
||||
const char *const *provides;
|
||||
u32 provides_count;
|
||||
u64 min_kernel_abi;
|
||||
u64 feature_bits;
|
||||
u64 reserved0;
|
||||
u64 reserved1;
|
||||
};
|
||||
|
||||
static const char *const g_depends[] = { "pci", "irq" };
|
||||
static const char *const g_provides[] = { "block" };
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(struct dkm_driver_desc),
|
||||
.name = "ahci",
|
||||
.version = "0.1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = 6, /* DKM_CLASS_STORAGE */
|
||||
.stage = 1,
|
||||
.flags = 0, /* optional */
|
||||
.priority = 0,
|
||||
.depends = g_depends,
|
||||
.depends_count = 2,
|
||||
.provides = g_provides,
|
||||
.provides_count = 1,
|
||||
.min_kernel_abi = 1,
|
||||
.feature_bits = 0,
|
||||
.reserved0 = 0,
|
||||
.reserved1 = 0
|
||||
};
|
||||
|
||||
#define PCI_ADDR 0xCF8
|
||||
#define PCI_DATA 0xCFC
|
||||
#define PCI_VENDOR_ID 0x00
|
||||
#define PCI_COMMAND 0x04
|
||||
#define PCI_PROG_IF 0x09
|
||||
#define PCI_SUBCLASS 0x0A
|
||||
#define PCI_CLASS 0x0B
|
||||
#define PCI_HEADER 0x0E
|
||||
#define PCI_BAR5 0x24
|
||||
#define PCI_IRQ_LINE 0x3C
|
||||
|
||||
#define PCI_CMD_IO (1u << 0)
|
||||
#define PCI_CMD_MEM (1u << 1)
|
||||
#define PCI_CMD_BUSM (1u << 2)
|
||||
|
||||
#define AHCI_CLASS_STORAGE 0x01
|
||||
#define AHCI_SUBCLASS_SATA 0x06
|
||||
#define AHCI_PROGIF_AHCI 0x01
|
||||
|
||||
#define HBA_CAP 0x00
|
||||
#define HBA_GHC 0x04
|
||||
#define HBA_IS 0x08
|
||||
#define HBA_PI 0x0C
|
||||
#define HBA_VS 0x10
|
||||
#define HBA_CAP2 0x24
|
||||
#define HBA_BOHC 0x28
|
||||
#define HBA_PORT_BASE 0x100
|
||||
#define HBA_PORT_SIZE 0x80
|
||||
|
||||
#define PxCLB 0x00
|
||||
#define PxFB 0x08
|
||||
#define PxIS 0x10
|
||||
#define PxIE 0x14
|
||||
#define PxCMD 0x18
|
||||
#define PxTFD 0x20
|
||||
#define PxSIG 0x24
|
||||
#define PxSSTS 0x28
|
||||
#define PxSCTL 0x2C
|
||||
#define PxSERR 0x30
|
||||
#define PxSACT 0x34
|
||||
#define PxCI 0x38
|
||||
#define PxSNTF 0x3C
|
||||
#define PxFBS 0x40
|
||||
|
||||
#define AHCI_CMD_ST (1u << 0)
|
||||
#define AHCI_CMD_FRE (1u << 4)
|
||||
#define AHCI_CMD_FR (1u << 14)
|
||||
#define AHCI_CMD_CR (1u << 15)
|
||||
#define AHCI_CMD_ICC_ACTIVE (1u << 28)
|
||||
|
||||
#define ATA_CMD_IDENTIFY 0xEC
|
||||
#define ATA_CMD_IDENTIFY_PACKET 0xA1
|
||||
|
||||
typedef struct __attribute__((packed)) ahci_cmd_header {
|
||||
u16 flags;
|
||||
u16 prdtl;
|
||||
u32 prdbc;
|
||||
u32 ctba;
|
||||
u32 ctbau;
|
||||
u32 reserved[4];
|
||||
} ahci_cmd_header;
|
||||
|
||||
typedef struct __attribute__((packed)) ahci_prdt_entry {
|
||||
u32 dba;
|
||||
u32 dbau;
|
||||
u32 reserved;
|
||||
u32 dbc_i;
|
||||
} ahci_prdt_entry;
|
||||
|
||||
typedef struct __attribute__((packed)) ahci_cmd_table {
|
||||
u8 cfis[64];
|
||||
u8 acmd[16];
|
||||
u8 reserved[48];
|
||||
ahci_prdt_entry prdt[1];
|
||||
} ahci_cmd_table;
|
||||
|
||||
static const struct dkm_log_api *g_log;
|
||||
static volatile u32 *g_abar;
|
||||
static u32 g_ready_port = 0xffffffffu;
|
||||
static ahci_cmd_header *g_cmd_header;
|
||||
static struct dkm_dma_buffer g_cmd_table;
|
||||
static struct dkm_dma_buffer g_data;
|
||||
|
||||
static __inline__ void outl(u16 port, u32 value) {
|
||||
__asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static __inline__ u32 inl(u16 port) {
|
||||
u32 value;
|
||||
__asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
||||
u32 addr = (1u << 31)
|
||||
| ((u32)bus << 16)
|
||||
| ((u32)dev << 11)
|
||||
| ((u32)func << 8)
|
||||
| ((u32)reg & 0xFC);
|
||||
outl(PCI_ADDR, addr);
|
||||
return inl(PCI_DATA);
|
||||
}
|
||||
|
||||
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
|
||||
u32 addr = (1u << 31)
|
||||
| ((u32)bus << 16)
|
||||
| ((u32)dev << 11)
|
||||
| ((u32)func << 8)
|
||||
| ((u32)reg & 0xFC);
|
||||
outl(PCI_ADDR, addr);
|
||||
outl(PCI_DATA, value);
|
||||
}
|
||||
|
||||
static void log_hex(const char *prefix, u64 value) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
char buf[19];
|
||||
u32 pos = 0;
|
||||
buf[pos++] = '0';
|
||||
buf[pos++] = 'x';
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
buf[pos++] = hex[(value >> (i * 4)) & 0xf];
|
||||
}
|
||||
buf[pos] = 0;
|
||||
g_log->info(prefix);
|
||||
g_log->info(buf);
|
||||
}
|
||||
|
||||
static void ahci_zero(void *ptr, u32 len) {
|
||||
u8 *p = (u8 *)ptr;
|
||||
for (u32 i = 0; i < len; i++) p[i] = 0;
|
||||
}
|
||||
|
||||
static int ahci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
||||
for (u8 bus = 0; bus < 8; bus++) {
|
||||
for (u8 dev = 0; dev < 32; dev++) {
|
||||
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
|
||||
if ((vd & 0xffff) == 0xffff) continue;
|
||||
|
||||
u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16);
|
||||
u8 func_count = (header & 0x80) ? 8 : 1;
|
||||
for (u8 func = 0; func < func_count; func++) {
|
||||
u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID);
|
||||
if ((vd2 & 0xffff) == 0xffff) continue;
|
||||
|
||||
u32 class_reg = pci_read(bus, dev, func, 0x08);
|
||||
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
|
||||
u8 subclass = (u8)((class_reg >> 16) & 0xff);
|
||||
u8 class_code = (u8)((class_reg >> 24) & 0xff);
|
||||
|
||||
if (class_code == AHCI_CLASS_STORAGE &&
|
||||
subclass == AHCI_SUBCLASS_SATA &&
|
||||
prog_if == AHCI_PROGIF_AHCI) {
|
||||
*out_bus = bus;
|
||||
*out_dev = dev;
|
||||
*out_func = func;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *sata_sig_name(u32 sig) {
|
||||
if (sig == 0x00000101u) return "SATA drive";
|
||||
if (sig == 0xEB140101u) return "ATAPI device";
|
||||
if (sig == 0xC33C0101u) return "Enclosure mgmt";
|
||||
if (sig == 0x96690101u) return "Port multiplier";
|
||||
if (sig == 0x00000000u) return "empty/unknown";
|
||||
return "unknown signature";
|
||||
}
|
||||
|
||||
static void ahci_log_port(u32 port) {
|
||||
if (!g_abar || port >= 32) {
|
||||
return;
|
||||
}
|
||||
u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE;
|
||||
u32 ssts = g_abar[(base + PxSSTS) / 4];
|
||||
u32 sig = g_abar[(base + PxSIG) / 4];
|
||||
u32 cmd = g_abar[(base + PxCMD) / 4];
|
||||
u32 tfd = g_abar[(base + PxTFD) / 4];
|
||||
u32 serr = g_abar[(base + PxSERR) / 4];
|
||||
u32 ci = g_abar[(base + PxCI) / 4];
|
||||
u32 sact = g_abar[(base + PxSACT) / 4];
|
||||
|
||||
u32 det = ssts & 0xf;
|
||||
u32 spd = (ssts >> 4) & 0xf;
|
||||
u32 ipm = (ssts >> 8) & 0xf;
|
||||
|
||||
log_hex("[ahci] port=", port);
|
||||
log_hex("[ahci] SSTS=", ssts);
|
||||
log_hex("[ahci] DET=", det);
|
||||
log_hex("[ahci] SPD=", spd);
|
||||
log_hex("[ahci] IPM=", ipm);
|
||||
log_hex("[ahci] SIG=", sig);
|
||||
g_log->info("[ahci] type");
|
||||
g_log->info(sata_sig_name(sig));
|
||||
log_hex("[ahci] CMD=", cmd);
|
||||
log_hex("[ahci] TFD=", tfd);
|
||||
log_hex("[ahci] SERR=", serr);
|
||||
log_hex("[ahci] CI=", ci);
|
||||
log_hex("[ahci] SACT=", sact);
|
||||
}
|
||||
|
||||
static int ahci_stop_port(u32 port) {
|
||||
u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE;
|
||||
u32 cmd = g_abar[(base + PxCMD) / 4];
|
||||
cmd &= ~AHCI_CMD_ST;
|
||||
g_abar[(base + PxCMD) / 4] = cmd;
|
||||
for (u32 i = 0; i < 100000; i++) {
|
||||
cmd = g_abar[(base + PxCMD) / 4];
|
||||
if (!(cmd & AHCI_CMD_CR)) break;
|
||||
}
|
||||
cmd &= ~AHCI_CMD_FRE;
|
||||
g_abar[(base + PxCMD) / 4] = cmd;
|
||||
for (u32 i = 0; i < 100000; i++) {
|
||||
cmd = g_abar[(base + PxCMD) / 4];
|
||||
if (!(cmd & AHCI_CMD_FR)) return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ahci_start_port(u32 port) {
|
||||
u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE;
|
||||
u32 cmd = g_abar[(base + PxCMD) / 4];
|
||||
cmd |= AHCI_CMD_FRE;
|
||||
g_abar[(base + PxCMD) / 4] = cmd;
|
||||
cmd |= AHCI_CMD_ST;
|
||||
g_abar[(base + PxCMD) / 4] = cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ahci_fill_identify_fis(u8 *fis, u8 command) {
|
||||
for (u32 i = 0; i < 64; i++) fis[i] = 0;
|
||||
fis[0] = 0x27; /* Register H2D FIS */
|
||||
fis[1] = 1u << 7; /* command */
|
||||
fis[2] = command;
|
||||
fis[7] = 1u << 6; /* LBA mode */
|
||||
}
|
||||
|
||||
static void ahci_fill_read_fis(u8 *fis, u64 lba, u32 count) {
|
||||
ahci_zero(fis, 64);
|
||||
fis[0] = 0x27;
|
||||
fis[1] = 1u << 7;
|
||||
fis[2] = 0x25; /* READ DMA EXT */
|
||||
fis[4] = (u8)(lba & 0xff);
|
||||
fis[5] = (u8)((lba >> 8) & 0xff);
|
||||
fis[6] = (u8)((lba >> 16) & 0xff);
|
||||
fis[7] = (1u << 6) | (u8)((lba >> 24) & 0x0f);
|
||||
fis[8] = (u8)((lba >> 24) & 0xff);
|
||||
fis[9] = (u8)((lba >> 32) & 0xff);
|
||||
fis[10] = (u8)((lba >> 40) & 0xff);
|
||||
fis[12] = (u8)(count & 0xff);
|
||||
fis[13] = (u8)((count >> 8) & 0xff);
|
||||
}
|
||||
|
||||
static int ahci_read_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm_dma_buffer *table, struct dkm_dma_buffer *data, u64 lba, u32 count, void *buffer) {
|
||||
if (!buffer || count == 0) return -1;
|
||||
if (count > 8) return -2;
|
||||
u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE;
|
||||
ahci_cmd_table *tbl = (ahci_cmd_table *)table->virt;
|
||||
u32 bytes = count * 512;
|
||||
ahci_zero(tbl, sizeof(ahci_cmd_table));
|
||||
ahci_zero(data->virt, bytes);
|
||||
|
||||
hdr[0].flags = 5;
|
||||
hdr[0].prdtl = 1;
|
||||
hdr[0].prdbc = 0;
|
||||
hdr[0].ctba = (u32)(table->phys & 0xffffffffu);
|
||||
hdr[0].ctbau = (u32)(table->phys >> 32);
|
||||
|
||||
tbl->prdt[0].dba = (u32)(data->phys & 0xffffffffu);
|
||||
tbl->prdt[0].dbau = (u32)(data->phys >> 32);
|
||||
tbl->prdt[0].dbc_i = (bytes - 1) | (1u << 31);
|
||||
ahci_fill_read_fis(tbl->cfis, lba, count);
|
||||
|
||||
g_abar[(base + PxIS) / 4] = 0xffffffffu;
|
||||
g_abar[HBA_IS / 4] = (1u << port);
|
||||
g_abar[(base + PxCI) / 4] = 1;
|
||||
for (u32 i = 0; i < 5000000; i++) {
|
||||
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
|
||||
u8 *dst = (u8 *)buffer;
|
||||
u8 *src = (u8 *)data->virt;
|
||||
for (u32 j = 0; j < bytes; j++) dst[j] = src[j];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
g_log->warn("[ahci] READ timeout");
|
||||
log_hex("[ahci] READ lba=", lba);
|
||||
log_hex("[ahci] READ count=", count);
|
||||
log_hex("[ahci] READ CI=", g_abar[(base + PxCI) / 4]);
|
||||
log_hex("[ahci] READ IS=", g_abar[(base + PxIS) / 4]);
|
||||
log_hex("[ahci] READ TFD=", g_abar[(base + PxTFD) / 4]);
|
||||
return -3;
|
||||
}
|
||||
|
||||
static int ahci_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
|
||||
(void)ctx;
|
||||
if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1;
|
||||
/* ensure port is in ST+FRE state */
|
||||
u32 base = HBA_PORT_BASE + g_ready_port * HBA_PORT_SIZE;
|
||||
u32 cmd = g_abar[(base + PxCMD) / 4];
|
||||
if (!(cmd & AHCI_CMD_ST) || !(cmd & AHCI_CMD_FRE)) {
|
||||
g_log->warn("[ahci] port not ready; restarting");
|
||||
ahci_stop_port(g_ready_port);
|
||||
ahci_start_port(g_ready_port);
|
||||
}
|
||||
while (count > 0) {
|
||||
u32 chunk = count > 8 ? 8 : count;
|
||||
int status = ahci_read_blocks_internal(g_ready_port, g_cmd_header, &g_cmd_table, &g_data, lba, chunk, buffer);
|
||||
if (status != 0) return status;
|
||||
lba += chunk;
|
||||
count -= chunk;
|
||||
buffer = (void *)((u8 *)buffer + (chunk * 512));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ahci_read_lba0(u32 port, ahci_cmd_header *hdr, struct dkm_dma_buffer *table, struct dkm_dma_buffer *data) {
|
||||
int status = ahci_read_blocks_internal(port, hdr, table, data, 0, 1, data->virt);
|
||||
if (status == 0) {
|
||||
log_hex("[ahci] READ LBA0 port=", port);
|
||||
log_hex("[ahci] READ LBA0 bytes=", hdr[0].prdbc);
|
||||
log_hex("[ahci] READ LBA0 word0=", ((u16 *)data->virt)[0]);
|
||||
log_hex("[ahci] READ LBA0 word255=", ((u16 *)data->virt)[255]);
|
||||
return 1;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int ahci_identify_port(u32 port, const struct dkm_dma_api *dma) {
|
||||
if (!dma || !dma->alloc_pages) return -1;
|
||||
|
||||
u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE;
|
||||
u32 ssts = g_abar[(base + PxSSTS) / 4];
|
||||
u32 sig = g_abar[(base + PxSIG) / 4];
|
||||
u32 det = ssts & 0xf;
|
||||
u32 ipm = (ssts >> 8) & 0xf;
|
||||
if (det != 3 || ipm != 1) return 0;
|
||||
if (sig != 0x00000101u) {
|
||||
g_log->info("[ahci] IDENTIFY skipped non-SATA port");
|
||||
log_hex("[ahci] IDENTIFY skip port=", port);
|
||||
log_hex("[ahci] IDENTIFY skip sig=", sig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dkm_dma_buffer clb;
|
||||
struct dkm_dma_buffer fis;
|
||||
struct dkm_dma_buffer table;
|
||||
struct dkm_dma_buffer data;
|
||||
if (dma->alloc_pages(1, 1024, 0x100000000ULL, &clb) != 0) return -2;
|
||||
if (dma->alloc_pages(1, 256, 0x100000000ULL, &fis) != 0) return -3;
|
||||
if (dma->alloc_pages(1, 128, 0x100000000ULL, &table) != 0) return -4;
|
||||
if (dma->alloc_pages(1, 512, 0x100000000ULL, &data) != 0) return -5;
|
||||
|
||||
if (ahci_stop_port(port) != 0) {
|
||||
g_log->warn("[ahci] port stop timeout");
|
||||
return -6;
|
||||
}
|
||||
|
||||
g_abar[(base + PxCLB) / 4] = (u32)(clb.phys & 0xffffffffu);
|
||||
g_abar[(base + PxCLB + 4) / 4] = (u32)(clb.phys >> 32);
|
||||
g_abar[(base + PxFB) / 4] = (u32)(fis.phys & 0xffffffffu);
|
||||
g_abar[(base + PxFB + 4) / 4] = (u32)(fis.phys >> 32);
|
||||
|
||||
ahci_cmd_header *hdr = (ahci_cmd_header *)clb.virt;
|
||||
ahci_cmd_table *tbl = (ahci_cmd_table *)table.virt;
|
||||
hdr[0].flags = 5; /* FIS length in DWORDS */
|
||||
hdr[0].prdtl = 1;
|
||||
hdr[0].ctba = (u32)(table.phys & 0xffffffffu);
|
||||
hdr[0].ctbau = (u32)(table.phys >> 32);
|
||||
tbl->prdt[0].dba = (u32)(data.phys & 0xffffffffu);
|
||||
tbl->prdt[0].dbau = (u32)(data.phys >> 32);
|
||||
tbl->prdt[0].dbc_i = (512 - 1) | (1u << 31);
|
||||
|
||||
u8 cmd = ATA_CMD_IDENTIFY;
|
||||
ahci_fill_identify_fis(tbl->cfis, cmd);
|
||||
|
||||
g_abar[(base + PxIS) / 4] = 0xffffffffu;
|
||||
g_abar[HBA_IS / 4] = (1u << port);
|
||||
ahci_start_port(port);
|
||||
|
||||
g_abar[(base + PxCI) / 4] = 1;
|
||||
for (u32 i = 0; i < 5000000; i++) {
|
||||
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
|
||||
log_hex("[ahci] IDENTIFY port=", port);
|
||||
log_hex("[ahci] IDENTIFY status=", g_abar[(base + PxTFD) / 4]);
|
||||
log_hex("[ahci] IDENTIFY bytes=", hdr[0].prdbc);
|
||||
u16 *id = (u16 *)data.virt;
|
||||
log_hex("[ahci] IDENTIFY word0=", id[0]);
|
||||
log_hex("[ahci] IDENTIFY word49=", id[49]);
|
||||
log_hex("[ahci] IDENTIFY word83=", id[83]);
|
||||
ahci_read_lba0(port, hdr, &table, &data);
|
||||
g_ready_port = port;
|
||||
g_cmd_header = hdr;
|
||||
g_cmd_table = table;
|
||||
g_data = data;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
g_log->warn("[ahci] IDENTIFY timeout");
|
||||
log_hex("[ahci] IDENTIFY CI=", g_abar[(base + PxCI) / 4]);
|
||||
log_hex("[ahci] IDENTIFY IS=", g_abar[(base + PxIS) / 4]);
|
||||
log_hex("[ahci] IDENTIFY TFD=", g_abar[(base + PxTFD) / 4]);
|
||||
return -7;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
|
||||
if (!api || !api->log) return -1;
|
||||
g_log = api->log;
|
||||
|
||||
g_log->info("[ahci] init begin");
|
||||
|
||||
u8 bus = 0;
|
||||
u8 dev = 0;
|
||||
u8 func = 0;
|
||||
if (ahci_find(&bus, &dev, &func) != 0) {
|
||||
g_log->warn("[ahci] AHCI controller not found");
|
||||
g_log->info("[ahci] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_log->info("[ahci] AHCI controller found");
|
||||
log_hex("[ahci] bus=", bus);
|
||||
log_hex("[ahci] dev=", dev);
|
||||
log_hex("[ahci] func=", func);
|
||||
|
||||
u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID);
|
||||
log_hex("[ahci] vendor=", vd & 0xffff);
|
||||
log_hex("[ahci] device=", vd >> 16);
|
||||
|
||||
u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
|
||||
command |= PCI_CMD_MEM | PCI_CMD_BUSM;
|
||||
pci_write(bus, dev, func, PCI_COMMAND, command);
|
||||
log_hex("[ahci] PCI command=", command);
|
||||
|
||||
u64 abar_phys = (u64)(pci_read(bus, dev, func, PCI_BAR5) & 0xFFFFFFF0u);
|
||||
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
|
||||
log_hex("[ahci] ABAR phys=", abar_phys);
|
||||
log_hex("[ahci] PCI IRQ line=", irq_line);
|
||||
|
||||
if (!api->hhdm_offset || !abar_phys) {
|
||||
g_log->warn("[ahci] missing HHDM or ABAR; skip MMIO enumeration");
|
||||
g_log->info("[ahci] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_abar = (volatile u32 *)(uintptr_t)(api->hhdm_offset + abar_phys);
|
||||
log_hex("[ahci] HHDM offset=", api->hhdm_offset);
|
||||
|
||||
u32 cap = g_abar[HBA_CAP / 4];
|
||||
u32 ghc = g_abar[HBA_GHC / 4];
|
||||
u32 is = g_abar[HBA_IS / 4];
|
||||
u32 pi = g_abar[HBA_PI / 4];
|
||||
u32 vs = g_abar[HBA_VS / 4];
|
||||
u32 cap2 = g_abar[HBA_CAP2 / 4];
|
||||
u32 bohc = g_abar[HBA_BOHC / 4];
|
||||
|
||||
log_hex("[ahci] CAP=", cap);
|
||||
log_hex("[ahci] GHC=", ghc);
|
||||
log_hex("[ahci] IS=", is);
|
||||
log_hex("[ahci] PI=", pi);
|
||||
log_hex("[ahci] VS=", vs);
|
||||
log_hex("[ahci] CAP2=", cap2);
|
||||
log_hex("[ahci] BOHC=", bohc);
|
||||
log_hex("[ahci] ports count=", (cap & 0x1f) + 1);
|
||||
log_hex("[ahci] cmd slots=", ((cap >> 8) & 0x1f) + 1);
|
||||
|
||||
u32 implemented = 0;
|
||||
for (u32 port = 0; port < 32; port++) {
|
||||
if (pi & (1u << port)) {
|
||||
implemented++;
|
||||
ahci_log_port(port);
|
||||
}
|
||||
}
|
||||
log_hex("[ahci] implemented ports=", implemented);
|
||||
|
||||
u32 identified = 0;
|
||||
if (api->dma) {
|
||||
for (u32 port = 0; port < 32; port++) {
|
||||
if (pi & (1u << port)) {
|
||||
int ident = ahci_identify_port(port, api->dma);
|
||||
if (ident > 0) {
|
||||
identified++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
g_log->warn("[ahci] DMA API unavailable; skip IDENTIFY");
|
||||
}
|
||||
log_hex("[ahci] identified ports=", identified);
|
||||
if (identified && api->block && api->block->register_device) {
|
||||
struct dkm_block_device_desc desc;
|
||||
desc.name = "ahci0";
|
||||
desc.sector_size = 512;
|
||||
desc.sector_count = 0;
|
||||
desc.ctx = 0;
|
||||
desc.read = ahci_block_read;
|
||||
int index = api->block->register_device(&desc);
|
||||
log_hex("[ahci] block provider index=", (u64)(i64)index);
|
||||
} else if (identified) {
|
||||
g_log->warn("[ahci] block API unavailable; provider not registered");
|
||||
}
|
||||
|
||||
g_log->info("[ahci] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/* DKM APIC Driver — APIC discovery with PIC fallback
|
||||
* Stage 0, required, depends on "timer", provides "irq" and "apic".
|
||||
* Current kernel IRQ backend is still PIC; this driver only discovers APIC
|
||||
* topology and keeps PIC routing active.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version;
|
||||
u32 size;
|
||||
u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem;
|
||||
const void *utsm;
|
||||
const void *irq;
|
||||
const void *pci;
|
||||
const void *dma;
|
||||
const void *vfs;
|
||||
const void *net;
|
||||
const void *timer;
|
||||
const void *drr;
|
||||
const void *rsdp_address;
|
||||
const void *fb_address;
|
||||
u64 fb_width;
|
||||
u64 fb_height;
|
||||
u64 fb_pitch;
|
||||
u16 fb_bpp;
|
||||
const void *boot_modules_response;
|
||||
int (*irq_register)(u8 irq, void *handler);
|
||||
u64 hhdm_offset;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
struct dkm_driver_desc {
|
||||
u32 magic;
|
||||
u16 abi_version;
|
||||
u16 desc_size;
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
u32 driver_class;
|
||||
u32 stage;
|
||||
u32 flags;
|
||||
u32 priority;
|
||||
const char *const *depends;
|
||||
u32 depends_count;
|
||||
const char *const *provides;
|
||||
u32 provides_count;
|
||||
u64 min_kernel_abi;
|
||||
u64 feature_bits;
|
||||
u64 reserved0;
|
||||
u64 reserved1;
|
||||
};
|
||||
|
||||
static const char *const g_depends[] = { "timer" };
|
||||
static const char *const g_provides[] = { "irq", "apic" };
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(struct dkm_driver_desc),
|
||||
.name = "apic",
|
||||
.version = "0.1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = 3, /* DKM_CLASS_INTERRUPT */
|
||||
.stage = 0,
|
||||
.flags = 1, /* DKM_F_REQUIRED */
|
||||
.priority = 0,
|
||||
.depends = g_depends,
|
||||
.depends_count = 1,
|
||||
.provides = g_provides,
|
||||
.provides_count = 2,
|
||||
.min_kernel_abi = 1,
|
||||
.feature_bits = 0,
|
||||
.reserved0 = 0,
|
||||
.reserved1 = 0
|
||||
};
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
char signature[8];
|
||||
u8 checksum;
|
||||
char oem_id[6];
|
||||
u8 revision;
|
||||
u32 rsdt_addr;
|
||||
} acpi_rsdp_v1;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
char signature[8];
|
||||
u8 checksum;
|
||||
char oem_id[6];
|
||||
u8 revision;
|
||||
u32 rsdt_addr;
|
||||
u32 length;
|
||||
u64 xsdt_addr;
|
||||
u8 ext_checksum;
|
||||
u8 reserved[3];
|
||||
} acpi_rsdp_v2;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
char signature[4];
|
||||
u32 length;
|
||||
u8 revision;
|
||||
u8 checksum;
|
||||
char oem_id[6];
|
||||
char oem_table_id[8];
|
||||
u32 oem_revision;
|
||||
u32 creator_id;
|
||||
u32 creator_revision;
|
||||
} acpi_sdt_header;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
acpi_sdt_header hdr;
|
||||
u32 lapic_addr;
|
||||
u32 flags;
|
||||
} acpi_madt;
|
||||
|
||||
struct madt_info {
|
||||
u64 lapic_phys;
|
||||
u32 flags;
|
||||
u32 lapic_count;
|
||||
u32 ioapic_count;
|
||||
u32 iso_count;
|
||||
};
|
||||
|
||||
static const struct dkm_log_api *g_log;
|
||||
|
||||
static int memeq(const char *a, const char *b, u32 n) {
|
||||
for (u32 i = 0; i < n; i++) {
|
||||
if (a[i] != b[i]) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u8 checksum8(const u8 *data, u32 len) {
|
||||
u8 sum = 0;
|
||||
for (u32 i = 0; i < len; i++) sum += data[i];
|
||||
return sum;
|
||||
}
|
||||
|
||||
static void log_hex(const char *prefix, u64 val) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
char buf[19];
|
||||
u32 pos = 0;
|
||||
buf[pos++] = '0';
|
||||
buf[pos++] = 'x';
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
buf[pos++] = hex[(val >> (i * 4)) & 0xf];
|
||||
}
|
||||
buf[pos] = 0;
|
||||
g_log->info(prefix);
|
||||
g_log->info(buf);
|
||||
}
|
||||
|
||||
static void cpuid(u32 leaf, u32 *a, u32 *b, u32 *c, u32 *d) {
|
||||
__asm__ volatile ("cpuid"
|
||||
: "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
|
||||
: "a"(leaf), "c"(0));
|
||||
}
|
||||
|
||||
static u64 rdmsr(u32 msr) {
|
||||
u32 lo;
|
||||
u32 hi;
|
||||
__asm__ volatile ("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
|
||||
return ((u64)hi << 32) | lo;
|
||||
}
|
||||
|
||||
static int cpu_has_lapic(void) {
|
||||
u32 a, b, c, d;
|
||||
cpuid(1, &a, &b, &c, &d);
|
||||
(void)a;
|
||||
(void)b;
|
||||
(void)c;
|
||||
return (d & (1u << 9)) != 0;
|
||||
}
|
||||
|
||||
static const acpi_sdt_header *find_table_from_rsdt(const acpi_sdt_header *rsdt, const char sig[4], u32 entry_size) {
|
||||
if (!rsdt || rsdt->length < sizeof(acpi_sdt_header)) return NULL;
|
||||
if (checksum8((const u8 *)rsdt, rsdt->length) != 0) return NULL;
|
||||
|
||||
u32 count = (rsdt->length - sizeof(acpi_sdt_header)) / entry_size;
|
||||
const u8 *entries = (const u8 *)rsdt + sizeof(acpi_sdt_header);
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
u64 addr;
|
||||
if (entry_size == 4) {
|
||||
addr = (u64)((const u32 *)entries)[i];
|
||||
} else {
|
||||
addr = ((const u64 *)entries)[i];
|
||||
}
|
||||
if (!addr) continue;
|
||||
|
||||
const acpi_sdt_header *hdr = (const acpi_sdt_header *)(uintptr_t)addr;
|
||||
if (hdr->length >= sizeof(acpi_sdt_header) && memeq(hdr->signature, sig, 4)) {
|
||||
if (checksum8((const u8 *)hdr, hdr->length) == 0) return hdr;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const acpi_madt *find_madt(const void *rsdp_addr) {
|
||||
if (!rsdp_addr) return NULL;
|
||||
|
||||
const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)rsdp_addr;
|
||||
if (!memeq(rsdp->signature, "RSD PTR ", 8)) return NULL;
|
||||
if (checksum8((const u8 *)rsdp, 20) != 0) return NULL;
|
||||
|
||||
if (rsdp->revision >= 2 && rsdp->length >= sizeof(acpi_rsdp_v2) && rsdp->xsdt_addr) {
|
||||
if (checksum8((const u8 *)rsdp, rsdp->length) == 0) {
|
||||
const acpi_sdt_header *xsdt = (const acpi_sdt_header *)(uintptr_t)rsdp->xsdt_addr;
|
||||
const acpi_sdt_header *madt = find_table_from_rsdt(xsdt, "APIC", 8);
|
||||
if (madt) return (const acpi_madt *)madt;
|
||||
}
|
||||
}
|
||||
|
||||
if (rsdp->rsdt_addr) {
|
||||
const acpi_sdt_header *rsdt = (const acpi_sdt_header *)(uintptr_t)(u64)rsdp->rsdt_addr;
|
||||
const acpi_sdt_header *madt = find_table_from_rsdt(rsdt, "APIC", 4);
|
||||
if (madt) return (const acpi_madt *)madt;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void parse_madt_entries(const acpi_madt *madt, struct madt_info *info) {
|
||||
const u8 *base = (const u8 *)madt;
|
||||
u32 off = (u32)sizeof(acpi_madt);
|
||||
|
||||
while (off + 2 <= madt->hdr.length) {
|
||||
const u8 *entry = base + off;
|
||||
u8 type = entry[0];
|
||||
u8 len = entry[1];
|
||||
if (len < 2 || off + len > madt->hdr.length) {
|
||||
g_log->warn("[apic] malformed MADT entry");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 0 && len >= 8) {
|
||||
u8 apic_id = entry[3];
|
||||
u32 flags = *(const u32 *)(const void *)(entry + 4);
|
||||
if (flags & 1u) info->lapic_count++;
|
||||
log_hex("[apic] CPU LAPIC id=", apic_id);
|
||||
} else if (type == 1 && len >= 12) {
|
||||
u8 ioapic_id = entry[2];
|
||||
u32 addr = *(const u32 *)(const void *)(entry + 4);
|
||||
u32 gsi = *(const u32 *)(const void *)(entry + 8);
|
||||
info->ioapic_count++;
|
||||
log_hex("[apic] IOAPIC id=", ioapic_id);
|
||||
log_hex("[apic] IOAPIC addr=", addr);
|
||||
log_hex("[apic] IOAPIC gsi_base=", gsi);
|
||||
} else if (type == 2 && len >= 10) {
|
||||
u8 source = entry[3];
|
||||
u32 gsi = *(const u32 *)(const void *)(entry + 4);
|
||||
u16 flags = *(const u16 *)(const void *)(entry + 8);
|
||||
info->iso_count++;
|
||||
log_hex("[apic] ISO source=", source);
|
||||
log_hex("[apic] ISO gsi=", gsi);
|
||||
log_hex("[apic] ISO flags=", flags);
|
||||
} else if (type == 5 && len >= 12) {
|
||||
u64 override = *(const u64 *)(const void *)(entry + 4);
|
||||
info->lapic_phys = override;
|
||||
log_hex("[apic] LAPIC override=", override);
|
||||
}
|
||||
|
||||
off += len;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
|
||||
if (!api || !api->log) return -1;
|
||||
g_log = api->log;
|
||||
|
||||
g_log->info("[apic] init begin");
|
||||
|
||||
if (!api->irq_register) {
|
||||
g_log->error("[apic] kernel irq_register missing");
|
||||
return -1;
|
||||
}
|
||||
g_log->info("[apic] kernel IRQ backend present: PIC");
|
||||
|
||||
if (!cpu_has_lapic()) {
|
||||
g_log->warn("[apic] CPUID local APIC not present; PIC fallback active");
|
||||
g_log->info("[apic] driver ready");
|
||||
return 0;
|
||||
}
|
||||
g_log->info("[apic] CPUID local APIC present");
|
||||
|
||||
u64 apic_base = rdmsr(0x1B);
|
||||
u64 lapic_phys = apic_base & 0xFFFFF000ULL;
|
||||
log_hex("[apic] IA32_APIC_BASE=", apic_base);
|
||||
log_hex("[apic] LAPIC phys base=", lapic_phys);
|
||||
if (apic_base & (1ULL << 8)) g_log->info("[apic] bootstrap processor");
|
||||
if (apic_base & (1ULL << 11)) g_log->info("[apic] LAPIC enabled by firmware");
|
||||
|
||||
struct madt_info info;
|
||||
info.lapic_phys = lapic_phys;
|
||||
info.flags = 0;
|
||||
info.lapic_count = 0;
|
||||
info.ioapic_count = 0;
|
||||
info.iso_count = 0;
|
||||
|
||||
const acpi_madt *madt = find_madt(api->rsdp_address);
|
||||
if (madt) {
|
||||
g_log->info("[apic] MADT found");
|
||||
info.lapic_phys = (u64)madt->lapic_addr;
|
||||
info.flags = madt->flags;
|
||||
log_hex("[apic] MADT lapic_phys=", info.lapic_phys);
|
||||
log_hex("[apic] MADT flags=", info.flags);
|
||||
parse_madt_entries(madt, &info);
|
||||
log_hex("[apic] CPU LAPIC count=", info.lapic_count);
|
||||
log_hex("[apic] IOAPIC count=", info.ioapic_count);
|
||||
log_hex("[apic] ISO count=", info.iso_count);
|
||||
} else {
|
||||
g_log->warn("[apic] MADT not found; PIC fallback active");
|
||||
}
|
||||
|
||||
if (api->hhdm_offset && info.lapic_phys) {
|
||||
volatile u32 *lapic = (volatile u32 *)(uintptr_t)(api->hhdm_offset + info.lapic_phys);
|
||||
log_hex("[apic] LAPIC ID reg=", lapic[0x20 / 4]);
|
||||
log_hex("[apic] LAPIC version reg=", lapic[0x30 / 4]);
|
||||
log_hex("[apic] LAPIC SVR reg=", lapic[0xF0 / 4]);
|
||||
} else {
|
||||
g_log->warn("[apic] HHDM unavailable; skip LAPIC MMIO read");
|
||||
}
|
||||
|
||||
g_log->info("[apic] discovery complete; PIC routing retained");
|
||||
g_log->info("[apic] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/* DKM BootFS Driver — exposes Limine boot modules as a simple filesystem
|
||||
* Stage 1, required, provides "bootfs".
|
||||
* Lists all boot modules with path, size, and address.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* DKM ABI types
|
||||
* --------------------------------------------------------------- */
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version;
|
||||
u32 size;
|
||||
u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem;
|
||||
const void *utsm;
|
||||
const void *irq;
|
||||
const void *pci_api;
|
||||
const void *dma;
|
||||
const void *vfs;
|
||||
const void *net;
|
||||
const void *timer;
|
||||
const void *drr;
|
||||
const void *rsdp_address;
|
||||
const void *fb_address;
|
||||
u64 fb_width;
|
||||
u64 fb_height;
|
||||
u64 fb_pitch;
|
||||
u16 fb_bpp;
|
||||
const void *boot_modules_response;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
struct dkm_driver_desc {
|
||||
u32 magic;
|
||||
u16 abi_version;
|
||||
u16 desc_size;
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
u32 driver_class;
|
||||
u32 stage;
|
||||
u32 flags;
|
||||
u32 priority;
|
||||
const char *const *depends;
|
||||
u32 depends_count;
|
||||
const char *const *provides;
|
||||
u32 provides_count;
|
||||
u64 min_kernel_abi;
|
||||
u64 feature_bits;
|
||||
u64 reserved0;
|
||||
u64 reserved1;
|
||||
};
|
||||
|
||||
/* minimal limine types matching kernel */
|
||||
struct limine_file {
|
||||
u64 revision;
|
||||
void *address;
|
||||
u64 size;
|
||||
char *path;
|
||||
char *cmdline;
|
||||
u32 media_type;
|
||||
u32 unused;
|
||||
u32 tftp_ip;
|
||||
u32 tftp_port;
|
||||
u32 partition_index;
|
||||
u32 mbr_disk_id;
|
||||
u8 gpt_disk_uuid[16];
|
||||
u8 gpt_part_uuid[16];
|
||||
u8 part_uuid[16];
|
||||
};
|
||||
|
||||
struct limine_module_response {
|
||||
u64 revision;
|
||||
u64 module_count;
|
||||
struct limine_file **modules;
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver descriptor
|
||||
* --------------------------------------------------------------- */
|
||||
static const char *const g_provides[] = { "bootfs" };
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(struct dkm_driver_desc),
|
||||
.name = "bootfs",
|
||||
.version = "0.1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = 7, /* DKM_CLASS_FS */
|
||||
.stage = 1,
|
||||
.flags = 1, /* DKM_F_REQUIRED */
|
||||
.priority = 0,
|
||||
.depends = NULL,
|
||||
.depends_count = 0,
|
||||
.provides = g_provides,
|
||||
.provides_count = 1,
|
||||
.min_kernel_abi = 1,
|
||||
.feature_bits = 0,
|
||||
.reserved0 = 0,
|
||||
.reserved1 = 0
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Log helpers
|
||||
* --------------------------------------------------------------- */
|
||||
static const struct dkm_log_api *g_log;
|
||||
|
||||
static void log_hex(const char *prefix, u64 val) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
char buf[19];
|
||||
u32 pos = 0;
|
||||
buf[pos++] = '0'; buf[pos++] = 'x';
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
buf[pos++] = hex[(val >> (i * 4)) & 0xf];
|
||||
}
|
||||
buf[pos] = 0;
|
||||
g_log->info(prefix);
|
||||
g_log->info(buf);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver entry
|
||||
* --------------------------------------------------------------- */
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
|
||||
if (!api || !api->log) return -1;
|
||||
g_log = api->log;
|
||||
|
||||
const struct limine_module_response *rsp =
|
||||
(const struct limine_module_response *)api->boot_modules_response;
|
||||
|
||||
if (!rsp) {
|
||||
g_log->error("[bootfs] no boot module response");
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_log->info("[bootfs] boot module filesystem");
|
||||
log_hex("[bootfs] file count=", rsp->module_count);
|
||||
|
||||
for (u64 i = 0; i < rsp->module_count; i++) {
|
||||
struct limine_file *f = rsp->modules[i];
|
||||
if (!f) continue;
|
||||
|
||||
g_log->info("[bootfs] ----------");
|
||||
if (f->path) {
|
||||
g_log->info("[bootfs] file");
|
||||
g_log->info(f->path);
|
||||
}
|
||||
log_hex("[bootfs] size=", f->size);
|
||||
log_hex("[bootfs] addr=", (u64)(uintptr_t)f->address);
|
||||
if (f->cmdline) {
|
||||
g_log->info("[bootfs] cmd");
|
||||
g_log->info(f->cmdline);
|
||||
}
|
||||
}
|
||||
|
||||
g_log->info("[bootfs] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
/* DKM Console Framebuffer Driver
|
||||
* Stage 1, optional, provides "console".
|
||||
* Light-blue background + centered circular loading animation (4-color arc ring).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* DKM ABI types
|
||||
* --------------------------------------------------------------- */
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version; u32 size; u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem, *utsm, *irq, *pci_api, *dma, *vfs, *net, *timer, *drr;
|
||||
const void *rsdp_address, *fb_address;
|
||||
u64 fb_width, fb_height, fb_pitch; u16 fbpp;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
struct dkm_driver_desc {
|
||||
u32 magic; u16 abi_version; u16 desc_size;
|
||||
const char *name, *version, *vendor;
|
||||
u32 driver_class, stage, flags, priority;
|
||||
const char *const *depends; u32 depends_count;
|
||||
const char *const *provides; u32 provides_count;
|
||||
u64 min_kernel_abi, feature_bits, reserved0, reserved1;
|
||||
};
|
||||
|
||||
static const char *const g_provides[] = { "console" };
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic=DKM_DRIVER_MAGIC, .abi_version=DKM_ABI_VERSION,
|
||||
.desc_size=sizeof(struct dkm_driver_desc),
|
||||
.name="console_fb", .version="0.2.0", .vendor="Deshab",
|
||||
.driver_class=5, .stage=1, .flags=0, .priority=0,
|
||||
.depends=NULL, .depends_count=0,
|
||||
.provides=g_provides, .provides_count=1,
|
||||
.min_kernel_abi=1,
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Colors
|
||||
* --------------------------------------------------------------- */
|
||||
static const u32 BG = 0xFFC8E0F0; /* light blue background */
|
||||
static const u32 ARC[4] = { 0xFF4488CC, 0xFF2266AA, 0xFF66AAEE, 0xFF88CCFF };
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Drawing helpers – all integer arithmetic, no soft-float
|
||||
* --------------------------------------------------------------- */
|
||||
static u64 fb_w, fb_h, fb_p;
|
||||
|
||||
/* double-buffer sprite for ring area (128×128 = no flicker) */
|
||||
#define SPR_SIZE 128
|
||||
static u32 g_sprite[SPR_SIZE * SPR_SIZE];
|
||||
|
||||
static void fb_fill(u32 *fb, u64 pitch, u64 w, u64 h, u32 color) {
|
||||
for (u64 y = 0; y < h; y++) {
|
||||
u32 *line = (u32 *)((u8 *)fb + y * pitch);
|
||||
for (u64 x = 0; x < w; x++) line[x] = color;
|
||||
}
|
||||
}
|
||||
|
||||
/* alpha-blend c2 over c1, alpha 0-255 */
|
||||
static u32 blend(u32 c1, u32 c2, u32 a) {
|
||||
u32 na = 256 - a;
|
||||
u32 r = ((c1 & 0xFF) * na + (c2 & 0xFF) * a) >> 8;
|
||||
u32 g = (((c1 >> 8) & 0xFF) * na + ((c2 >> 8) & 0xFF) * a) >> 8;
|
||||
u32 b = (((c1 >> 16) & 0xFF) * na + ((c2 >> 16) & 0xFF) * a) >> 8;
|
||||
return 0xFF000000 | (b << 16) | (g << 8) | r;
|
||||
}
|
||||
|
||||
/* integer sqrt – returns floor(sqrt(v)) */
|
||||
static u64 isqrt(u64 v) {
|
||||
u64 x = v, y = (x + 1) >> 1;
|
||||
while (y < x) { x = y; y = (y + v / y) >> 1; }
|
||||
return x;
|
||||
}
|
||||
|
||||
/* approximate atan2 in degrees (0=right, CCW). dx,dy = pixel offset from center */
|
||||
static i64 approx_angle(i64 dx, i64 dy) {
|
||||
if (dx == 0) return dy < 0 ? 90 : 270;
|
||||
i64 ax = dx < 0 ? -dx : dx;
|
||||
i64 ay = dy < 0 ? -dy : dy;
|
||||
i64 a;
|
||||
if (ax > ay) {
|
||||
a = ay * 45 / ax;
|
||||
} else {
|
||||
a = 90 - ax * 45 / (ay ? ay : 1);
|
||||
}
|
||||
/* quadrant correction */
|
||||
if (dx >= 0 && dy <= 0) return a; /* Q1 */
|
||||
else if (dx < 0 && dy <= 0) return 180 - a; /* Q2 */
|
||||
else if (dx < 0 && dy > 0) return 180 + a; /* Q3 */
|
||||
else return 360 - a; /* Q4 */
|
||||
}
|
||||
|
||||
/* draw one arc segment: rounded caps (6° head/tail), bright mid, comet-tail fade */
|
||||
static void arc_segment(u32 *fb, i64 cx, i64 cy, i64 r, i64 start, i64 sweep, u32 color, i64 thk) {
|
||||
i64 ro = r + thk, ri = r - thk;
|
||||
i64 ri2 = ri * ri, ro2 = (ro + 1) * (ro + 1);
|
||||
i64 rnd = 6; /* rounding zone at each end, in degrees */
|
||||
|
||||
for (i64 dy = -ro - 1; dy <= ro + 1; dy++) {
|
||||
i64 y = cy + dy;
|
||||
if ((u64)y >= fb_h) continue;
|
||||
u32 *line = (u32 *)((u8 *)fb + (u64)y * fb_p);
|
||||
for (i64 dx = -ro - 1; dx <= ro + 1; dx++) {
|
||||
i64 x = cx + dx;
|
||||
if ((u64)x >= fb_w) continue;
|
||||
i64 d2 = dx * dx + dy * dy;
|
||||
if (d2 < ri2 - 2 || d2 > ro2 + 2) continue;
|
||||
|
||||
i64 ang = approx_angle(dx, dy);
|
||||
i64 a = ang - start;
|
||||
if (a < 0) a += 360;
|
||||
if ((u64)a > (u64)sweep) continue;
|
||||
|
||||
/* rounded-cap fade: 0→255 over rnd°, then 255→0 over last rnd° */
|
||||
u32 fade;
|
||||
if ((u64)a < (u64)rnd) {
|
||||
fade = (u32)(a * 255 / rnd);
|
||||
} else if ((u64)a > (u64)(sweep - rnd)) {
|
||||
fade = (u32)((sweep - a) * 255 / rnd);
|
||||
} else {
|
||||
fade = 255;
|
||||
}
|
||||
|
||||
i64 dist = (i64)isqrt((u64)d2);
|
||||
u32 effective = blend(BG, color, fade);
|
||||
if (dist >= ri && dist <= ro) {
|
||||
line[(u64)x] = effective;
|
||||
} else if (dist >= ri - 2 && dist < ri) {
|
||||
line[(u64)x] = blend(BG, effective, (u32)((dist - (ri - 2)) * 85));
|
||||
} else if (dist > ro && dist <= ro + 2) {
|
||||
line[(u64)x] = blend(effective, BG, (u32)((dist - ro) * 85));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* filled disc with anti-aliased edge */
|
||||
static void fill_disc(u32 *fb, i64 cx, i64 cy, i64 r, u32 color) {
|
||||
i64 r2 = r * r, o2 = (r + 1) * (r + 1);
|
||||
for (i64 dy = -r - 2; dy <= r + 2; dy++) {
|
||||
i64 y = cy + dy;
|
||||
if ((u64)y >= fb_h) continue;
|
||||
u32 *line = (u32 *)((u8 *)fb + (u64)y * fb_p);
|
||||
for (i64 dx = -r - 2; dx <= r + 2; dx++) {
|
||||
i64 x = cx + dx;
|
||||
if ((u64)x >= fb_w) continue;
|
||||
i64 d2 = dx * dx + dy * dy;
|
||||
if (d2 <= r2) {
|
||||
line[(u64)x] = color;
|
||||
} else if (d2 <= o2) {
|
||||
u32 a = (u32)((o2 - d2) * 255 / (o2 - r2));
|
||||
line[(u64)x] = blend(BG, color, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver entry
|
||||
* --------------------------------------------------------------- */
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
if (!api || !api->log) return -1;
|
||||
if (!api->fb_address || !api->fb_width) {
|
||||
api->log->warn("[console_fb] no framebuffer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
api->log->info("[console_fb] boot screen");
|
||||
|
||||
fb_w = api->fb_width; fb_h = api->fb_height; fb_p = api->fb_pitch;
|
||||
u32 *fb = (u32 *)api->fb_address;
|
||||
i64 cx = (i64)fb_w / 2, cy = (i64)fb_h / 2, r = 48, thk = 4;
|
||||
|
||||
/* light-blue background */
|
||||
fb_fill(fb, fb_p, fb_w, fb_h, BG);
|
||||
|
||||
/* animation: 2000 frames × 2° clockwise × ~1ms, no flicker */
|
||||
i64 spr_p = SPR_SIZE * 4;
|
||||
i64 scx = SPR_SIZE / 2;
|
||||
i64 scy = SPR_SIZE / 2;
|
||||
i64 frame = 0, base = 0;
|
||||
i64 bx = cx - scx; /* screen position of sprite top-left */
|
||||
i64 by = cy - scy;
|
||||
|
||||
while (frame < 2000) {
|
||||
/* draw into sprite buffer — swap globals to sprite layout */
|
||||
u64 save_w = fb_w, save_h = fb_h, save_p = fb_p;
|
||||
fb_w = SPR_SIZE; fb_h = SPR_SIZE; fb_p = spr_p;
|
||||
|
||||
fb_fill(g_sprite, spr_p, SPR_SIZE, SPR_SIZE, BG);
|
||||
|
||||
arc_segment(g_sprite, scx, scy, r, base + 0, 82, ARC[0], thk);
|
||||
arc_segment(g_sprite, scx, scy, r, base + 90, 82, ARC[1], thk);
|
||||
arc_segment(g_sprite, scx, scy, r, base +180, 82, ARC[2], thk);
|
||||
arc_segment(g_sprite, scx, scy, r, base +270, 82, ARC[3], thk);
|
||||
|
||||
fill_disc(g_sprite, scx, scy, r - 8, blend(BG, 0xFFD0F0FF, 80));
|
||||
|
||||
/* restore screen globals for blit */
|
||||
fb_w = save_w; fb_h = save_h; fb_p = save_p;
|
||||
|
||||
/* blit sprite to screen */
|
||||
for (i64 sr = 0; sr < SPR_SIZE; sr++) {
|
||||
i64 sy = by + sr;
|
||||
if ((u64)sy >= fb_h) continue;
|
||||
u32 *dst = (u32 *)((u8 *)fb + (u64)sy * fb_p) + bx;
|
||||
u32 *src = &g_sprite[sr * SPR_SIZE];
|
||||
for (i64 sc = 0; sc < SPR_SIZE; sc++) {
|
||||
i64 sx = bx + sc;
|
||||
if ((u64)sx >= fb_w) continue;
|
||||
dst[sc] = src[sc];
|
||||
}
|
||||
}
|
||||
|
||||
for (volatile u32 d = 0; d < 72000; d++) {
|
||||
__asm__ volatile ("pause");
|
||||
}
|
||||
|
||||
base -= 2;
|
||||
if (base < 0) base += 360;
|
||||
frame++;
|
||||
}
|
||||
|
||||
api->log->info("[console_fb] ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
/* DKM DevFS Driver — virtual device filesystem
|
||||
* Stage 2, optional, depends on "vfs", provides "devfs".
|
||||
* Exposes kernel platform info as virtual files under /dev/.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* DKM ABI types
|
||||
* --------------------------------------------------------------- */
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version;
|
||||
u32 size;
|
||||
u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem;
|
||||
const void *utsm;
|
||||
const void *irq;
|
||||
const void *pci_api;
|
||||
const void *dma;
|
||||
const void *vfs_api;
|
||||
const void *net;
|
||||
const void *timer;
|
||||
const void *drr;
|
||||
const void *rsdp_address;
|
||||
const void *fb_address;
|
||||
u64 fb_width;
|
||||
u64 fb_height;
|
||||
u64 fb_pitch;
|
||||
u16 fb_bpp;
|
||||
const void *boot_modules_response;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
struct dkm_driver_desc {
|
||||
u32 magic;
|
||||
u16 abi_version;
|
||||
u16 desc_size;
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
u32 driver_class;
|
||||
u32 stage;
|
||||
u32 flags;
|
||||
u32 priority;
|
||||
const char *const *depends;
|
||||
u32 depends_count;
|
||||
const char *const *provides;
|
||||
u32 provides_count;
|
||||
u64 min_kernel_abi;
|
||||
u64 feature_bits;
|
||||
u64 reserved0;
|
||||
u64 reserved1;
|
||||
};
|
||||
|
||||
struct limine_file {
|
||||
u64 revision;
|
||||
void *address;
|
||||
u64 size;
|
||||
char *path;
|
||||
char *cmdline;
|
||||
u8 _pad[56];
|
||||
};
|
||||
|
||||
struct limine_module_response {
|
||||
u64 revision;
|
||||
u64 module_count;
|
||||
struct limine_file **modules;
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver descriptor
|
||||
* --------------------------------------------------------------- */
|
||||
static const char *const g_depends[] = { "vfs" };
|
||||
static const char *const g_provides[] = { "devfs" };
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(struct dkm_driver_desc),
|
||||
.name = "devfs",
|
||||
.version = "0.1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = 7,
|
||||
.stage = 2,
|
||||
.flags = 0,
|
||||
.priority = 0,
|
||||
.depends = g_depends,
|
||||
.depends_count = 1,
|
||||
.provides = g_provides,
|
||||
.provides_count = 1,
|
||||
.min_kernel_abi = 1,
|
||||
.feature_bits = 0,
|
||||
.reserved0 = 0,
|
||||
.reserved1 = 0
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Helpers
|
||||
* --------------------------------------------------------------- */
|
||||
static const struct dkm_log_api *g_log;
|
||||
|
||||
static void log_hex(const char *prefix, u64 val) {
|
||||
static const char h[] = "0123456789abcdef";
|
||||
char buf[19]; u32 p = 0;
|
||||
buf[p++] = '0'; buf[p++] = 'x';
|
||||
for (int i = 15; i >= 0; i--) buf[p++] = h[(val>>(i*4))&0xf];
|
||||
buf[p] = 0;
|
||||
g_log->info(prefix); g_log->info(buf);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Virtual file helpers
|
||||
* --------------------------------------------------------------- */
|
||||
static void devfs_file(const char *name, const char *content) {
|
||||
g_log->info("[devfs] /dev/");
|
||||
g_log->info(name);
|
||||
g_log->info("[devfs] ");
|
||||
g_log->info(content);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver entry
|
||||
* --------------------------------------------------------------- */
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
if (!api || !api->log) return -1;
|
||||
g_log = api->log;
|
||||
|
||||
g_log->info("[devfs] virtual device filesystem");
|
||||
|
||||
/* /dev/version */
|
||||
devfs_file("version", "Deshab kernel 0.1.0");
|
||||
|
||||
/* /dev/platform */
|
||||
devfs_file("platform", "x86_64 SAS-R0");
|
||||
|
||||
/* /dev/fb0 */
|
||||
if (api->fb_address) {
|
||||
char info[64];
|
||||
info[0] = 0;
|
||||
/* manual number conversion for width */
|
||||
{
|
||||
u64 v = api->fb_width;
|
||||
char tmp[24]; u32 p = 0;
|
||||
if (v == 0) tmp[p++] = '0';
|
||||
else { char r[24]; u32 q = 0; while (v) { r[q++] = '0' + (char)(v%10); v/=10; } while (q) tmp[p++] = r[--q]; }
|
||||
tmp[p++] = 'x';
|
||||
v = api->fb_height;
|
||||
if (v == 0) tmp[p++] = '0';
|
||||
else { char r[24]; u32 q = 0; while (v) { r[q++] = '0' + (char)(v%10); v/=10; } while (q) tmp[p++] = r[--q]; }
|
||||
tmp[p++] = 'x';
|
||||
v = api->fb_bpp;
|
||||
if (v == 0) tmp[p++] = '0';
|
||||
else { char r[24]; u32 q = 0; while (v) { r[q++] = '0' + (char)(v%10); v/=10; } while (q) tmp[p++] = r[--q]; }
|
||||
tmp[p++] = ' ';
|
||||
tmp[p++] = 'f';
|
||||
tmp[p++] = 'b';
|
||||
tmp[p++] = '0';
|
||||
tmp[p] = 0;
|
||||
for (u32 i = 0; i <= p; i++) info[i] = tmp[i];
|
||||
}
|
||||
devfs_file("fb0", info);
|
||||
}
|
||||
|
||||
/* /dev/modules — count from boot_modules */
|
||||
const struct limine_module_response *rsp =
|
||||
(const struct limine_module_response *)api->boot_modules_response;
|
||||
if (rsp) {
|
||||
char buf[24];
|
||||
buf[0] = '0'; buf[1] = 'x';
|
||||
u64 v = rsp->module_count;
|
||||
u32 p = 2;
|
||||
if (v == 0) buf[p++] = '0';
|
||||
else {
|
||||
char r[18]; u32 q = 0;
|
||||
while (v) { r[q++] = "0123456789abcdef"[v&0xf]; v>>=4; }
|
||||
while (q) buf[p++] = r[--q];
|
||||
}
|
||||
buf[p] = 0;
|
||||
devfs_file("modules", buf);
|
||||
|
||||
/* /dev/boot — list paths */
|
||||
g_log->info("[devfs] /dev/boot/");
|
||||
for (u64 i = 0; i < rsp->module_count; i++) {
|
||||
struct limine_file *f = rsp->modules[i];
|
||||
if (f && f->path) {
|
||||
g_log->info("[devfs] ");
|
||||
g_log->info(f->path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_log->info("[devfs] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
/* DKM e1000 Driver — Intel 8254x Gigabit Ethernet
|
||||
* Stage 3, optional, depends on "pci" and "irq", provides "netdev".
|
||||
* Finds e1000 via PCI, maps MMIO, reads MAC address.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_net_device_desc {
|
||||
const char *name;
|
||||
u8 mac[6];
|
||||
u32 flags;
|
||||
void *ctx;
|
||||
int (*tx)(void *ctx, const void *packet, u32 length);
|
||||
int (*rx_poll)(void *ctx, void *buffer, u32 capacity, u32 *out_length);
|
||||
};
|
||||
|
||||
struct dkm_net_api {
|
||||
int (*register_device)(const struct dkm_net_device_desc *desc);
|
||||
u32 (*device_count)(void);
|
||||
int (*device_info)(u32 index, void *out);
|
||||
};
|
||||
|
||||
struct dkm_dma_buffer {
|
||||
void *virt;
|
||||
u64 phys;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
struct dkm_dma_api {
|
||||
int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, struct dkm_dma_buffer *out);
|
||||
};
|
||||
|
||||
#define DKM_NET_F_LINK_UP (1u << 0)
|
||||
#define DKM_NET_F_TX_READY (1u << 1)
|
||||
#define DKM_NET_F_RX_READY (1u << 2)
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version; u32 size; u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem,*utsm,*irq_api,*pci_api;
|
||||
const struct dkm_dma_api *dma;
|
||||
const void *vfs_api;
|
||||
const struct dkm_net_api *net;
|
||||
const void *timer,*drr;
|
||||
const void *rsdp_address,*fb_address;
|
||||
u64 fb_width,fb_height,fb_pitch; u16 fb_bpp;
|
||||
const void *boot_modules_response;
|
||||
int (*irq_register)(u8 irq, void *handler);
|
||||
u64 hhdm_offset;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
struct dkm_driver_desc {
|
||||
u32 magic; u16 abi_version; u16 desc_size;
|
||||
const char *name,*version,*vendor;
|
||||
u32 driver_class,stage,flags,priority;
|
||||
const char *const *depends; u32 depends_count;
|
||||
const char *const *provides; u32 provides_count;
|
||||
u64 min_kernel_abi,feature_bits,reserved0,reserved1;
|
||||
};
|
||||
|
||||
static const char *const g_depends[] = {"pci","irq"};
|
||||
static const char *const g_provides[] = {"netdev"};
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic=DKM_DRIVER_MAGIC,.abi_version=DKM_ABI_VERSION,
|
||||
.desc_size=sizeof(struct dkm_driver_desc),
|
||||
.name="e1000",.version="0.1.0",.vendor="Deshab",
|
||||
.driver_class=9,.stage=3,.flags=0,.priority=0,
|
||||
.depends=g_depends,.depends_count=2,
|
||||
.provides=g_provides,.provides_count=1,.min_kernel_abi=1,
|
||||
};
|
||||
|
||||
/* PCI config ports */
|
||||
#define PCI_ADDR 0xCF8
|
||||
#define PCI_DATA 0xCFC
|
||||
#define PCI_VENDOR_ID 0x00
|
||||
#define PCI_DEVICE_ID 0x02
|
||||
#define PCI_COMMAND 0x04
|
||||
#define PCI_BAR0 0x10
|
||||
#define PCI_IRQ_LINE 0x3C
|
||||
|
||||
/* e1000 MMIO registers */
|
||||
#define E1000_CTRL 0x0000
|
||||
#define E1000_STATUS 0x0008
|
||||
#define E1000_EERD 0x0014
|
||||
#define E1000_ICR 0x00C0
|
||||
#define E1000_IMS 0x00D0
|
||||
#define E1000_IMC 0x00D8
|
||||
#define E1000_RAL 0x5400
|
||||
#define E1000_RAH 0x5404
|
||||
#define E1000_RDBAL 0x2800
|
||||
#define E1000_RDBAH 0x2804
|
||||
#define E1000_RDLEN 0x2808
|
||||
#define E1000_RDH 0x2810
|
||||
#define E1000_RDT 0x2818
|
||||
#define E1000_RXDCTL 0x2828
|
||||
#define E1000_RCTL 0x0100
|
||||
#define E1000_TDBAL 0x3800
|
||||
#define E1000_TDBAH 0x3804
|
||||
#define E1000_TDLEN 0x3808
|
||||
#define E1000_TDH 0x3810
|
||||
#define E1000_TDT 0x3818
|
||||
#define E1000_TXDCTL 0x3828
|
||||
#define E1000_TCTL 0x0400
|
||||
#define E1000_TIPG 0x0410
|
||||
|
||||
#define E1000_RX_DESC_COUNT 8u
|
||||
#define E1000_TX_DESC_COUNT 8u
|
||||
#define E1000_RX_BUF_SIZE 2048u
|
||||
#define E1000_TX_BUF_SIZE 2048u
|
||||
#define E1000_POOL_BYTES(count, size) ((u32)((count) * (size)))
|
||||
#define E1000_POOL_PAGES(bytes) (((bytes) + 4095u) / 4096u)
|
||||
|
||||
#define E1000_RCTL_EN (1u << 1)
|
||||
#define E1000_RCTL_SBP (1u << 2)
|
||||
#define E1000_RCTL_UPE (1u << 3)
|
||||
#define E1000_RCTL_MPE (1u << 4)
|
||||
#define E1000_RCTL_BAM (1u << 15)
|
||||
#define E1000_RCTL_SECRC (1u << 26)
|
||||
#define E1000_RCTL_BSIZE_2048 0u
|
||||
|
||||
#define E1000_TCTL_EN (1u << 1)
|
||||
#define E1000_TCTL_PSP (1u << 3)
|
||||
#define E1000_TCTL_CT_SHIFT 4
|
||||
#define E1000_TCTL_COLD_SHIFT 12
|
||||
|
||||
#define E1000_TX_CMD_EOP (1u << 0)
|
||||
#define E1000_TX_CMD_IFCS (1u << 1)
|
||||
#define E1000_TX_CMD_RS (1u << 3)
|
||||
#define E1000_TX_STA_DD (1u << 0)
|
||||
#define E1000_RX_STA_DD (1u << 0)
|
||||
#define E1000_CTRL_FD (1u << 0)
|
||||
#define E1000_CTRL_SLU (1u << 6)
|
||||
|
||||
struct e1000_rx_desc {
|
||||
u64 addr;
|
||||
u16 length;
|
||||
u16 checksum;
|
||||
u8 status;
|
||||
u8 errors;
|
||||
u16 special;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct e1000_tx_desc {
|
||||
u64 addr;
|
||||
u16 length;
|
||||
u8 cso;
|
||||
u8 cmd;
|
||||
u8 status;
|
||||
u8 css;
|
||||
u16 special;
|
||||
} __attribute__((packed));
|
||||
|
||||
static const struct dkm_log_api *g_log;
|
||||
static volatile u32 *g_mmio;
|
||||
static u64 g_bar0_phys;
|
||||
static struct e1000_rx_desc *g_rx_desc;
|
||||
static struct e1000_tx_desc *g_tx_desc;
|
||||
static u64 g_rx_desc_phys;
|
||||
static u64 g_tx_desc_phys;
|
||||
static u8 *g_rx_buf[E1000_RX_DESC_COUNT];
|
||||
static u64 g_rx_buf_phys[E1000_RX_DESC_COUNT];
|
||||
static u8 *g_tx_buf[E1000_TX_DESC_COUNT];
|
||||
static u64 g_tx_buf_phys[E1000_TX_DESC_COUNT];
|
||||
static u32 g_rx_tail;
|
||||
static u32 g_tx_tail;
|
||||
static int g_rings_ready;
|
||||
|
||||
static __inline__ void outl(u16 p, u32 v) { __asm__("outl %0,%1"::"a"(v),"Nd"(p)); }
|
||||
static __inline__ u32 inl(u16 p) { u32 v; __asm__("inl %1,%0":"=a"(v):"Nd"(p)); return v; }
|
||||
static u8 inb(u16 p) { u8 v; __asm__("inb %1,%0":"=a"(v):"Nd"(p)); return v; }
|
||||
static void outb(u16 p, u8 v) { __asm__("outb %0,%1"::"a"(v),"Nd"(p)); }
|
||||
|
||||
static void raw_log(const char *s) {
|
||||
while (*s) {
|
||||
char c = *s++;
|
||||
if (c == '\n') {
|
||||
for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break;
|
||||
outb(0x3F8, '\r');
|
||||
}
|
||||
for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break;
|
||||
outb(0x3F8, (u8)c);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
||||
u32 a = (1u<<31)|((u32)bus<<16)|((u32)dev<<11)|((u32)func<<8)|((u32)reg&0xFC);
|
||||
outl(PCI_ADDR,a); return inl(PCI_DATA);
|
||||
}
|
||||
|
||||
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 v) {
|
||||
u32 a = (1u<<31)|((u32)bus<<16)|((u32)dev<<11)|((u32)func<<8)|((u32)reg&0xFC);
|
||||
outl(PCI_ADDR,a); outl(PCI_DATA,v);
|
||||
}
|
||||
|
||||
static int e1000_find(u8 *bus, u8 *dev) {
|
||||
for (u8 d=0; d<32; d++) {
|
||||
u32 vd = pci_read(0,d,0,PCI_VENDOR_ID);
|
||||
if ((vd&0xffff)==0xffff) continue;
|
||||
if ((vd&0xffff)!=0x8086) continue;
|
||||
u16 did = (u16)(vd>>16);
|
||||
/* 82540EM(0x100E) / 82545 等经典 e1000,以及 82574L(0x10D3) e1000e */
|
||||
if (did==0x100E || did==0x10D3 || did==0x1004 || did==0x100F ||
|
||||
did==0x10D3 || did==0x153A || did==0x1533) {
|
||||
*bus=0; *dev=d; return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 读取 EEPROM 一个 16-bit 字(经典 e1000 EERD 接口)。失败返回 0xffff。 */
|
||||
static u16 e1000_eeprom_read(u16 addr) {
|
||||
/* EERD: bit0=START, addr<<8, 完成后 bit4=DONE, data 在高 16 位 */
|
||||
g_mmio[E1000_EERD/4] = ((u32)addr << 8) | 1u;
|
||||
for (u32 i=0;i<100000;i++) {
|
||||
u32 v = g_mmio[E1000_EERD/4];
|
||||
if (v & (1u<<4)) return (u16)(v >> 16);
|
||||
}
|
||||
return 0xffff;
|
||||
}
|
||||
|
||||
static int irq_handler(u8 irq) {
|
||||
(void)irq;
|
||||
if (!g_mmio) return 0;
|
||||
u32 icr = g_mmio[E1000_ICR/4];
|
||||
if (icr) {
|
||||
g_mmio[E1000_ICR/4] = icr; /* clear */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void copy_bytes(void *dst, const void *src, u32 len) {
|
||||
u8 *d = (u8 *)dst;
|
||||
const u8 *s = (const u8 *)src;
|
||||
for (u32 i=0;i<len;i++) d[i]=s[i];
|
||||
}
|
||||
|
||||
static int e1000_init_rings(const struct dkm_dma_api *dma) {
|
||||
if (!dma || !dma->alloc_pages || !g_mmio) return -1;
|
||||
|
||||
struct dkm_dma_buffer rx_ring, tx_ring;
|
||||
if (dma->alloc_pages(1, 16, 0x100000000ULL, &rx_ring) != 0) return -2;
|
||||
if (dma->alloc_pages(1, 16, 0x100000000ULL, &tx_ring) != 0) return -3;
|
||||
g_rx_desc = (struct e1000_rx_desc *)rx_ring.virt;
|
||||
g_tx_desc = (struct e1000_tx_desc *)tx_ring.virt;
|
||||
g_rx_desc_phys = rx_ring.phys;
|
||||
g_tx_desc_phys = tx_ring.phys;
|
||||
|
||||
for (u32 i=0;i<E1000_RX_DESC_COUNT;i++) {
|
||||
struct dkm_dma_buffer b;
|
||||
if (dma->alloc_pages(1, 16, 0x100000000ULL, &b) != 0) return -4;
|
||||
g_rx_buf[i] = (u8 *)b.virt;
|
||||
g_rx_buf_phys[i] = b.phys;
|
||||
g_rx_desc[i].addr = b.phys;
|
||||
g_rx_desc[i].status = 0;
|
||||
}
|
||||
|
||||
for (u32 i=0;i<E1000_TX_DESC_COUNT;i++) {
|
||||
struct dkm_dma_buffer b;
|
||||
if (dma->alloc_pages(1, 16, 0x100000000ULL, &b) != 0) return -5;
|
||||
g_tx_buf[i] = (u8 *)b.virt;
|
||||
g_tx_buf_phys[i] = b.phys;
|
||||
g_tx_desc[i].addr = b.phys;
|
||||
g_tx_desc[i].status = E1000_TX_STA_DD;
|
||||
}
|
||||
raw_log("[e1000.raw] ring/buffers allocated\n");
|
||||
|
||||
g_mmio[E1000_RCTL/4] = 0;
|
||||
g_mmio[E1000_TCTL/4] = 0;
|
||||
|
||||
g_mmio[E1000_RDBAL/4] = (u32)(g_rx_desc_phys & 0xffffffffu);
|
||||
g_mmio[E1000_RDBAH/4] = (u32)(g_rx_desc_phys >> 32);
|
||||
g_mmio[E1000_RDLEN/4] = E1000_RX_DESC_COUNT * sizeof(struct e1000_rx_desc);
|
||||
g_mmio[E1000_RDH/4] = 0;
|
||||
g_rx_tail = E1000_RX_DESC_COUNT - 1;
|
||||
g_mmio[E1000_RDT/4] = g_rx_tail;
|
||||
|
||||
g_mmio[E1000_TDBAL/4] = (u32)(g_tx_desc_phys & 0xffffffffu);
|
||||
g_mmio[E1000_TDBAH/4] = (u32)(g_tx_desc_phys >> 32);
|
||||
g_mmio[E1000_TDLEN/4] = E1000_TX_DESC_COUNT * sizeof(struct e1000_tx_desc);
|
||||
g_mmio[E1000_TDH/4] = 0;
|
||||
g_tx_tail = 0;
|
||||
g_mmio[E1000_TDT/4] = 0;
|
||||
|
||||
/* e1000e/部分实现需要显式打开队列描述符控制;经典 e1000 会忽略/兼容。 */
|
||||
g_mmio[E1000_RXDCTL/4] = (1u << 25);
|
||||
g_mmio[E1000_TXDCTL/4] = (1u << 25);
|
||||
|
||||
g_mmio[E1000_TIPG/4] = 0x0060200Au;
|
||||
g_mmio[E1000_RCTL/4] = E1000_RCTL_EN | E1000_RCTL_UPE | E1000_RCTL_MPE | E1000_RCTL_BAM | E1000_RCTL_SECRC | E1000_RCTL_BSIZE_2048;
|
||||
g_mmio[E1000_TCTL/4] = E1000_TCTL_EN | E1000_TCTL_PSP | (0x0Fu << E1000_TCTL_CT_SHIFT) | (0x40u << E1000_TCTL_COLD_SHIFT);
|
||||
|
||||
g_rings_ready = 1;
|
||||
raw_log("[e1000.raw] RX/TX rings initialized\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int e1000_tx(void *ctx, const void *packet, u32 length) {
|
||||
(void)ctx;
|
||||
raw_log("[e1000.raw] tx enter\n");
|
||||
if (!g_rings_ready || !packet || length == 0 || length > E1000_RX_BUF_SIZE) return -1;
|
||||
u32 index = g_tx_tail;
|
||||
if (!(g_tx_desc[index].status & E1000_TX_STA_DD)) return -2;
|
||||
copy_bytes(g_tx_buf[index], packet, length);
|
||||
raw_log("[e1000.raw] tx copied\n");
|
||||
g_tx_desc[index].length = (u16)length;
|
||||
g_tx_desc[index].cmd = E1000_TX_CMD_EOP | E1000_TX_CMD_IFCS | E1000_TX_CMD_RS;
|
||||
g_tx_desc[index].status = 0;
|
||||
g_tx_tail = (index + 1) % E1000_TX_DESC_COUNT;
|
||||
g_mmio[E1000_TDT/4] = g_tx_tail;
|
||||
raw_log("[e1000.raw] tx tdt written\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int e1000_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
|
||||
(void)ctx;
|
||||
if (!g_rings_ready || !buffer || !out_length) return -1;
|
||||
u32 next = (g_rx_tail + 1) % E1000_RX_DESC_COUNT;
|
||||
if (!(g_rx_desc[next].status & E1000_RX_STA_DD)) return 1;
|
||||
u32 len = g_rx_desc[next].length;
|
||||
if (len > capacity) return -2;
|
||||
copy_bytes(buffer, g_rx_buf[next], len);
|
||||
*out_length = len;
|
||||
g_rx_desc[next].status = 0;
|
||||
g_rx_tail = next;
|
||||
g_mmio[E1000_RDT/4] = g_rx_tail;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
raw_log("[e1000.raw] driver_init entered\n");
|
||||
(void)handle;
|
||||
if (!api||!api->log) return -1;
|
||||
g_log=api->log;
|
||||
|
||||
raw_log("[e1000.raw] before find\n");
|
||||
u8 bus, dev;
|
||||
if (e1000_find(&bus,&dev)!=0) {
|
||||
raw_log("[e1000.raw] device not found\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
raw_log("[e1000.raw] found device\n");
|
||||
|
||||
u32 cmd = pci_read(bus, dev, 0, PCI_COMMAND);
|
||||
cmd |= 0x0007u; /* IO Space | Memory Space | Bus Master */
|
||||
pci_write(bus, dev, 0, PCI_COMMAND, cmd);
|
||||
raw_log("[e1000.raw] PCI command enabled\n");
|
||||
|
||||
g_bar0_phys = (u64)pci_read(bus,dev,0,PCI_BAR0) & 0xFFFFFFF0ULL;
|
||||
if (!g_bar0_phys) {
|
||||
raw_log("[e1000.raw] BAR0 unavailable\n");
|
||||
return 0;
|
||||
}
|
||||
raw_log("[e1000.raw] BAR0 ok\n");
|
||||
|
||||
u64 hhdm = api->hhdm_offset;
|
||||
g_mmio = (volatile u32*)(uintptr_t)(hhdm + g_bar0_phys);
|
||||
(void)g_mmio[E1000_STATUS/4];
|
||||
raw_log("[e1000.raw] MMIO status read ok\n");
|
||||
|
||||
/* 软件复位,确保 RX/TX/RAL/RAH 从已知状态开始配置。 */
|
||||
g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | (1u << 26);
|
||||
for (u32 wait=0; wait<1000000; wait++) {
|
||||
if (!(g_mmio[E1000_CTRL/4] & (1u << 26))) break;
|
||||
}
|
||||
raw_log("[e1000.raw] reset done\n");
|
||||
|
||||
u32 ral = g_mmio[E1000_RAL/4];
|
||||
u32 rah = g_mmio[E1000_RAH/4];
|
||||
u8 mac[6];
|
||||
mac[0] = (u8)(ral & 0xff);
|
||||
mac[1] = (u8)((ral>>8) & 0xff);
|
||||
mac[2] = (u8)((ral>>16) & 0xff);
|
||||
mac[3] = (u8)((ral>>24) & 0xff);
|
||||
mac[4] = (u8)(rah & 0xff);
|
||||
mac[5] = (u8)((rah>>8) & 0xff);
|
||||
|
||||
/* RAL/RAH 为空时从 EEPROM 读 MAC */
|
||||
if (ral == 0 && (rah & 0xffff) == 0) {
|
||||
u16 w0 = e1000_eeprom_read(0);
|
||||
u16 w1 = e1000_eeprom_read(1);
|
||||
u16 w2 = e1000_eeprom_read(2);
|
||||
if (!(w0==0xffff && w1==0xffff && w2==0xffff)) {
|
||||
mac[0]=(u8)(w0&0xff); mac[1]=(u8)(w0>>8);
|
||||
mac[2]=(u8)(w1&0xff); mac[3]=(u8)(w1>>8);
|
||||
mac[4]=(u8)(w2&0xff); mac[5]=(u8)(w2>>8);
|
||||
}
|
||||
}
|
||||
|
||||
/* 仍为全 0 时使用与虚拟机配置一致的回退 MAC,保证收发地址匹配 */
|
||||
if (mac[0]==0&&mac[1]==0&&mac[2]==0&&mac[3]==0&&mac[4]==0&&mac[5]==0) {
|
||||
mac[0]=0x52; mac[1]=0x54; mac[2]=0x00; mac[3]=0x12; mac[4]=0x34; mac[5]=0x56;
|
||||
raw_log("[e1000.raw] using fallback MAC 52:54:00:12:34:56\n");
|
||||
}
|
||||
|
||||
/* 写入 RAL/RAH 并置 AV(bit31) 使能接收该单播地址 */
|
||||
g_mmio[E1000_RAL/4] = (u32)mac[0] | ((u32)mac[1]<<8) | ((u32)mac[2]<<16) | ((u32)mac[3]<<24);
|
||||
g_mmio[E1000_RAH/4] = (u32)mac[4] | ((u32)mac[5]<<8) | (1u<<31);
|
||||
raw_log("[e1000.raw] MAC read ok\n");
|
||||
|
||||
u32 irq_line = pci_read(bus,dev,0,PCI_IRQ_LINE) & 0xff;
|
||||
|
||||
g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | E1000_CTRL_FD | E1000_CTRL_SLU;
|
||||
raw_log("[e1000.raw] link setup\n");
|
||||
|
||||
g_mmio[E1000_IMC/4] = 0xffffffffu;
|
||||
(void)g_mmio[E1000_ICR/4];
|
||||
raw_log("[e1000.raw] interrupts masked\n");
|
||||
|
||||
int ring_rc = e1000_init_rings(api->dma);
|
||||
if (ring_rc != 0) raw_log("[e1000.raw] ring init failed\n");
|
||||
|
||||
if (api->net && api->net->register_device) {
|
||||
struct dkm_net_device_desc netdev;
|
||||
netdev.name = "e1000";
|
||||
for (int i=0;i<6;i++) netdev.mac[i] = mac[i];
|
||||
netdev.flags = DKM_NET_F_LINK_UP;
|
||||
if (g_rings_ready) netdev.flags |= DKM_NET_F_TX_READY | DKM_NET_F_RX_READY;
|
||||
netdev.ctx = 0;
|
||||
netdev.tx = g_rings_ready ? e1000_tx : 0;
|
||||
netdev.rx_poll = g_rings_ready ? e1000_rx_poll : 0;
|
||||
int net_index = api->net->register_device(&netdev);
|
||||
(void)net_index;
|
||||
raw_log("[e1000.raw] netdev registered\n");
|
||||
} else {
|
||||
raw_log("[e1000.raw] net API unavailable\n");
|
||||
}
|
||||
|
||||
g_mmio[E1000_IMC/4] = 0xffffffffu;
|
||||
|
||||
if (api->irq_register && irq_line < 16) {
|
||||
api->irq_register((u8)irq_line, (void*)(uintptr_t)irq_handler);
|
||||
raw_log("[e1000.raw] IRQ registered\n");
|
||||
}
|
||||
|
||||
raw_log("[e1000.raw] driver ready\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle; return 0;
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
/* DKM FAT32 Driver — read-only FAT32 filesystem parser
|
||||
* Stage 2, optional, depends on "vfs" and "block".
|
||||
* Reads from an embedded FAT32 test image (boot module "dkm:test_fat32").
|
||||
* Parses BPB, walks FAT, reads root dir, lists files, reads a file.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* DKM ABI types
|
||||
* --------------------------------------------------------------- */
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_block_api {
|
||||
int (*register_device)(const void *desc);
|
||||
u32 (*device_count)(void);
|
||||
int (*read)(u32 index, u64 lba, u32 count, void *buffer);
|
||||
u64 (*sector_size)(u32 index);
|
||||
const char *(*device_name)(u32 index);
|
||||
};
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version; u32 size; u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem,*utsm,*irq,*pci_api,*dma,*vfs_api,*net,*timer,*drr;
|
||||
const void *rsdp_address;
|
||||
const void *fb_address;
|
||||
u64 fb_width,fb_height,fb_pitch; u16 fb_bpp;
|
||||
const void *boot_modules_response;
|
||||
int (*irq_register)(u8 irq, void *handler);
|
||||
u64 hhdm_offset;
|
||||
const struct dkm_block_api *block;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
struct dkm_driver_desc {
|
||||
u32 magic; u16 abi_version; u16 desc_size;
|
||||
const char *name,*version,*vendor;
|
||||
u32 driver_class,stage,flags,priority;
|
||||
const char *const *depends; u32 depends_count;
|
||||
const char *const *provides; u32 provides_count;
|
||||
u64 min_kernel_abi,feature_bits,reserved0,reserved1;
|
||||
};
|
||||
|
||||
struct limine_file {
|
||||
u64 revision; void *address; u64 size; char *path; char *cmdline; u8 _pad[56];
|
||||
};
|
||||
struct limine_module_response {
|
||||
u64 revision; u64 module_count; struct limine_file **modules;
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* FAT32 BPB
|
||||
* --------------------------------------------------------------- */
|
||||
typedef struct __attribute__((packed)) {
|
||||
u8 jmp[3];
|
||||
char oem[8];
|
||||
u16 bytes_per_sector;
|
||||
u8 sectors_per_cluster;
|
||||
u16 reserved_sector_count;
|
||||
u8 fat_count;
|
||||
u16 root_entry_count;
|
||||
u16 total_sectors_16;
|
||||
u8 media;
|
||||
u16 sectors_per_fat_16;
|
||||
u16 sectors_per_track;
|
||||
u16 heads;
|
||||
u32 hidden_sectors;
|
||||
u32 total_sectors_32;
|
||||
/* FAT32 extended */
|
||||
u32 sectors_per_fat;
|
||||
u16 flags;
|
||||
u16 version;
|
||||
u32 root_cluster;
|
||||
u16 fsinfo_sector;
|
||||
u16 backup_boot;
|
||||
u8 reserved[12];
|
||||
u8 drive;
|
||||
u8 nt_flags;
|
||||
u8 signature;
|
||||
u32 serial;
|
||||
char label[11];
|
||||
char type[8];
|
||||
u8 code[420];
|
||||
u16 boot_sig;
|
||||
} fat32_bpb;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
char name[11];
|
||||
u8 attr;
|
||||
u8 nt_reserved;
|
||||
u8 creation_tenth;
|
||||
u16 creation_time;
|
||||
u16 creation_date;
|
||||
u16 access_date;
|
||||
u16 cluster_high;
|
||||
u16 write_time;
|
||||
u16 write_date;
|
||||
u16 cluster_low;
|
||||
u32 file_size;
|
||||
} fat32_dir_entry;
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver descriptor
|
||||
* --------------------------------------------------------------- */
|
||||
static const char *const g_depends[] = { "vfs", "block" };
|
||||
static const char *const g_provides[] = { "fat32" };
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic=DKM_DRIVER_MAGIC, .abi_version=DKM_ABI_VERSION,
|
||||
.desc_size=sizeof(struct dkm_driver_desc),
|
||||
.name="fat32", .version="0.1.0", .vendor="Deshab",
|
||||
.driver_class=7, .stage=2, .flags=0, .priority=0,
|
||||
.depends=g_depends, .depends_count=2,
|
||||
.provides=g_provides, .provides_count=1,
|
||||
.min_kernel_abi=1,
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Helpers
|
||||
* --------------------------------------------------------------- */
|
||||
static const struct dkm_log_api *g_log;
|
||||
static u8 g_block_image[65536];
|
||||
|
||||
static int fat32_bpb_valid(const u8 *disk, u64 disk_size) {
|
||||
if (!disk || disk_size < 512) return 0;
|
||||
const fat32_bpb *bpb = (const fat32_bpb *)disk;
|
||||
if (bpb->boot_sig != 0xAA55) return 0;
|
||||
if (bpb->bytes_per_sector != 512) return 0;
|
||||
if (bpb->sectors_per_cluster == 0) return 0;
|
||||
if (bpb->reserved_sector_count == 0) return 0;
|
||||
if (bpb->fat_count == 0) return 0;
|
||||
if (bpb->sectors_per_fat == 0) return 0;
|
||||
if (bpb->root_cluster < 2) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void log_hex(const char *p, u64 v) {
|
||||
static const char h[] = "0123456789abcdef";
|
||||
char b[19]; u32 i=0; b[i++]='0'; b[i++]='x';
|
||||
for (int j=15;j>=0;j--) b[i++]=h[(v>>(j*4))&0xf];
|
||||
b[i]=0; g_log->info(p); g_log->info(b);
|
||||
}
|
||||
|
||||
static u32 read_u32_le(const u8 *p) {
|
||||
return (u32)p[0]|((u32)p[1]<<8)|((u32)p[2]<<16)|((u32)p[3]<<24);
|
||||
}
|
||||
|
||||
static u16 read_u16_le(const u8 *p) {
|
||||
return (u16)p[0]|((u16)p[1]<<8);
|
||||
}
|
||||
|
||||
static int strneq(const char *a, const char *b, int n) {
|
||||
for (int i=0;i<n;i++) { if (a[i]!=b[i]) return 0; }
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void log_name11(const char *n) {
|
||||
char buf[13]; int j=0;
|
||||
for (int i=0;i<8 && n[i]!=0x20 && n[i];i++) buf[j++]=n[i];
|
||||
if (n[8]!=0x20 || (n[9]!=0x20 && n[9]!=0)) {
|
||||
buf[j++]='.';
|
||||
for (int i=8;i<11 && n[i]!=0x20 && n[i];i++) buf[j++]=n[i];
|
||||
}
|
||||
buf[j]=0; g_log->info(buf);
|
||||
}
|
||||
|
||||
static const u8 *fat32_find_boot_module_image(const struct dkm_kernel_api *api, u64 *size_out) {
|
||||
const struct limine_module_response *rsp =
|
||||
(const struct limine_module_response *)api->boot_modules_response;
|
||||
if (!rsp) return NULL;
|
||||
for (u64 i=0;i<rsp->module_count;i++) {
|
||||
struct limine_file *f=rsp->modules[i];
|
||||
if (f&&f->cmdline&&strneq(f->cmdline,"dkm:test_fat32",13)) {
|
||||
if (size_out) *size_out=f->size;
|
||||
return (const u8*)f->address;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const u8 *fat32_select_disk_image(const struct dkm_kernel_api *api, u64 *size_out) {
|
||||
if (api->block && api->block->device_count && api->block->read && api->block->sector_size) {
|
||||
u32 count = api->block->device_count();
|
||||
log_hex("[fat32] block devices=", count);
|
||||
if (count > 0 && api->block->sector_size(0) == 512) {
|
||||
const char *name = api->block->device_name ? api->block->device_name(0) : NULL;
|
||||
g_log->info("[fat32] trying block provider");
|
||||
if (name) g_log->info(name);
|
||||
int st = api->block->read(0, 0, 128, g_block_image);
|
||||
log_hex("[fat32] block read status=", (u64)(i64)st);
|
||||
if (st == 0 && fat32_bpb_valid(g_block_image, sizeof(g_block_image))) {
|
||||
g_log->info("[fat32] using block provider image");
|
||||
if (size_out) *size_out = sizeof(g_block_image);
|
||||
return g_block_image;
|
||||
}
|
||||
g_log->warn("[fat32] block provider image is not FAT32; fallback");
|
||||
}
|
||||
}
|
||||
|
||||
u64 module_size = 0;
|
||||
const u8 *module = fat32_find_boot_module_image(api, &module_size);
|
||||
if (module) {
|
||||
g_log->info("[fat32] using boot module image");
|
||||
if (size_out) *size_out = module_size;
|
||||
return module;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver entry
|
||||
* --------------------------------------------------------------- */
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
if (!api||!api->log) return -1;
|
||||
g_log=api->log;
|
||||
|
||||
u64 disk_size = 0;
|
||||
const u8 *disk = fat32_select_disk_image(api, &disk_size);
|
||||
if (!disk) { g_log->warn("[fat32] no FAT32 image source"); return 0; }
|
||||
if (!fat32_bpb_valid(disk, disk_size)) { g_log->warn("[fat32] invalid FAT32 BPB"); return 0; }
|
||||
|
||||
g_log->info("[fat32] parsing FAT32 image");
|
||||
const fat32_bpb *bpb=(const fat32_bpb*)disk;
|
||||
u32 bps=bpb->bytes_per_sector;
|
||||
u32 spc=bpb->sectors_per_cluster;
|
||||
u32 rsc=bpb->reserved_sector_count;
|
||||
u32 spf=bpb->sectors_per_fat;
|
||||
u32 root_clus=bpb->root_cluster;
|
||||
|
||||
log_hex("[fat32] bytes/sec=",bps);
|
||||
log_hex("[fat32] sec/cluster=",spc);
|
||||
log_hex("[fat32] fat start sec=",rsc);
|
||||
log_hex("[fat32] sec/fat=",spf);
|
||||
log_hex("[fat32] root cluster=",root_clus);
|
||||
|
||||
/* read root directory */
|
||||
u32 cluster_size = bps * spc;
|
||||
u32 fat_start = rsc * bps;
|
||||
u32 data_start = fat_start + spf * bps;
|
||||
|
||||
g_log->info("[fat32] root dir entries:");
|
||||
|
||||
u32 clus = root_clus;
|
||||
while (clus >= 2 && clus < 0x0FFFFFF8) {
|
||||
u32 clus_off = data_start + (clus - 2) * cluster_size;
|
||||
const fat32_dir_entry *dir = (const fat32_dir_entry *)(disk + clus_off);
|
||||
for (u32 e=0; e*32 < cluster_size; e++) {
|
||||
if (dir[e].name[0]==0) break;
|
||||
if ((u8)dir[e].name[0]==0xE5) continue;
|
||||
if (dir[e].attr==0x0F) continue; /* skip LFN */
|
||||
if (dir[e].attr & 0x08) continue; /* skip volume label */
|
||||
g_log->info("[fat32] ");
|
||||
log_name11(dir[e].name);
|
||||
log_hex("[fat32] size=",dir[e].file_size);
|
||||
|
||||
/* try to read README.TXT content */
|
||||
if (strneq(dir[e].name,"README TXT",11)) {
|
||||
u32 f_clus = read_u16_le((const u8*)&dir[e].cluster_low);
|
||||
u32 f_off = data_start + (f_clus - 2)*cluster_size;
|
||||
u32 f_size = dir[e].file_size;
|
||||
g_log->info("[fat32] --- README.TXT content ---");
|
||||
u32 n = f_size < 128 ? f_size : 128;
|
||||
char line[80]; u32 lp=0;
|
||||
for (u32 k=0;k<n;k++) {
|
||||
u8 c = disk[f_off+k];
|
||||
line[lp++]=c;
|
||||
if (c=='\n'||lp>=78||k==n-1) { line[lp]=0; g_log->info("[fat32] "); g_log->info(line); lp=0; }
|
||||
}
|
||||
g_log->info("[fat32] --- end ---");
|
||||
}
|
||||
}
|
||||
|
||||
/* read next cluster from FAT */
|
||||
u32 fat_ent_off = fat_start + clus * 4;
|
||||
clus = read_u32_le(disk + fat_ent_off) & 0x0FFFFFFF;
|
||||
}
|
||||
|
||||
g_log->info("[fat32] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle; return 0;
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/* DKM NVMe Driver — PCI NVMe discovery and controller register enumeration
|
||||
* Stage 1 optional storage driver, depends on "pci" and "irq", provides "block".
|
||||
* This first version only discovers NVMe controllers and reads BAR0 registers;
|
||||
* it does not enable queues or submit admin commands.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version;
|
||||
u32 size;
|
||||
u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem;
|
||||
const void *utsm;
|
||||
const void *irq;
|
||||
const void *pci;
|
||||
const void *dma;
|
||||
const void *vfs;
|
||||
const void *net;
|
||||
const void *timer;
|
||||
const void *drr;
|
||||
const void *rsdp_address;
|
||||
const void *fb_address;
|
||||
u64 fb_width;
|
||||
u64 fb_height;
|
||||
u64 fb_pitch;
|
||||
u16 fb_bpp;
|
||||
const void *boot_modules_response;
|
||||
int (*irq_register)(u8 irq, void *handler);
|
||||
u64 hhdm_offset;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
struct dkm_driver_desc {
|
||||
u32 magic;
|
||||
u16 abi_version;
|
||||
u16 desc_size;
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
u32 driver_class;
|
||||
u32 stage;
|
||||
u32 flags;
|
||||
u32 priority;
|
||||
const char *const *depends;
|
||||
u32 depends_count;
|
||||
const char *const *provides;
|
||||
u32 provides_count;
|
||||
u64 min_kernel_abi;
|
||||
u64 feature_bits;
|
||||
u64 reserved0;
|
||||
u64 reserved1;
|
||||
};
|
||||
|
||||
static const char *const g_depends[] = { "pci", "irq" };
|
||||
static const char *const g_provides[] = { "block" };
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(struct dkm_driver_desc),
|
||||
.name = "nvme",
|
||||
.version = "0.1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = 6, /* DKM_CLASS_STORAGE */
|
||||
.stage = 1,
|
||||
.flags = 0, /* optional */
|
||||
.priority = 0,
|
||||
.depends = g_depends,
|
||||
.depends_count = 2,
|
||||
.provides = g_provides,
|
||||
.provides_count = 1,
|
||||
.min_kernel_abi = 1,
|
||||
.feature_bits = 0,
|
||||
.reserved0 = 0,
|
||||
.reserved1 = 0
|
||||
};
|
||||
|
||||
#define PCI_ADDR 0xCF8
|
||||
#define PCI_DATA 0xCFC
|
||||
#define PCI_VENDOR_ID 0x00
|
||||
#define PCI_COMMAND 0x04
|
||||
#define PCI_CLASS_REG 0x08
|
||||
#define PCI_HEADER 0x0E
|
||||
#define PCI_BAR0 0x10
|
||||
#define PCI_BAR1 0x14
|
||||
#define PCI_IRQ_LINE 0x3C
|
||||
|
||||
#define PCI_CMD_MEM (1u << 1)
|
||||
#define PCI_CMD_BUSM (1u << 2)
|
||||
|
||||
#define NVME_CLASS_STORAGE 0x01
|
||||
#define NVME_SUBCLASS_NVM 0x08
|
||||
#define NVME_PROGIF_NVME 0x02
|
||||
|
||||
#define NVME_CAP 0x00
|
||||
#define NVME_VS 0x08
|
||||
#define NVME_INTMS 0x0C
|
||||
#define NVME_INTMC 0x10
|
||||
#define NVME_CC 0x14
|
||||
#define NVME_CSTS 0x1C
|
||||
#define NVME_NSSR 0x20
|
||||
#define NVME_AQA 0x24
|
||||
#define NVME_ASQ 0x28
|
||||
#define NVME_ACQ 0x30
|
||||
#define NVME_CMBLOC 0x38
|
||||
#define NVME_CMBSZ 0x3C
|
||||
|
||||
static const struct dkm_log_api *g_log;
|
||||
static volatile u8 *g_regs;
|
||||
|
||||
static __inline__ void outl(u16 port, u32 value) {
|
||||
__asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static __inline__ u32 inl(u16 port) {
|
||||
u32 value;
|
||||
__asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
||||
u32 addr = (1u << 31)
|
||||
| ((u32)bus << 16)
|
||||
| ((u32)dev << 11)
|
||||
| ((u32)func << 8)
|
||||
| ((u32)reg & 0xFC);
|
||||
outl(PCI_ADDR, addr);
|
||||
return inl(PCI_DATA);
|
||||
}
|
||||
|
||||
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
|
||||
u32 addr = (1u << 31)
|
||||
| ((u32)bus << 16)
|
||||
| ((u32)dev << 11)
|
||||
| ((u32)func << 8)
|
||||
| ((u32)reg & 0xFC);
|
||||
outl(PCI_ADDR, addr);
|
||||
outl(PCI_DATA, value);
|
||||
}
|
||||
|
||||
static u32 mmio_read32(u32 off) {
|
||||
return *(volatile u32 *)(void *)(g_regs + off);
|
||||
}
|
||||
|
||||
static u64 mmio_read64(u32 off) {
|
||||
u32 lo = mmio_read32(off);
|
||||
u32 hi = mmio_read32(off + 4);
|
||||
return ((u64)hi << 32) | lo;
|
||||
}
|
||||
|
||||
static void log_hex(const char *prefix, u64 value) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
char buf[19];
|
||||
u32 pos = 0;
|
||||
buf[pos++] = '0';
|
||||
buf[pos++] = 'x';
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
buf[pos++] = hex[(value >> (i * 4)) & 0xf];
|
||||
}
|
||||
buf[pos] = 0;
|
||||
g_log->info(prefix);
|
||||
g_log->info(buf);
|
||||
}
|
||||
|
||||
static int nvme_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
||||
for (u8 bus = 0; bus < 16; bus++) {
|
||||
for (u8 dev = 0; dev < 32; dev++) {
|
||||
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
|
||||
if ((vd & 0xffff) == 0xffff) continue;
|
||||
|
||||
u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16);
|
||||
u8 func_count = (header & 0x80) ? 8 : 1;
|
||||
for (u8 func = 0; func < func_count; func++) {
|
||||
u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID);
|
||||
if ((vd2 & 0xffff) == 0xffff) continue;
|
||||
|
||||
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
|
||||
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
|
||||
u8 subclass = (u8)((class_reg >> 16) & 0xff);
|
||||
u8 class_code = (u8)((class_reg >> 24) & 0xff);
|
||||
|
||||
if (class_code == NVME_CLASS_STORAGE &&
|
||||
subclass == NVME_SUBCLASS_NVM &&
|
||||
prog_if == NVME_PROGIF_NVME) {
|
||||
*out_bus = bus;
|
||||
*out_dev = dev;
|
||||
*out_func = func;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void log_nvme_version(u32 vs) {
|
||||
log_hex("[nvme] version major=", (vs >> 16) & 0xffff);
|
||||
log_hex("[nvme] version minor=", (vs >> 8) & 0xff);
|
||||
log_hex("[nvme] version tertiary=", vs & 0xff);
|
||||
}
|
||||
|
||||
static void log_cap_fields(u64 cap) {
|
||||
log_hex("[nvme] CAP.MQES=", cap & 0xffff);
|
||||
log_hex("[nvme] CAP.CQR=", (cap >> 16) & 1);
|
||||
log_hex("[nvme] CAP.AMS=", (cap >> 17) & 3);
|
||||
log_hex("[nvme] CAP.TO=", (cap >> 24) & 0xff);
|
||||
log_hex("[nvme] CAP.DSTRD=", (cap >> 32) & 0xf);
|
||||
log_hex("[nvme] CAP.NSSRS=", (cap >> 36) & 1);
|
||||
log_hex("[nvme] CAP.CSS=", (cap >> 37) & 0xff);
|
||||
log_hex("[nvme] CAP.MPSMIN=", (cap >> 48) & 0xf);
|
||||
log_hex("[nvme] CAP.MPSMAX=", (cap >> 52) & 0xf);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
|
||||
if (!api || !api->log) return -1;
|
||||
g_log = api->log;
|
||||
|
||||
g_log->info("[nvme] init begin");
|
||||
|
||||
u8 bus = 0;
|
||||
u8 dev = 0;
|
||||
u8 func = 0;
|
||||
if (nvme_find(&bus, &dev, &func) != 0) {
|
||||
g_log->warn("[nvme] NVMe controller not found");
|
||||
g_log->info("[nvme] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_log->info("[nvme] NVMe controller found");
|
||||
log_hex("[nvme] bus=", bus);
|
||||
log_hex("[nvme] dev=", dev);
|
||||
log_hex("[nvme] func=", func);
|
||||
|
||||
u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID);
|
||||
log_hex("[nvme] vendor=", vd & 0xffff);
|
||||
log_hex("[nvme] device=", vd >> 16);
|
||||
|
||||
u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
|
||||
command |= PCI_CMD_MEM | PCI_CMD_BUSM;
|
||||
pci_write(bus, dev, func, PCI_COMMAND, command);
|
||||
log_hex("[nvme] PCI command=", command);
|
||||
|
||||
u32 bar0_lo = pci_read(bus, dev, func, PCI_BAR0);
|
||||
u32 bar1_hi = pci_read(bus, dev, func, PCI_BAR1);
|
||||
u64 bar_phys;
|
||||
if (bar0_lo & 1u) {
|
||||
g_log->warn("[nvme] BAR0 is IO space; unsupported");
|
||||
g_log->info("[nvme] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((bar0_lo & 0x6u) == 0x4u) {
|
||||
bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u);
|
||||
} else {
|
||||
bar_phys = (u64)(bar0_lo & 0xFFFFFFF0u);
|
||||
}
|
||||
|
||||
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
|
||||
log_hex("[nvme] BAR0 phys=", bar_phys);
|
||||
log_hex("[nvme] PCI IRQ line=", irq_line);
|
||||
|
||||
if (!api->hhdm_offset || !bar_phys) {
|
||||
g_log->warn("[nvme] missing HHDM or BAR0; skip MMIO enumeration");
|
||||
g_log->info("[nvme] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bar_phys >= 0x100000000ULL) {
|
||||
g_log->warn("[nvme] BAR0 is above 4G; current HHDM MMIO map cannot safely read it");
|
||||
g_log->info("[nvme] discovery only; MMIO enumeration skipped");
|
||||
g_log->info("[nvme] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_regs = (volatile u8 *)(uintptr_t)(api->hhdm_offset + bar_phys);
|
||||
log_hex("[nvme] HHDM offset=", api->hhdm_offset);
|
||||
|
||||
u64 cap = mmio_read64(NVME_CAP);
|
||||
u32 vs = mmio_read32(NVME_VS);
|
||||
u32 intms = mmio_read32(NVME_INTMS);
|
||||
u32 intmc = mmio_read32(NVME_INTMC);
|
||||
u32 cc = mmio_read32(NVME_CC);
|
||||
u32 csts = mmio_read32(NVME_CSTS);
|
||||
u32 nssr = mmio_read32(NVME_NSSR);
|
||||
u32 aqa = mmio_read32(NVME_AQA);
|
||||
u64 asq = mmio_read64(NVME_ASQ);
|
||||
u64 acq = mmio_read64(NVME_ACQ);
|
||||
u32 cmbloc = mmio_read32(NVME_CMBLOC);
|
||||
u32 cmbsz = mmio_read32(NVME_CMBSZ);
|
||||
|
||||
log_hex("[nvme] CAP=", cap);
|
||||
log_cap_fields(cap);
|
||||
log_hex("[nvme] VS=", vs);
|
||||
log_nvme_version(vs);
|
||||
log_hex("[nvme] INTMS=", intms);
|
||||
log_hex("[nvme] INTMC=", intmc);
|
||||
log_hex("[nvme] CC=", cc);
|
||||
log_hex("[nvme] CSTS=", csts);
|
||||
log_hex("[nvme] NSSR=", nssr);
|
||||
log_hex("[nvme] AQA=", aqa);
|
||||
log_hex("[nvme] ASQ=", asq);
|
||||
log_hex("[nvme] ACQ=", acq);
|
||||
log_hex("[nvme] CMBLOC=", cmbloc);
|
||||
log_hex("[nvme] CMBSZ=", cmbsz);
|
||||
|
||||
if (cc & 1u) {
|
||||
g_log->warn("[nvme] controller already enabled; leaving state unchanged");
|
||||
} else {
|
||||
g_log->info("[nvme] controller disabled; queues not configured");
|
||||
}
|
||||
|
||||
g_log->info("[nvme] discovery only; admin queues disabled");
|
||||
g_log->info("[nvme] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/* DKM PCI Driver — PCI bus enumeration via config space (0xCF8/0xCFC)
|
||||
* Stage 0, required, depends on "irq", provides "pci".
|
||||
* No IRQ needed for enumeration. Scans all buses/devices/functions.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* DKM ABI types
|
||||
* --------------------------------------------------------------- */
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version;
|
||||
u32 size;
|
||||
u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem;
|
||||
const void *utsm;
|
||||
const void *irq;
|
||||
const void *pci_api;
|
||||
const void *dma;
|
||||
const void *vfs;
|
||||
const void *net;
|
||||
const void *timer;
|
||||
const void *drr;
|
||||
const void *rsdp_address;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
struct dkm_driver_desc {
|
||||
u32 magic;
|
||||
u16 abi_version;
|
||||
u16 desc_size;
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
u32 driver_class;
|
||||
u32 stage;
|
||||
u32 flags;
|
||||
u32 priority;
|
||||
const char *const *depends;
|
||||
u32 depends_count;
|
||||
const char *const *provides;
|
||||
u32 provides_count;
|
||||
u64 min_kernel_abi;
|
||||
u64 feature_bits;
|
||||
u64 reserved0;
|
||||
u64 reserved1;
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* PCI constants
|
||||
* --------------------------------------------------------------- */
|
||||
#define PCI_CFG_ADDR 0xCF8
|
||||
#define PCI_CFG_DATA 0xCFC
|
||||
|
||||
#define PCI_VENDOR_ID 0x00
|
||||
#define PCI_DEVICE_ID 0x02
|
||||
#define PCI_COMMAND 0x04
|
||||
#define PCI_STATUS 0x06
|
||||
#define PCI_CLASS 0x0B /* class code */
|
||||
#define PCI_SUBCLASS 0x0A /* subclass */
|
||||
#define PCI_PROG_IF 0x09 /* prog IF */
|
||||
#define PCI_HEADER_TYPE 0x0E
|
||||
#define PCI_BAR0 0x10
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver descriptor
|
||||
* --------------------------------------------------------------- */
|
||||
static const char *const g_depends[] = { "irq" };
|
||||
static const char *const g_provides[] = { "pci" };
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(struct dkm_driver_desc),
|
||||
.name = "pci",
|
||||
.version = "0.1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = 2, /* DKM_CLASS_BUS */
|
||||
.stage = 0,
|
||||
.flags = 1, /* DKM_F_REQUIRED */
|
||||
.priority = 0,
|
||||
.depends = g_depends,
|
||||
.depends_count = 1,
|
||||
.provides = g_provides,
|
||||
.provides_count = 1,
|
||||
.min_kernel_abi = 1,
|
||||
.feature_bits = 0,
|
||||
.reserved0 = 0,
|
||||
.reserved1 = 0
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* I/O helpers
|
||||
* --------------------------------------------------------------- */
|
||||
static const struct dkm_log_api *g_log;
|
||||
|
||||
static __inline__ void outl(u16 port, u32 value) {
|
||||
__asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static __inline__ u32 inl(u16 port) {
|
||||
u32 value;
|
||||
__asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* PCI config access
|
||||
* --------------------------------------------------------------- */
|
||||
static u32 pci_cfg_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
||||
u32 addr = (u32)((1u << 31)
|
||||
| ((u32)bus << 16)
|
||||
| ((u32)dev << 11)
|
||||
| ((u32)func << 8)
|
||||
| ((u32)reg & 0xFC));
|
||||
outl(PCI_CFG_ADDR, addr);
|
||||
return inl(PCI_CFG_DATA);
|
||||
}
|
||||
|
||||
static void pci_cfg_write(u8 bus, u8 dev, u8 func, u8 reg, u32 val) {
|
||||
u32 addr = (u32)((1u << 31)
|
||||
| ((u32)bus << 16)
|
||||
| ((u32)dev << 11)
|
||||
| ((u32)func << 8)
|
||||
| ((u32)reg & 0xFC));
|
||||
outl(PCI_CFG_ADDR, addr);
|
||||
outl(PCI_CFG_DATA, val);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Log helpers
|
||||
* --------------------------------------------------------------- */
|
||||
static void log_hex(const char *prefix, u64 val) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
char buf[19];
|
||||
u32 pos = 0;
|
||||
buf[pos++] = '0'; buf[pos++] = 'x';
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
buf[pos++] = hex[(val >> (i * 4)) & 0xf];
|
||||
}
|
||||
buf[pos] = 0;
|
||||
g_log->info(prefix);
|
||||
g_log->info(buf);
|
||||
}
|
||||
|
||||
static const char *pci_class_name(u8 base, u8 sub) {
|
||||
switch (base) {
|
||||
case 0x00: return "Legacy";
|
||||
case 0x01: return "Storage";
|
||||
case 0x02: return "Network";
|
||||
case 0x03: return "Display";
|
||||
case 0x04: return "Multimedia";
|
||||
case 0x05: return "Memory";
|
||||
case 0x06: return "Bridge";
|
||||
case 0x07: return "SimpleComm";
|
||||
case 0x08: return "BasePeriph";
|
||||
case 0x09: return "Input";
|
||||
case 0x0A: return "Docking";
|
||||
case 0x0B: return "CPU";
|
||||
case 0x0C: return "SerialBus";
|
||||
case 0x0D: return "Wireless";
|
||||
case 0x0E: return "IntelligentIO";
|
||||
case 0x0F: return "Satellite";
|
||||
case 0x10: return "Crypto";
|
||||
case 0x11: return "SignalProc";
|
||||
case 0x12: return "Accelerator";
|
||||
default: return "Unknown";
|
||||
}
|
||||
(void)sub;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Enumeration
|
||||
* --------------------------------------------------------------- */
|
||||
static void pci_scan_bus(u8 bus) {
|
||||
for (u8 dev = 0; dev < 32; dev++) {
|
||||
u32 vid_dev = pci_cfg_read(bus, dev, 0, PCI_VENDOR_ID);
|
||||
u16 vendor = (u16)(vid_dev & 0xffff);
|
||||
if (vendor == 0xffff) continue;
|
||||
|
||||
u8 header_type = (u8)(pci_cfg_read(bus, dev, 0, PCI_HEADER_TYPE) >> 16);
|
||||
int multi_func = (header_type & 0x80) != 0;
|
||||
|
||||
u8 func_count = multi_func ? 8 : 1;
|
||||
for (u8 func = 0; func < func_count; func++) {
|
||||
u32 vid_dev2 = pci_cfg_read(bus, dev, func, PCI_VENDOR_ID);
|
||||
u16 vendor2 = (u16)(vid_dev2 & 0xffff);
|
||||
if (vendor2 == 0xffff) continue;
|
||||
|
||||
u16 device = (u16)(vid_dev2 >> 16);
|
||||
u32 cc = pci_cfg_read(bus, dev, func, PCI_CLASS);
|
||||
u8 class_code = (u8)(cc >> 24);
|
||||
u8 subclass = (u8)((cc >> 16) & 0xff);
|
||||
|
||||
g_log->info("[pci]");
|
||||
log_hex("[pci] bus=", bus);
|
||||
log_hex("[pci] dev=", dev);
|
||||
log_hex("[pci] func=", func);
|
||||
log_hex("[pci] vendor=", vendor2);
|
||||
log_hex("[pci] device=", device);
|
||||
g_log->info("[pci] class");
|
||||
g_log->info(pci_class_name(class_code, subclass));
|
||||
log_hex("[pci] class_hex=", class_code);
|
||||
log_hex("[pci] subclass=", subclass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver entry
|
||||
* --------------------------------------------------------------- */
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
|
||||
if (!api || !api->log) return -1;
|
||||
g_log = api->log;
|
||||
|
||||
g_log->info("[pci] scanning PCI bus");
|
||||
pci_scan_bus(0);
|
||||
g_log->info("[pci] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/* DKM PS/2 Keyboard Driver — IRQ1 scan code → ASCII
|
||||
* Stage 3, optional, depends on "irq", provides "keyboard".
|
||||
* Registers IRQ1 handler, translates scan codes via lookup table.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* DKM ABI types */
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version; u32 size; u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem,*utsm,*irq_api,*pci_api,*dma,*vfs_api,*net,*timer,*drr;
|
||||
const void *rsdp_address,*fb_address;
|
||||
u64 fb_width,fb_height,fb_pitch; u16 fb_bpp;
|
||||
const void *boot_modules_response;
|
||||
int (*irq_register)(u8 irq, void *handler);
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
struct dkm_driver_desc {
|
||||
u32 magic; u16 abi_version; u16 desc_size;
|
||||
const char *name,*version,*vendor;
|
||||
u32 driver_class,stage,flags,priority;
|
||||
const char *const *depends; u32 depends_count;
|
||||
const char *const *provides; u32 provides_count;
|
||||
u64 min_kernel_abi,feature_bits,reserved0,reserved1;
|
||||
};
|
||||
|
||||
/* Driver descriptor */
|
||||
static const char *const g_depends[] = {"irq"};
|
||||
static const char *const g_provides[] = {"keyboard"};
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic=DKM_DRIVER_MAGIC, .abi_version=DKM_ABI_VERSION,
|
||||
.desc_size=sizeof(struct dkm_driver_desc),
|
||||
.name="ps2kbd", .version="0.1.0", .vendor="Deshab",
|
||||
.driver_class=10, .stage=3, .flags=0, .priority=0,
|
||||
.depends=g_depends, .depends_count=1,
|
||||
.provides=g_provides, .provides_count=1,
|
||||
.min_kernel_abi=1,
|
||||
};
|
||||
|
||||
/* Scan code set 1 → ASCII (lowercase) */
|
||||
static const char sc_ascii[] = {
|
||||
0,0,'1','2','3','4','5','6','7','8','9','0','-','=',0,
|
||||
0,'q','w','e','r','t','y','u','i','o','p','[',']',0,
|
||||
0,'a','s','d','f','g','h','j','k','l',';','\'','`',0,
|
||||
'\\','z','x','c','v','b','n','m',',','.','/',0,
|
||||
'*',0,' ',0
|
||||
};
|
||||
|
||||
static const struct dkm_log_api *g_log;
|
||||
static volatile int g_has_key;
|
||||
static volatile u8 g_last_sc;
|
||||
|
||||
/* inline I/O */
|
||||
static u8 inb(u16 p) { u8 v; __asm__("inb %1,%0":"=a"(v):"Nd"(p)); return v; }
|
||||
static void outb(u16 p, u8 v) { __asm__("outb %0,%1"::"a"(v),"Nd"(p)); }
|
||||
|
||||
static void log_hex(const char *prefix, u64 v) {
|
||||
static const char h[]="0123456789abcdef";
|
||||
char b[19]; u32 i=0; b[i++]='0'; b[i++]='x';
|
||||
for (int j=15;j>=0;j--) b[i++]=h[(v>>(j*4))&0xf];
|
||||
b[i]=0; g_log->info(prefix); g_log->info(b);
|
||||
}
|
||||
|
||||
static int kbd_handler(u8 irq) {
|
||||
(void)irq;
|
||||
g_last_sc = inb(0x60);
|
||||
g_has_key = 1;
|
||||
return 0; /* let kernel send EOI */
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
if (!api||!api->log) return -1;
|
||||
g_log=api->log;
|
||||
|
||||
if (!api->irq_register) {
|
||||
g_log->warn("[kbd] irq_register not available");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (api->irq_register(1, (void*)(uintptr_t)kbd_handler) != 0) {
|
||||
g_log->error("[kbd] failed to register IRQ1");
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_log->info("[kbd] IRQ1 registered, waiting for keypress");
|
||||
g_log->info("[kbd] (boot will continue, key echo in ISR)");
|
||||
|
||||
/* spin briefly to show a key echo demo */
|
||||
int ticks = 0;
|
||||
while (ticks < 200000) {
|
||||
if (g_has_key) {
|
||||
u8 sc = g_last_sc;
|
||||
g_has_key = 0;
|
||||
if (sc < 0x80) {
|
||||
/* make code */
|
||||
log_hex("[kbd] sc=", sc);
|
||||
if (sc < sizeof(sc_ascii) && sc_ascii[sc]) {
|
||||
char buf[2] = {sc_ascii[sc], 0};
|
||||
g_log->info("[kbd] key");
|
||||
g_log->info(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (volatile int z=0;z<100;z++) {}
|
||||
ticks++;
|
||||
}
|
||||
|
||||
g_log->info("[kbd] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle; return 0;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$Root = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $ScriptDir))
|
||||
$Source = Join-Path $Root 'CODE\DKM\stub_driver\stub_driver.c'
|
||||
$Manifest = Join-Path $Root 'SYSTEM\driver\manifest.json'
|
||||
$SystemDriver = Join-Path $Root 'SYSTEM\driver'
|
||||
$BuildDir = Join-Path $Root '.build_tmp\drivers'
|
||||
|
||||
function Find-Tool($Name, $ExtraNames = @()) {
|
||||
$names = @($Name) + $ExtraNames
|
||||
foreach ($n in $names) {
|
||||
$cmd = Get-Command $n -ErrorAction SilentlyContinue
|
||||
if ($cmd) { return $cmd.Source }
|
||||
}
|
||||
$dirs = @(
|
||||
'C:\Program Files\LLVM\bin',
|
||||
'C:\Program Files (x86)\LLVM\bin',
|
||||
'C:\LLVM\bin',
|
||||
'D:\LLVM\bin',
|
||||
'D:\Program Files\LLVM\bin',
|
||||
'D:\Code\LLVM\bin',
|
||||
'C:\msys64\clang64\bin',
|
||||
'C:\msys64\mingw64\bin',
|
||||
'C:\mingw64\bin'
|
||||
)
|
||||
foreach ($dir in $dirs) {
|
||||
foreach ($n in $names) {
|
||||
$candidate = Join-Path $dir $n
|
||||
if (Test-Path $candidate) { return $candidate }
|
||||
if (-not $n.EndsWith('.exe')) {
|
||||
$candidateExe = Join-Path $dir "$n.exe"
|
||||
if (Test-Path $candidateExe) { return $candidateExe }
|
||||
}
|
||||
}
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-DriverClassValue([string]$Class) {
|
||||
switch ($Class) {
|
||||
'platform' { return 1 }
|
||||
'bus' { return 2 }
|
||||
'interrupt' { return 3 }
|
||||
'timer' { return 4 }
|
||||
'console' { return 5 }
|
||||
'storage' { return 6 }
|
||||
'fs' { return 7 }
|
||||
'fs-core' { return 8 }
|
||||
'net' { return 9 }
|
||||
'input' { return 10 }
|
||||
'gpu' { return 11 }
|
||||
default { return 0 }
|
||||
}
|
||||
}
|
||||
|
||||
$clang = Find-Tool 'clang'
|
||||
$lld = Find-Tool 'ld.lld'
|
||||
if (-not $clang) {
|
||||
throw 'clang was not found. Install LLVM/Clang.'
|
||||
}
|
||||
if (-not $lld) {
|
||||
throw 'ld.lld was not found. Install LLVM lld.'
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $BuildDir | Out-Null
|
||||
$manifestData = Get-Content -Raw -LiteralPath $Manifest | ConvertFrom-Json
|
||||
$count = 0
|
||||
|
||||
foreach ($stage in $manifestData.stages) {
|
||||
foreach ($driver in $stage.drivers) {
|
||||
$relative = [string]$driver.path
|
||||
if (-not $relative.StartsWith('driver/')) {
|
||||
throw "Unexpected driver path: $relative"
|
||||
}
|
||||
|
||||
$destRelative = $relative.Substring('driver/'.Length).Replace('/', '\')
|
||||
$dest = Join-Path $SystemDriver $destRelative
|
||||
$destDir = Split-Path -Parent $dest
|
||||
New-Item -ItemType Directory -Force -Path $destDir | Out-Null
|
||||
|
||||
$name = [string]$driver.name
|
||||
$classValue = Get-DriverClassValue ([string]$driver.class)
|
||||
$stageValue = [int]$stage.id
|
||||
$flags = 0
|
||||
if ([bool]$driver.required) { $flags = $flags -bor 1 }
|
||||
if ($stageValue -le 1) { $flags = $flags -bor 2 }
|
||||
|
||||
$obj = Join-Path $BuildDir "$name.o"
|
||||
$drv = Join-Path $BuildDir "$name.drv"
|
||||
$driverNameDefine = 'DRIVER_NAME=' + $name
|
||||
|
||||
& $clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-stack-protector -fno-pic -fno-pie -mno-red-zone -Wall -Wextra -O2 -c $Source -o $obj "-D$driverNameDefine" "-DDRIVER_CLASS=$classValue" "-DDRIVER_STAGE=$stageValue" "-DDRIVER_FLAGS=$flags"
|
||||
if ($LASTEXITCODE -ne 0) { throw "failed to compile stub driver: $name" }
|
||||
|
||||
& $lld -r -o $drv $obj
|
||||
if ($LASTEXITCODE -ne 0) { throw "failed to link stub driver: $name" }
|
||||
|
||||
Copy-Item -LiteralPath $drv -Destination $dest -Force
|
||||
Write-Host "[drivers] installed $relative"
|
||||
$count++
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "[drivers] installed $count DKM stub drivers"
|
||||
@@ -0,0 +1,93 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1
|
||||
|
||||
#ifndef DRIVER_NAME
|
||||
#define DRIVER_NAME stub
|
||||
#endif
|
||||
|
||||
#define DKM_STRINGIFY_INNER(x) #x
|
||||
#define DKM_STRINGIFY(x) DKM_STRINGIFY_INNER(x)
|
||||
#define DRIVER_NAME_STRING DKM_STRINGIFY(DRIVER_NAME)
|
||||
|
||||
#ifndef DRIVER_CLASS
|
||||
#define DRIVER_CLASS 0
|
||||
#endif
|
||||
|
||||
#ifndef DRIVER_STAGE
|
||||
#define DRIVER_STAGE 0
|
||||
#endif
|
||||
|
||||
#ifndef DRIVER_FLAGS
|
||||
#define DRIVER_FLAGS 0
|
||||
#endif
|
||||
|
||||
#ifndef DRIVER_PRIORITY
|
||||
#define DRIVER_PRIORITY 0
|
||||
#endif
|
||||
|
||||
struct dkm_driver_desc {
|
||||
uint32_t magic;
|
||||
uint16_t abi_version;
|
||||
uint16_t desc_size;
|
||||
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
|
||||
uint32_t driver_class;
|
||||
uint32_t stage;
|
||||
uint32_t flags;
|
||||
uint32_t priority;
|
||||
|
||||
const char *const *depends;
|
||||
uint32_t depends_count;
|
||||
|
||||
const char *const *provides;
|
||||
uint32_t provides_count;
|
||||
|
||||
uint64_t min_kernel_abi;
|
||||
uint64_t feature_bits;
|
||||
|
||||
uint64_t reserved0;
|
||||
uint64_t reserved1;
|
||||
};
|
||||
|
||||
struct dkm_kernel_api;
|
||||
struct dkm_driver_handle;
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(struct dkm_driver_desc),
|
||||
.name = DRIVER_NAME_STRING,
|
||||
.version = "0.1.0-stub",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = DRIVER_CLASS,
|
||||
.stage = DRIVER_STAGE,
|
||||
.flags = DRIVER_FLAGS,
|
||||
.priority = DRIVER_PRIORITY,
|
||||
.depends = 0,
|
||||
.depends_count = 0,
|
||||
.provides = 0,
|
||||
.provides_count = 0,
|
||||
.min_kernel_abi = 1,
|
||||
.feature_bits = 0,
|
||||
.reserved0 = 0,
|
||||
.reserved1 = 0
|
||||
};
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle) {
|
||||
(void)api;
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/* DKM Timer Driver - PIT (Programmable Interval Timer)
|
||||
* Stage 0 platform driver, provides "timer".
|
||||
* No IDT/IRQ required - uses PIT counter polling for busy-wait delay.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* DKM ABI types (standalone, no kernel headers)
|
||||
* --------------------------------------------------------------- */
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version;
|
||||
u32 size;
|
||||
u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
struct dkm_driver_desc {
|
||||
u32 magic;
|
||||
u16 abi_version;
|
||||
u16 desc_size;
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
u32 driver_class;
|
||||
u32 stage;
|
||||
u32 flags;
|
||||
u32 priority;
|
||||
const char *const *depends;
|
||||
u32 depends_count;
|
||||
const char *const *provides;
|
||||
u32 provides_count;
|
||||
u64 min_kernel_abi;
|
||||
u64 feature_bits;
|
||||
u64 reserved0;
|
||||
u64 reserved1;
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* PIT I/O ports
|
||||
* --------------------------------------------------------------- */
|
||||
#define PIT_CH0_DATA 0x40
|
||||
#define PIT_CH1_DATA 0x41
|
||||
#define PIT_CH2_DATA 0x42
|
||||
#define PIT_CMD 0x43
|
||||
|
||||
/* PIT frequency: 1.193182 MHz */
|
||||
#define PIT_BASE_FREQ 1193182ULL
|
||||
|
||||
static const char *const g_provides[] = { "timer" };
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(struct dkm_driver_desc),
|
||||
.name = "timer",
|
||||
.version = "0.1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = 4, /* DKM_CLASS_TIMER */
|
||||
.stage = 0,
|
||||
.flags = 1, /* DKM_F_REQUIRED */
|
||||
.priority = 0,
|
||||
.depends = NULL,
|
||||
.depends_count = 0,
|
||||
.provides = g_provides,
|
||||
.provides_count = 1,
|
||||
.min_kernel_abi = 1,
|
||||
.feature_bits = 0,
|
||||
.reserved0 = 0,
|
||||
.reserved1 = 0
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Inline assembly for port I/O (works in Ring0 SAS-R0)
|
||||
* --------------------------------------------------------------- */
|
||||
static __inline__ void outb(u16 port, u8 value) {
|
||||
__asm__ volatile ("outb %0, %1" :: "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static __inline__ u8 inb(u16 port) {
|
||||
u8 value;
|
||||
__asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* PIT operations
|
||||
* --------------------------------------------------------------- */
|
||||
static const struct dkm_log_api *g_log;
|
||||
|
||||
static void pit_set_channel(u8 channel, u8 mode, u16 count) {
|
||||
u8 cmd = (u8)((channel << 6) | (3 << 4) | (mode << 1));
|
||||
outb(PIT_CMD, cmd);
|
||||
outb((u16)(PIT_CH0_DATA + channel), (u8)(count & 0xff));
|
||||
outb((u16)(PIT_CH0_DATA + channel), (u8)((count >> 8) & 0xff));
|
||||
}
|
||||
|
||||
/* Read back current counter value via latch command */
|
||||
static u16 pit_read_counter(void) {
|
||||
u8 cmd = 0xD0; /* read-back: channel 0, latch count, no status */
|
||||
outb(PIT_CMD, cmd);
|
||||
u8 lo = inb(PIT_CH0_DATA);
|
||||
u8 hi = inb(PIT_CH0_DATA);
|
||||
return (u16)(((u16)hi << 8) | lo);
|
||||
}
|
||||
|
||||
/* Busy-wait delay using PIT channel 0 counter.
|
||||
* count: PIT divisor (1193182 = ~1 second)
|
||||
* The counter counts DOWN from 'count' to 0.
|
||||
* We set it to 'count', then poll until it reaches near 0.
|
||||
*/
|
||||
static void pit_delay(u16 count) {
|
||||
pit_set_channel(0, 0, count); /* mode 0: interrupt on terminal count */
|
||||
u16 prev = pit_read_counter();
|
||||
u64 loops = 0;
|
||||
while (1) {
|
||||
u16 cur = pit_read_counter();
|
||||
if (cur > prev && loops > 10) {
|
||||
break; /* counter wrapped → terminal count reached */
|
||||
}
|
||||
prev = cur;
|
||||
loops++;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver entry points
|
||||
* --------------------------------------------------------------- */
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
|
||||
if (!api || !api->log) {
|
||||
return -1;
|
||||
}
|
||||
g_log = api->log;
|
||||
|
||||
g_log->info("[timer] PIT driver initializing");
|
||||
|
||||
/* Quick calibration: set PIT to max count, measure how many
|
||||
* iterations of read_counter() happen before terminal count.
|
||||
*/
|
||||
pit_set_channel(0, 0, 0xFFFF); /* 0xFFFF = max, ~55ms at 1.193 MHz */
|
||||
u16 prev = pit_read_counter();
|
||||
u64 cal_loops = 0;
|
||||
while (1) {
|
||||
u16 cur = pit_read_counter();
|
||||
cal_loops++;
|
||||
if (cur > prev && cal_loops > 5) break;
|
||||
prev = cur;
|
||||
}
|
||||
|
||||
/* Expected: PIT_BASE_FREQ / 0xFFFF ≈ 18.2 Hz, period ≈ 55 ms */
|
||||
g_log->info("[timer] PIT calibration complete");
|
||||
g_log->info("[timer] PIT channel 0 operational");
|
||||
|
||||
/* Do a quick ~100ms delay demo */
|
||||
g_log->info("[timer] running 100ms busy-wait demo");
|
||||
pit_delay((u16)(PIT_BASE_FREQ / 10)); /* ~0.1 sec = 119318 counts */
|
||||
g_log->info("[timer] 100ms delay done");
|
||||
|
||||
g_log->info("[timer] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/* DKM VFS Driver — Virtual File System layer
|
||||
* Stage 2, required, depends on "bootfs", provides "vfs".
|
||||
* Mounts bootfs modules as files, implements basic open/read.
|
||||
* Demo: reads and displays first 64 bytes of manifest.json.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* DKM ABI types
|
||||
* --------------------------------------------------------------- */
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version;
|
||||
u32 size;
|
||||
u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem;
|
||||
const void *utsm;
|
||||
const void *irq;
|
||||
const void *pci_api;
|
||||
const void *dma;
|
||||
const void *vfs_api;
|
||||
const void *net;
|
||||
const void *timer;
|
||||
const void *drr;
|
||||
const void *rsdp_address;
|
||||
const void *fb_address;
|
||||
u64 fb_width;
|
||||
u64 fb_height;
|
||||
u64 fb_pitch;
|
||||
u16 fb_bpp;
|
||||
const void *boot_modules_response;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
struct dkm_driver_desc {
|
||||
u32 magic;
|
||||
u16 abi_version;
|
||||
u16 desc_size;
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
u32 driver_class;
|
||||
u32 stage;
|
||||
u32 flags;
|
||||
u32 priority;
|
||||
const char *const *depends;
|
||||
u32 depends_count;
|
||||
const char *const *provides;
|
||||
u32 provides_count;
|
||||
u64 min_kernel_abi;
|
||||
u64 feature_bits;
|
||||
u64 reserved0;
|
||||
u64 reserved1;
|
||||
};
|
||||
|
||||
struct limine_file {
|
||||
u64 revision;
|
||||
void *address;
|
||||
u64 size;
|
||||
char *path;
|
||||
char *cmdline;
|
||||
u8 _pad[56];
|
||||
};
|
||||
|
||||
struct limine_module_response {
|
||||
u64 revision;
|
||||
u64 module_count;
|
||||
struct limine_file **modules;
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver descriptor
|
||||
* --------------------------------------------------------------- */
|
||||
static const char *const g_depends[] = { "bootfs" };
|
||||
static const char *const g_provides[] = { "vfs" };
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(struct dkm_driver_desc),
|
||||
.name = "vfs",
|
||||
.version = "0.1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = 8, /* DKM_CLASS_FS_CORE */
|
||||
.stage = 2,
|
||||
.flags = 1,
|
||||
.priority = 0,
|
||||
.depends = g_depends,
|
||||
.depends_count = 1,
|
||||
.provides = g_provides,
|
||||
.provides_count = 1,
|
||||
.min_kernel_abi = 1,
|
||||
.feature_bits = 0,
|
||||
.reserved0 = 0,
|
||||
.reserved1 = 0
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Helpers
|
||||
* --------------------------------------------------------------- */
|
||||
static const struct dkm_log_api *g_log;
|
||||
|
||||
static void log_hex(const char *prefix, u64 val) {
|
||||
static const char h[] = "0123456789abcdef";
|
||||
char buf[19];
|
||||
u32 p = 0;
|
||||
buf[p++] = '0'; buf[p++] = 'x';
|
||||
for (int i = 15; i >= 0; i--) buf[p++] = h[(val >> (i*4)) & 0xf];
|
||||
buf[p] = 0;
|
||||
g_log->info(prefix); g_log->info(buf);
|
||||
}
|
||||
|
||||
static u64 min64(u64 a, u64 b) { return a < b ? a : b; }
|
||||
|
||||
static int path_match(const char *bp, const char *mp) {
|
||||
/* strip leading '/' from bootloader path */
|
||||
while (*bp == '/') bp++;
|
||||
u64 i = 0;
|
||||
while (bp[i] && mp[i] && bp[i] == mp[i]) i++;
|
||||
return bp[i] == 0 && mp[i] == 0;
|
||||
}
|
||||
|
||||
static int str_len(const char *s) {
|
||||
int l = 0; while (s[l]) l++; return l;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Driver entry
|
||||
* --------------------------------------------------------------- */
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
if (!api || !api->log) return -1;
|
||||
g_log = api->log;
|
||||
|
||||
const struct limine_module_response *rsp =
|
||||
(const struct limine_module_response *)api->boot_modules_response;
|
||||
|
||||
if (!rsp) { g_log->error("[vfs] no modules"); return -1; }
|
||||
|
||||
g_log->info("[vfs] mounting bootfs as root");
|
||||
log_hex("[vfs] files=", rsp->module_count);
|
||||
|
||||
/* list all files */
|
||||
for (u64 i = 0; i < rsp->module_count; i++) {
|
||||
struct limine_file *f = rsp->modules[i];
|
||||
if (!f || !f->path) continue;
|
||||
g_log->info("[vfs] /");
|
||||
g_log->info(f->path);
|
||||
}
|
||||
|
||||
/* ---- demo: open & read /driver/manifest.json ---- */
|
||||
g_log->info("[vfs] demo: open //driver/manifest.json");
|
||||
|
||||
struct limine_file *target = NULL;
|
||||
for (u64 i = 0; i < rsp->module_count; i++) {
|
||||
struct limine_file *f = rsp->modules[i];
|
||||
if (f && f->path && path_match(f->path, "driver/manifest.json")) {
|
||||
target = f; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!target) {
|
||||
g_log->warn("[vfs] manifest.json not found");
|
||||
} else {
|
||||
log_hex("[vfs] size=", target->size);
|
||||
g_log->info("[vfs] content (first 64 bytes):");
|
||||
|
||||
/* dump first 64 bytes as hex + ascii */
|
||||
u64 n = min64(64, target->size);
|
||||
const u8 *data = (const u8 *)target->address;
|
||||
char line[80];
|
||||
for (u64 off = 0; off < n; off += 16) {
|
||||
u32 pos = 0;
|
||||
for (u32 j = 0; j < 16 && off + j < n; j++) {
|
||||
u8 b = data[off + j];
|
||||
line[pos++] = "0123456789abcdef"[b >> 4];
|
||||
line[pos++] = "0123456789abcdef"[b & 0xf];
|
||||
line[pos++] = ' ';
|
||||
}
|
||||
/* ascii */
|
||||
line[pos++] = ' ';
|
||||
for (u32 j = 0; j < 16 && off + j < n; j++) {
|
||||
u8 c = data[off + j];
|
||||
line[pos++] = (c >= 32 && c < 127) ? (char)c : '.';
|
||||
}
|
||||
line[pos] = 0;
|
||||
g_log->info(line);
|
||||
}
|
||||
}
|
||||
|
||||
g_log->info("[vfs] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
/* DKM virtio-net Driver — PCI virtio network discovery
|
||||
* Stage 3 optional network driver, depends on "pci" and "irq", provides "netdev".
|
||||
* This first version discovers virtio-net devices and logs PCI capabilities;
|
||||
* it does not negotiate features, create virtqueues, or transmit packets.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
};
|
||||
|
||||
struct dkm_net_device_desc {
|
||||
const char *name;
|
||||
u8 mac[6];
|
||||
u32 flags;
|
||||
void *ctx;
|
||||
int (*tx)(void *ctx, const void *packet, u32 length);
|
||||
int (*rx_poll)(void *ctx, void *buffer, u32 capacity, u32 *out_length);
|
||||
};
|
||||
|
||||
struct dkm_net_api {
|
||||
int (*register_device)(const struct dkm_net_device_desc *desc);
|
||||
u32 (*device_count)(void);
|
||||
int (*device_info)(u32 index, void *out);
|
||||
};
|
||||
|
||||
#define DKM_NET_F_LINK_UP (1u << 0)
|
||||
|
||||
struct dkm_kernel_api {
|
||||
u32 version;
|
||||
u32 size;
|
||||
u64 feature_bits;
|
||||
const struct dkm_log_api *log;
|
||||
const void *mem;
|
||||
const void *utsm;
|
||||
const void *irq;
|
||||
const void *pci;
|
||||
const void *dma;
|
||||
const void *vfs;
|
||||
const struct dkm_net_api *net;
|
||||
const void *timer;
|
||||
const void *drr;
|
||||
const void *rsdp_address;
|
||||
const void *fb_address;
|
||||
u64 fb_width;
|
||||
u64 fb_height;
|
||||
u64 fb_pitch;
|
||||
u16 fb_bpp;
|
||||
const void *boot_modules_response;
|
||||
int (*irq_register)(u8 irq, void *handler);
|
||||
u64 hhdm_offset;
|
||||
};
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
struct dkm_driver_desc {
|
||||
u32 magic;
|
||||
u16 abi_version;
|
||||
u16 desc_size;
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
u32 driver_class;
|
||||
u32 stage;
|
||||
u32 flags;
|
||||
u32 priority;
|
||||
const char *const *depends;
|
||||
u32 depends_count;
|
||||
const char *const *provides;
|
||||
u32 provides_count;
|
||||
u64 min_kernel_abi;
|
||||
u64 feature_bits;
|
||||
u64 reserved0;
|
||||
u64 reserved1;
|
||||
};
|
||||
|
||||
static const char *const g_depends[] = { "pci", "irq" };
|
||||
static const char *const g_provides[] = { "netdev" };
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
const struct dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(struct dkm_driver_desc),
|
||||
.name = "virtio_net",
|
||||
.version = "0.1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = 9, /* DKM_CLASS_NET */
|
||||
.stage = 3,
|
||||
.flags = 0,
|
||||
.priority = 0,
|
||||
.depends = g_depends,
|
||||
.depends_count = 2,
|
||||
.provides = g_provides,
|
||||
.provides_count = 1,
|
||||
.min_kernel_abi = 1,
|
||||
.feature_bits = 0,
|
||||
.reserved0 = 0,
|
||||
.reserved1 = 0
|
||||
};
|
||||
|
||||
#define PCI_ADDR 0xCF8
|
||||
#define PCI_DATA 0xCFC
|
||||
#define PCI_VENDOR_ID 0x00
|
||||
#define PCI_COMMAND 0x04
|
||||
#define PCI_CLASS_REG 0x08
|
||||
#define PCI_HEADER 0x0E
|
||||
#define PCI_CAP_PTR 0x34
|
||||
#define PCI_IRQ_LINE 0x3C
|
||||
#define PCI_BAR0 0x10
|
||||
#define PCI_BAR1 0x14
|
||||
#define PCI_BAR2 0x18
|
||||
#define PCI_BAR3 0x1C
|
||||
#define PCI_BAR4 0x20
|
||||
#define PCI_BAR5 0x24
|
||||
|
||||
#define PCI_CMD_IO (1u << 0)
|
||||
#define PCI_CMD_MEM (1u << 1)
|
||||
#define PCI_CMD_BUSM (1u << 2)
|
||||
|
||||
#define PCI_VENDOR_VIRTIO 0x1AF4u
|
||||
#define VIRTIO_TRANS_DEVICE_MIN 0x1000u
|
||||
#define VIRTIO_TRANS_DEVICE_MAX 0x103Fu
|
||||
#define VIRTIO_MODERN_DEVICE_MIN 0x1040u
|
||||
#define VIRTIO_MODERN_DEVICE_MAX 0x107Fu
|
||||
#define VIRTIO_NET_DEVICE_ID 1u
|
||||
|
||||
#define VIRTIO_PCI_CAP_COMMON_CFG 1u
|
||||
#define VIRTIO_PCI_CAP_NOTIFY_CFG 2u
|
||||
#define VIRTIO_PCI_CAP_ISR_CFG 3u
|
||||
#define VIRTIO_PCI_CAP_DEVICE_CFG 4u
|
||||
#define VIRTIO_PCI_CAP_PCI_CFG 5u
|
||||
|
||||
static const struct dkm_log_api *g_log;
|
||||
|
||||
static __inline__ void outl(u16 port, u32 value) {
|
||||
__asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static __inline__ u32 inl(u16 port) {
|
||||
u32 value;
|
||||
__asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
||||
u32 addr = (1u << 31)
|
||||
| ((u32)bus << 16)
|
||||
| ((u32)dev << 11)
|
||||
| ((u32)func << 8)
|
||||
| ((u32)reg & 0xFC);
|
||||
outl(PCI_ADDR, addr);
|
||||
return inl(PCI_DATA);
|
||||
}
|
||||
|
||||
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
|
||||
u32 addr = (1u << 31)
|
||||
| ((u32)bus << 16)
|
||||
| ((u32)dev << 11)
|
||||
| ((u32)func << 8)
|
||||
| ((u32)reg & 0xFC);
|
||||
outl(PCI_ADDR, addr);
|
||||
outl(PCI_DATA, value);
|
||||
}
|
||||
|
||||
static u8 pci_read8(u8 bus, u8 dev, u8 func, u8 reg) {
|
||||
u32 v = pci_read(bus, dev, func, reg);
|
||||
return (u8)((v >> ((reg & 3) * 8)) & 0xff);
|
||||
}
|
||||
|
||||
static void log_hex(const char *prefix, u64 value) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
char buf[19];
|
||||
u32 pos = 0;
|
||||
buf[pos++] = '0';
|
||||
buf[pos++] = 'x';
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
buf[pos++] = hex[(value >> (i * 4)) & 0xf];
|
||||
}
|
||||
buf[pos] = 0;
|
||||
g_log->info(prefix);
|
||||
g_log->info(buf);
|
||||
}
|
||||
|
||||
static u16 virtio_subsystem_id(u16 device_id) {
|
||||
if (device_id >= VIRTIO_TRANS_DEVICE_MIN && device_id <= VIRTIO_TRANS_DEVICE_MAX) {
|
||||
return (u16)(device_id - VIRTIO_TRANS_DEVICE_MIN);
|
||||
}
|
||||
if (device_id >= VIRTIO_MODERN_DEVICE_MIN && device_id <= VIRTIO_MODERN_DEVICE_MAX) {
|
||||
return (u16)(device_id - VIRTIO_MODERN_DEVICE_MIN);
|
||||
}
|
||||
return 0xffff;
|
||||
}
|
||||
|
||||
static int virtio_net_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
||||
for (u8 bus = 0; bus < 16; bus++) {
|
||||
for (u8 dev = 0; dev < 32; dev++) {
|
||||
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
|
||||
if ((vd & 0xffff) == 0xffff) continue;
|
||||
|
||||
u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16);
|
||||
u8 func_count = (header & 0x80) ? 8 : 1;
|
||||
for (u8 func = 0; func < func_count; func++) {
|
||||
u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID);
|
||||
u16 vendor = (u16)(vd2 & 0xffff);
|
||||
u16 device = (u16)(vd2 >> 16);
|
||||
if (vendor != PCI_VENDOR_VIRTIO) continue;
|
||||
|
||||
u16 virtio_id = virtio_subsystem_id(device);
|
||||
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
|
||||
u8 class_code = (u8)(class_reg >> 24);
|
||||
if (virtio_id == VIRTIO_NET_DEVICE_ID && class_code == 0x02) {
|
||||
*out_bus = bus;
|
||||
*out_dev = dev;
|
||||
*out_func = func;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *virtio_cap_name(u8 cfg_type) {
|
||||
if (cfg_type == VIRTIO_PCI_CAP_COMMON_CFG) return "common_cfg";
|
||||
if (cfg_type == VIRTIO_PCI_CAP_NOTIFY_CFG) return "notify_cfg";
|
||||
if (cfg_type == VIRTIO_PCI_CAP_ISR_CFG) return "isr_cfg";
|
||||
if (cfg_type == VIRTIO_PCI_CAP_DEVICE_CFG) return "device_cfg";
|
||||
if (cfg_type == VIRTIO_PCI_CAP_PCI_CFG) return "pci_cfg";
|
||||
return "unknown_cfg";
|
||||
}
|
||||
|
||||
static u32 read_bar_raw(u8 bus, u8 dev, u8 func, u8 bar) {
|
||||
return pci_read(bus, dev, func, (u8)(PCI_BAR0 + bar * 4));
|
||||
}
|
||||
|
||||
static void log_bars(u8 bus, u8 dev, u8 func) {
|
||||
for (u8 bar = 0; bar < 6; bar++) {
|
||||
u32 raw = read_bar_raw(bus, dev, func, bar);
|
||||
if (raw == 0 || raw == 0xffffffffu) continue;
|
||||
log_hex("[virtio_net] BAR index=", bar);
|
||||
log_hex("[virtio_net] raw=", raw);
|
||||
if (raw & 1u) {
|
||||
log_hex("[virtio_net] io base=", raw & 0xfffffffcU);
|
||||
} else {
|
||||
log_hex("[virtio_net] mmio base=", raw & 0xfffffff0U);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void log_virtio_caps(u8 bus, u8 dev, u8 func) {
|
||||
u8 cap = pci_read8(bus, dev, func, PCI_CAP_PTR) & 0xfc;
|
||||
u32 guard = 0;
|
||||
u32 found = 0;
|
||||
|
||||
while (cap && guard++ < 32) {
|
||||
u8 cap_id = pci_read8(bus, dev, func, cap + 0);
|
||||
u8 next = pci_read8(bus, dev, func, cap + 1) & 0xfc;
|
||||
|
||||
if (cap_id == 0x09) {
|
||||
u8 cfg_type = pci_read8(bus, dev, func, cap + 3);
|
||||
u8 bar = pci_read8(bus, dev, func, cap + 4);
|
||||
u32 offset = pci_read(bus, dev, func, cap + 8);
|
||||
u32 length = pci_read(bus, dev, func, cap + 12);
|
||||
found++;
|
||||
g_log->info("[virtio_net] virtio capability");
|
||||
g_log->info(virtio_cap_name(cfg_type));
|
||||
log_hex("[virtio_net] cap_offset=", cap);
|
||||
log_hex("[virtio_net] cfg_type=", cfg_type);
|
||||
log_hex("[virtio_net] bar=", bar);
|
||||
log_hex("[virtio_net] offset=", offset);
|
||||
log_hex("[virtio_net] length=", length);
|
||||
if (cfg_type == VIRTIO_PCI_CAP_NOTIFY_CFG) {
|
||||
u32 mult = pci_read(bus, dev, func, cap + 16);
|
||||
log_hex("[virtio_net] notify_off_multiplier=", mult);
|
||||
}
|
||||
}
|
||||
|
||||
cap = next;
|
||||
}
|
||||
|
||||
log_hex("[virtio_net] virtio cap count=", found);
|
||||
if (!found) {
|
||||
g_log->warn("[virtio_net] no modern virtio capabilities; transitional IO path expected");
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const struct dkm_kernel_api *api,
|
||||
struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
|
||||
if (!api || !api->log) return -1;
|
||||
g_log = api->log;
|
||||
|
||||
g_log->info("[virtio_net] init begin");
|
||||
|
||||
u8 bus = 0;
|
||||
u8 dev = 0;
|
||||
u8 func = 0;
|
||||
if (virtio_net_find(&bus, &dev, &func) != 0) {
|
||||
g_log->warn("[virtio_net] virtio-net device not found");
|
||||
g_log->info("[virtio_net] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_log->info("[virtio_net] virtio-net device found");
|
||||
log_hex("[virtio_net] bus=", bus);
|
||||
log_hex("[virtio_net] dev=", dev);
|
||||
log_hex("[virtio_net] func=", func);
|
||||
|
||||
u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID);
|
||||
u16 vendor = (u16)(vd & 0xffff);
|
||||
u16 device = (u16)(vd >> 16);
|
||||
u16 virtio_id = virtio_subsystem_id(device);
|
||||
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
|
||||
u8 revision = (u8)(class_reg & 0xff);
|
||||
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
|
||||
u8 subclass = (u8)((class_reg >> 16) & 0xff);
|
||||
u8 class_code = (u8)((class_reg >> 24) & 0xff);
|
||||
|
||||
log_hex("[virtio_net] vendor=", vendor);
|
||||
log_hex("[virtio_net] device=", device);
|
||||
log_hex("[virtio_net] virtio_id=", virtio_id);
|
||||
log_hex("[virtio_net] class=", class_code);
|
||||
log_hex("[virtio_net] subclass=", subclass);
|
||||
log_hex("[virtio_net] prog_if=", prog_if);
|
||||
log_hex("[virtio_net] revision=", revision);
|
||||
|
||||
u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
|
||||
command |= PCI_CMD_IO | PCI_CMD_MEM | PCI_CMD_BUSM;
|
||||
pci_write(bus, dev, func, PCI_COMMAND, command);
|
||||
log_hex("[virtio_net] PCI command=", command);
|
||||
|
||||
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
|
||||
log_hex("[virtio_net] PCI IRQ line=", irq_line);
|
||||
|
||||
log_bars(bus, dev, func);
|
||||
log_virtio_caps(bus, dev, func);
|
||||
|
||||
if (api->net && api->net->register_device) {
|
||||
struct dkm_net_device_desc netdev;
|
||||
netdev.name = "virtio-net";
|
||||
for (int i = 0; i < 6; i++) netdev.mac[i] = 0;
|
||||
netdev.flags = DKM_NET_F_LINK_UP;
|
||||
netdev.ctx = 0;
|
||||
netdev.tx = 0;
|
||||
netdev.rx_poll = 0;
|
||||
int net_index = api->net->register_device(&netdev);
|
||||
log_hex("[virtio_net] netdev register rc=", (u64)(i64)net_index);
|
||||
} else {
|
||||
g_log->warn("[virtio_net] net API unavailable; netdev not registered");
|
||||
}
|
||||
|
||||
g_log->info("[virtio_net] discovery only; virtqueues disabled");
|
||||
g_log->info("[virtio_net] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(struct dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
CC := clang
|
||||
LD := ld.lld
|
||||
OBJCOPY := llvm-objcopy
|
||||
|
||||
ROOT := ../..
|
||||
OUT := $(ROOT)/SYSTEM/boot/utsm.elf
|
||||
|
||||
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -Iinclude -O2
|
||||
ASFLAGS := -target x86_64-unknown-none -ffreestanding -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Iinclude
|
||||
LDFLAGS := -nostdlib -static -z max-page-size=0x1000 -T linker.ld
|
||||
|
||||
C_SOURCES := $(wildcard arch/x86_64/*.c kernel/*.c runtime/*.c mm/*.c core/*.c crypto/*.c pckc/*.c utrw/*.c drr/*.c dkm/*.c test/*.c)
|
||||
S_SOURCES := $(wildcard arch/x86_64/*.S)
|
||||
OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
$(OUT): $(OBJECTS) linker.ld
|
||||
@if not exist "$(ROOT)\SYSTEM\boot" mkdir "$(ROOT)\SYSTEM\boot"
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
%.o: %.S
|
||||
$(CC) $(ASFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
||||
@@ -0,0 +1,935 @@
|
||||
// 编译到 SYSTEM/boot/utsm.elf
|
||||
|
||||
# UTSM 内核模块设计
|
||||
|
||||
UTSM 是当前内核架构中的封缄内存模块。
|
||||
|
||||
```text
|
||||
UTSM = UUID-Tuned Sealed Memory
|
||||
中文名:UUID 调谐封缄内存
|
||||
```
|
||||
|
||||
UTSM 的目标是在单地址空间 Ring0 高性能内核中提供一种轻量内存语义:
|
||||
|
||||
```text
|
||||
所有普通内存可以被直接读取,但直接读取到的是密文。
|
||||
真正有效的明文读写必须经过 UTSM 读写器。
|
||||
UUID 作为进程和内存段的调谐因子。
|
||||
DRR 负责 root key、checkpoint、recovery log 和回滚。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. 所属总架构
|
||||
|
||||
```text
|
||||
SAS-R0-PCQ
|
||||
单地址空间 Ring0 任务模型
|
||||
Per-CPU O(1) 位图调度器
|
||||
|
||||
DRR
|
||||
Dedicated Recovery Root
|
||||
看门狗 / checkpoint / recovery log / A-B 回滚
|
||||
|
||||
UTSM
|
||||
UUID-Tuned Sealed Memory
|
||||
UUID 调谐封缄内存
|
||||
|
||||
UTRW
|
||||
UTSM Tagged Reader/Writer
|
||||
UTSM 带标记读写器
|
||||
|
||||
DMP
|
||||
Descriptor Meta Page
|
||||
段描述符元页
|
||||
|
||||
PCKC
|
||||
Per-CPU Key Cache
|
||||
每 CPU 密钥缓存
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. UTSM 核心规则
|
||||
|
||||
```text
|
||||
1. 数据区只存连续密文。
|
||||
2. UUID 不插入数据区。
|
||||
3. UUID 使用 128-bit 二进制格式。
|
||||
4. 每个加密段有独立 DMP。
|
||||
5. 每个任务有 process_uuid 和 capability table。
|
||||
6. 读写明文必须经过 UTRW。
|
||||
7. 直接读取 cipher_base 只得到密文。
|
||||
8. 直接写入 cipher_base 会破坏段一致性,段可被标记为 POISONED。
|
||||
9. DRR 保存 root key、checkpoint、recovery log 和恢复状态。
|
||||
10. 调度器只加载 crypto context,不扫描段,不破坏 O(1) 调度。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 内存布局
|
||||
|
||||
每个 UTSM 段由 DMP 和密文数据区组成。
|
||||
|
||||
```text
|
||||
+-------------------------------+
|
||||
| DMP: Descriptor Meta Page |
|
||||
| 段描述符元页 |
|
||||
+-------------------------------+
|
||||
| Cipher Data Page 0 |
|
||||
+-------------------------------+
|
||||
| Cipher Data Page 1 |
|
||||
+-------------------------------+
|
||||
| Cipher Data Page 2 |
|
||||
+-------------------------------+
|
||||
| ... |
|
||||
+-------------------------------+
|
||||
```
|
||||
|
||||
全局区域:
|
||||
|
||||
```text
|
||||
+--------------------------------------------------+
|
||||
| Normal Encrypted Segment Region |
|
||||
| 普通加密段数据区 |
|
||||
+--------------------------------------------------+
|
||||
| DMP Pool |
|
||||
| 段描述符元页池 |
|
||||
+--------------------------------------------------+
|
||||
| Segment Table |
|
||||
| 全局段表 |
|
||||
+--------------------------------------------------+
|
||||
| PCKC Area |
|
||||
| Per-CPU Key Cache 区 |
|
||||
+--------------------------------------------------+
|
||||
| DRR Reserved Region |
|
||||
| root key / checkpoint / recovery log |
|
||||
+--------------------------------------------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 基础类型
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint64_t hi;
|
||||
uint64_t lo;
|
||||
} uuid128_t;
|
||||
```
|
||||
|
||||
UUID 不转十进制字符串,原因:
|
||||
|
||||
```text
|
||||
16 字节固定长度
|
||||
比较快
|
||||
cache 友好
|
||||
适合 KDF
|
||||
适合 SIMD
|
||||
避免十进制解析和孔洞布局
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 进程加密上下文
|
||||
|
||||
```c
|
||||
struct utsm_process_context {
|
||||
uuid128_t process_uuid;
|
||||
|
||||
uint64_t crypto_epoch;
|
||||
uint64_t recovery_generation;
|
||||
|
||||
uint32_t capability_table_slot;
|
||||
uint32_t capability_count;
|
||||
|
||||
uint32_t flags;
|
||||
};
|
||||
```
|
||||
|
||||
字段含义:
|
||||
|
||||
```text
|
||||
process_uuid
|
||||
创建任务时生成的进程 UUID。
|
||||
|
||||
crypto_epoch
|
||||
当前密钥版本,用于重加密、回滚和 crypto erase。
|
||||
|
||||
recovery_generation
|
||||
当前 DRR checkpoint 版本。
|
||||
|
||||
capability_table_slot
|
||||
该任务 capability table 的位置。
|
||||
|
||||
capability_count
|
||||
该任务拥有的段能力数量。
|
||||
|
||||
flags
|
||||
PWC、DMA、强恢复等策略标志。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 加密段描述符
|
||||
|
||||
```c
|
||||
struct utsm_segment_desc {
|
||||
uuid128_t segment_uuid;
|
||||
uuid128_t owner_process_uuid;
|
||||
|
||||
void *cipher_base;
|
||||
uint64_t cipher_length;
|
||||
|
||||
void *dmp_base;
|
||||
|
||||
uint64_t key_epoch;
|
||||
uint64_t tweak_seed;
|
||||
|
||||
void *dirty_bitmap;
|
||||
void *mac_table;
|
||||
|
||||
uint32_t generation;
|
||||
uint32_t flags;
|
||||
|
||||
uint32_t state;
|
||||
uint32_t last_cpu;
|
||||
|
||||
uint64_t writer_seq;
|
||||
uint64_t checkpoint_generation;
|
||||
};
|
||||
```
|
||||
|
||||
段状态:
|
||||
|
||||
```text
|
||||
FREE
|
||||
ACTIVE
|
||||
CHECKPOINTING
|
||||
SEALED
|
||||
RECOVERING
|
||||
POISONED
|
||||
DESTROYED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. DMP 段描述符元页
|
||||
|
||||
DMP 是热路径加速结构,替代“UUID 数字位置表”。
|
||||
|
||||
```c
|
||||
struct utsm_dmp {
|
||||
uint64_t magic;
|
||||
uint32_t version;
|
||||
uint32_t segment_slot;
|
||||
|
||||
uint64_t fast_base;
|
||||
uint64_t fast_limit;
|
||||
|
||||
uint64_t fast_tweak_seed;
|
||||
uint64_t fast_key_epoch;
|
||||
|
||||
uint64_t dirty_bitmap_base;
|
||||
uint64_t mac_table_base;
|
||||
|
||||
uint64_t last_writer_seq;
|
||||
uint32_t last_cpu;
|
||||
uint32_t flags;
|
||||
|
||||
uint64_t crc;
|
||||
};
|
||||
```
|
||||
|
||||
DMP 用于:
|
||||
|
||||
```text
|
||||
O(1) 段定位
|
||||
快速边界检查
|
||||
tweak 生成
|
||||
Dirty Bitmap 定位
|
||||
MAC Table 定位
|
||||
checkpoint
|
||||
recovery
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Capability
|
||||
|
||||
任务访问段时使用 capability,而不是裸 UUID。
|
||||
|
||||
```c
|
||||
struct utsm_capability {
|
||||
uint32_t segment_slot;
|
||||
uint32_t generation;
|
||||
|
||||
uint32_t rights;
|
||||
uint32_t epoch;
|
||||
|
||||
uint64_t uuid_digest;
|
||||
uint64_t auth_tag;
|
||||
};
|
||||
```
|
||||
|
||||
权限:
|
||||
|
||||
```text
|
||||
READ
|
||||
WRITE
|
||||
EXEC
|
||||
SHARE
|
||||
DMA
|
||||
```
|
||||
|
||||
热路径通过:
|
||||
|
||||
```text
|
||||
segment_slot + generation + epoch
|
||||
```
|
||||
|
||||
直接定位并验证段。
|
||||
|
||||
---
|
||||
|
||||
## 9. 加密规则
|
||||
|
||||
UTSM 不使用十进制凯撒和 UUID 孔洞,改为按 cache line 的 UUID 调谐流式加密。
|
||||
|
||||
```text
|
||||
segment_key = KDF(
|
||||
DRR_root_key,
|
||||
process_uuid,
|
||||
segment_uuid,
|
||||
key_epoch
|
||||
)
|
||||
```
|
||||
|
||||
每 64B cache line 生成 tweak:
|
||||
|
||||
```text
|
||||
line_index = offset / 64
|
||||
tweak = hash(tweak_seed, line_index, key_epoch)
|
||||
```
|
||||
|
||||
加密和解密:
|
||||
|
||||
```text
|
||||
ciphertext = plaintext XOR stream(segment_key, tweak)
|
||||
plaintext = ciphertext XOR stream(segment_key, tweak)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. UTRW 读写器
|
||||
|
||||
UTRW 分三层:
|
||||
|
||||
```text
|
||||
Level 0: Raw Access
|
||||
直接读 cipher_base,得到密文。
|
||||
|
||||
Level 1: Fast Path
|
||||
capability O(1) 定位段,PCKC 命中,无全局锁,无 trap。
|
||||
|
||||
Level 2: Slow Path
|
||||
key miss、epoch mismatch、checkpoint、recovery、MAC fail 等异常处理。
|
||||
```
|
||||
|
||||
### 10.1 读流程
|
||||
|
||||
```text
|
||||
UTRW_READ(cap, offset, len, dst)
|
||||
-> segment_slot 找 desc
|
||||
-> generation 检查
|
||||
-> rights 检查
|
||||
-> epoch 检查
|
||||
-> 边界检查
|
||||
-> PCKC 取 key
|
||||
-> 按 64B line 解密
|
||||
-> 返回明文
|
||||
```
|
||||
|
||||
### 10.2 写流程
|
||||
|
||||
```text
|
||||
UTRW_WRITE(cap, offset, len, src)
|
||||
-> segment_slot 找 desc
|
||||
-> generation 检查
|
||||
-> rights 检查
|
||||
-> epoch 检查
|
||||
-> 边界检查
|
||||
-> PCKC 取 key
|
||||
-> 按 64B line 加密
|
||||
-> 写入 cipher data
|
||||
-> 标记 dirty bitmap
|
||||
-> writer_seq++
|
||||
-> 按策略写 recovery log
|
||||
```
|
||||
|
||||
部分写必须先解密旧 64B line,合并新明文后重新加密整条 line。
|
||||
|
||||
---
|
||||
|
||||
## 11. PCKC 每 CPU 密钥缓存
|
||||
|
||||
```c
|
||||
struct utsm_pckc {
|
||||
uuid128_t current_process_uuid;
|
||||
|
||||
uint32_t hot_segment_slot[8];
|
||||
uint64_t hot_key_epoch[8];
|
||||
|
||||
void *key_schedule[8];
|
||||
|
||||
uint64_t hit_count;
|
||||
uint64_t miss_count;
|
||||
};
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
```text
|
||||
每 CPU 一个 PCKC。
|
||||
调度切换时只加载 next.crypto 指针和 hot_segment_hint。
|
||||
不扫描 capability table。
|
||||
不刷新全部 key。
|
||||
不破坏 PCQ 的 O(1) 调度。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 与 SAS-R0-PCQ 的整合
|
||||
|
||||
任务结构扩展:
|
||||
|
||||
```c
|
||||
struct task {
|
||||
uint64_t tid;
|
||||
uint32_t state;
|
||||
uint32_t priority;
|
||||
|
||||
uint64_t runtime;
|
||||
uint64_t time_slice;
|
||||
|
||||
void *stack_top;
|
||||
void *entry;
|
||||
|
||||
struct context cpu_context;
|
||||
|
||||
uint32_t last_cpu;
|
||||
uint64_t affinity_mask;
|
||||
|
||||
struct utsm_process_context *crypto;
|
||||
struct utsm_capability_table *cap_table;
|
||||
|
||||
uint32_t hot_segment_hint;
|
||||
uint32_t fault_segment_slot;
|
||||
|
||||
uint64_t recovery_generation;
|
||||
uint64_t heartbeat_seq;
|
||||
|
||||
uint32_t flags;
|
||||
|
||||
struct task *prev;
|
||||
struct task *next;
|
||||
};
|
||||
```
|
||||
|
||||
任务状态:
|
||||
|
||||
```text
|
||||
READY
|
||||
RUNNING
|
||||
BLOCKED
|
||||
SLEEPING
|
||||
RECOVERING
|
||||
FAULTED
|
||||
ZOMBIE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. 任务创建流程
|
||||
|
||||
```text
|
||||
task_create(entry, priority, flags)
|
||||
-> 分配 TCB
|
||||
-> 生成 tid
|
||||
-> 生成 process_uuid
|
||||
-> 创建 utsm_process_context
|
||||
-> 创建 stack segment
|
||||
-> 创建 heap segment
|
||||
-> 创建 ipc segment 可选
|
||||
-> 生成 capability table
|
||||
-> cap[0] = stack
|
||||
-> cap[1] = heap
|
||||
-> 初始化 CPU context
|
||||
-> 通过 UTRW_WRITE 写入 start packet
|
||||
-> hot_segment_hint = stack segment
|
||||
-> state = READY
|
||||
-> enqueue_task(current_cpu)
|
||||
```
|
||||
|
||||
默认 capability:
|
||||
|
||||
```text
|
||||
cap[0] = stack segment, READ | WRITE
|
||||
cap[1] = heap segment, READ | WRITE
|
||||
cap[2] = ipc segment, READ | WRITE | SHARE,可选
|
||||
cap[3] = code segment, READ | EXEC,可选
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. 调度切换流程
|
||||
|
||||
```text
|
||||
schedule()
|
||||
-> old = current
|
||||
-> utsm_switch_out(old)
|
||||
-> next = pick_next_task()
|
||||
-> utsm_switch_in(next)
|
||||
-> context_switch(old, next)
|
||||
```
|
||||
|
||||
`utsm_switch_out` 只做:
|
||||
|
||||
```text
|
||||
标记 PWC dirty flush
|
||||
保存 hot_segment_hint
|
||||
更新 recovery_generation
|
||||
更新 heartbeat
|
||||
```
|
||||
|
||||
`utsm_switch_in` 只做:
|
||||
|
||||
```text
|
||||
设置 PCKC.current_process_uuid
|
||||
加载 next.crypto
|
||||
检查 recovery_generation
|
||||
预热 hot_segment_hint
|
||||
```
|
||||
|
||||
禁止在调度路径中:
|
||||
|
||||
```text
|
||||
扫描所有 segment
|
||||
扫描 capability table
|
||||
计算 MAC
|
||||
写 checkpoint
|
||||
重加密整段
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 15. Checkpoint / Rollback 协议
|
||||
|
||||
### 15.1 Checkpoint 类型
|
||||
|
||||
```text
|
||||
Light Checkpoint
|
||||
保存 segment table、DMP、dirty shard summary、writer_seq、key_epoch、MAC root。
|
||||
|
||||
Dirty Page Checkpoint
|
||||
保存 dirty page 的密文副本、dirty page MAC、dirty shard、segment descriptor。
|
||||
|
||||
Boot Checkpoint
|
||||
保存内核镜像 slot、UTSM root epoch、DRR boot status、启动阶段状态。
|
||||
```
|
||||
|
||||
### 15.2 Dirty Shard 分片
|
||||
|
||||
Dirty Bitmap 不再只是一整块大 bitmap,而是拆成多个 Dirty Shard。
|
||||
|
||||
```c
|
||||
struct utsm_dirty_shard {
|
||||
uint32_t dirty_count;
|
||||
uint32_t shard_id;
|
||||
|
||||
uint64_t page_base;
|
||||
uint64_t page_count;
|
||||
|
||||
uint64_t last_checkpoint_epoch;
|
||||
void *bitmap;
|
||||
};
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
```text
|
||||
1. 每个 shard 管理一段连续页范围。
|
||||
2. UTRW_WRITE 标记 dirty page 时,同时更新 dirty_count。
|
||||
3. checkpoint 时 dirty_count == 0 的 shard 直接跳过。
|
||||
4. checkpoint 只扫描 dirty_count > 0 的 shard。
|
||||
5. 每 CPU 可以维护 dirty shard list,避免全局扫描。
|
||||
```
|
||||
|
||||
复杂度从:
|
||||
|
||||
```text
|
||||
扫描全 dirty bitmap
|
||||
```
|
||||
|
||||
优化为:
|
||||
|
||||
```text
|
||||
O(number_of_dirty_shards + number_of_dirty_pages)
|
||||
```
|
||||
|
||||
### 15.3 A/B Checkpoint Metadata 双槽
|
||||
|
||||
每个 checkpoint metadata 使用双槽。
|
||||
|
||||
```text
|
||||
Checkpoint Metadata Slot A
|
||||
Checkpoint Metadata Slot B
|
||||
```
|
||||
|
||||
提交规则:
|
||||
|
||||
```text
|
||||
当前 active = A
|
||||
新 checkpoint 写 B
|
||||
B 写完 metadata、dirty index、MAC root、CRC
|
||||
flush
|
||||
最后原子切换 active = B
|
||||
旧 A 保留为 fallback
|
||||
```
|
||||
|
||||
恢复规则:
|
||||
|
||||
```text
|
||||
优先读取 active slot
|
||||
如果 active slot CRC 错误或 generation 不完整
|
||||
fallback 到另一个 slot
|
||||
如果两个 slot 都损坏
|
||||
进入 system rollback
|
||||
```
|
||||
|
||||
这样可以避免 checkpoint 写一半崩溃导致旧稳定点也损坏。
|
||||
|
||||
### 15.4 正常 checkpoint 流程
|
||||
|
||||
```text
|
||||
DRR 发起 checkpoint
|
||||
-> checkpoint_generation++
|
||||
-> 选择 inactive metadata slot
|
||||
-> UTSM 遍历 dirty shard list
|
||||
-> 跳过 dirty_count == 0 的 shard
|
||||
-> 对 dirty shard 内的 ACTIVE 段执行 checkpoint
|
||||
-> desc.state = CHECKPOINTING
|
||||
-> 刷出 PWC / per-CPU dirty buffer
|
||||
-> 复制 dirty ciphertext page 到 checkpoint region
|
||||
-> 计算 page MAC
|
||||
-> 写 segment desc snapshot
|
||||
-> 写 DMP snapshot
|
||||
-> 写 dirty shard summary
|
||||
-> 写 checkpoint metadata CRC
|
||||
-> flush checkpoint metadata
|
||||
-> 原子切换 active metadata slot
|
||||
-> CHECKPOINT_COMMIT
|
||||
-> 清 dirty bitmap / dirty_count
|
||||
-> desc.state = ACTIVE
|
||||
```
|
||||
|
||||
### 15.5 rollback 级别
|
||||
|
||||
```text
|
||||
Page Rollback
|
||||
单页损坏,从 checkpoint slot 恢复密文页。
|
||||
|
||||
Segment Rollback
|
||||
整段损坏,恢复 segment desc、DMP、key_epoch、dirty shard、MAC table。
|
||||
|
||||
System Rollback
|
||||
控制面严重损坏,写 crash reason,reboot,由 bootloader 切换 last_good_slot。
|
||||
```
|
||||
|
||||
### 15.6 DRR Emergency Pool
|
||||
|
||||
DRR 启动时必须预留独立紧急内存池。
|
||||
|
||||
```text
|
||||
DRR Emergency Pool
|
||||
emergency pages
|
||||
recovery log buffer
|
||||
checkpoint metadata buffer
|
||||
DRR private stack
|
||||
DRR metadata slab
|
||||
crash buffer
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
```text
|
||||
1. 普通 allocator 永远不能分配 Emergency Pool。
|
||||
2. Emergency Pool 只供 watchdog、checkpoint、rollback、crash dump 使用。
|
||||
3. 普通系统 OOM 不等于 DRR OOM。
|
||||
4. recovery 路径禁止依赖普通堆分配器。
|
||||
5. Emergency Pool 耗尽时直接进入 system rollback。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 16. 启动流程
|
||||
|
||||
### 16.1 Bootloader 阶段
|
||||
|
||||
```text
|
||||
1. 读取 Boot Control Block。
|
||||
2. 校验 BCB CRC。
|
||||
3. 选择 active_slot。
|
||||
4. 如果 active_slot 失败次数超限,切换 last_good_slot。
|
||||
5. 将 SYSTEM 作为打包后 IMG 内的系统根目录。
|
||||
6. 加载 SYSTEM/boot/utsm.elf。
|
||||
7. 加载或暴露 SYSTEM/driver/manifest.json。
|
||||
8. 传入 memory map、BCB 地址、DRR reserved region 信息。
|
||||
```
|
||||
|
||||
### 16.2 DRR 初始化
|
||||
|
||||
```text
|
||||
1. 保留 DRR region。
|
||||
2. 初始化 watchdog。
|
||||
3. 建立 root key 或 root key handle。
|
||||
4. 初始化 recovery log。
|
||||
5. 初始化 checkpoint region。
|
||||
6. 初始化 heartbeat page。
|
||||
7. 初始化 DRR Emergency Pool。
|
||||
8. 初始化 A/B checkpoint metadata slot。
|
||||
```
|
||||
|
||||
### 16.3 UTSM 初始化
|
||||
|
||||
```text
|
||||
1. 接收 DRR root key handle。
|
||||
2. 初始化 global crypto epoch。
|
||||
3. 初始化 segment table。
|
||||
4. 初始化 DMP pool。
|
||||
5. 初始化 PCKC area。
|
||||
6. 初始化 dirty shard pool。
|
||||
7. 初始化 dirty bitmap pool。
|
||||
8. 初始化 MAC table pool。
|
||||
9. 注册 UTRW fast path / slow path。
|
||||
```
|
||||
|
||||
### 16.4 Scheduler 初始化
|
||||
|
||||
```text
|
||||
1. 初始化 per-CPU runqueue。
|
||||
2. 初始化 per-CPU heartbeat。
|
||||
3. 初始化每 CPU PCKC。
|
||||
4. 创建 bootstrap task。
|
||||
5. 创建核心服务 task。
|
||||
6. 将 task 加入 PCQ runqueue。
|
||||
```
|
||||
|
||||
### 16.5 Driver Manifest 加载
|
||||
|
||||
驱动清单位于系统根目录下:
|
||||
|
||||
```text
|
||||
SYSTEM/driver/manifest.json
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
```text
|
||||
1. manifest.json 是启动驱动模块的唯一默认清单。
|
||||
2. 驱动文件使用 ELF64 .drv 格式。
|
||||
3. 驱动按 stage 分阶段加载。
|
||||
4. required=true 的驱动失败时进入 DRR recovery 或 panic。
|
||||
5. required=false 的驱动失败时标记 FAILED 并继续启动。
|
||||
6. stage0/stage1 可由 bootloader 预加载为 boot module。
|
||||
7. stage2/stage3 在文件系统可用后从 SYSTEM/driver 扫描加载。
|
||||
```
|
||||
|
||||
默认阶段:
|
||||
|
||||
```text
|
||||
stage0: platform,timer / apic / acpi / pci
|
||||
stage1: boot,console / block / bootfs
|
||||
stage2: filesystem,vfs / fat32 / devfs
|
||||
stage3: optional,net / input / gpu
|
||||
```
|
||||
|
||||
### 16.6 DKM 驱动 ABI 摘要
|
||||
|
||||
驱动模块系统:
|
||||
|
||||
```text
|
||||
DKM = Deshab Kernel Module
|
||||
DSM = Driver Startup Manager
|
||||
```
|
||||
|
||||
每个 ELF .drv 必须导出:
|
||||
|
||||
```text
|
||||
driver_desc
|
||||
driver_init
|
||||
driver_exit
|
||||
```
|
||||
|
||||
`driver_desc` 至少描述:
|
||||
|
||||
```text
|
||||
magic / abi_version / desc_size
|
||||
name / version / vendor
|
||||
driver_class / stage / flags / priority
|
||||
depends / provides
|
||||
min_kernel_abi / feature_bits
|
||||
```
|
||||
|
||||
驱动入口:
|
||||
|
||||
```c
|
||||
int driver_init(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle);
|
||||
int driver_exit(struct dkm_driver_handle *handle);
|
||||
```
|
||||
|
||||
驱动默认通过 `dkm_kernel_api` 访问内核服务:
|
||||
|
||||
```text
|
||||
log
|
||||
mem
|
||||
utsm
|
||||
irq
|
||||
pci
|
||||
dma
|
||||
vfs
|
||||
net
|
||||
timer
|
||||
drr
|
||||
```
|
||||
|
||||
ELF loader 第一版支持:
|
||||
|
||||
```text
|
||||
必须支持:
|
||||
R_X86_64_64
|
||||
R_X86_64_RELATIVE
|
||||
R_X86_64_GLOB_DAT
|
||||
R_X86_64_JUMP_SLOT
|
||||
|
||||
建议支持:
|
||||
R_X86_64_PC32
|
||||
R_X86_64_PLT32
|
||||
R_X86_64_32 / R_X86_64_32S
|
||||
|
||||
第一版禁止:
|
||||
TLS relocation
|
||||
IFUNC
|
||||
lazy binding
|
||||
外部动态库依赖
|
||||
用户态 libc 依赖
|
||||
```
|
||||
|
||||
驱动加载状态机:
|
||||
|
||||
```text
|
||||
DISCOVERED
|
||||
-> QUEUED
|
||||
-> DEP_WAIT
|
||||
-> LOADING
|
||||
-> ELF_CHECKED
|
||||
-> MEMORY_ALLOCATED
|
||||
-> RELOCATED
|
||||
-> ABI_CHECKED
|
||||
-> INITING
|
||||
-> ACTIVE
|
||||
```
|
||||
|
||||
失败状态:
|
||||
|
||||
```text
|
||||
FAILED_ELF
|
||||
FAILED_RELOCATION
|
||||
FAILED_DEPENDENCY
|
||||
FAILED_ABI
|
||||
FAILED_INIT
|
||||
FAILED_REQUIRED
|
||||
```
|
||||
|
||||
stage0/stage1 boot module 查找规则:
|
||||
|
||||
```text
|
||||
1. 如果 path 在 boot module table 中,直接从内存加载。
|
||||
2. 否则如果 VFS 已可用,从 SYSTEM/path 加载。
|
||||
3. 否则返回 BOOT_MODULE_NOT_FOUND。
|
||||
```
|
||||
|
||||
完整设计见:
|
||||
|
||||
```text
|
||||
RE/驱动模块ABI设计.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 17. 错误处理
|
||||
|
||||
UTRW 错误状态:
|
||||
|
||||
```text
|
||||
OK
|
||||
KEY_MISS_RETRY
|
||||
STALE_CAPABILITY
|
||||
EPOCH_EXPIRED
|
||||
OUT_OF_BOUNDS
|
||||
ACCESS_DENIED
|
||||
CHECKPOINT_BUSY
|
||||
RECOVERING
|
||||
MAC_FAILED
|
||||
SEGMENT_POISONED
|
||||
CORRUPTED_DESCRIPTOR
|
||||
```
|
||||
|
||||
严重错误处理:
|
||||
|
||||
```text
|
||||
1. 记录 task.fault_segment_slot。
|
||||
2. task.state = FAULTED。
|
||||
3. 从 runqueue 移除。
|
||||
4. 通知 DRR。
|
||||
5. DRR 决定刷新 capability、回滚 segment、kill task 或 system rollback。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 18. UTSM v1 定稿
|
||||
|
||||
```text
|
||||
UTSM v1 包含:
|
||||
二进制 UUID
|
||||
连续密文数据区
|
||||
独立 DMP
|
||||
全局 Segment Table
|
||||
Segment Capability
|
||||
Per-CPU Key Cache
|
||||
UTRW 统一读写器
|
||||
Dirty Shard + Dirty Bitmap
|
||||
MAC Table
|
||||
DRR Emergency Pool
|
||||
A/B Checkpoint Metadata
|
||||
DRR checkpoint / rollback 对接
|
||||
SAS-R0-PCQ task 整合
|
||||
|
||||
加密粒度:
|
||||
64B cache line
|
||||
|
||||
Dirty 粒度:
|
||||
4KB page
|
||||
|
||||
Dirty 分片:
|
||||
Dirty Shard,checkpoint 只扫描 dirty_count > 0 的 shard
|
||||
|
||||
MAC 粒度:
|
||||
4KB page
|
||||
|
||||
Checkpoint 元数据:
|
||||
A/B 双槽,CRC 校验,原子切换 active slot
|
||||
|
||||
大页优化:
|
||||
2MB hugepage 可选
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
.section .text
|
||||
.global _start
|
||||
.type _start, @function
|
||||
_start:
|
||||
cli
|
||||
cld
|
||||
lea kernel_stack_top(%rip), %rsp
|
||||
andq $-16, %rsp
|
||||
call kernel_main
|
||||
.hang:
|
||||
cli
|
||||
hlt
|
||||
jmp .hang
|
||||
|
||||
.section .bss
|
||||
.align 16
|
||||
.global kernel_stack
|
||||
.global kernel_stack_top
|
||||
kernel_stack:
|
||||
.skip 16384
|
||||
kernel_stack_top:
|
||||
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
void arch_halt_forever(void) {
|
||||
for (;;) {
|
||||
__asm__ volatile ("cli; hlt");
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,222 @@
|
||||
#include <utsm/idt.h>
|
||||
#include <utsm/log.h>
|
||||
#include <utsm/types.h>
|
||||
|
||||
/* IDT gate descriptor (64-bit) */
|
||||
typedef struct __attribute__((packed)) {
|
||||
u16 offset_low;
|
||||
u16 selector;
|
||||
u8 ist;
|
||||
u8 type_attr;
|
||||
u16 offset_mid;
|
||||
u32 offset_high;
|
||||
u32 reserved;
|
||||
} idt_entry;
|
||||
|
||||
/* IDT register */
|
||||
typedef struct __attribute__((packed)) {
|
||||
u16 limit;
|
||||
u64 base;
|
||||
} idt_ptr;
|
||||
|
||||
#define IDT_ENTRIES 256
|
||||
static idt_entry g_idt[IDT_ENTRIES] __attribute__((aligned(16)));
|
||||
|
||||
#define PIC1_CMD 0x20
|
||||
#define PIC1_DATA 0x21
|
||||
#define PIC2_CMD 0xA0
|
||||
#define PIC2_DATA 0xA1
|
||||
#define ICW1_ICW4 0x01
|
||||
#define ICW1_INIT 0x10
|
||||
|
||||
/* assembly stub declarations */
|
||||
extern void isr_stub_0(void);
|
||||
extern void isr_stub_1(void);
|
||||
extern void isr_stub_2(void);
|
||||
extern void isr_stub_3(void);
|
||||
extern void isr_stub_4(void);
|
||||
extern void isr_stub_5(void);
|
||||
extern void isr_stub_6(void);
|
||||
extern void isr_stub_7(void);
|
||||
extern void isr_stub_8(void);
|
||||
extern void isr_stub_9(void);
|
||||
extern void isr_stub_10(void);
|
||||
extern void isr_stub_11(void);
|
||||
extern void isr_stub_12(void);
|
||||
extern void isr_stub_13(void);
|
||||
extern void isr_stub_14(void);
|
||||
extern void isr_stub_15(void);
|
||||
extern void isr_stub_16(void);
|
||||
extern void isr_stub_17(void);
|
||||
extern void isr_stub_18(void);
|
||||
extern void isr_stub_19(void);
|
||||
extern void isr_stub_20(void);
|
||||
extern void isr_stub_21(void);
|
||||
extern void isr_stub_22(void);
|
||||
extern void isr_stub_23(void);
|
||||
extern void isr_stub_24(void);
|
||||
extern void isr_stub_25(void);
|
||||
extern void isr_stub_26(void);
|
||||
extern void isr_stub_27(void);
|
||||
extern void isr_stub_28(void);
|
||||
extern void isr_stub_29(void);
|
||||
extern void isr_stub_30(void);
|
||||
extern void isr_stub_31(void);
|
||||
extern void isr_stub_32(void);
|
||||
extern void isr_stub_33(void);
|
||||
extern void isr_stub_34(void);
|
||||
extern void isr_stub_35(void);
|
||||
extern void isr_stub_36(void);
|
||||
extern void isr_stub_37(void);
|
||||
extern void isr_stub_38(void);
|
||||
extern void isr_stub_39(void);
|
||||
extern void isr_stub_40(void);
|
||||
extern void isr_stub_41(void);
|
||||
extern void isr_stub_42(void);
|
||||
extern void isr_stub_43(void);
|
||||
extern void isr_stub_44(void);
|
||||
extern void isr_stub_45(void);
|
||||
extern void isr_stub_46(void);
|
||||
extern void isr_stub_47(void);
|
||||
|
||||
static void *g_isr_table[] = {
|
||||
isr_stub_0, isr_stub_1, isr_stub_2, isr_stub_3,
|
||||
isr_stub_4, isr_stub_5, isr_stub_6, isr_stub_7,
|
||||
isr_stub_8, isr_stub_9, isr_stub_10, isr_stub_11,
|
||||
isr_stub_12, isr_stub_13, isr_stub_14, isr_stub_15,
|
||||
isr_stub_16, isr_stub_17, isr_stub_18, isr_stub_19,
|
||||
isr_stub_20, isr_stub_21, isr_stub_22, isr_stub_23,
|
||||
isr_stub_24, isr_stub_25, isr_stub_26, isr_stub_27,
|
||||
isr_stub_28, isr_stub_29, isr_stub_30, isr_stub_31,
|
||||
isr_stub_32, isr_stub_33, isr_stub_34, isr_stub_35,
|
||||
isr_stub_36, isr_stub_37, isr_stub_38, isr_stub_39,
|
||||
isr_stub_40, isr_stub_41, isr_stub_42, isr_stub_43,
|
||||
isr_stub_44, isr_stub_45, isr_stub_46, isr_stub_47,
|
||||
};
|
||||
|
||||
static void outb(u16 port, u8 value) {
|
||||
__asm__ volatile ("outb %0, %1" :: "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static u8 inb(u16 port) {
|
||||
u8 value;
|
||||
__asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
static void io_wait(void) {
|
||||
outb(0x80, 0);
|
||||
}
|
||||
|
||||
static void pic_remap(void) {
|
||||
/* save masks */
|
||||
u8 m1 = inb(PIC1_DATA);
|
||||
u8 m2 = inb(PIC2_DATA);
|
||||
|
||||
/* ICW1: start init + ICW4 */
|
||||
outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4);
|
||||
io_wait();
|
||||
outb(PIC2_CMD, ICW1_INIT | ICW1_ICW4);
|
||||
io_wait();
|
||||
|
||||
/* ICW2: vector offsets */
|
||||
outb(PIC1_DATA, 0x20); /* master: vectors 0x20-0x27 */
|
||||
io_wait();
|
||||
outb(PIC2_DATA, 0x28); /* slave: vectors 0x28-0x2F */
|
||||
io_wait();
|
||||
|
||||
/* ICW3: master/slave wiring */
|
||||
outb(PIC1_DATA, 0x04); /* master: IRQ2 → slave */
|
||||
io_wait();
|
||||
outb(PIC2_DATA, 0x02); /* slave: cascade identity */
|
||||
io_wait();
|
||||
|
||||
/* ICW4: 8086 mode */
|
||||
outb(PIC1_DATA, 0x01);
|
||||
io_wait();
|
||||
outb(PIC2_DATA, 0x01);
|
||||
io_wait();
|
||||
|
||||
/* restore masks: mask all except IRQ1 (keyboard) */
|
||||
outb(PIC1_DATA, 0xFD); /* 1111_1101: enable IRQ0 (timer) and IRQ1 (kb) */
|
||||
outb(PIC2_DATA, 0xFF); /* mask all slave */
|
||||
}
|
||||
|
||||
static void idt_set_entry(u8 vector, void *handler, u8 ist, u8 type) {
|
||||
u64 addr = (u64)handler;
|
||||
g_idt[vector].offset_low = (u16)(addr & 0xffff);
|
||||
g_idt[vector].selector = 0x08; /* kernel code segment (from GDT) */
|
||||
g_idt[vector].ist = ist;
|
||||
g_idt[vector].type_attr = type;
|
||||
g_idt[vector].offset_mid = (u16)((addr >> 16) & 0xffff);
|
||||
g_idt[vector].offset_high = (u32)(addr >> 32);
|
||||
g_idt[vector].reserved = 0;
|
||||
}
|
||||
|
||||
void idt_init(void) {
|
||||
log_info("[IDT] init begin");
|
||||
|
||||
for (u16 i = 0; i < 48; i++) {
|
||||
idt_set_entry((u8)i, g_isr_table[i], 0, 0x8E); /* present, ring0, 64-bit interrupt gate */
|
||||
}
|
||||
/* fill unused entries with null */
|
||||
for (u16 i = 48; i < IDT_ENTRIES; i++) {
|
||||
idt_set_entry((u8)i, 0, 0, 0);
|
||||
}
|
||||
|
||||
idt_ptr idtr;
|
||||
idtr.limit = (u16)(sizeof(g_idt) - 1);
|
||||
idtr.base = (u64)&g_idt;
|
||||
|
||||
__asm__ volatile ("lidt %0" :: "m"(idtr));
|
||||
|
||||
pic_remap();
|
||||
log_info("[IDT] PIC remapped (master=0x20 slave=0x28)");
|
||||
|
||||
__asm__ volatile ("sti");
|
||||
log_info("[IDT] interrupts enabled");
|
||||
log_info("[IDT] init ok");
|
||||
}
|
||||
|
||||
/* IRQ handler table */
|
||||
static irq_handler_t g_irq_handlers[16];
|
||||
|
||||
int irq_register(u8 irq, irq_handler_t handler) {
|
||||
if (irq >= 16) return -1;
|
||||
g_irq_handlers[irq] = handler;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Stack frame pushed by isr_common in idt.S */
|
||||
typedef struct {
|
||||
u64 r11, r10, r9, r8, rax, rcx, rdx, rsi, rdi;
|
||||
u64 vector;
|
||||
u64 error_code;
|
||||
u64 rip, cs, rflags;
|
||||
} isr_frame;
|
||||
|
||||
void idt_handler(isr_frame *frame) {
|
||||
if (frame->vector < 32) {
|
||||
log_error("[IDT] exception");
|
||||
log_hex64("[IDT] vector=", frame->vector);
|
||||
log_hex64("[IDT] err=", frame->error_code);
|
||||
log_hex64("[IDT] rip=", frame->rip);
|
||||
for (;;) { __asm__ volatile ("cli; hlt"); }
|
||||
}
|
||||
|
||||
if (frame->vector >= 32 && frame->vector <= 47) {
|
||||
u8 irq = (u8)(frame->vector - 0x20);
|
||||
|
||||
/* call registered handler */
|
||||
if (irq < 16 && g_irq_handlers[irq]) {
|
||||
int suppress_eoi = g_irq_handlers[irq](irq);
|
||||
if (suppress_eoi) return;
|
||||
}
|
||||
|
||||
/* send EOI */
|
||||
if (frame->vector >= 0x28) {
|
||||
outb(PIC2_CMD, 0x20);
|
||||
}
|
||||
outb(PIC1_CMD, 0x20);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,62 @@
|
||||
/* IDT assembly stubs — SAS-R0 interrupt vectors */
|
||||
.section .text
|
||||
|
||||
.macro isr_no_err n
|
||||
.global isr_stub_\n
|
||||
.type isr_stub_\n, @function
|
||||
isr_stub_\n:
|
||||
pushq $0
|
||||
pushq $\n
|
||||
jmp isr_common
|
||||
.endm
|
||||
|
||||
.macro isr_err n
|
||||
.global isr_stub_\n
|
||||
.type isr_stub_\n, @function
|
||||
isr_stub_\n:
|
||||
pushq $\n
|
||||
jmp isr_common
|
||||
.endm
|
||||
|
||||
/* Exceptions 0-31 */
|
||||
isr_no_err 0; isr_no_err 1; isr_no_err 2; isr_no_err 3
|
||||
isr_no_err 4; isr_no_err 5; isr_no_err 6; isr_no_err 7
|
||||
isr_err 8; isr_no_err 9; isr_err 10; isr_err 11
|
||||
isr_err 12; isr_err 13; isr_err 14; isr_no_err 15
|
||||
isr_no_err 16; isr_err 17; isr_no_err 18; isr_no_err 19
|
||||
isr_no_err 20; isr_no_err 21; isr_no_err 22; isr_no_err 23
|
||||
isr_no_err 24; isr_no_err 25; isr_no_err 26; isr_no_err 27
|
||||
isr_no_err 28; isr_no_err 29; isr_err 30; isr_no_err 31
|
||||
|
||||
/* IRQ 32-47 */
|
||||
isr_no_err 32; isr_no_err 33; isr_no_err 34; isr_no_err 35
|
||||
isr_no_err 36; isr_no_err 37; isr_no_err 38; isr_no_err 39
|
||||
isr_no_err 40; isr_no_err 41; isr_no_err 42; isr_no_err 43
|
||||
isr_no_err 44; isr_no_err 45; isr_no_err 46; isr_no_err 47
|
||||
|
||||
/* Common ISR */
|
||||
isr_common:
|
||||
pushq %rdi
|
||||
pushq %rsi
|
||||
pushq %rdx
|
||||
pushq %rcx
|
||||
pushq %rax
|
||||
pushq %r8
|
||||
pushq %r9
|
||||
pushq %r10
|
||||
pushq %r11
|
||||
|
||||
movq %rsp, %rdi
|
||||
call idt_handler
|
||||
|
||||
popq %r11
|
||||
popq %r10
|
||||
popq %r9
|
||||
popq %r8
|
||||
popq %rax
|
||||
popq %rcx
|
||||
popq %rdx
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
addq $16, %rsp
|
||||
iretq
|
||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
||||
#include <utsm/types.h>
|
||||
|
||||
void outb(u16 port, u8 value) {
|
||||
__asm__ volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
u8 inb(u16 port) {
|
||||
u8 value;
|
||||
__asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
void io_wait(void) {
|
||||
outb(0x80, 0);
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,154 @@
|
||||
#ifndef UTSM_LIMINE_H
|
||||
#define UTSM_LIMINE_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
#define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88ULL, 0x0a82e883a194f07bULL
|
||||
|
||||
struct limine_hhdm_response {
|
||||
u64 revision;
|
||||
u64 offset;
|
||||
};
|
||||
|
||||
struct limine_hhdm_request {
|
||||
u64 id[4];
|
||||
u64 revision;
|
||||
struct limine_hhdm_response *response;
|
||||
};
|
||||
|
||||
struct limine_kernel_address_response {
|
||||
u64 revision;
|
||||
u64 physical_base;
|
||||
u64 virtual_base;
|
||||
};
|
||||
|
||||
struct limine_kernel_address_request {
|
||||
u64 id[4];
|
||||
u64 revision;
|
||||
struct limine_kernel_address_response *response;
|
||||
};
|
||||
|
||||
#define LIMINE_KERNEL_ADDRESS_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63ULL, 0xb2644a48c516a487ULL }
|
||||
|
||||
struct limine_file {
|
||||
u64 revision;
|
||||
void *address;
|
||||
u64 size;
|
||||
char *path;
|
||||
char *cmdline;
|
||||
u32 media_type;
|
||||
u32 unused;
|
||||
u32 tftp_ip;
|
||||
u32 tftp_port;
|
||||
u32 partition_index;
|
||||
u32 mbr_disk_id;
|
||||
u8 gpt_disk_uuid[16];
|
||||
u8 gpt_part_uuid[16];
|
||||
u8 part_uuid[16];
|
||||
};
|
||||
|
||||
struct limine_module_response {
|
||||
u64 revision;
|
||||
u64 module_count;
|
||||
struct limine_file **modules;
|
||||
};
|
||||
|
||||
struct limine_module_request {
|
||||
u64 id[4];
|
||||
u64 revision;
|
||||
struct limine_module_response *response;
|
||||
};
|
||||
|
||||
#define LIMINE_MODULE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x3e7e279702be32afULL, 0xca1c4f3bd1280ceeULL }
|
||||
|
||||
struct limine_rsdp_response {
|
||||
u64 revision;
|
||||
void *address;
|
||||
};
|
||||
|
||||
struct limine_rsdp_request {
|
||||
u64 id[4];
|
||||
u64 revision;
|
||||
struct limine_rsdp_response *response;
|
||||
};
|
||||
|
||||
#define LIMINE_RSDP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43ULL, 0x27637845accdcf3cULL }
|
||||
|
||||
struct limine_framebuffer {
|
||||
void *address;
|
||||
u64 width;
|
||||
u64 height;
|
||||
u64 pitch;
|
||||
u16 bpp;
|
||||
u8 memory_model;
|
||||
u8 red_mask_size;
|
||||
u8 red_mask_shift;
|
||||
u8 green_mask_size;
|
||||
u8 green_mask_shift;
|
||||
u8 blue_mask_size;
|
||||
u8 blue_mask_shift;
|
||||
u8 unused[7];
|
||||
u64 edid_size;
|
||||
void *edid;
|
||||
u64 mode_count;
|
||||
struct limine_video_mode **modes;
|
||||
};
|
||||
|
||||
struct limine_video_mode {
|
||||
u64 pitch;
|
||||
u64 width;
|
||||
u64 height;
|
||||
u16 bpp;
|
||||
u8 memory_model;
|
||||
u8 red_mask_size;
|
||||
u8 red_mask_shift;
|
||||
u8 green_mask_size;
|
||||
u8 green_mask_shift;
|
||||
u8 blue_mask_size;
|
||||
u8 blue_mask_shift;
|
||||
};
|
||||
|
||||
struct limine_framebuffer_response {
|
||||
u64 revision;
|
||||
u64 framebuffer_count;
|
||||
struct limine_framebuffer **framebuffers;
|
||||
};
|
||||
|
||||
struct limine_framebuffer_request {
|
||||
u64 id[4];
|
||||
u64 revision;
|
||||
struct limine_framebuffer_response *response;
|
||||
};
|
||||
|
||||
#define LIMINE_FRAMEBUFFER_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75ULL, 0xa3148604f6fab11bULL }
|
||||
|
||||
#define LIMINE_MEMMAP_USABLE 0
|
||||
#define LIMINE_MEMMAP_RESERVED 1
|
||||
#define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2
|
||||
#define LIMINE_MEMMAP_ACPI_NVS 3
|
||||
#define LIMINE_MEMMAP_BAD_MEMORY 4
|
||||
#define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5
|
||||
#define LIMINE_MEMMAP_KERNEL_AND_MODULES 6
|
||||
#define LIMINE_MEMMAP_FRAMEBUFFER 7
|
||||
|
||||
struct limine_memmap_entry {
|
||||
u64 base;
|
||||
u64 length;
|
||||
u64 type;
|
||||
};
|
||||
|
||||
struct limine_memmap_response {
|
||||
u64 revision;
|
||||
u64 entry_count;
|
||||
struct limine_memmap_entry **entries;
|
||||
};
|
||||
|
||||
struct limine_memmap_request {
|
||||
u64 id[4];
|
||||
u64 revision;
|
||||
struct limine_memmap_response *response;
|
||||
};
|
||||
|
||||
#define LIMINE_MEMMAP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806fULL, 0xe304acdfc50c3c62ULL }
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <utsm/types.h>
|
||||
|
||||
void outb(u16 port, u8 value);
|
||||
u8 inb(u16 port);
|
||||
|
||||
#define COM1 0x3F8
|
||||
|
||||
static int serial_ready(void) {
|
||||
return (inb(COM1 + 5) & 0x20) != 0;
|
||||
}
|
||||
|
||||
static int serial_wait_ready(void) {
|
||||
for (u64 spin = 0; spin < 1000000ULL; spin++) {
|
||||
if (serial_ready()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void serial_init(void) {
|
||||
outb(COM1 + 1, 0x00);
|
||||
outb(COM1 + 3, 0x80);
|
||||
outb(COM1 + 0, 0x03);
|
||||
outb(COM1 + 1, 0x00);
|
||||
outb(COM1 + 3, 0x03);
|
||||
outb(COM1 + 2, 0xC7);
|
||||
outb(COM1 + 4, 0x0B);
|
||||
}
|
||||
|
||||
void serial_putc(char c) {
|
||||
if (c == '\n') {
|
||||
serial_putc('\r');
|
||||
}
|
||||
if (!serial_wait_ready()) {
|
||||
return;
|
||||
}
|
||||
outb(COM1, (u8)c);
|
||||
}
|
||||
|
||||
void serial_write(const char *s) {
|
||||
while (*s) {
|
||||
serial_putc(*s++);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,29 @@
|
||||
#include <utsm/capability.h>
|
||||
#include <utsm/segment.h>
|
||||
|
||||
int utsm_cap_validate(utsm_capability cap, u32 required_rights, u64 offset, u64 len, utsm_segment_desc **out_desc) {
|
||||
utsm_segment_desc *desc = utsm_get_segment(cap.segment_slot);
|
||||
if (!desc) {
|
||||
return UTSM_ERR_INVALID;
|
||||
}
|
||||
if (desc->state == UTSM_SEG_POISONED) {
|
||||
return UTSM_ERR_POISONED;
|
||||
}
|
||||
if (desc->state != UTSM_SEG_ACTIVE) {
|
||||
return UTSM_ERR_STATE;
|
||||
}
|
||||
if (desc->generation != cap.generation) {
|
||||
return UTSM_ERR_STALE_CAP;
|
||||
}
|
||||
if ((cap.rights & required_rights) != required_rights) {
|
||||
return UTSM_ERR_ACCESS;
|
||||
}
|
||||
if ((u32)desc->key_epoch != cap.epoch) {
|
||||
return UTSM_ERR_EPOCH;
|
||||
}
|
||||
if (offset > desc->cipher_length || len > desc->cipher_length || offset + len > desc->cipher_length) {
|
||||
return UTSM_ERR_BOUNDS;
|
||||
}
|
||||
*out_desc = desc;
|
||||
return UTSM_OK;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,47 @@
|
||||
#include <utsm/dirty.h>
|
||||
#include <utsm/segment.h>
|
||||
#include <utsm/arena.h>
|
||||
#include <utsm/config.h>
|
||||
|
||||
static void bitmap_set(u64 *bitmap, u64 bit) {
|
||||
bitmap[bit / 64] |= 1ULL << (bit % 64);
|
||||
}
|
||||
|
||||
static bool bitmap_test(u64 *bitmap, u64 bit) {
|
||||
return (bitmap[bit / 64] & (1ULL << (bit % 64))) != 0;
|
||||
}
|
||||
|
||||
void utsm_dirty_init(utsm_segment_desc *desc, u64 page_count) {
|
||||
desc->dirty_shard_count = 1;
|
||||
desc->dirty_shards = (utsm_dirty_shard *)kmem_alloc(sizeof(utsm_dirty_shard));
|
||||
if (!desc->dirty_shards) {
|
||||
return;
|
||||
}
|
||||
u64 words = (page_count + 63) / 64;
|
||||
desc->dirty_shards[0].dirty_count = 0;
|
||||
desc->dirty_shards[0].shard_id = 0;
|
||||
desc->dirty_shards[0].page_base = 0;
|
||||
desc->dirty_shards[0].page_count = page_count;
|
||||
desc->dirty_shards[0].last_checkpoint_epoch = 0;
|
||||
desc->dirty_shards[0].bitmap = (u64 *)kmem_alloc_aligned(words * sizeof(u64), 8);
|
||||
if (desc->dirty_shards[0].bitmap) {
|
||||
for (u64 i = 0; i < words; i++) {
|
||||
desc->dirty_shards[0].bitmap[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void utsm_mark_dirty(utsm_segment_desc *desc, u64 offset, u64 len) {
|
||||
if (!desc || !desc->dirty_shards || !desc->dirty_shards[0].bitmap || len == 0) {
|
||||
return;
|
||||
}
|
||||
u64 first = offset / UTSM_PAGE_SIZE;
|
||||
u64 last = (offset + len - 1) / UTSM_PAGE_SIZE;
|
||||
utsm_dirty_shard *shard = &desc->dirty_shards[0];
|
||||
for (u64 page = first; page <= last && page < shard->page_count; page++) {
|
||||
if (!bitmap_test(shard->bitmap, page)) {
|
||||
bitmap_set(shard->bitmap, page);
|
||||
shard->dirty_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,20 @@
|
||||
#include <utsm/dmp.h>
|
||||
#include <utsm/segment.h>
|
||||
|
||||
#define UTSM_DMP_MAGIC 0x5554534d444d5031ULL
|
||||
|
||||
void utsm_dmp_init(utsm_dmp *dmp, utsm_segment_desc *desc, u32 slot) {
|
||||
dmp->magic = UTSM_DMP_MAGIC;
|
||||
dmp->version = 1;
|
||||
dmp->segment_slot = slot;
|
||||
dmp->fast_base = (u64)desc->cipher_base;
|
||||
dmp->fast_limit = (u64)desc->cipher_base + desc->cipher_length;
|
||||
dmp->fast_tweak_seed = desc->tweak_seed;
|
||||
dmp->fast_key_epoch = desc->key_epoch;
|
||||
dmp->dirty_bitmap_base = desc->dirty_shards ? (u64)desc->dirty_shards[0].bitmap : 0;
|
||||
dmp->mac_table_base = (u64)desc->mac_table;
|
||||
dmp->last_writer_seq = desc->writer_seq;
|
||||
dmp->last_cpu = desc->last_cpu;
|
||||
dmp->flags = desc->flags;
|
||||
dmp->crc = dmp->magic ^ dmp->fast_base ^ dmp->fast_limit ^ dmp->fast_tweak_seed;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
#include <utsm/process.h>
|
||||
#include <utsm/drr.h>
|
||||
|
||||
void utsm_process_create_test(utsm_process_context *process, u64 seed) {
|
||||
process->process_uuid = uuid_make_test(seed);
|
||||
process->crypto_epoch = 1;
|
||||
process->recovery_generation = drr_recovery_generation();
|
||||
process->capability_table_slot = 0;
|
||||
process->capability_count = 0;
|
||||
process->flags = 0;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
#include <utsm/segment.h>
|
||||
#include <utsm/process.h>
|
||||
#include <utsm/arena.h>
|
||||
#include <utsm/dmp.h>
|
||||
#include <utsm/config.h>
|
||||
|
||||
u64 utsm_next_generation(void);
|
||||
|
||||
static u64 digest_uuid(uuid128_t id) {
|
||||
return id.hi ^ id.lo ^ (id.hi >> 32) ^ (id.lo << 17);
|
||||
}
|
||||
|
||||
static u64 make_tweak_seed(uuid128_t process_uuid, uuid128_t segment_uuid, u64 epoch) {
|
||||
return process_uuid.hi ^ process_uuid.lo ^ segment_uuid.hi ^ segment_uuid.lo ^ (epoch * 0x9e3779b97f4a7c15ULL);
|
||||
}
|
||||
|
||||
int utsm_create_segment(utsm_process_context *process, u64 size, u32 flags, utsm_capability *out_cap) {
|
||||
if (!process || !out_cap || size == 0) {
|
||||
return UTSM_ERR_INVALID;
|
||||
}
|
||||
|
||||
u32 slot = UTSM_MAX_SEGMENTS;
|
||||
utsm_segment_desc *desc = NULL;
|
||||
for (u32 i = 0; i < UTSM_MAX_SEGMENTS; i++) {
|
||||
utsm_segment_desc *candidate = utsm_get_segment(i);
|
||||
if (candidate->state == UTSM_SEG_FREE) {
|
||||
slot = i;
|
||||
desc = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!desc) {
|
||||
return UTSM_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
u64 length = utsm_align_up_u64(size, UTSM_CACHE_LINE_SIZE);
|
||||
u64 pages = utsm_align_up_u64(length, UTSM_PAGE_SIZE) / UTSM_PAGE_SIZE;
|
||||
u8 *cipher = (u8 *)kmem_alloc_page(pages);
|
||||
utsm_dmp *dmp = (utsm_dmp *)kmem_alloc_aligned(sizeof(utsm_dmp), 64);
|
||||
void *mac_table = kmem_alloc_aligned(pages * sizeof(u64), 8);
|
||||
if (!cipher || !dmp || !mac_table) {
|
||||
return UTSM_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (u64 i = 0; i < pages * UTSM_PAGE_SIZE; i++) {
|
||||
cipher[i] = 0;
|
||||
}
|
||||
|
||||
desc->segment_uuid = uuid_make_test(utsm_next_generation() + slot);
|
||||
desc->owner_process_uuid = process->process_uuid;
|
||||
desc->cipher_base = cipher;
|
||||
desc->cipher_length = length;
|
||||
desc->dmp_base = dmp;
|
||||
desc->key_epoch = process->crypto_epoch;
|
||||
desc->tweak_seed = make_tweak_seed(process->process_uuid, desc->segment_uuid, desc->key_epoch);
|
||||
desc->mac_table = mac_table;
|
||||
desc->generation++;
|
||||
desc->flags = flags;
|
||||
desc->state = UTSM_SEG_ACTIVE;
|
||||
desc->last_cpu = 0;
|
||||
desc->writer_seq = 0;
|
||||
desc->checkpoint_generation = process->recovery_generation;
|
||||
|
||||
utsm_dirty_init(desc, pages);
|
||||
utsm_dmp_init(dmp, desc, slot);
|
||||
|
||||
out_cap->segment_slot = slot;
|
||||
out_cap->generation = desc->generation;
|
||||
out_cap->rights = 0;
|
||||
if (flags & UTSM_SEG_F_READ) out_cap->rights |= UTSM_RIGHT_READ;
|
||||
if (flags & UTSM_SEG_F_WRITE) out_cap->rights |= UTSM_RIGHT_WRITE;
|
||||
if (flags & UTSM_SEG_F_EXEC) out_cap->rights |= UTSM_RIGHT_EXEC;
|
||||
if (flags & UTSM_SEG_F_DMA) out_cap->rights |= UTSM_RIGHT_DMA;
|
||||
out_cap->epoch = (u32)desc->key_epoch;
|
||||
out_cap->uuid_digest = digest_uuid(desc->segment_uuid);
|
||||
out_cap->auth_tag = out_cap->uuid_digest ^ digest_uuid(process->process_uuid) ^ desc->key_epoch;
|
||||
|
||||
process->capability_count++;
|
||||
return UTSM_OK;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
#include <utsm/utsm.h>
|
||||
#include <utsm/segment.h>
|
||||
#include <utsm/pckc.h>
|
||||
#include <utsm/log.h>
|
||||
|
||||
static utsm_segment_desc g_segments[UTSM_MAX_SEGMENTS];
|
||||
static u64 g_generation = 1;
|
||||
|
||||
void utsm_init(void) {
|
||||
log_info("[UTSM] segment table reset begin");
|
||||
for (u32 i = 0; i < UTSM_MAX_SEGMENTS; i++) {
|
||||
g_segments[i].state = UTSM_SEG_FREE;
|
||||
g_segments[i].generation = 1;
|
||||
}
|
||||
log_info("[UTSM] segment table reset ok");
|
||||
|
||||
g_generation = 1;
|
||||
log_info("[UTSM] pckc init begin");
|
||||
utsm_pckc_init();
|
||||
log_info("[UTSM] pckc init ok");
|
||||
}
|
||||
|
||||
utsm_segment_desc *utsm_get_segment(u32 slot) {
|
||||
if (slot >= UTSM_MAX_SEGMENTS) {
|
||||
return NULL;
|
||||
}
|
||||
return &g_segments[slot];
|
||||
}
|
||||
|
||||
u64 utsm_next_generation(void) {
|
||||
return g_generation++;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
#include <utsm/crypto.h>
|
||||
#include <utsm/drr.h>
|
||||
|
||||
static u64 mix64(u64 x) {
|
||||
x ^= x >> 30;
|
||||
x *= 0xbf58476d1ce4e5b9ULL;
|
||||
x ^= x >> 27;
|
||||
x *= 0x94d049bb133111ebULL;
|
||||
x ^= x >> 31;
|
||||
return x;
|
||||
}
|
||||
|
||||
void utsm_kdf_segment_key(uuid128_t process_uuid, uuid128_t segment_uuid, u64 key_epoch, utsm_key_material *out_key) {
|
||||
const u64 *root = drr_get_root_key();
|
||||
out_key->words[0] = mix64(root[0] ^ process_uuid.hi ^ segment_uuid.lo ^ key_epoch);
|
||||
out_key->words[1] = mix64(root[1] ^ process_uuid.lo ^ segment_uuid.hi ^ (key_epoch << 1));
|
||||
out_key->words[2] = mix64(root[2] ^ process_uuid.hi ^ segment_uuid.hi ^ (key_epoch << 7));
|
||||
out_key->words[3] = mix64(root[3] ^ process_uuid.lo ^ segment_uuid.lo ^ (key_epoch << 13));
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
#include <utsm/crypto.h>
|
||||
#include <utsm/config.h>
|
||||
|
||||
static u64 next_stream_word(u64 x) {
|
||||
x ^= x << 13;
|
||||
x ^= x >> 7;
|
||||
x ^= x << 17;
|
||||
return x;
|
||||
}
|
||||
|
||||
void utsm_crypt_line(const utsm_key_material *key, u64 tweak, const u8 *in, u8 *out) {
|
||||
u64 state = key->words[0] ^ key->words[1] ^ key->words[2] ^ key->words[3] ^ tweak;
|
||||
for (u64 i = 0; i < UTSM_CACHE_LINE_SIZE; i += 8) {
|
||||
state = next_stream_word(state + i + 0xa5a5a5a5a5a5a5a5ULL);
|
||||
for (u64 j = 0; j < 8; j++) {
|
||||
out[i + j] = in[i + j] ^ (u8)(state >> (j * 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,14 @@
|
||||
#include <utsm/crypto.h>
|
||||
|
||||
static u64 mix64_tweak(u64 x) {
|
||||
x ^= x >> 33;
|
||||
x *= 0xff51afd7ed558ccdULL;
|
||||
x ^= x >> 33;
|
||||
x *= 0xc4ceb9fe1a85ec53ULL;
|
||||
x ^= x >> 33;
|
||||
return x;
|
||||
}
|
||||
|
||||
u64 utsm_make_tweak(u64 tweak_seed, u64 line_index, u64 key_epoch) {
|
||||
return mix64_tweak(tweak_seed ^ (line_index * 0x9e3779b97f4a7c15ULL) ^ (key_epoch << 32));
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,444 @@
|
||||
#include <utsm/dkm.h>
|
||||
#include <utsm/log.h>
|
||||
#include <utsm/types.h>
|
||||
#include <utsm/arena.h>
|
||||
#include "../arch/x86_64/limine.h"
|
||||
|
||||
#define ELF_MAGIC0 0x7f
|
||||
#define ELF_MAGIC1 'E'
|
||||
#define ELF_MAGIC2 'L'
|
||||
#define ELF_MAGIC3 'F'
|
||||
#define ELFCLASS64 2
|
||||
#define ELFDATA2LSB 1
|
||||
#define ET_REL 1
|
||||
#define ET_DYN 3
|
||||
#define ET_EXEC 2
|
||||
#define EM_X86_64 62
|
||||
#define SHT_SYMTAB 2
|
||||
#define SHT_STRTAB 3
|
||||
#define SHT_RELA 4
|
||||
#define SHT_PROGBITS 1
|
||||
#define SHT_NOBITS 8
|
||||
|
||||
#define SHF_ALLOC 0x2
|
||||
#define SHF_WRITE 0x1
|
||||
#define SHF_EXECINSTR 0x4
|
||||
|
||||
#define R_X86_64_64 1
|
||||
#define R_X86_64_32 10
|
||||
#define R_X86_64_32S 11
|
||||
#define R_X86_64_PC32 2
|
||||
|
||||
void *memset(void *dst, int value, usize len);
|
||||
void *memcpy(void *dst, const void *src, usize len);
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile struct limine_module_request g_module_request = {
|
||||
.id = LIMINE_MODULE_REQUEST_ID,
|
||||
.revision = 0,
|
||||
.response = 0
|
||||
};
|
||||
|
||||
__attribute__((used, section(".limine_requests_start")))
|
||||
static volatile u64 g_limine_requests_start[4] = { LIMINE_COMMON_MAGIC, 0xf6b8f4b39de7d1aeULL, 0xfab91a6940fcb9cfULL };
|
||||
|
||||
__attribute__((used, section(".limine_requests_end")))
|
||||
static volatile u64 g_limine_requests_end[2] = { 0, 0 };
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile struct limine_rsdp_request g_rsdp_request = {
|
||||
.id = LIMINE_RSDP_REQUEST_ID,
|
||||
.revision = 0,
|
||||
.response = 0
|
||||
};
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile struct limine_framebuffer_request g_fb_request = {
|
||||
.id = LIMINE_FRAMEBUFFER_REQUEST_ID,
|
||||
.revision = 0,
|
||||
.response = 0
|
||||
};
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile struct limine_hhdm_request g_hhdm_request = {
|
||||
.id = { LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852ULL, 0x63984e959a98244bULL },
|
||||
.revision = 0,
|
||||
.response = 0
|
||||
};
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile struct limine_kernel_address_request g_kernel_address_request = {
|
||||
.id = LIMINE_KERNEL_ADDRESS_REQUEST_ID,
|
||||
.revision = 0,
|
||||
.response = 0
|
||||
};
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile struct limine_memmap_request g_memmap_request = {
|
||||
.id = LIMINE_MEMMAP_REQUEST_ID,
|
||||
.revision = 0,
|
||||
.response = 0
|
||||
};
|
||||
|
||||
typedef struct elf64_ehdr {
|
||||
u8 ident[16];
|
||||
u16 type;
|
||||
u16 machine;
|
||||
u32 version;
|
||||
u64 entry;
|
||||
u64 phoff;
|
||||
u64 shoff;
|
||||
u32 flags;
|
||||
u16 ehsize;
|
||||
u16 phentsize;
|
||||
u16 phnum;
|
||||
u16 shentsize;
|
||||
u16 shnum;
|
||||
u16 shstrndx;
|
||||
} elf64_ehdr;
|
||||
|
||||
typedef struct elf64_shdr {
|
||||
u32 name;
|
||||
u32 type;
|
||||
u64 flags;
|
||||
u64 addr;
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u32 link;
|
||||
u32 info;
|
||||
u64 addralign;
|
||||
u64 entsize;
|
||||
} elf64_shdr;
|
||||
|
||||
typedef struct elf64_sym {
|
||||
u32 name;
|
||||
u8 info;
|
||||
u8 other;
|
||||
u16 shndx;
|
||||
u64 value;
|
||||
u64 size;
|
||||
} elf64_sym;
|
||||
|
||||
typedef struct elf64_rela {
|
||||
u64 offset;
|
||||
u64 info;
|
||||
i64 addend;
|
||||
} elf64_rela;
|
||||
|
||||
static int streq(const char *a, const char *b) {
|
||||
usize i = 0;
|
||||
while (a[i] && b[i]) {
|
||||
if (a[i] != b[i]) {
|
||||
return 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return a[i] == b[i];
|
||||
}
|
||||
|
||||
int dkm_check_elf64(const void *address, u64 size) {
|
||||
if (!address || size < sizeof(elf64_ehdr)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const elf64_ehdr *ehdr = (const elf64_ehdr *)address;
|
||||
if (ehdr->ident[0] != ELF_MAGIC0 || ehdr->ident[1] != ELF_MAGIC1 || ehdr->ident[2] != ELF_MAGIC2 || ehdr->ident[3] != ELF_MAGIC3) {
|
||||
return -2;
|
||||
}
|
||||
if (ehdr->ident[4] != ELFCLASS64 || ehdr->ident[5] != ELFDATA2LSB) {
|
||||
return -3;
|
||||
}
|
||||
if (ehdr->machine != EM_X86_64) {
|
||||
return -4;
|
||||
}
|
||||
if (ehdr->type != ET_REL && ehdr->type != ET_DYN && ehdr->type != ET_EXEC) {
|
||||
return -5;
|
||||
}
|
||||
if (ehdr->ehsize < sizeof(elf64_ehdr)) {
|
||||
return -6;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int range_ok(u64 offset, u64 length, u64 size) {
|
||||
if (offset > size) {
|
||||
return 0;
|
||||
}
|
||||
if (length > size - offset) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out) {
|
||||
const elf64_ehdr *ehdr = (const elf64_ehdr *)address;
|
||||
out->driver_desc = 0;
|
||||
out->driver_init = 0;
|
||||
out->driver_exit = 0;
|
||||
|
||||
if (ehdr->shoff == 0 || ehdr->shnum == 0 || ehdr->shentsize < sizeof(elf64_shdr)) {
|
||||
log_error("[DKM] ELF has no section table");
|
||||
return -1;
|
||||
}
|
||||
if (!range_ok(ehdr->shoff, (u64)ehdr->shnum * ehdr->shentsize, size)) {
|
||||
log_error("[DKM] ELF section table out of range");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const elf64_shdr *sections = (const elf64_shdr *)((const u8 *)address + ehdr->shoff);
|
||||
const elf64_shdr *symtab = 0;
|
||||
const elf64_shdr *strtab = 0;
|
||||
|
||||
for (u16 i = 0; i < ehdr->shnum; i++) {
|
||||
const elf64_shdr *section = (const elf64_shdr *)((const u8 *)sections + (u64)i * ehdr->shentsize);
|
||||
if (section->type == SHT_SYMTAB) {
|
||||
symtab = section;
|
||||
if (section->link < ehdr->shnum) {
|
||||
strtab = (const elf64_shdr *)((const u8 *)sections + (u64)section->link * ehdr->shentsize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!symtab || !strtab || strtab->type != SHT_STRTAB) {
|
||||
log_error("[DKM] missing symtab/strtab");
|
||||
return -1;
|
||||
}
|
||||
if (symtab->entsize < sizeof(elf64_sym) || !range_ok(symtab->offset, symtab->size, size) || !range_ok(strtab->offset, strtab->size, size)) {
|
||||
log_error("[DKM] invalid symtab/strtab range");
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_info("[DKM] symtab found");
|
||||
log_hex64("[DKM] symbol count=", symtab->size / symtab->entsize);
|
||||
|
||||
for (u64 off = 0; off + sizeof(elf64_sym) <= symtab->size; off += symtab->entsize) {
|
||||
const elf64_sym *sym = (const elf64_sym *)((const u8 *)address + symtab->offset + off);
|
||||
if (sym->name >= strtab->size) {
|
||||
continue;
|
||||
}
|
||||
const char *name = (const char *)address + strtab->offset + sym->name;
|
||||
if (streq(name, "driver_desc")) {
|
||||
out->driver_desc = sym;
|
||||
log_info("[DKM] found driver_desc");
|
||||
log_hex64("[DKM] driver_desc section=", sym->shndx);
|
||||
log_hex64("[DKM] driver_desc value=", sym->value);
|
||||
} else if (streq(name, "driver_init")) {
|
||||
out->driver_init = sym;
|
||||
log_info("[DKM] found driver_init");
|
||||
log_hex64("[DKM] driver_init section=", sym->shndx);
|
||||
log_hex64("[DKM] driver_init value=", sym->value);
|
||||
} else if (streq(name, "driver_exit")) {
|
||||
out->driver_exit = sym;
|
||||
log_info("[DKM] found driver_exit");
|
||||
log_hex64("[DKM] driver_exit section=", sym->shndx);
|
||||
log_hex64("[DKM] driver_exit value=", sym->value);
|
||||
}
|
||||
}
|
||||
|
||||
if (out->driver_desc && out->driver_init && out->driver_exit) {
|
||||
log_info("[DKM] required driver symbols ok");
|
||||
return 0;
|
||||
}
|
||||
log_error("[DKM] required driver symbols missing");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan *symbols) {
|
||||
const elf64_ehdr *ehdr = (const elf64_ehdr *)address;
|
||||
const elf64_shdr *sections = (const elf64_shdr *)((const u8 *)address + ehdr->shoff);
|
||||
u64 image_offset[64];
|
||||
u64 total_size = 0;
|
||||
u64 max_align = 16;
|
||||
u16 alloc_count = 0;
|
||||
|
||||
for (u16 i = 1; i < ehdr->shnum && alloc_count < 64; i++) {
|
||||
const elf64_shdr *sh = (const elf64_shdr *)((const u8 *)sections + (u64)i * ehdr->shentsize);
|
||||
if (!(sh->flags & SHF_ALLOC)) {
|
||||
continue;
|
||||
}
|
||||
if (sh->addralign > max_align) {
|
||||
max_align = sh->addralign;
|
||||
}
|
||||
total_size = utsm_align_up_u64(total_size, sh->addralign);
|
||||
image_offset[i] = total_size;
|
||||
total_size += sh->size;
|
||||
alloc_count++;
|
||||
}
|
||||
|
||||
if (total_size == 0) {
|
||||
log_error("[DKM] no allocatable sections");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u8 *image = (u8 *)kmem_alloc_aligned(total_size, max_align);
|
||||
if (!image) {
|
||||
log_error("[DKM] failed to allocate module memory");
|
||||
return -1;
|
||||
}
|
||||
memset(image, 0, total_size);
|
||||
|
||||
for (u16 i = 1; i < ehdr->shnum; i++) {
|
||||
const elf64_shdr *sh = (const elf64_shdr *)((const u8 *)sections + (u64)i * ehdr->shentsize);
|
||||
if (!(sh->flags & SHF_ALLOC)) {
|
||||
continue;
|
||||
}
|
||||
if (sh->type == SHT_PROGBITS && sh->size > 0 && range_ok(sh->offset, sh->size, size)) {
|
||||
memcpy(image + image_offset[i], (const u8 *)address + sh->offset, sh->size);
|
||||
}
|
||||
}
|
||||
|
||||
for (u16 i = 1; i < ehdr->shnum; i++) {
|
||||
const elf64_shdr *sh = (const elf64_shdr *)((const u8 *)sections + (u64)i * ehdr->shentsize);
|
||||
if (sh->type != SHT_RELA) {
|
||||
continue;
|
||||
}
|
||||
if (sh->entsize < sizeof(elf64_rela) || !range_ok(sh->offset, sh->size, size)) {
|
||||
continue;
|
||||
}
|
||||
if (sh->info >= ehdr->shnum) {
|
||||
continue;
|
||||
}
|
||||
const elf64_shdr *target = (const elf64_shdr *)((const u8 *)sections + (u64)sh->info * ehdr->shentsize);
|
||||
if (!(target->flags & SHF_ALLOC)) {
|
||||
continue;
|
||||
}
|
||||
u8 *target_base = image + image_offset[sh->info];
|
||||
|
||||
const elf64_shdr *rela_symtab = 0;
|
||||
if (sh->link < ehdr->shnum) {
|
||||
rela_symtab = (const elf64_shdr *)((const u8 *)sections + (u64)sh->link * ehdr->shentsize);
|
||||
}
|
||||
const elf64_shdr *rela_strtab = 0;
|
||||
if (rela_symtab && rela_symtab->link < ehdr->shnum) {
|
||||
rela_strtab = (const elf64_shdr *)((const u8 *)sections + (u64)rela_symtab->link * ehdr->shentsize);
|
||||
}
|
||||
(void)rela_strtab;
|
||||
|
||||
for (u64 off = 0; off + sizeof(elf64_rela) <= sh->size; off += sh->entsize) {
|
||||
const elf64_rela *rela = (const elf64_rela *)((const u8 *)address + sh->offset + off);
|
||||
u32 sym_idx = (u32)(rela->info >> 32);
|
||||
u32 reloc_type = (u32)(rela->info & 0xffffffff);
|
||||
|
||||
u64 sym_value = 0;
|
||||
u16 sym_shndx = 0;
|
||||
if (rela_symtab && rela_strtab &&
|
||||
rela_symtab->entsize >= sizeof(elf64_sym) &&
|
||||
range_ok(rela_symtab->offset, rela_symtab->size, size)) {
|
||||
u64 sym_entry_off = (u64)sym_idx * rela_symtab->entsize;
|
||||
if (sym_entry_off + sizeof(elf64_sym) <= rela_symtab->size) {
|
||||
const elf64_sym *sym = (const elf64_sym *)((const u8 *)address + rela_symtab->offset + sym_entry_off);
|
||||
sym_value = sym->value;
|
||||
sym_shndx = sym->shndx;
|
||||
}
|
||||
}
|
||||
|
||||
u64 S = sym_value;
|
||||
if (sym_shndx > 0 && sym_shndx < 64) {
|
||||
S = (u64)image + image_offset[sym_shndx] + sym_value;
|
||||
}
|
||||
u64 P = (u64)image + image_offset[sh->info] + rela->offset;
|
||||
u64 A = rela->addend;
|
||||
|
||||
u64 patch = 0;
|
||||
if (reloc_type == R_X86_64_64) {
|
||||
patch = S + A;
|
||||
} else if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S) {
|
||||
patch = (u64)((i64)(i32)(S + A));
|
||||
} else if (reloc_type == R_X86_64_PC32) {
|
||||
patch = (u64)((i64)(i32)(S + A - P));
|
||||
} else {
|
||||
log_error("[DKM] unsupported relocation type");
|
||||
log_hex64("[DKM] reloc type=", (u64)reloc_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rela->offset + 8 <= target->size) {
|
||||
if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S || reloc_type == R_X86_64_PC32) {
|
||||
*(u32 *)(target_base + rela->offset) = (u32)patch;
|
||||
} else {
|
||||
*(u64 *)(target_base + rela->offset) = patch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const elf64_sym *desc_sym = (const elf64_sym *)symbols->driver_desc;
|
||||
const elf64_sym *init_sym = (const elf64_sym *)symbols->driver_init;
|
||||
const dkm_driver_desc *desc = (const dkm_driver_desc *)(image + image_offset[desc_sym->shndx] + desc_sym->value);
|
||||
if (desc->magic != DKM_DRIVER_MAGIC) {
|
||||
log_error("[DKM] external driver_desc magic mismatch");
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_info("[DKM] external driver loaded");
|
||||
log_hex64("[DKM] desc ptr=", (u64)desc);
|
||||
log_hex64("[DKM] name ptr=", (u64)desc->name);
|
||||
log_info(desc->name);
|
||||
|
||||
dkm_driver_init_fn init = (dkm_driver_init_fn)(image + image_offset[init_sym->shndx] + init_sym->value);
|
||||
|
||||
dkm_driver_handle handle;
|
||||
handle.desc = desc;
|
||||
handle.state = DKM_STATE_INITING;
|
||||
handle.init_status = 0;
|
||||
handle.load_stage = desc->stage;
|
||||
handle.flags = desc->flags;
|
||||
|
||||
int result = init(dkm_get_kernel_api(), &handle);
|
||||
handle.init_status = result;
|
||||
if (result != 0) {
|
||||
handle.state = DKM_STATE_FAILED;
|
||||
log_error("[DKM] external driver init failed");
|
||||
return result;
|
||||
}
|
||||
handle.state = DKM_STATE_ACTIVE;
|
||||
log_info("[DKM] external driver active");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dkm_scan_boot_modules(void) {
|
||||
struct limine_module_response *response = g_module_request.response;
|
||||
if (!response) {
|
||||
log_warn("[DKM] no Limine module response");
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("[DKM] boot modules scan begin");
|
||||
log_hex64("[DKM] boot module count=", response->module_count);
|
||||
|
||||
for (u64 i = 0; i < response->module_count; i++) {
|
||||
struct limine_file *file = response->modules[i];
|
||||
if (!file) {
|
||||
log_warn("[DKM] null module entry");
|
||||
continue;
|
||||
}
|
||||
|
||||
log_info("[DKM] module path");
|
||||
if (file->path) {
|
||||
log_info(file->path);
|
||||
}
|
||||
if (file->cmdline) {
|
||||
log_info(file->cmdline);
|
||||
}
|
||||
log_hex64("[DKM] module size=", file->size);
|
||||
|
||||
int status = dkm_check_elf64(file->address, file->size);
|
||||
if (status == 0) {
|
||||
log_info("[DKM] module ELF64 x86_64 ok");
|
||||
struct dkm_symbol_scan symbols;
|
||||
if (dkm_scan_symbols(file->address, file->size, &symbols) == 0) {
|
||||
log_info("[DKM] loading external ELF driver");
|
||||
int load_result = dkm_load_elf_rel(file->address, file->size, &symbols);
|
||||
if (load_result != 0) {
|
||||
log_error("[DKM] external driver load failed");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log_error("[DKM] module ELF64 rejected");
|
||||
log_hex64("[DKM] elf status=", (u64)(i64)status);
|
||||
}
|
||||
}
|
||||
|
||||
log_info("[DKM] boot modules scan end");
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,47 @@
|
||||
#include <utsm/dkm.h>
|
||||
|
||||
static const char *const console_early_provides[] = {
|
||||
"console",
|
||||
"early-log"
|
||||
};
|
||||
|
||||
static const dkm_driver_desc console_early_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(dkm_driver_desc),
|
||||
.name = "console_early",
|
||||
.version = "0.1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = DKM_CLASS_CONSOLE,
|
||||
.stage = 0,
|
||||
.flags = DKM_F_REQUIRED | DKM_F_BOOT_MODULE | DKM_F_NO_UNLOAD | DKM_F_EARLY_LOG,
|
||||
.priority = 0,
|
||||
.depends = 0,
|
||||
.depends_count = 0,
|
||||
.provides = console_early_provides,
|
||||
.provides_count = 2,
|
||||
.min_kernel_abi = DKM_KERNEL_API_VERSION,
|
||||
.feature_bits = 0,
|
||||
.reserved0 = 0,
|
||||
.reserved1 = 0
|
||||
};
|
||||
|
||||
static int console_early_init(const dkm_kernel_api *api, dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
if (!api || !api->log || !api->log->info) {
|
||||
return -1;
|
||||
}
|
||||
api->log->info("[DKM:console_early] serial console online");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int console_early_exit(dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const dkm_builtin_driver dkm_builtin_console_early = {
|
||||
.desc = &console_early_desc,
|
||||
.init = console_early_init,
|
||||
.exit = console_early_exit
|
||||
};
|
||||
Binary file not shown.
@@ -0,0 +1,67 @@
|
||||
#include <utsm/dkm.h>
|
||||
#include <utsm/log.h>
|
||||
|
||||
static int dkm_validate_desc(const dkm_driver_desc *desc) {
|
||||
if (!desc) {
|
||||
return -1;
|
||||
}
|
||||
if (desc->magic != DKM_DRIVER_MAGIC) {
|
||||
return -2;
|
||||
}
|
||||
if (desc->abi_version != DKM_ABI_VERSION) {
|
||||
return -3;
|
||||
}
|
||||
if (desc->desc_size < sizeof(dkm_driver_desc)) {
|
||||
return -4;
|
||||
}
|
||||
if (!desc->name || !desc->version) {
|
||||
return -5;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dkm_init(void) {
|
||||
log_info("[DKM] init begin");
|
||||
int status = dkm_load_builtin(&dkm_builtin_console_early);
|
||||
if (status == 0) {
|
||||
log_info("[DKM] init ok");
|
||||
} else {
|
||||
log_error("[DKM] init failed");
|
||||
}
|
||||
}
|
||||
|
||||
int dkm_load_builtin(const dkm_builtin_driver *driver) {
|
||||
if (!driver || !driver->desc || !driver->init) {
|
||||
log_error("[DKM] invalid builtin driver");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int status = dkm_validate_desc(driver->desc);
|
||||
if (status != 0) {
|
||||
log_error("[DKM] driver_desc rejected");
|
||||
return status;
|
||||
}
|
||||
|
||||
dkm_driver_handle handle;
|
||||
handle.desc = driver->desc;
|
||||
handle.state = DKM_STATE_ABI_CHECKED;
|
||||
handle.init_status = 0;
|
||||
handle.load_stage = driver->desc->stage;
|
||||
handle.flags = driver->desc->flags;
|
||||
|
||||
log_info("[DKM] builtin driver init begin");
|
||||
log_info(driver->desc->name);
|
||||
handle.state = DKM_STATE_INITING;
|
||||
status = driver->init(dkm_get_kernel_api(), &handle);
|
||||
handle.init_status = status;
|
||||
|
||||
if (status != 0) {
|
||||
handle.state = DKM_STATE_FAILED;
|
||||
log_error("[DKM] builtin driver init failed");
|
||||
return status;
|
||||
}
|
||||
|
||||
handle.state = DKM_STATE_ACTIVE;
|
||||
log_info("[DKM] builtin driver active");
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,90 @@
|
||||
#include <utsm/dkm.h>
|
||||
#include <utsm/log.h>
|
||||
#include <utsm/idt.h>
|
||||
#include <utsm/net.h>
|
||||
|
||||
static void dkm_api_info(const char *msg) {
|
||||
log_info(msg);
|
||||
}
|
||||
|
||||
static void dkm_api_warn(const char *msg) {
|
||||
log_warn(msg);
|
||||
}
|
||||
|
||||
static void dkm_api_error(const char *msg) {
|
||||
log_error(msg);
|
||||
}
|
||||
|
||||
static void dkm_api_panic(const char *msg) {
|
||||
log_error(msg);
|
||||
for (;;) {
|
||||
__asm__ volatile("cli; hlt");
|
||||
}
|
||||
}
|
||||
|
||||
static const dkm_log_api g_log_api = {
|
||||
.info = dkm_api_info,
|
||||
.warn = dkm_api_warn,
|
||||
.error = dkm_api_error,
|
||||
.panic = dkm_api_panic
|
||||
};
|
||||
|
||||
static dkm_kernel_api g_kernel_api = {
|
||||
.version = DKM_KERNEL_API_VERSION,
|
||||
.size = sizeof(dkm_kernel_api),
|
||||
.feature_bits = 0,
|
||||
.log = &g_log_api,
|
||||
.mem = 0,
|
||||
.utsm = 0,
|
||||
.irq = 0,
|
||||
.pci = 0,
|
||||
.dma = 0,
|
||||
.vfs = 0,
|
||||
.net = 0,
|
||||
.timer = 0,
|
||||
.drr = 0,
|
||||
.rsdp_address = 0,
|
||||
.fb_address = 0,
|
||||
.fb_width = 0,
|
||||
.fb_height = 0,
|
||||
.fb_pitch = 0,
|
||||
.fb_bpp = 0,
|
||||
.boot_modules_response = 0,
|
||||
.irq_register = 0,
|
||||
.hhdm_offset = 0,
|
||||
.block = 0
|
||||
};
|
||||
|
||||
const dkm_kernel_api *dkm_get_kernel_api(void) {
|
||||
return &g_kernel_api;
|
||||
}
|
||||
|
||||
#include "../arch/x86_64/limine.h"
|
||||
extern volatile struct limine_rsdp_request g_rsdp_request;
|
||||
extern volatile struct limine_framebuffer_request g_fb_request;
|
||||
extern volatile struct limine_module_request g_module_request;
|
||||
extern volatile struct limine_hhdm_request g_hhdm_request;
|
||||
|
||||
void dkm_fill_platform_info(void) {
|
||||
g_kernel_api.dma = dma_get_api();
|
||||
g_kernel_api.net = net_get_api();
|
||||
g_kernel_api.block = block_get_api();
|
||||
if (g_rsdp_request.response && g_rsdp_request.response->address) {
|
||||
g_kernel_api.rsdp_address = g_rsdp_request.response->address;
|
||||
}
|
||||
if (g_fb_request.response && g_fb_request.response->framebuffer_count > 0) {
|
||||
struct limine_framebuffer *fb = g_fb_request.response->framebuffers[0];
|
||||
g_kernel_api.fb_address = fb->address;
|
||||
g_kernel_api.fb_width = fb->width;
|
||||
g_kernel_api.fb_height = fb->height;
|
||||
g_kernel_api.fb_pitch = fb->pitch;
|
||||
g_kernel_api.fb_bpp = fb->bpp;
|
||||
}
|
||||
if (g_module_request.response) {
|
||||
g_kernel_api.boot_modules_response = g_module_request.response;
|
||||
}
|
||||
g_kernel_api.irq_register = (int (*)(u8,void*))((u64)irq_register);
|
||||
if (g_hhdm_request.response) {
|
||||
g_kernel_api.hhdm_offset = g_hhdm_request.response->offset;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,359 @@
|
||||
#include <utsm/dkm.h>
|
||||
#include <utsm/log.h>
|
||||
#include <utsm/types.h>
|
||||
#include "../arch/x86_64/limine.h"
|
||||
|
||||
#define DSM_MAX_DRIVERS_PER_STAGE 8
|
||||
#define DSM_MAX_STAGES 4
|
||||
#define DSM_MAX_DEPENDS 4
|
||||
#define DSM_MAX_PROVIDES 4
|
||||
|
||||
typedef struct dsm_driver_entry {
|
||||
char name[32];
|
||||
char path[64];
|
||||
u32 class_id;
|
||||
u32 required;
|
||||
char depends[DSM_MAX_DEPENDS][32];
|
||||
u32 depend_count;
|
||||
char provides[DSM_MAX_PROVIDES][32];
|
||||
u32 provide_count;
|
||||
struct limine_file *boot_module;
|
||||
int loaded;
|
||||
} dsm_driver_entry;
|
||||
|
||||
typedef struct dsm_stage_entry {
|
||||
u32 id;
|
||||
char name[16];
|
||||
u32 required;
|
||||
dsm_driver_entry drivers[DSM_MAX_DRIVERS_PER_STAGE];
|
||||
u32 driver_count;
|
||||
} dsm_stage_entry;
|
||||
|
||||
static dsm_stage_entry g_stages[DSM_MAX_STAGES];
|
||||
static u32 g_stage_count;
|
||||
|
||||
extern volatile struct limine_module_request g_module_request;
|
||||
|
||||
static int dsm_skip_ws(const char **p) {
|
||||
while (**p == ' ' || **p == '\t' || **p == '\n' || **p == '\r') { (*p)++; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsm_expect(const char **p, char c) {
|
||||
dsm_skip_ws(p);
|
||||
if (**p == c) { (*p)++; return 0; }
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int dsm_read_string(const char **p, char *out, u32 max_len) {
|
||||
dsm_skip_ws(p);
|
||||
if (**p != '"') return -1;
|
||||
(*p)++;
|
||||
u32 i = 0;
|
||||
while (**p && **p != '"' && i + 1 < max_len) {
|
||||
out[i++] = **p;
|
||||
(*p)++;
|
||||
}
|
||||
out[i] = 0;
|
||||
if (**p != '"') return -1;
|
||||
(*p)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsm_skip_value(const char **p) {
|
||||
dsm_skip_ws(p);
|
||||
if (**p == '"') {
|
||||
char tmp[256];
|
||||
return dsm_read_string(p, tmp, sizeof(tmp));
|
||||
}
|
||||
if (**p == '{') {
|
||||
(*p)++;
|
||||
int depth = 1;
|
||||
while (depth > 0 && **p) {
|
||||
if (**p == '{') depth++;
|
||||
else if (**p == '}') depth--;
|
||||
else if (**p == '"') { (*p)++; while (**p && **p != '"') (*p)++; if (**p) (*p)++; continue; }
|
||||
if (depth > 0) (*p)++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (**p == '[') {
|
||||
(*p)++;
|
||||
int depth = 1;
|
||||
while (depth > 0 && **p) {
|
||||
if (**p == '[') depth++;
|
||||
else if (**p == ']') depth--;
|
||||
else if (**p == '"') { (*p)++; while (**p && **p != '"') (*p)++; if (**p) (*p)++; continue; }
|
||||
if (depth > 0) (*p)++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
while (**p && **p != ',' && **p != '}' && **p != ']') (*p)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct limine_file *dsm_find_boot_module(const char *path) {
|
||||
struct limine_module_response *rsp = g_module_request.response;
|
||||
if (!rsp) return 0;
|
||||
for (u64 i = 0; i < rsp->module_count; i++) {
|
||||
struct limine_file *f = rsp->modules[i];
|
||||
if (f && f->path) {
|
||||
const char *fp = f->path;
|
||||
while (*fp == '/') fp++;
|
||||
usize j = 0;
|
||||
usize k = 0;
|
||||
while (fp[j]) j++;
|
||||
while (path[k]) k++;
|
||||
if (j == k) {
|
||||
int match = 1;
|
||||
for (usize x = 0; x < j; x++) {
|
||||
if (fp[x] != path[x]) { match = 0; break; }
|
||||
}
|
||||
if (match) return f;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int streq_static(const char *a, const char *b) {
|
||||
usize i = 0;
|
||||
while (a[i] && b[i]) {
|
||||
if (a[i] != b[i]) return 0;
|
||||
i++;
|
||||
}
|
||||
return a[i] == b[i];
|
||||
}
|
||||
|
||||
static void dsm_parse_driver(const char **p, dsm_driver_entry *drv) {
|
||||
drv->depend_count = 0;
|
||||
drv->provide_count = 0;
|
||||
drv->required = 1;
|
||||
drv->boot_module = 0;
|
||||
drv->loaded = 0;
|
||||
drv->class_id = 0;
|
||||
|
||||
if (dsm_expect(p, '{') != 0) return;
|
||||
while (**p && **p != '}') {
|
||||
dsm_skip_ws(p);
|
||||
if (**p == '}') break;
|
||||
char key[32];
|
||||
if (dsm_read_string(p, key, sizeof(key)) != 0) return;
|
||||
dsm_expect(p, ':');
|
||||
|
||||
if (streq_static(key, "name")) {
|
||||
dsm_read_string(p, drv->name, sizeof(drv->name));
|
||||
} else if (streq_static(key, "path")) {
|
||||
dsm_read_string(p, drv->path, sizeof(drv->path));
|
||||
} else if (streq_static(key, "class")) {
|
||||
char cls[32];
|
||||
dsm_read_string(p, cls, sizeof(cls));
|
||||
if (streq_static(cls, "platform")) drv->class_id = 1;
|
||||
else if (streq_static(cls, "bus")) drv->class_id = 2;
|
||||
else if (streq_static(cls, "interrupt")) drv->class_id = 3;
|
||||
else if (streq_static(cls, "timer")) drv->class_id = 4;
|
||||
else if (streq_static(cls, "console")) drv->class_id = 5;
|
||||
else if (streq_static(cls, "storage")) drv->class_id = 6;
|
||||
else if (streq_static(cls, "fs")) drv->class_id = 7;
|
||||
else if (streq_static(cls, "fs-core")) drv->class_id = 8;
|
||||
else if (streq_static(cls, "net")) drv->class_id = 9;
|
||||
else if (streq_static(cls, "input")) drv->class_id = 10;
|
||||
} else if (streq_static(key, "required")) {
|
||||
dsm_skip_ws(p);
|
||||
if (**p == 't') { drv->required = 1; (*p) += 4; }
|
||||
else if (**p == 'f') { drv->required = 0; (*p) += 5; }
|
||||
} else if (streq_static(key, "depends")) {
|
||||
if (dsm_expect(p, '[') != 0) return;
|
||||
while (**p && **p != ']') {
|
||||
dsm_skip_ws(p);
|
||||
if (**p == ']') break;
|
||||
if (drv->depend_count < DSM_MAX_DEPENDS) {
|
||||
dsm_read_string(p, drv->depends[drv->depend_count], 32);
|
||||
drv->depend_count++;
|
||||
} else { dsm_skip_value(p); }
|
||||
dsm_skip_ws(p);
|
||||
if (**p == ',') { (*p)++; continue; }
|
||||
if (**p == ']') break;
|
||||
}
|
||||
dsm_expect(p, ']');
|
||||
} else if (streq_static(key, "provides")) {
|
||||
if (dsm_expect(p, '[') != 0) return;
|
||||
while (**p && **p != ']') {
|
||||
dsm_skip_ws(p);
|
||||
if (**p == ']') break;
|
||||
if (drv->provide_count < DSM_MAX_PROVIDES) {
|
||||
dsm_read_string(p, drv->provides[drv->provide_count], 32);
|
||||
drv->provide_count++;
|
||||
} else { dsm_skip_value(p); }
|
||||
dsm_skip_ws(p);
|
||||
if (**p == ',') { (*p)++; continue; }
|
||||
if (**p == ']') break;
|
||||
}
|
||||
dsm_expect(p, ']');
|
||||
} else {
|
||||
dsm_skip_value(p);
|
||||
}
|
||||
dsm_skip_ws(p);
|
||||
if (**p == ',') (*p)++;
|
||||
}
|
||||
dsm_expect(p, '}');
|
||||
}
|
||||
|
||||
static void dsm_parse_stage(const char **p, dsm_stage_entry *stage) {
|
||||
stage->driver_count = 0;
|
||||
if (dsm_expect(p, '{') != 0) return;
|
||||
while (**p && **p != '}') {
|
||||
dsm_skip_ws(p);
|
||||
if (**p == '}') break;
|
||||
char key[32];
|
||||
if (dsm_read_string(p, key, sizeof(key)) != 0) return;
|
||||
dsm_expect(p, ':');
|
||||
|
||||
if (streq_static(key, "id")) {
|
||||
dsm_skip_ws(p);
|
||||
u32 val = 0;
|
||||
while (**p >= '0' && **p <= '9') { val = val * 10 + (u32)(**p - '0'); (*p)++; }
|
||||
stage->id = val;
|
||||
} else if (streq_static(key, "name")) {
|
||||
dsm_read_string(p, stage->name, sizeof(stage->name));
|
||||
} else if (streq_static(key, "required")) {
|
||||
dsm_skip_ws(p);
|
||||
if (**p == 't') { stage->required = 1; (*p) += 4; }
|
||||
else if (**p == 'f') { stage->required = 0; (*p) += 5; }
|
||||
} else if (streq_static(key, "drivers")) {
|
||||
if (dsm_expect(p, '[') != 0) return;
|
||||
while (**p && **p != ']') {
|
||||
dsm_skip_ws(p);
|
||||
if (**p == ']') break;
|
||||
if (stage->driver_count < DSM_MAX_DRIVERS_PER_STAGE) {
|
||||
dsm_parse_driver(p, &stage->drivers[stage->driver_count]);
|
||||
stage->driver_count++;
|
||||
} else {
|
||||
dsm_skip_value(p);
|
||||
}
|
||||
dsm_skip_ws(p);
|
||||
if (**p == ',') { (*p)++; continue; }
|
||||
if (**p == ']') break;
|
||||
}
|
||||
dsm_expect(p, ']');
|
||||
} else {
|
||||
dsm_skip_value(p);
|
||||
}
|
||||
dsm_skip_ws(p);
|
||||
if (**p == ',') (*p)++;
|
||||
}
|
||||
dsm_expect(p, '}');
|
||||
}
|
||||
|
||||
static int dsm_parse_manifest(const char *text, u64 size) {
|
||||
g_stage_count = 0;
|
||||
const char *p = text;
|
||||
const char *end = text + size;
|
||||
|
||||
while (p < end && *p) {
|
||||
dsm_skip_ws(&p);
|
||||
if (p >= end || !*p) break;
|
||||
if (*p == '{' || *p == '}') { p++; continue; }
|
||||
if (*p == ',' || *p == ':') { p++; continue; }
|
||||
if (*p == '[' || *p == ']') { p++; continue; }
|
||||
|
||||
char key[32];
|
||||
if (dsm_read_string(&p, key, sizeof(key)) != 0) break;
|
||||
dsm_expect(&p, ':');
|
||||
|
||||
if (streq_static(key, "stages")) {
|
||||
if (dsm_expect(&p, '[') != 0) return -1;
|
||||
while (p < end && *p) {
|
||||
dsm_skip_ws(&p);
|
||||
if (*p == ']') { p++; break; }
|
||||
if (g_stage_count < DSM_MAX_STAGES) {
|
||||
dsm_parse_stage(&p, &g_stages[g_stage_count]);
|
||||
g_stage_count++;
|
||||
}
|
||||
dsm_skip_ws(&p);
|
||||
if (*p == ',') p++;
|
||||
}
|
||||
} else {
|
||||
dsm_skip_value(&p);
|
||||
}
|
||||
dsm_skip_ws(&p);
|
||||
if (*p == ',' || *p == '}' || *p == ']') p++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dsm_load_by_manifest(void) {
|
||||
struct limine_module_response *rsp = g_module_request.response;
|
||||
if (!rsp) {
|
||||
log_warn("[DSM] no Limine module response");
|
||||
return;
|
||||
}
|
||||
|
||||
struct limine_file *manifest_file = 0;
|
||||
for (u64 i = 0; i < rsp->module_count; i++) {
|
||||
struct limine_file *f = rsp->modules[i];
|
||||
if (f && f->path && f->cmdline && streq_static(f->cmdline, "dkm:manifest")) {
|
||||
manifest_file = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!manifest_file) {
|
||||
log_warn("[DSM] manifest not found in boot modules");
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("[DSM] parsing manifest");
|
||||
dsm_parse_manifest((const char *)manifest_file->address, manifest_file->size);
|
||||
log_info("[DSM] manifest parsed");
|
||||
log_hex64("[DSM] stages=", (u64)g_stage_count);
|
||||
|
||||
for (u32 si = 0; si < g_stage_count; si++) {
|
||||
dsm_stage_entry *stage = &g_stages[si];
|
||||
log_info("[DSM] stage begin");
|
||||
log_info(stage->name);
|
||||
log_hex64("[DSM] driver count=", stage->driver_count);
|
||||
|
||||
for (u32 di = 0; di < stage->driver_count; di++) {
|
||||
dsm_driver_entry *drv = &stage->drivers[di];
|
||||
|
||||
drv->boot_module = dsm_find_boot_module(drv->path);
|
||||
if (!drv->boot_module) {
|
||||
log_info("[DSM] module not found");
|
||||
log_info(drv->name);
|
||||
if (drv->required && stage->required) {
|
||||
log_error("[DSM] required module missing, abort stage");
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
struct limine_file *f = drv->boot_module;
|
||||
log_info("[DSM] loading driver");
|
||||
log_info(drv->name);
|
||||
|
||||
if (dkm_check_elf64(f->address, f->size) != 0) {
|
||||
log_error("[DSM] ELF check failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
struct dkm_symbol_scan symbols;
|
||||
if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) {
|
||||
log_error("[DSM] symbol scan failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
int result = dkm_load_elf_rel(f->address, f->size, &symbols);
|
||||
if (result != 0) {
|
||||
log_error("[DSM] load failed");
|
||||
if (drv->required && stage->required) {
|
||||
log_error("[DSM] required driver failed, abort stage");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
drv->loaded = 1;
|
||||
}
|
||||
}
|
||||
log_info("[DSM] stage end");
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,37 @@
|
||||
#include <utsm/drr.h>
|
||||
#include <utsm/log.h>
|
||||
|
||||
static const u64 g_root_key[4] = {
|
||||
0x4452525554534d31ULL,
|
||||
0xfeedfacecafebeefULL,
|
||||
0x0123456789abcdefULL,
|
||||
0xfedcba9876543210ULL
|
||||
};
|
||||
|
||||
void drr_stub_init(void) {
|
||||
}
|
||||
|
||||
const u64 *drr_get_root_key(void) {
|
||||
return g_root_key;
|
||||
}
|
||||
|
||||
u64 drr_recovery_generation(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void drr_report_fault(const char *reason) {
|
||||
log_error("[DRR] fault reported");
|
||||
log_error(reason);
|
||||
}
|
||||
|
||||
void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len) {
|
||||
UTSM_UNUSED(segment_slot);
|
||||
UTSM_UNUSED(offset);
|
||||
UTSM_UNUSED(len);
|
||||
}
|
||||
|
||||
void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len) {
|
||||
UTSM_UNUSED(segment_slot);
|
||||
UTSM_UNUSED(offset);
|
||||
UTSM_UNUSED(len);
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
#ifndef UTSM_ARENA_H
|
||||
#define UTSM_ARENA_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
void arena_init(void);
|
||||
void *kmem_alloc(u64 size);
|
||||
void *kmem_alloc_aligned(u64 size, u64 alignment);
|
||||
void *kmem_alloc_page(u64 page_count);
|
||||
u64 kmem_arena_virtual_base(void);
|
||||
u64 kmem_arena_virtual_end(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef UTSM_ASSERT_H
|
||||
#define UTSM_ASSERT_H
|
||||
|
||||
#include <utsm/log.h>
|
||||
|
||||
#define UTSM_ASSERT(cond) do { if (!(cond)) { panic("assertion failed: " #cond); } } while (0)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef UTSM_BLOCK_H
|
||||
#define UTSM_BLOCK_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
#define DKM_BLOCK_MAX_DEVICES 8u
|
||||
|
||||
typedef int (*dkm_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer);
|
||||
|
||||
typedef struct dkm_block_device_desc {
|
||||
const char *name;
|
||||
u64 sector_size;
|
||||
u64 sector_count;
|
||||
void *ctx;
|
||||
dkm_block_read_fn read;
|
||||
} dkm_block_device_desc;
|
||||
|
||||
typedef struct dkm_block_api {
|
||||
int (*register_device)(const dkm_block_device_desc *desc);
|
||||
u32 (*device_count)(void);
|
||||
int (*read)(u32 index, u64 lba, u32 count, void *buffer);
|
||||
u64 (*sector_size)(u32 index);
|
||||
const char *(*device_name)(u32 index);
|
||||
} dkm_block_api;
|
||||
|
||||
void block_init(void);
|
||||
const dkm_block_api *block_get_api(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef UTSM_CAPABILITY_H
|
||||
#define UTSM_CAPABILITY_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
#include <utsm/status.h>
|
||||
|
||||
#define UTSM_RIGHT_READ (1U << 0)
|
||||
#define UTSM_RIGHT_WRITE (1U << 1)
|
||||
#define UTSM_RIGHT_EXEC (1U << 2)
|
||||
#define UTSM_RIGHT_SHARE (1U << 3)
|
||||
#define UTSM_RIGHT_DMA (1U << 4)
|
||||
|
||||
struct utsm_segment_desc;
|
||||
|
||||
typedef struct {
|
||||
u32 segment_slot;
|
||||
u32 generation;
|
||||
u32 rights;
|
||||
u32 epoch;
|
||||
u64 uuid_digest;
|
||||
u64 auth_tag;
|
||||
} utsm_capability;
|
||||
|
||||
int utsm_cap_validate(utsm_capability cap, u32 required_rights, u64 offset, u64 len, struct utsm_segment_desc **out_desc);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef UTSM_CONFIG_H
|
||||
#define UTSM_CONFIG_H
|
||||
|
||||
#define UTSM_CACHE_LINE_SIZE 64ULL
|
||||
#define UTSM_PAGE_SIZE 4096ULL
|
||||
#define UTSM_MAX_SEGMENTS 1024U
|
||||
#define UTSM_MAX_CAPABILITIES 256U
|
||||
#define UTSM_MAX_PCKC_KEYS 8U
|
||||
#define UTSM_ARENA_SIZE (16ULL * 1024ULL * 1024ULL)
|
||||
#define UTSM_DIRTY_SHARD_PAGES 1024ULL
|
||||
#define UTSM_KERNEL_STACK_SIZE 16384ULL
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef UTSM_CRYPTO_H
|
||||
#define UTSM_CRYPTO_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
#include <utsm/uuid.h>
|
||||
|
||||
struct utsm_segment_desc;
|
||||
|
||||
typedef struct {
|
||||
u64 words[4];
|
||||
} utsm_key_material;
|
||||
|
||||
void utsm_kdf_segment_key(uuid128_t process_uuid, uuid128_t segment_uuid, u64 key_epoch, utsm_key_material *out_key);
|
||||
u64 utsm_make_tweak(u64 tweak_seed, u64 line_index, u64 key_epoch);
|
||||
void utsm_crypt_line(const utsm_key_material *key, u64 tweak, const u8 *in, u8 *out);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef UTSM_DIRTY_H
|
||||
#define UTSM_DIRTY_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
typedef struct {
|
||||
u32 dirty_count;
|
||||
u32 shard_id;
|
||||
u64 page_base;
|
||||
u64 page_count;
|
||||
u64 last_checkpoint_epoch;
|
||||
u64 *bitmap;
|
||||
} utsm_dirty_shard;
|
||||
|
||||
struct utsm_segment_desc;
|
||||
|
||||
void utsm_dirty_init(struct utsm_segment_desc *desc, u64 page_count);
|
||||
void utsm_mark_dirty(struct utsm_segment_desc *desc, u64 offset, u64 len);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,141 @@
|
||||
#ifndef UTSM_DKM_H
|
||||
#define UTSM_DKM_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
#include <utsm/dma.h>
|
||||
#include <utsm/block.h>
|
||||
#include <utsm/net.h>
|
||||
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||
#define DKM_ABI_VERSION 1u
|
||||
#define DKM_KERNEL_API_VERSION 1u
|
||||
|
||||
#define DKM_CLASS_PLATFORM 1u
|
||||
#define DKM_CLASS_BUS 2u
|
||||
#define DKM_CLASS_INTERRUPT 3u
|
||||
#define DKM_CLASS_TIMER 4u
|
||||
#define DKM_CLASS_CONSOLE 5u
|
||||
#define DKM_CLASS_STORAGE 6u
|
||||
#define DKM_CLASS_FS 7u
|
||||
#define DKM_CLASS_NET 8u
|
||||
#define DKM_CLASS_INPUT 9u
|
||||
#define DKM_CLASS_GPU 10u
|
||||
#define DKM_CLASS_MISC 11u
|
||||
|
||||
#define DKM_F_REQUIRED (1u << 0)
|
||||
#define DKM_F_BOOT_MODULE (1u << 1)
|
||||
#define DKM_F_NO_UNLOAD (1u << 2)
|
||||
#define DKM_F_STRONG_RECOVERY (1u << 3)
|
||||
#define DKM_F_DMA_REQUIRED (1u << 4)
|
||||
#define DKM_F_EARLY_LOG (1u << 5)
|
||||
|
||||
struct dkm_kernel_api;
|
||||
struct dkm_driver_handle;
|
||||
|
||||
typedef struct dkm_driver_desc {
|
||||
u32 magic;
|
||||
u16 abi_version;
|
||||
u16 desc_size;
|
||||
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
|
||||
u32 driver_class;
|
||||
u32 stage;
|
||||
u32 flags;
|
||||
u32 priority;
|
||||
|
||||
const char *const *depends;
|
||||
u32 depends_count;
|
||||
|
||||
const char *const *provides;
|
||||
u32 provides_count;
|
||||
|
||||
u64 min_kernel_abi;
|
||||
u64 feature_bits;
|
||||
|
||||
u64 reserved0;
|
||||
u64 reserved1;
|
||||
} dkm_driver_desc;
|
||||
|
||||
typedef int (*dkm_driver_init_fn)(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle);
|
||||
typedef int (*dkm_driver_exit_fn)(struct dkm_driver_handle *handle);
|
||||
|
||||
typedef struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
} dkm_log_api;
|
||||
|
||||
typedef struct dkm_kernel_api {
|
||||
u32 version;
|
||||
u32 size;
|
||||
u64 feature_bits;
|
||||
|
||||
const dkm_log_api *log;
|
||||
const void *mem;
|
||||
const void *utsm;
|
||||
const void *irq;
|
||||
const void *pci;
|
||||
const dkm_dma_api *dma;
|
||||
const void *vfs;
|
||||
const dkm_net_api *net;
|
||||
const void *timer;
|
||||
const void *drr;
|
||||
|
||||
const void *rsdp_address; /* ACPI RSDP physical address */
|
||||
const void *fb_address; /* framebuffer base */
|
||||
u64 fb_width;
|
||||
u64 fb_height;
|
||||
u64 fb_pitch;
|
||||
u16 fb_bpp;
|
||||
const void *boot_modules_response; /* limine_module_response* */
|
||||
int (*irq_register)(u8 irq, void *handler); /* kernel irq_register */
|
||||
u64 hhdm_offset; /* physical → virtual */
|
||||
const dkm_block_api *block; /* block provider registry */
|
||||
} dkm_kernel_api;
|
||||
|
||||
typedef enum dkm_driver_state {
|
||||
DKM_STATE_DISCOVERED = 0,
|
||||
DKM_STATE_ABI_CHECKED,
|
||||
DKM_STATE_INITING,
|
||||
DKM_STATE_ACTIVE,
|
||||
DKM_STATE_FAILED
|
||||
} dkm_driver_state;
|
||||
|
||||
typedef struct dkm_driver_handle {
|
||||
const dkm_driver_desc *desc;
|
||||
dkm_driver_state state;
|
||||
i32 init_status;
|
||||
u32 load_stage;
|
||||
u32 flags;
|
||||
} dkm_driver_handle;
|
||||
|
||||
typedef struct dkm_builtin_driver {
|
||||
const dkm_driver_desc *desc;
|
||||
dkm_driver_init_fn init;
|
||||
dkm_driver_exit_fn exit;
|
||||
} dkm_builtin_driver;
|
||||
|
||||
void dkm_init(void);
|
||||
int dkm_load_builtin(const dkm_builtin_driver *driver);
|
||||
void dkm_scan_boot_modules(void);
|
||||
void dsm_load_by_manifest(void);
|
||||
void dkm_fill_platform_info(void);
|
||||
const dkm_kernel_api *dkm_get_kernel_api(void);
|
||||
|
||||
/* exposed for manifest parser */
|
||||
struct dkm_symbol_scan {
|
||||
const void *driver_desc;
|
||||
const void *driver_init;
|
||||
const void *driver_exit;
|
||||
};
|
||||
int dkm_check_elf64(const void *address, u64 size);
|
||||
int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out);
|
||||
int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan *symbols);
|
||||
|
||||
extern const dkm_builtin_driver dkm_builtin_console_early;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef UTSM_DMA_H
|
||||
#define UTSM_DMA_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
typedef struct dkm_dma_buffer {
|
||||
void *virt;
|
||||
u64 phys;
|
||||
u64 size;
|
||||
} dkm_dma_buffer;
|
||||
|
||||
typedef struct dkm_dma_api {
|
||||
int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, dkm_dma_buffer *out);
|
||||
} dkm_dma_api;
|
||||
|
||||
void dma_init(void);
|
||||
int dma_alloc_pages(u64 page_count, u64 alignment, u64 max_phys, dkm_dma_buffer *out);
|
||||
const dkm_dma_api *dma_get_api(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef UTSM_DMP_H
|
||||
#define UTSM_DMP_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
typedef struct {
|
||||
u64 magic;
|
||||
u32 version;
|
||||
u32 segment_slot;
|
||||
u64 fast_base;
|
||||
u64 fast_limit;
|
||||
u64 fast_tweak_seed;
|
||||
u64 fast_key_epoch;
|
||||
u64 dirty_bitmap_base;
|
||||
u64 mac_table_base;
|
||||
u64 last_writer_seq;
|
||||
u32 last_cpu;
|
||||
u32 flags;
|
||||
u64 crc;
|
||||
} utsm_dmp;
|
||||
|
||||
struct utsm_segment_desc;
|
||||
|
||||
void utsm_dmp_init(utsm_dmp *dmp, struct utsm_segment_desc *desc, u32 slot);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef UTSM_DRR_H
|
||||
#define UTSM_DRR_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
void drr_stub_init(void);
|
||||
const u64 *drr_get_root_key(void);
|
||||
u64 drr_recovery_generation(void);
|
||||
void drr_report_fault(const char *reason);
|
||||
void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len);
|
||||
void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef UTSM_DSK_H
|
||||
#define UTSM_DSK_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
#define DSK_BOOT_MAGIC 0x44534B31424F4F54ULL /* "DSK1BOOT" */
|
||||
#define DSK_BOOT_ABI_VERSION 1u
|
||||
|
||||
#define DSK_BOOT_FLAG_FROM_UTSM (1ULL << 0)
|
||||
#define DSK_BOOT_FLAG_DKM_READY (1ULL << 1)
|
||||
#define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2)
|
||||
|
||||
typedef struct dsk_boot_context {
|
||||
u64 magic;
|
||||
u32 abi_version;
|
||||
u32 size;
|
||||
|
||||
u64 flags;
|
||||
|
||||
u64 hhdm_offset;
|
||||
u64 rsdp_address;
|
||||
|
||||
u64 framebuffer_address;
|
||||
u64 framebuffer_width;
|
||||
u64 framebuffer_height;
|
||||
u64 framebuffer_pitch;
|
||||
u32 framebuffer_bpp;
|
||||
u32 reserved_fb;
|
||||
|
||||
u64 boot_modules_response;
|
||||
|
||||
u64 dkm_kernel_api;
|
||||
u64 dkm_driver_table;
|
||||
u64 dkm_driver_count;
|
||||
|
||||
u64 utsm_state;
|
||||
u64 drr_state;
|
||||
|
||||
u64 memory_map;
|
||||
u64 memory_map_count;
|
||||
u64 memory_map_entry_size;
|
||||
|
||||
u64 kernel_stack_top;
|
||||
u64 reserved[8];
|
||||
} dsk_boot_context;
|
||||
|
||||
typedef void (*dsk_entry_fn)(const dsk_boot_context *context);
|
||||
|
||||
int dsk_load_and_jump(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef UTSM_IDT_H
|
||||
#define UTSM_IDT_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
void idt_init(void);
|
||||
|
||||
/* IRQ handler: return 1 to suppress default ACK, 0 for normal EOI */
|
||||
typedef int (*irq_handler_t)(u8 irq);
|
||||
int irq_register(u8 irq, irq_handler_t handler);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef UTSM_LOG_H
|
||||
#define UTSM_LOG_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
void serial_init(void);
|
||||
void serial_putc(char c);
|
||||
void serial_write(const char *s);
|
||||
|
||||
void log_info(const char *msg);
|
||||
void log_warn(const char *msg);
|
||||
void log_error(const char *msg);
|
||||
void log_hex64(const char *prefix, u64 value);
|
||||
void panic(const char *msg);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef UTSM_NET_H
|
||||
#define UTSM_NET_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
#define DKM_NET_MAX_DEVICES 8u
|
||||
|
||||
/* 网卡链路/能力标志。 */
|
||||
#define DKM_NET_F_LINK_UP (1u << 0)
|
||||
#define DKM_NET_F_TX_READY (1u << 1)
|
||||
#define DKM_NET_F_RX_READY (1u << 2)
|
||||
|
||||
typedef int (*dkm_net_tx_fn)(void *ctx, const void *packet, u32 length);
|
||||
typedef int (*dkm_net_rx_poll_fn)(void *ctx, void *buffer, u32 capacity, u32 *out_length);
|
||||
|
||||
typedef struct dkm_net_device_desc {
|
||||
const char *name;
|
||||
u8 mac[6];
|
||||
u32 flags;
|
||||
void *ctx;
|
||||
dkm_net_tx_fn tx;
|
||||
dkm_net_rx_poll_fn rx_poll;
|
||||
} dkm_net_device_desc;
|
||||
|
||||
typedef struct dkm_net_device_info {
|
||||
const char *name;
|
||||
u8 mac[6];
|
||||
u32 flags;
|
||||
} dkm_net_device_info;
|
||||
|
||||
typedef struct dkm_net_api {
|
||||
int (*register_device)(const dkm_net_device_desc *desc);
|
||||
u32 (*device_count)(void);
|
||||
int (*device_info)(u32 index, dkm_net_device_info *out);
|
||||
int (*tx)(u32 index, const void *packet, u32 length);
|
||||
int (*rx_poll)(u32 index, void *buffer, u32 capacity, u32 *out_length);
|
||||
} dkm_net_api;
|
||||
|
||||
void net_init(void);
|
||||
const dkm_net_api *net_get_api(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef UTSM_PCKC_H
|
||||
#define UTSM_PCKC_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
#include <utsm/config.h>
|
||||
#include <utsm/uuid.h>
|
||||
#include <utsm/crypto.h>
|
||||
|
||||
struct utsm_segment_desc;
|
||||
|
||||
typedef struct {
|
||||
bool valid;
|
||||
u32 segment_slot;
|
||||
u64 key_epoch;
|
||||
utsm_key_material key;
|
||||
} utsm_pckc_entry;
|
||||
|
||||
typedef struct {
|
||||
uuid128_t current_process_uuid;
|
||||
utsm_pckc_entry entries[UTSM_MAX_PCKC_KEYS];
|
||||
u64 hit_count;
|
||||
u64 miss_count;
|
||||
u32 next_replace;
|
||||
} utsm_pckc;
|
||||
|
||||
void utsm_pckc_init(void);
|
||||
int utsm_pckc_get_or_derive(u32 segment_slot, struct utsm_segment_desc *desc, utsm_key_material *out_key);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef UTSM_PROCESS_H
|
||||
#define UTSM_PROCESS_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
#include <utsm/uuid.h>
|
||||
|
||||
typedef struct utsm_process_context {
|
||||
uuid128_t process_uuid;
|
||||
u64 crypto_epoch;
|
||||
u64 recovery_generation;
|
||||
u32 capability_table_slot;
|
||||
u32 capability_count;
|
||||
u32 flags;
|
||||
} utsm_process_context;
|
||||
|
||||
void utsm_process_create_test(utsm_process_context *process, u64 seed);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef UTSM_SEGMENT_H
|
||||
#define UTSM_SEGMENT_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
#include <utsm/uuid.h>
|
||||
#include <utsm/status.h>
|
||||
#include <utsm/capability.h>
|
||||
#include <utsm/dirty.h>
|
||||
|
||||
typedef enum {
|
||||
UTSM_SEG_FREE = 0,
|
||||
UTSM_SEG_ACTIVE,
|
||||
UTSM_SEG_CHECKPOINTING,
|
||||
UTSM_SEG_SEALED,
|
||||
UTSM_SEG_RECOVERING,
|
||||
UTSM_SEG_POISONED,
|
||||
UTSM_SEG_DESTROYED
|
||||
} utsm_segment_state;
|
||||
|
||||
#define UTSM_SEG_F_READ (1U << 0)
|
||||
#define UTSM_SEG_F_WRITE (1U << 1)
|
||||
#define UTSM_SEG_F_EXEC (1U << 2)
|
||||
#define UTSM_SEG_F_DMA (1U << 3)
|
||||
#define UTSM_SEG_F_STRONG_RECOVERY (1U << 4)
|
||||
|
||||
typedef struct utsm_segment_desc {
|
||||
uuid128_t segment_uuid;
|
||||
uuid128_t owner_process_uuid;
|
||||
u8 *cipher_base;
|
||||
u64 cipher_length;
|
||||
void *dmp_base;
|
||||
u64 key_epoch;
|
||||
u64 tweak_seed;
|
||||
utsm_dirty_shard *dirty_shards;
|
||||
u32 dirty_shard_count;
|
||||
void *mac_table;
|
||||
u32 generation;
|
||||
u32 flags;
|
||||
u32 state;
|
||||
u32 last_cpu;
|
||||
volatile u64 writer_seq;
|
||||
u64 checkpoint_generation;
|
||||
} utsm_segment_desc;
|
||||
|
||||
struct utsm_process_context;
|
||||
|
||||
int utsm_create_segment(struct utsm_process_context *process, u64 size, u32 flags, utsm_capability *out_cap);
|
||||
utsm_segment_desc *utsm_get_segment(u32 slot);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef UTSM_STATUS_H
|
||||
#define UTSM_STATUS_H
|
||||
|
||||
typedef enum {
|
||||
UTSM_OK = 0,
|
||||
UTSM_ERR_INVALID = -1,
|
||||
UTSM_ERR_NO_MEMORY = -2,
|
||||
UTSM_ERR_ACCESS = -3,
|
||||
UTSM_ERR_BOUNDS = -4,
|
||||
UTSM_ERR_STALE_CAP = -5,
|
||||
UTSM_ERR_EPOCH = -6,
|
||||
UTSM_ERR_KEY_MISS = -7,
|
||||
UTSM_ERR_STATE = -8,
|
||||
UTSM_ERR_POISONED = -9,
|
||||
UTSM_ERR_RETRY = -10
|
||||
} utsm_status;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef UTSM_TYPES_H
|
||||
#define UTSM_TYPES_H
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
|
||||
typedef signed char i8;
|
||||
typedef signed short i16;
|
||||
typedef signed int i32;
|
||||
typedef signed long long i64;
|
||||
|
||||
typedef u64 usize;
|
||||
typedef i64 isize;
|
||||
|
||||
typedef int bool;
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
#define UTSM_UNUSED(x) ((void)(x))
|
||||
#define UTSM_ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
static inline u64 utsm_align_up_u64(u64 value, u64 alignment) {
|
||||
return (value + alignment - 1ULL) & ~(alignment - 1ULL);
|
||||
}
|
||||
|
||||
static inline void *utsm_align_up_ptr(void *ptr, u64 alignment) {
|
||||
return (void *)utsm_align_up_u64((u64)ptr, alignment);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef UTSM_UTRW_H
|
||||
#define UTSM_UTRW_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
#include <utsm/status.h>
|
||||
#include <utsm/capability.h>
|
||||
|
||||
int utsm_read(utsm_capability cap, u64 offset, void *dst, u64 len);
|
||||
int utsm_write(utsm_capability cap, u64 offset, const void *src, u64 len);
|
||||
int utsm_slow_path(int reason);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef UTSM_H
|
||||
#define UTSM_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
#include <utsm/status.h>
|
||||
#include <utsm/config.h>
|
||||
#include <utsm/uuid.h>
|
||||
#include <utsm/process.h>
|
||||
#include <utsm/segment.h>
|
||||
#include <utsm/capability.h>
|
||||
#include <utsm/utrw.h>
|
||||
|
||||
void utsm_init(void);
|
||||
int utsm_selftest_run(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef UTSM_UUID_H
|
||||
#define UTSM_UUID_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
typedef struct {
|
||||
u64 hi;
|
||||
u64 lo;
|
||||
} uuid128_t;
|
||||
|
||||
static inline uuid128_t uuid_make_test(u64 seed) {
|
||||
uuid128_t id;
|
||||
id.hi = 0x5554534d00000000ULL ^ (seed * 0x9e3779b97f4a7c15ULL);
|
||||
id.lo = 0x4453484200000000ULL ^ (seed + 0xd1b54a32d192ed03ULL);
|
||||
return id;
|
||||
}
|
||||
|
||||
static inline bool uuid_equal(uuid128_t a, uuid128_t b) {
|
||||
return a.hi == b.hi && a.lo == b.lo;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,58 @@
|
||||
#include <utsm/block.h>
|
||||
#include <utsm/log.h>
|
||||
|
||||
static dkm_block_device_desc g_devices[DKM_BLOCK_MAX_DEVICES];
|
||||
static u32 g_device_count;
|
||||
|
||||
void block_init(void) {
|
||||
g_device_count = 0;
|
||||
log_info("[BLOCK] registry init");
|
||||
}
|
||||
|
||||
static int block_register_device_impl(const dkm_block_device_desc *desc) {
|
||||
if (!desc || !desc->read || !desc->sector_size) return -1;
|
||||
if (g_device_count >= DKM_BLOCK_MAX_DEVICES) return -2;
|
||||
|
||||
u32 index = g_device_count++;
|
||||
g_devices[index] = *desc;
|
||||
|
||||
log_info("[BLOCK] device registered");
|
||||
if (desc->name) log_info(desc->name);
|
||||
log_hex64("[BLOCK] index=", index);
|
||||
log_hex64("[BLOCK] sector_size=", desc->sector_size);
|
||||
log_hex64("[BLOCK] sector_count=", desc->sector_count);
|
||||
return (int)index;
|
||||
}
|
||||
|
||||
static u32 block_device_count_impl(void) {
|
||||
return g_device_count;
|
||||
}
|
||||
|
||||
static int block_read_impl(u32 index, u64 lba, u32 count, void *buffer) {
|
||||
if (index >= g_device_count || !buffer || count == 0) return -1;
|
||||
dkm_block_device_desc *dev = &g_devices[index];
|
||||
if (!dev->read) return -2;
|
||||
return dev->read(dev->ctx, lba, count, buffer);
|
||||
}
|
||||
|
||||
static u64 block_sector_size_impl(u32 index) {
|
||||
if (index >= g_device_count) return 0;
|
||||
return g_devices[index].sector_size;
|
||||
}
|
||||
|
||||
static const char *block_device_name_impl(u32 index) {
|
||||
if (index >= g_device_count) return 0;
|
||||
return g_devices[index].name;
|
||||
}
|
||||
|
||||
static const dkm_block_api g_block_api = {
|
||||
.register_device = block_register_device_impl,
|
||||
.device_count = block_device_count_impl,
|
||||
.read = block_read_impl,
|
||||
.sector_size = block_sector_size_impl,
|
||||
.device_name = block_device_name_impl
|
||||
};
|
||||
|
||||
const dkm_block_api *block_get_api(void) {
|
||||
return &g_block_api;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,377 @@
|
||||
#include <utsm/dsk.h>
|
||||
#include <utsm/arena.h>
|
||||
#include <utsm/dkm.h>
|
||||
#include <utsm/log.h>
|
||||
#include <utsm/types.h>
|
||||
#include "../arch/x86_64/limine.h"
|
||||
|
||||
#define DSK_PATH "/system/deshab64/deshab.elf"
|
||||
#define DSK_CMDLINE "dsk:main"
|
||||
|
||||
#define ELF_MAGIC0 0x7f
|
||||
#define ELF_MAGIC1 'E'
|
||||
#define ELF_MAGIC2 'L'
|
||||
#define ELF_MAGIC3 'F'
|
||||
#define ELFCLASS64 2
|
||||
#define ELFDATA2LSB 1
|
||||
#define ET_DYN 3
|
||||
#define ET_EXEC 2
|
||||
#define EM_X86_64 62
|
||||
#define PT_LOAD 1
|
||||
|
||||
void *memset(void *dst, int value, usize len);
|
||||
void *memcpy(void *dst, const void *src, usize len);
|
||||
|
||||
extern volatile struct limine_module_request g_module_request;
|
||||
extern volatile struct limine_rsdp_request g_rsdp_request;
|
||||
extern volatile struct limine_framebuffer_request g_fb_request;
|
||||
extern volatile struct limine_hhdm_request g_hhdm_request;
|
||||
|
||||
typedef struct elf64_ehdr {
|
||||
u8 ident[16];
|
||||
u16 type;
|
||||
u16 machine;
|
||||
u32 version;
|
||||
u64 entry;
|
||||
u64 phoff;
|
||||
u64 shoff;
|
||||
u32 flags;
|
||||
u16 ehsize;
|
||||
u16 phentsize;
|
||||
u16 phnum;
|
||||
u16 shentsize;
|
||||
u16 shnum;
|
||||
u16 shstrndx;
|
||||
} elf64_ehdr;
|
||||
|
||||
typedef struct elf64_phdr {
|
||||
u32 type;
|
||||
u32 flags;
|
||||
u64 offset;
|
||||
u64 vaddr;
|
||||
u64 paddr;
|
||||
u64 filesz;
|
||||
u64 memsz;
|
||||
u64 align;
|
||||
} elf64_phdr;
|
||||
|
||||
/* ---- minimal FAT32 parser ---- */
|
||||
typedef struct __attribute__((packed)) {
|
||||
u8 jmp[3];
|
||||
char oem[8];
|
||||
u16 bytes_per_sector;
|
||||
u8 sectors_per_cluster;
|
||||
u16 reserved_sector_count;
|
||||
u8 fat_count;
|
||||
u16 root_entry_count;
|
||||
u16 total_sectors_16;
|
||||
u8 media;
|
||||
u16 sectors_per_fat_16;
|
||||
u16 sectors_per_track;
|
||||
u16 heads;
|
||||
u32 hidden_sectors;
|
||||
u32 total_sectors_32;
|
||||
u32 sectors_per_fat;
|
||||
u16 flags;
|
||||
u16 version;
|
||||
u32 root_cluster;
|
||||
u16 fsinfo_sector;
|
||||
u16 backup_boot;
|
||||
u8 reserved[12];
|
||||
u8 drive;
|
||||
u8 nt_flags;
|
||||
u8 signature;
|
||||
u32 serial;
|
||||
char label[11];
|
||||
char type[8];
|
||||
u8 code[420];
|
||||
u16 boot_sig;
|
||||
} fat32_bpb;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
char name[11];
|
||||
u8 attr;
|
||||
u8 nt_reserved;
|
||||
u8 creation_tenth;
|
||||
u16 creation_time;
|
||||
u16 creation_date;
|
||||
u16 access_date;
|
||||
u16 cluster_high;
|
||||
u16 write_time;
|
||||
u16 write_date;
|
||||
u16 cluster_low;
|
||||
u32 file_size;
|
||||
} fat32_dir_entry;
|
||||
|
||||
static u32 fat32_read_u32(const u8 *p) {
|
||||
return (u32)p[0] | ((u32)p[1] << 8) | ((u32)p[2] << 16) | ((u32)p[3] << 24);
|
||||
}
|
||||
|
||||
static u16 fat32_read_u16(const u8 *p) {
|
||||
return (u16)p[0] | ((u16)p[1] << 8);
|
||||
}
|
||||
|
||||
static int fat32_name11_eq(const char *n, const char *s) {
|
||||
for (int i = 0; i < 11; i++) {
|
||||
if (n[i] != s[i]) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* core helpers reused from earlier loader */
|
||||
static int streq(const char *a, const char *b) {
|
||||
if (!a || !b) return 0;
|
||||
while (*a && *b) {
|
||||
if (*a != *b) return 0;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
static int range_ok(u64 offset, u64 length, u64 size) {
|
||||
if (offset > size) return 0;
|
||||
if (length > size - offset) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct limine_file *dsk_find_module(void) {
|
||||
struct limine_module_response *rsp = g_module_request.response;
|
||||
if (!rsp) return 0;
|
||||
|
||||
for (u64 i = 0; i < rsp->module_count; i++) {
|
||||
struct limine_file *file = rsp->modules[i];
|
||||
if (!file) continue;
|
||||
if (streq(file->path, DSK_PATH) || streq(file->cmdline, DSK_CMDLINE)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- FAT32 block-provider file reader ---- */
|
||||
static u8 g_dsk_fat32_disk[65536]; /* 128-sector BPB+FAT+root dir buffer */
|
||||
static u8 g_dsk_fat32_cluster[4096]; /* 8-sector cluster buffer */
|
||||
static u8 g_dsk_fat32_filedata[65536]; /* file data buffer */
|
||||
|
||||
static int dsk_load_from_block_fat32(const void **out_addr, u64 *out_size) {
|
||||
const dkm_kernel_api *api = dkm_get_kernel_api();
|
||||
if (!api || !api->block || !api->block->device_count) return -1;
|
||||
u32 count = api->block->device_count();
|
||||
if (count == 0 || api->block->sector_size(0) != 512) return -1;
|
||||
|
||||
/* Read first 128 sectors: BPB + FAT + root dir */
|
||||
u8 *disk = g_dsk_fat32_disk;
|
||||
int st = api->block->read(0, 0, 128, disk);
|
||||
if (st != 0) return -1;
|
||||
|
||||
const fat32_bpb *bpb = (const fat32_bpb *)disk;
|
||||
if (bpb->boot_sig != 0xAA55 || bpb->bytes_per_sector != 512) return -1;
|
||||
|
||||
u32 fat_start = bpb->reserved_sector_count * 512;
|
||||
u32 data_start_sec = bpb->reserved_sector_count + (u32)bpb->fat_count * bpb->sectors_per_fat;
|
||||
|
||||
u32 clus = bpb->root_cluster;
|
||||
u32 found_clus = 0;
|
||||
u32 found_size = 0;
|
||||
const char *target = "DESHAB ELF";
|
||||
|
||||
while (clus >= 2 && clus < 0x0FFFFFF8) {
|
||||
u32 clus_lba = data_start_sec + (clus - 2) * bpb->sectors_per_cluster;
|
||||
u32 cls_sec = bpb->sectors_per_cluster;
|
||||
u8 *cb = g_dsk_fat32_cluster;
|
||||
st = api->block->read(0, clus_lba, cls_sec, cb);
|
||||
if (st != 0) return -1;
|
||||
|
||||
const fat32_dir_entry *dir = (const fat32_dir_entry *)cb;
|
||||
for (u32 e = 0; e * 32 < cls_sec * 512; e++) {
|
||||
if (dir[e].name[0] == 0) break;
|
||||
if ((u8)dir[e].name[0] == 0xE5) continue;
|
||||
if (dir[e].attr == 0x0F) continue;
|
||||
if (dir[e].attr & 0x08) continue;
|
||||
if (fat32_name11_eq(dir[e].name, target)) {
|
||||
found_size = dir[e].file_size;
|
||||
found_clus = fat32_read_u16((const u8 *)&dir[e].cluster_low);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found_clus) break;
|
||||
u32 fat_ent = fat_start + clus * 4;
|
||||
if (fat_ent + 4 > 65536) break;
|
||||
clus = fat32_read_u32(disk + fat_ent) & 0x0FFFFFFF;
|
||||
}
|
||||
|
||||
if (!found_clus || found_size == 0) return -1;
|
||||
|
||||
if (found_size > sizeof(g_dsk_fat32_filedata)) return -1;
|
||||
u8 *dst = g_dsk_fat32_filedata;
|
||||
u32 remaining = found_size;
|
||||
u32 fc = found_clus;
|
||||
|
||||
while (fc >= 2 && fc < 0x0FFFFFF8 && remaining > 0) {
|
||||
u32 fc_lba = data_start_sec + (fc - 2) * bpb->sectors_per_cluster;
|
||||
u32 fc_sec = bpb->sectors_per_cluster;
|
||||
u32 fc_bytes = fc_sec * 512;
|
||||
if (fc_bytes > remaining) fc_bytes = remaining;
|
||||
|
||||
u8 *chunk = g_dsk_fat32_cluster;
|
||||
st = api->block->read(0, fc_lba, fc_sec, chunk);
|
||||
if (st != 0) return -1;
|
||||
for (u32 b = 0; b < fc_bytes; b++) dst[b] = chunk[b];
|
||||
dst += fc_bytes;
|
||||
remaining -= fc_bytes;
|
||||
|
||||
u32 fat_ent = fat_start + fc * 4;
|
||||
if (fat_ent + 4 > 65536) { fc = 0x0FFFFFFF; break; }
|
||||
fc = fat32_read_u32(disk + fat_ent) & 0x0FFFFFFF;
|
||||
}
|
||||
|
||||
/* Copy to arena for ELF loader */
|
||||
u8 *file_buf = (u8 *)kmem_alloc_aligned(found_size, 16);
|
||||
if (!file_buf) return -1;
|
||||
for (u32 i = 0; i < found_size; i++) file_buf[i] = g_dsk_fat32_filedata[i];
|
||||
|
||||
*out_addr = file_buf;
|
||||
*out_size = found_size;
|
||||
log_hex64("[UTSM] FAT32 file size=", found_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- ELF checks ---- */
|
||||
static int dsk_check_elf(const void *address, u64 size) {
|
||||
if (!address || size < sizeof(elf64_ehdr)) return -1;
|
||||
const elf64_ehdr *eh = (const elf64_ehdr *)address;
|
||||
if (eh->ident[0] != ELF_MAGIC0 || eh->ident[1] != ELF_MAGIC1 ||
|
||||
eh->ident[2] != ELF_MAGIC2 || eh->ident[3] != ELF_MAGIC3) return -2;
|
||||
if (eh->ident[4] != ELFCLASS64 || eh->ident[5] != ELFDATA2LSB) return -3;
|
||||
if (eh->machine != EM_X86_64) return -4;
|
||||
if (eh->type != ET_DYN && eh->type != ET_EXEC) return -5;
|
||||
if (eh->phoff == 0 || eh->phnum == 0 || eh->phentsize < sizeof(elf64_phdr)) return -6;
|
||||
if (!range_ok(eh->phoff, (u64)eh->phnum * eh->phentsize, size)) return -7;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsk_load_elf_image(const void *address, u64 size, dsk_entry_fn *entry_out) {
|
||||
const elf64_ehdr *eh = (const elf64_ehdr *)address;
|
||||
const u8 *base = (const u8 *)address;
|
||||
|
||||
u64 min_vaddr = ~0ULL;
|
||||
u64 max_vaddr = 0;
|
||||
u64 max_align = 0x1000;
|
||||
u32 load_count = 0;
|
||||
|
||||
for (u16 i = 0; i < eh->phnum; i++) {
|
||||
const elf64_phdr *ph = (const elf64_phdr *)(base + eh->phoff + (u64)i * eh->phentsize);
|
||||
if (ph->type != PT_LOAD) continue;
|
||||
if (ph->filesz > ph->memsz) return -1;
|
||||
if (!range_ok(ph->offset, ph->filesz, size)) return -2;
|
||||
if (ph->vaddr < min_vaddr) min_vaddr = ph->vaddr;
|
||||
if (ph->vaddr + ph->memsz > max_vaddr) max_vaddr = ph->vaddr + ph->memsz;
|
||||
if (ph->align > max_align) max_align = ph->align;
|
||||
load_count++;
|
||||
}
|
||||
|
||||
if (load_count == 0 || min_vaddr == ~0ULL || max_vaddr <= min_vaddr) return -3;
|
||||
if (eh->entry < min_vaddr || eh->entry >= max_vaddr) return -4;
|
||||
|
||||
u64 image_size = utsm_align_up_u64(max_vaddr - min_vaddr, 0x1000);
|
||||
u8 *image = (u8 *)kmem_alloc_aligned(image_size, max_align);
|
||||
if (!image) return -5;
|
||||
memset(image, 0, image_size);
|
||||
|
||||
for (u16 i = 0; i < eh->phnum; i++) {
|
||||
const elf64_phdr *ph = (const elf64_phdr *)(base + eh->phoff + (u64)i * eh->phentsize);
|
||||
if (ph->type != PT_LOAD) continue;
|
||||
u64 dst_off = ph->vaddr - min_vaddr;
|
||||
if (dst_off + ph->memsz > image_size) return -6;
|
||||
memcpy(image + dst_off, base + ph->offset, ph->filesz);
|
||||
}
|
||||
|
||||
*entry_out = (dsk_entry_fn)(image + (eh->entry - min_vaddr));
|
||||
log_hex64("[UTSM] DSK image base=", (u64)image);
|
||||
log_hex64("[UTSM] DSK image size=", image_size);
|
||||
log_hex64("[UTSM] DSK entry=", (u64)*entry_out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dsk_fill_boot_context(dsk_boot_context *ctx) {
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->magic = DSK_BOOT_MAGIC;
|
||||
ctx->abi_version = DSK_BOOT_ABI_VERSION;
|
||||
ctx->size = sizeof(*ctx);
|
||||
ctx->flags = DSK_BOOT_FLAG_FROM_UTSM | DSK_BOOT_FLAG_DKM_READY | DSK_BOOT_FLAG_FAT32_PATH;
|
||||
|
||||
if (g_hhdm_request.response) {
|
||||
ctx->hhdm_offset = g_hhdm_request.response->offset;
|
||||
}
|
||||
if (g_rsdp_request.response) {
|
||||
ctx->rsdp_address = (u64)g_rsdp_request.response->address;
|
||||
}
|
||||
if (g_fb_request.response && g_fb_request.response->framebuffer_count > 0) {
|
||||
struct limine_framebuffer *fb = g_fb_request.response->framebuffers[0];
|
||||
ctx->framebuffer_address = (u64)fb->address;
|
||||
ctx->framebuffer_width = fb->width;
|
||||
ctx->framebuffer_height = fb->height;
|
||||
ctx->framebuffer_pitch = fb->pitch;
|
||||
ctx->framebuffer_bpp = fb->bpp;
|
||||
}
|
||||
ctx->boot_modules_response = (u64)g_module_request.response;
|
||||
ctx->dkm_kernel_api = (u64)dkm_get_kernel_api();
|
||||
|
||||
u64 rsp;
|
||||
__asm__ volatile ("movq %%rsp, %0" : "=r"(rsp));
|
||||
ctx->kernel_stack_top = rsp;
|
||||
}
|
||||
|
||||
int dsk_load_and_jump(void) {
|
||||
log_info("[UTSM] loading DSK");
|
||||
log_info(DSK_PATH);
|
||||
|
||||
const void *data = 0;
|
||||
u64 data_size = 0;
|
||||
|
||||
/* 1) try FAT32 block provider */
|
||||
int fat32_ok = dsk_load_from_block_fat32(&data, &data_size);
|
||||
if (fat32_ok == 0) {
|
||||
log_info("[UTSM] DSK loaded from FAT32 block provider");
|
||||
} else {
|
||||
/* 2) fallback: Limine boot module */
|
||||
log_info("[UTSM] FAT32 block path unavailable; trying Limine module");
|
||||
struct limine_file *file = dsk_find_module();
|
||||
if (!file) {
|
||||
log_error("[UTSM] DSK module not found (neither FAT32 nor Limine)");
|
||||
return -1;
|
||||
}
|
||||
data = file->address;
|
||||
data_size = file->size;
|
||||
log_hex64("[UTSM] DSK module size=", data_size);
|
||||
}
|
||||
|
||||
int status = dsk_check_elf(data, data_size);
|
||||
if (status != 0) {
|
||||
log_error("[UTSM] DSK ELF rejected");
|
||||
log_hex64("[UTSM] DSK ELF status=", (u64)(i64)status);
|
||||
return status;
|
||||
}
|
||||
|
||||
dsk_entry_fn entry = 0;
|
||||
status = dsk_load_elf_image(data, data_size, &entry);
|
||||
if (status != 0) {
|
||||
log_error("[UTSM] DSK load failed");
|
||||
log_hex64("[UTSM] DSK load status=", (u64)(i64)status);
|
||||
return status;
|
||||
}
|
||||
|
||||
dsk_boot_context *ctx = (dsk_boot_context *)kmem_alloc_aligned(sizeof(dsk_boot_context), 16);
|
||||
if (!ctx) {
|
||||
log_error("[UTSM] DSK context allocation failed");
|
||||
return -1;
|
||||
}
|
||||
dsk_fill_boot_context(ctx);
|
||||
|
||||
log_info("[UTSM] jumping to DSK");
|
||||
entry(ctx);
|
||||
|
||||
log_error("[UTSM] DSK returned unexpectedly");
|
||||
return -1;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
#include <utsm/log.h>
|
||||
|
||||
static void log_line(const char *level, const char *msg) {
|
||||
serial_write(level);
|
||||
serial_write(" ");
|
||||
serial_write(msg);
|
||||
serial_write("\n");
|
||||
}
|
||||
|
||||
void log_info(const char *msg) {
|
||||
log_line("[INFO]", msg);
|
||||
}
|
||||
|
||||
void log_warn(const char *msg) {
|
||||
log_line("[WARN]", msg);
|
||||
}
|
||||
|
||||
void log_error(const char *msg) {
|
||||
log_line("[ERR ]", msg);
|
||||
}
|
||||
|
||||
void log_hex64(const char *prefix, u64 value) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
char buf[19];
|
||||
buf[0] = '0';
|
||||
buf[1] = 'x';
|
||||
for (int i = 0; i < 16; i++) {
|
||||
buf[2 + i] = hex[(value >> ((15 - i) * 4)) & 0xf];
|
||||
}
|
||||
buf[18] = 0;
|
||||
serial_write(prefix);
|
||||
serial_write(buf);
|
||||
serial_write("\n");
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,54 @@
|
||||
#include <utsm/log.h>
|
||||
#include <utsm/arena.h>
|
||||
#include <utsm/utsm.h>
|
||||
#include <utsm/drr.h>
|
||||
#include <utsm/dkm.h>
|
||||
#include <utsm/idt.h>
|
||||
#include <utsm/dsk.h>
|
||||
#include <utsm/block.h>
|
||||
#include <utsm/net.h>
|
||||
|
||||
void arch_halt_forever(void);
|
||||
|
||||
void kernel_main(void) {
|
||||
serial_init();
|
||||
log_info("[UTSM] boot");
|
||||
|
||||
idt_init();
|
||||
|
||||
log_info("[UTSM] arena init begin");
|
||||
arena_init();
|
||||
log_info("[UTSM] arena init ok");
|
||||
|
||||
log_info("[UTSM] dma init begin");
|
||||
dma_init();
|
||||
log_info("[UTSM] dma init ok");
|
||||
|
||||
log_info("[UTSM] net registry init begin");
|
||||
net_init();
|
||||
log_info("[UTSM] net registry init ok");
|
||||
|
||||
log_info("[UTSM] drr stub init begin");
|
||||
drr_stub_init();
|
||||
log_info("[UTSM] drr stub init ok");
|
||||
|
||||
log_info("[UTSM] core init begin");
|
||||
utsm_init();
|
||||
log_info("[UTSM] init ok");
|
||||
|
||||
dkm_init();
|
||||
dkm_fill_platform_info();
|
||||
dsm_load_by_manifest();
|
||||
|
||||
int result = utsm_selftest_run();
|
||||
if (result == 0) {
|
||||
log_info("[UTSM] SELFTEST PASS");
|
||||
if (dsk_load_and_jump() != 0) {
|
||||
log_error("[UTSM] DSK jump failed");
|
||||
}
|
||||
} else {
|
||||
log_error("[UTSM] SELFTEST FAIL");
|
||||
}
|
||||
|
||||
arch_halt_forever();
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,73 @@
|
||||
#include <utsm/net.h>
|
||||
#include <utsm/log.h>
|
||||
|
||||
/*
|
||||
* 最小 netdev 注册表:
|
||||
* - 早期 DKM 驱动加载路径是单线程顺序执行,当前无需引入锁,避免在网络底座阶段扩大复杂度。
|
||||
* - 后续进入多核/热插拔后,应在 register/device_info 外围加自旋锁或 RCU 风格只读快照。
|
||||
*/
|
||||
static dkm_net_device_desc g_net_devices[DKM_NET_MAX_DEVICES];
|
||||
static u32 g_net_device_count;
|
||||
|
||||
void net_init(void) {
|
||||
g_net_device_count = 0;
|
||||
log_info("[NET] registry init");
|
||||
}
|
||||
|
||||
static int net_register_device_impl(const dkm_net_device_desc *desc) {
|
||||
if (!desc || !desc->name) return -1;
|
||||
if (g_net_device_count >= DKM_NET_MAX_DEVICES) return -2;
|
||||
|
||||
u32 index = g_net_device_count++;
|
||||
g_net_devices[index] = *desc;
|
||||
|
||||
log_info("[NET] device registered");
|
||||
log_info(desc->name);
|
||||
log_hex64("[NET] index=", index);
|
||||
log_hex64("[NET] flags=", desc->flags);
|
||||
return (int)index;
|
||||
}
|
||||
|
||||
static u32 net_device_count_impl(void) {
|
||||
return g_net_device_count;
|
||||
}
|
||||
|
||||
static int net_device_info_impl(u32 index, dkm_net_device_info *out) {
|
||||
if (!out) return -1;
|
||||
if (index >= g_net_device_count) return -2;
|
||||
|
||||
const dkm_net_device_desc *dev = &g_net_devices[index];
|
||||
out->name = dev->name;
|
||||
for (u32 i = 0; i < 6; i++) {
|
||||
out->mac[i] = dev->mac[i];
|
||||
}
|
||||
out->flags = dev->flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int net_tx_impl(u32 index, const void *packet, u32 length) {
|
||||
if (index >= g_net_device_count) return -2;
|
||||
const dkm_net_device_desc *dev = &g_net_devices[index];
|
||||
if (!dev->tx) return -3;
|
||||
log_hex64("[NET] tx dispatch fn=", (u64)dev->tx);
|
||||
return dev->tx(dev->ctx, packet, length);
|
||||
}
|
||||
|
||||
static int net_rx_poll_impl(u32 index, void *buffer, u32 capacity, u32 *out_length) {
|
||||
if (index >= g_net_device_count) return -2;
|
||||
const dkm_net_device_desc *dev = &g_net_devices[index];
|
||||
if (!dev->rx_poll) return -3;
|
||||
return dev->rx_poll(dev->ctx, buffer, capacity, out_length);
|
||||
}
|
||||
|
||||
static const dkm_net_api g_net_api = {
|
||||
.register_device = net_register_device_impl,
|
||||
.device_count = net_device_count_impl,
|
||||
.device_info = net_device_info_impl,
|
||||
.tx = net_tx_impl,
|
||||
.rx_poll = net_rx_poll_impl
|
||||
};
|
||||
|
||||
const dkm_net_api *net_get_api(void) {
|
||||
return &g_net_api;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
#include <utsm/log.h>
|
||||
|
||||
void arch_halt_forever(void);
|
||||
|
||||
void panic(const char *msg) {
|
||||
log_error("[PANIC]");
|
||||
log_error(msg);
|
||||
arch_halt_forever();
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
ENTRY(_start)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(5); /* R-X */
|
||||
rodata PT_LOAD FLAGS(4); /* R-- */
|
||||
data PT_LOAD FLAGS(7); /* RWX (SAS-R0) */
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0xffffffff80000000;
|
||||
|
||||
.text : ALIGN(4K) {
|
||||
*(.text*)
|
||||
} :text
|
||||
|
||||
.rodata : ALIGN(4K) {
|
||||
*(.limine_requests_start*)
|
||||
*(.limine_requests*)
|
||||
*(.limine_requests_end*)
|
||||
*(.rodata*)
|
||||
} :rodata
|
||||
|
||||
.data : ALIGN(4K) {
|
||||
*(.data*)
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
} :data
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user