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,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;
|
||||
}
|
||||
Reference in New Issue
Block a user