Compare commits
8
Commits
2774c10d0c
..
ui
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6287513794 | ||
|
|
86df253a32 | ||
|
|
99b51c8a1f | ||
|
|
019585f2bf | ||
|
|
a26866e339 | ||
|
|
ecd2f7246e | ||
|
|
f52157b7af | ||
|
|
5ea84fac97 |
@@ -4,3 +4,10 @@ ISO/deshab.img
|
|||||||
.trae/
|
.trae/
|
||||||
docs/workbuddy-planning/
|
docs/workbuddy-planning/
|
||||||
sata_fat32_dsk.img
|
sata_fat32_dsk.img
|
||||||
|
|
||||||
|
# Rust build artifacts
|
||||||
|
CODE/Deaicup Software/src/target/
|
||||||
|
CODE/Deaicup Software/pe/target/
|
||||||
|
|
||||||
|
# Large binary images (regenerate from build)
|
||||||
|
SYSTEM/boot/linux-rootfs.img
|
||||||
|
|||||||
Generated
+15
@@ -0,0 +1,15 @@
|
|||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 基于编辑器的 HTTP 客户端请求
|
||||||
|
/httpRequests/
|
||||||
|
# Rider 忽略的文件
|
||||||
|
/modules.xml
|
||||||
|
/contentModel.xml
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
/.idea.Deshab-OS.iml
|
||||||
|
# 已忽略包含查询文件的默认文件夹
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
Generated
+4
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -55,6 +55,10 @@ DMP
|
|||||||
PCKC
|
PCKC
|
||||||
Per-CPU Key Cache
|
Per-CPU Key Cache
|
||||||
每 CPU 密钥缓存
|
每 CPU 密钥缓存
|
||||||
|
|
||||||
|
FUCK
|
||||||
|
内核启动配置文件
|
||||||
|
INI 分区格式,控制启动行为/驱动开关/算法参数/桌面行为
|
||||||
```
|
```
|
||||||
|
|
||||||
## 关键设计约束
|
## 关键设计约束
|
||||||
@@ -81,6 +85,15 @@ SYSTEM/
|
|||||||
SYSTEM/driver/manifest.json
|
SYSTEM/driver/manifest.json
|
||||||
内核启动驱动模块清单,控制 ELF .drv 驱动的分阶段加载。
|
内核启动驱动模块清单,控制 ELF .drv 驱动的分阶段加载。
|
||||||
|
|
||||||
|
SYSTEM/system/deshab64/FUCK
|
||||||
|
内核启动配置文件(INI 分区格式),控制启动行为开关、驱动加载开关、UTSM 算法参数、DSK 桌面行为。
|
||||||
|
UTSM 通过 Limine boot module 预加载读取(cmdline="fuck:config")。
|
||||||
|
DSK 通过 FAT32 子目录遍历读取(路径 "SYSTEM /DESHAB64 /FUCK ")。
|
||||||
|
缺失或为空时使用硬编码默认值,行为与无配置文件时一致。
|
||||||
|
|
||||||
|
CODE/UTSM/kernel/ini_parser.h / ini_parser.c
|
||||||
|
UTSM INI 配置解析器,支持分区格式解析、key/value 查询、整型和布尔值便捷获取。
|
||||||
|
|
||||||
CODE/DKM/README.md
|
CODE/DKM/README.md
|
||||||
DKM 驱动模块系统说明,记录驱动加载目标、ABI、loader、状态机和实现清单。
|
DKM 驱动模块系统说明,记录驱动加载目标、ABI、loader、状态机和实现清单。
|
||||||
|
|
||||||
@@ -123,7 +136,7 @@ DKM/DSM 驱动加载系统已完工:内置 `console_early` + manifest.json 解
|
|||||||
|
|
||||||
当前已实现 14 个真实 DKM 驱动:
|
当前已实现 14 个真实 DKM 驱动:
|
||||||
- **timer** (stage0): PIT 校准, 100ms busy-wait demo
|
- **timer** (stage0): PIT 校准, 100ms busy-wait demo
|
||||||
- **apic** (stage0): CPUID Local APIC 探测, IA32_APIC_BASE MSR, ACPI MADT 枚举, LAPIC MMIO 只读寄存器验证;当前保留 PIC IRQ 路由
|
- **apic** (stage0): CPUID Local APIC 探测, IA32_APIC_BASE MSR, ACPI MADT 枚举, LAPIC enable + IOAPIC 重定向表编程(ISO 表感知); B7: FUCK `apic_route=1` 时 IOAPIC→LAPIC 接管(注册 UTSM LAPIC EOI 钩子 + PIC 全屏蔽 + TPR=0 + tick vec 0x20 链路自验,失败自动完整降级回 PIC); `apic_route_legacy=1` 额外放开 IRQ1/12/11 持续投递(e1000 强制 level/active-low); `apic_diag=1` HPET 绝对时基对拍 + 键鼠流观察窗
|
||||||
- **acpi** (stage0): Limine RSDP → XSDT, ACPI 表枚举
|
- **acpi** (stage0): Limine RSDP → XSDT, ACPI 表枚举
|
||||||
- **pci** (stage0): PCI config space 扫描, 设备枚举
|
- **pci** (stage0): PCI config space 扫描, 设备枚举
|
||||||
- **console_fb** (stage1): Limine framebuffer, 淡蓝色背景清屏(原四色圆弧加载环已移除,启动画面改由 DSK 静态 Logo 接管)
|
- **console_fb** (stage1): Limine framebuffer, 淡蓝色背景清屏(原四色圆弧加载环已移除,启动画面改由 DSK 静态 Logo 接管)
|
||||||
@@ -137,18 +150,69 @@ DKM/DSM 驱动加载系统已完工:内置 `console_early` + manifest.json 解
|
|||||||
- **e1000** (stage3): PCI `8086:10d3` 探测, BAR0 HHDM MMIO 映射, MAC 读取, IRQ 注册, 基础 link setup
|
- **e1000** (stage3): PCI `8086:10d3` 探测, BAR0 HHDM MMIO 映射, MAC 读取, IRQ 注册, 基础 link setup
|
||||||
- **virtio_net** (stage3): PCI virtio-net `1af4:1001` 探测, BAR 枚举, modern virtio PCI capability 枚举;暂不进行 feature negotiation / virtqueue / 收发包
|
- **virtio_net** (stage3): PCI virtio-net `1af4:1001` 探测, BAR 枚举, modern virtio PCI capability 枚举;暂不进行 feature negotiation / virtqueue / 收发包
|
||||||
|
|
||||||
当前内核平台能力:IDT 0–47 stub、异常处理、PIC remap 到 `0x20–0x2f`、IRQ handler 注册与 EOI、Limine RSDP/framebuffer/module/HHDM request。
|
当前内核平台能力:IDT 0–255 stub 全覆盖、异常处理、PIC remap 到 `0x20–0x2f`、IRQ handler 注册与 EOI、UTSM 侧 LAPIC EOI 钩子(`idt_register_apic_eoi`,IRQ 分发末尾调用,spurious 0xFF 特判跳过)、IOAPIC→LAPIC 路由接管(B7,legacy IRQ1/12/11 可持续投递)、Limine RSDP/framebuffer/module/HHDM request。
|
||||||
|
|
||||||
kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bpp, boot_modules_response, irq_register, hhdm_offset, dma.alloc_pages。
|
kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bpp, boot_modules_response, irq_register, hhdm_offset, dma.alloc_pages, block provider registry, mmio/mm_map_mmio/mm_unmap_mmio, register_apic_eoi(尾部追加,ABI 兼容)。
|
||||||
|
|
||||||
当前 manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。
|
当前 manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。
|
||||||
|
|
||||||
下一阶段优先路线:
|
下一阶段优先路线:
|
||||||
1. **内存与 MMIO 基础设施**: 实现物理页分配器、页表映射接口、低位/高位 PCI MMIO 映射,解决 NVMe 4G 以上 BAR 和后续 DMA 映射问题。
|
1. **内存与 MMIO 基础设施**: 实现物理页分配器、页表映射接口、低位/高位 PCI MMIO 映射,解决 NVMe 4G 以上 BAR 和后续 DMA 映射问题。
|
||||||
2. **DMA 与块设备数据路径**: 建立 contiguous DMA buffer、cache/屏障约定、PRDT/队列内存管理,然后推进 AHCI IDENTIFY/READ 与 NVMe admin queue/identify。
|
2. **DMA 与块设备数据路径**: 建立 contiguous DMA buffer、cache/屏障约定、PRDT/队列内存管理,然后推进 AHCI IDENTIFY/READ 与 NVMe admin queue/identify。
|
||||||
3. **IRQ 后端升级**: 在现有 PIC fallback 稳定基础上扩展 IDT/vector allocator/APIC EOI/IOAPIC redirection,逐步迁移设备 IRQ,最后接入 MSI/MSI-X。
|
3. **IRQ 后端升级**: ~~APIC EOI/IOAPIC redirection~~(B7 已完成:UTSM EOI 钩子 + legacy IRQ1/12/11 持续投递,QEMU 全链路验证;tick 2x 定论 = PIT mode3 IOAPIC edge 双触发 ~200Hz)。剩余:vector allocator 规范化、设备 IRQ 从 PIC 默认全面迁移、MSI/MSI-X(e1000/NVMe)。
|
||||||
4. **网络数据路径**: 在 DMA/IRQ 完成后推进 e1000 RX/TX ring 与 virtio-net feature negotiation/virtqueue/RX-TX。
|
4. **网络数据路径**: 在 DMA/IRQ 完成后推进 e1000 RX/TX ring 与 virtio-net feature negotiation/virtqueue/RX-TX。
|
||||||
5. **VFS 与真实块设备接入**: 将 FAT32 从测试镜像迁移到 AHCI/NVMe block provider,完善挂载、读取、目录遍历和错误路径。
|
5. **VFS 与真实块设备接入**: 将 FAT32 从测试镜像迁移到 AHCI/NVMe block provider,完善挂载、读取、目录遍历和错误路径。
|
||||||
6. **DKM 工程化收尾**: 统一驱动本地 ABI 头、清理 warning、补充自动化 QEMU 场景(e1000/virtio-net/nvme/ahci)。
|
6. **DKM 工程化收尾**: 统一驱动本地 ABI 头、清理 warning、补充自动化 QEMU 场景(e1000/virtio-net/nvme/ahci)。
|
||||||
7. **FirstInit 完善**: 修复按键崩溃(fb_text 优化问题)、实现鼠标光标移动、user.conf 写盘(需 block write API)、网络配置界面。
|
7. **FirstInit 完善**: ~~修复按键崩溃(fb_text 优化问题)~~(B6 已验证未复现)、~~实现鼠标光标移动~~(M1 已完成)、~~user.conf 写盘~~(已完成)、网络配置界面。
|
||||||
8. **鼠标输入驱动**: 从 mouseInit stub 升级为完整 PS/2 鼠标 IRQ12 + 包解码 + 光标移动。
|
8. ~~**鼠标输入驱动**: 从 mouseInit stub 升级为完整 PS/2 鼠标 IRQ12 + 包解码 + 光标移动~~(M1 已完成)。
|
||||||
|
|
||||||
|
## M1 里程碑完成状态(2026-07-29)
|
||||||
|
|
||||||
|
### 任务 A: B6 FirstInit 按键崩溃修复
|
||||||
|
**状态**: [RESOLVED-NOT-REPRODUCIBLE]
|
||||||
|
|
||||||
|
**验证结果**:
|
||||||
|
- QEMU 复现测试完成,系统完整运行 FirstInit 流程无崩溃
|
||||||
|
- 按键扫描码处理正常,字段输入正确(测试序列: a, b, Enter, shift+comma, Enter, 1, 2, Enter)
|
||||||
|
- user.conf 成功写入磁盘,firstInit.txt 成功更新为 "1"
|
||||||
|
- 系统继续加载 desktop
|
||||||
|
|
||||||
|
**证据**: `.build_tmp/qemu_serial_b6.log` 串口日志,调试记录 `debug-firstinit-key-crash.md`
|
||||||
|
|
||||||
|
**结论**: B6 崩溃在当前构建中未复现,可能已在之前的修改中修复,或崩溃条件较特殊未触发。
|
||||||
|
|
||||||
|
### 任务 B: PS/2 鼠标完整实现
|
||||||
|
**状态**: [COMPLETED]
|
||||||
|
|
||||||
|
**实现内容**:
|
||||||
|
1. **完整初始化序列** (`CODE/mouse/main.c`):
|
||||||
|
- ps2_drain() 冲刷控制器残留数据
|
||||||
|
- 读配置字节 → 启用 AUX 时钟/IRQ/翻译 → 写回
|
||||||
|
- 启用 AUX 端口
|
||||||
|
- 复位鼠标 → 验证 ACK/自检/设备 ID
|
||||||
|
- Set Defaults → 采样率 100 → 分辨率 2 → 启用数据报告
|
||||||
|
- 全程超时保护(100000 次),单步失败只记日志继续
|
||||||
|
|
||||||
|
2. **IRQ12 中断处理**:
|
||||||
|
- 经 kernel_api.irq_register 注册 IRQ12 handler
|
||||||
|
- 读数据前检查 AUX 位(键盘数据留给键盘路径)
|
||||||
|
- 3 字节包解码:同步位验证、溢出检测、9 位有符号位移合成
|
||||||
|
- 坐标累计并钳位到 framebuffer 边界
|
||||||
|
- 安全不变式:注册但不解屏蔽 PIC,DSK 阶段 IF=0
|
||||||
|
|
||||||
|
3. **共享状态接口**:
|
||||||
|
- `mouse_state_t` 结构体(magic/version/x/y/buttons/计数器/边界)
|
||||||
|
- `g_mouse_state` 导出全局符号
|
||||||
|
- `mouse_poll_state()` 只读快照查询接口
|
||||||
|
|
||||||
|
**验证结果**:
|
||||||
|
- mouseInit 初始化成功:`cfg byte=0x47`, `data reporting enabled`, `IRQ12 handler registered`
|
||||||
|
- FirstInit 中鼠标轮询正常:AUX 端口启用、复位、自检、流模式启用成功
|
||||||
|
- 鼠标数据包正确接收和解码(串口日志显示 "[mouse] pkt")
|
||||||
|
|
||||||
|
**证据**: `.build_tmp/qemu_serial_mouse.log` 串口日志
|
||||||
|
|
||||||
|
**技术要点**:
|
||||||
|
- 严格遵守"任何单步失败只记日志继续执行,绝不阻塞或崩溃首次启动"准则
|
||||||
|
- handler 存活性结论:镜像会被后续模块覆盖,IRQ12 保持屏蔽避免野指针执行
|
||||||
|
- 后续集成路径:DKM 输入驱动 或 DSK 加载器增加"常驻"标志
|
||||||
|
|||||||
@@ -103,6 +103,15 @@ typedef struct __attribute__((packed)) {
|
|||||||
u32 creator_revision;
|
u32 creator_revision;
|
||||||
} acpi_sdt_header;
|
} acpi_sdt_header;
|
||||||
|
|
||||||
|
/* ACPI Generic Address Structure (GAS) — HPET 等表使用 */
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
u8 space_id; /* 0=SystemMemory, 1=SystemIO, 2=PCIConfig, ... */
|
||||||
|
u8 bit_width;
|
||||||
|
u8 bit_offset;
|
||||||
|
u8 access_size; /* 0=undefined, 1=byte, 2=word, 3=dword, 4=qword */
|
||||||
|
u64 address;
|
||||||
|
} acpi_generic_address;
|
||||||
|
|
||||||
/* ---------------------------------------------------------------
|
/* ---------------------------------------------------------------
|
||||||
* Driver descriptor
|
* Driver descriptor
|
||||||
* --------------------------------------------------------------- */
|
* --------------------------------------------------------------- */
|
||||||
@@ -212,6 +221,168 @@ static const acpi_rsdp_v2 *acpi_find_rsdp(const void *limine_rsdp) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------
|
||||||
|
* MCFG (PCIe ECAM Memory-mapped Configuration) 解析
|
||||||
|
*
|
||||||
|
* **QEMU 无法测试但真机必需**: PCIe 配置空间通过 ECAM 访问,
|
||||||
|
* MCFG 表提供 ECAM 基址和总线范围。没有 MCFG 解析,
|
||||||
|
* 真机无法访问 Bus 0 以外的 PCIe 设备配置空间。
|
||||||
|
* QEMU 的 PCI 设备都在 Bus 0, 使用传统 CF8/CFC 端口即可。
|
||||||
|
* --------------------------------------------------------------- */
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
u64 base_address; /* ECAM 基址 (4K 对齐, 256MB per bus) */
|
||||||
|
u16 pci_segment_group; /* PCI 段组号 (通常为 0) */
|
||||||
|
u8 start_bus_number; /* 起始总线号 */
|
||||||
|
u8 end_bus_number; /* 结束总线号 */
|
||||||
|
u32 reserved;
|
||||||
|
} mcfg_entry;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
acpi_sdt_header hdr;
|
||||||
|
u64 reserved; /* 保留, 通常为 0 */
|
||||||
|
/* mcfg_entry entries[] 紧跟 */
|
||||||
|
} mcfg_table;
|
||||||
|
|
||||||
|
static void acpi_parse_mcfg(const acpi_sdt_header *hdr) {
|
||||||
|
if (!hdr || hdr->length < sizeof(mcfg_table)) return;
|
||||||
|
|
||||||
|
const mcfg_table *mcfg = (const mcfg_table *)hdr;
|
||||||
|
u32 entry_size = sizeof(mcfg_entry);
|
||||||
|
u32 entries_len = mcfg->hdr.length - sizeof(mcfg_table);
|
||||||
|
u32 count = entries_len / entry_size;
|
||||||
|
|
||||||
|
g_log->info("[acpi] MCFG entries:");
|
||||||
|
log_hex("[acpi] count=", count);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < count; i++) {
|
||||||
|
const mcfg_entry *e = (const mcfg_entry *)((const u8 *)mcfg + sizeof(mcfg_table) + i * entry_size);
|
||||||
|
log_hex("[acpi] MCFG base=", e->base_address);
|
||||||
|
log_hex("[acpi] MCFG seg=", e->pci_segment_group);
|
||||||
|
log_hex("[acpi] MCFG bus_start=", e->start_bus_number);
|
||||||
|
log_hex("[acpi] MCFG bus_end=", e->end_bus_number);
|
||||||
|
|
||||||
|
/* 验证 ECAM 基址合理性 */
|
||||||
|
if (e->base_address == 0 || e->base_address == 0xFFFFFFFFFFFFFFFFULL) {
|
||||||
|
g_log->warn("[acpi] MCFG entry has invalid base address");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* 真机: ECAM 基址通常在 0xB0000000 - 0xBFFFFFFF (3G-4G 范围),
|
||||||
|
* 但也可能在 4G 以上 (特别是大内存系统) */
|
||||||
|
if (e->base_address >= 0x100000000ULL) {
|
||||||
|
g_log->info("[acpi] MCFG base above 4G — requires MMIO mapping");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------
|
||||||
|
* HPET (High Precision Event Timer) 解析
|
||||||
|
*
|
||||||
|
* 真机需要 HPET 替代 PIT 作为高精度定时器源。
|
||||||
|
* QEMU 默认也提供 HPET, 但当前内核使用 PIT 即可。
|
||||||
|
* HPET 信息留作后续高精度定时器实现的参考。
|
||||||
|
* --------------------------------------------------------------- */
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
u8 hardware_rev_id;
|
||||||
|
u8 comparator_count : 5;
|
||||||
|
u8 counter_size : 1; /* 0=32-bit, 1=64-bit */
|
||||||
|
u8 reserved0 : 1;
|
||||||
|
u8 legacy_replacement : 1; /* 1=LegacyReplacement IRQ routing available */
|
||||||
|
u16 pci_vendor_id;
|
||||||
|
acpi_generic_address address; /* HPET 寄存器基址 */
|
||||||
|
u8 hpet_number;
|
||||||
|
u16 minimum_tick;
|
||||||
|
u8 page_protection;
|
||||||
|
} hpet_entry_data;
|
||||||
|
|
||||||
|
/* HPET 表不含子条目, 只有 1 个 data block */
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
acpi_sdt_header hdr;
|
||||||
|
u32 event_timer_block_id; /* 与 hpet_entry_data 的前 4 字节相同 */
|
||||||
|
acpi_generic_address base_address;
|
||||||
|
u8 hpet_number;
|
||||||
|
u16 minimum_tick;
|
||||||
|
u8 page_protection;
|
||||||
|
} hpet_table;
|
||||||
|
|
||||||
|
static void acpi_parse_hpet(const acpi_sdt_header *hdr) {
|
||||||
|
if (!hdr || hdr->length < sizeof(hpet_table)) return;
|
||||||
|
|
||||||
|
const hpet_table *hpet = (const hpet_table *)hdr;
|
||||||
|
|
||||||
|
u32 block_id = hpet->event_timer_block_id;
|
||||||
|
u8 comp_count = (block_id >> 8) & 0x1f;
|
||||||
|
u8 count_size = (block_id >> 13) & 1;
|
||||||
|
u8 legacy = (block_id >> 15) & 1;
|
||||||
|
u8 rev_id = block_id & 0xff;
|
||||||
|
u16 vendor_id = (block_id >> 16) & 0xffff;
|
||||||
|
|
||||||
|
g_log->info("[acpi] HPET info:");
|
||||||
|
log_hex("[acpi] rev_id=", rev_id);
|
||||||
|
log_hex("[acpi] comparators=", comp_count);
|
||||||
|
log_hex("[acpi] counter_size=", count_size ? 64 : 32);
|
||||||
|
log_hex("[acpi] legacy_irq=", legacy);
|
||||||
|
log_hex("[acpi] vendor_id=", vendor_id);
|
||||||
|
log_hex("[acpi] min_tick=", hpet->minimum_tick);
|
||||||
|
|
||||||
|
/* HPET 地址空间类型 */
|
||||||
|
if (hpet->base_address.space_id == 0) {
|
||||||
|
/* System Memory — MMIO 访问 */
|
||||||
|
u64 addr = hpet->base_address.address;
|
||||||
|
log_hex("[acpi] HPET MMIO addr=", addr);
|
||||||
|
if (addr >= 0x100000000ULL) {
|
||||||
|
g_log->info("[acpi] HPET base above 4G — requires MMIO mapping");
|
||||||
|
}
|
||||||
|
} else if (hpet->base_address.space_id == 1) {
|
||||||
|
/* System I/O — 端口 I/O */
|
||||||
|
log_hex("[acpi] HPET I/O port=", hpet->base_address.address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------
|
||||||
|
* MADT (APIC) 解析 — 为 apic 驱动提供数据
|
||||||
|
* --------------------------------------------------------------- */
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
acpi_sdt_header hdr;
|
||||||
|
u32 lapic_addr;
|
||||||
|
u32 flags;
|
||||||
|
} acpi_madt;
|
||||||
|
|
||||||
|
static void acpi_parse_madt(const acpi_sdt_header *hdr) {
|
||||||
|
if (!hdr || hdr->length < sizeof(acpi_madt)) return;
|
||||||
|
const acpi_madt *madt = (const acpi_madt *)hdr;
|
||||||
|
|
||||||
|
g_log->info("[acpi] MADT detail:");
|
||||||
|
log_hex("[acpi] LAPIC addr=", madt->lapic_addr);
|
||||||
|
log_hex("[acpi] flags=", madt->flags);
|
||||||
|
if (madt->flags & 1) {
|
||||||
|
g_log->info("[acpi] PCAT_COMPAT=1 (8259 PIC present)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 枚举 MADT 子条目做汇总统计 */
|
||||||
|
const u8 *base = (const u8 *)madt;
|
||||||
|
u32 off = sizeof(acpi_madt);
|
||||||
|
u32 lapic_count = 0, ioapic_count = 0, iso_count = 0, nmi_count = 0;
|
||||||
|
|
||||||
|
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) break;
|
||||||
|
|
||||||
|
if (type == 0) lapic_count++;
|
||||||
|
else if (type == 1) ioapic_count++;
|
||||||
|
else if (type == 2) iso_count++;
|
||||||
|
else if (type == 3 || type == 4) nmi_count++;
|
||||||
|
|
||||||
|
off += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_hex("[acpi] LAPIC entries=", lapic_count);
|
||||||
|
log_hex("[acpi] IOAPIC entries=", ioapic_count);
|
||||||
|
log_hex("[acpi] ISO entries=", iso_count);
|
||||||
|
log_hex("[acpi] NMI entries=", nmi_count);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------
|
/* ---------------------------------------------------------------
|
||||||
* Table enumeration
|
* Table enumeration
|
||||||
* --------------------------------------------------------------- */
|
* --------------------------------------------------------------- */
|
||||||
@@ -241,6 +412,15 @@ static void acpi_enumerate_rsdt(const acpi_sdt_header *rsdt, u32 entry_size) {
|
|||||||
g_log->info(sig);
|
g_log->info(sig);
|
||||||
log_hex("[acpi] addr=", addr);
|
log_hex("[acpi] addr=", addr);
|
||||||
log_hex("[acpi] len =", (u64)hdr->length);
|
log_hex("[acpi] len =", (u64)hdr->length);
|
||||||
|
|
||||||
|
/* 对关键表做详细解析 */
|
||||||
|
if (sig[0]=='A' && sig[1]=='P' && sig[2]=='I' && sig[3]=='C') {
|
||||||
|
acpi_parse_madt(hdr);
|
||||||
|
} else if (sig[0]=='M' && sig[1]=='C' && sig[2]=='F' && sig[3]=='G') {
|
||||||
|
acpi_parse_mcfg(hdr);
|
||||||
|
} else if (sig[0]=='H' && sig[1]=='P' && sig[2]=='E' && sig[3]=='T') {
|
||||||
|
acpi_parse_hpet(hdr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+102
-20
@@ -5,6 +5,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../dkm_shared.h"
|
#include "../dkm_shared.h"
|
||||||
|
#include "../dkm_instr.h"
|
||||||
|
|
||||||
|
DKM_STAT_DECL(ahci_port_found);
|
||||||
|
DKM_STAT_DECL(ahci_identify_ok);
|
||||||
|
DKM_STAT_DECL(ahci_block_reads);
|
||||||
|
DKM_STAT_DECL(ahci_block_writes);
|
||||||
|
DKM_TS_DECL(ts_abar);
|
||||||
|
|
||||||
static const char *const g_depends[] = { "pci", "irq" };
|
static const char *const g_depends[] = { "pci", "irq" };
|
||||||
static const char *const g_provides[] = { "block" };
|
static const char *const g_provides[] = { "block" };
|
||||||
@@ -130,7 +137,8 @@ static void ahci_zero(void *ptr, u32 len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int ahci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
static int ahci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
||||||
for (u8 bus = 0; bus < 8; bus++) {
|
/* PCI 规范允许 256 条 bus (0-255),实机 AHCI 可能位于 bus > 7 */
|
||||||
|
for (u16 bus = 0; bus < 256; bus++) {
|
||||||
for (u8 dev = 0; dev < 32; dev++) {
|
for (u8 dev = 0; dev < 32; dev++) {
|
||||||
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
|
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
|
||||||
if ((vd & 0xffff) == 0xffff) continue;
|
if ((vd & 0xffff) == 0xffff) continue;
|
||||||
@@ -298,13 +306,16 @@ static int ahci_read_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm_
|
|||||||
g_abar[(base + PxIS) / 4] = 0xffffffffu;
|
g_abar[(base + PxIS) / 4] = 0xffffffffu;
|
||||||
g_abar[HBA_IS / 4] = (1u << port);
|
g_abar[HBA_IS / 4] = (1u << port);
|
||||||
g_abar[(base + PxCI) / 4] = 1;
|
g_abar[(base + PxCI) / 4] = 1;
|
||||||
for (u32 i = 0; i < 5000000; i++) {
|
/* TSC deadline 超时: 5 秒,实机机械 HDD 首次寻道需数百 ms */
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 5000;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
|
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
|
||||||
u8 *dst = (u8 *)buffer;
|
u8 *dst = (u8 *)buffer;
|
||||||
u8 *src = (u8 *)data->virt;
|
u8 *src = (u8 *)data->virt;
|
||||||
for (u32 j = 0; j < bytes; j++) dst[j] = src[j];
|
for (u32 j = 0; j < bytes; j++) dst[j] = src[j];
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
}
|
}
|
||||||
g_log->warn("[ahci] READ timeout");
|
g_log->warn("[ahci] READ timeout");
|
||||||
log_hex("[ahci] READ lba=", lba);
|
log_hex("[ahci] READ lba=", lba);
|
||||||
@@ -330,6 +341,10 @@ static int ahci_write_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm
|
|||||||
for (u32 j = 0; j < bytes; j++) dst[j] = src[j];
|
for (u32 j = 0; j < bytes; j++) dst[j] = src[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* BUG-P0-2: 写入前必须 flush CPU cache,否则 DMA 控制器从物理内存
|
||||||
|
* 读到陈旧数据(CPU write-back cache 尚未写回)。实机必现。 */
|
||||||
|
__asm__ volatile("wbinvd" ::: "memory");
|
||||||
|
|
||||||
hdr[0].flags = 5 | (1u << 6); /* CFL=5, W=1 (write) */
|
hdr[0].flags = 5 | (1u << 6); /* CFL=5, W=1 (write) */
|
||||||
hdr[0].prdtl = 1;
|
hdr[0].prdtl = 1;
|
||||||
hdr[0].prdbc = 0;
|
hdr[0].prdbc = 0;
|
||||||
@@ -341,29 +356,39 @@ static int ahci_write_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm
|
|||||||
tbl->prdt[0].dbc_i = (bytes - 1) | (1u << 31);
|
tbl->prdt[0].dbc_i = (bytes - 1) | (1u << 31);
|
||||||
ahci_fill_write_fis(tbl->cfis, lba, count);
|
ahci_fill_write_fis(tbl->cfis, lba, count);
|
||||||
|
|
||||||
/* set PxCMD.W to indicate write direction */
|
/* P0 修复: 移除 PxCMD.W 写入。AHCI 规范中 PxCMD.W 是只读位,
|
||||||
u32 cmd = g_abar[(base + PxCMD) / 4];
|
* 由硬件根据命令头 flags.W 自动设置。软件写入此位违反规范,
|
||||||
cmd |= AHCI_CMD_W;
|
* 可能在某些实机 AHCI 控制器上导致未定义行为。 */
|
||||||
g_abar[(base + PxCMD) / 4] = cmd;
|
/* (原代码: cmd |= AHCI_CMD_W; g_abar[...] = cmd; — 已删除) */
|
||||||
|
|
||||||
g_abar[(base + PxIS) / 4] = 0xffffffffu;
|
g_abar[(base + PxIS) / 4] = 0xffffffffu;
|
||||||
g_abar[HBA_IS / 4] = (1u << port);
|
g_abar[HBA_IS / 4] = (1u << port);
|
||||||
g_abar[(base + PxCI) / 4] = 1;
|
g_abar[(base + PxCI) / 4] = 1;
|
||||||
for (u32 i = 0; i < 5000000; i++) {
|
/* P0 修复: 写入超时改为 TSC deadline,与读取路径一致。
|
||||||
|
* 固定循环在实机 HDD 上会因 CPU 频率差异导致提前超时或死循环。 */
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 5000;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
|
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
|
||||||
/* clear PxCMD.W after write completes */
|
/* BUG-P1-1 fix: CI 清除后检查 TFD 错误位。
|
||||||
cmd = g_abar[(base + PxCMD) / 4];
|
* TFD 低字节是 STS,正常值 0x50 (DRDY|DRQ) 非零但不是错误。
|
||||||
cmd &= ~AHCI_CMD_W;
|
* 只有 STS bit0 (ERR) 或 ERR 寄存器 (bits15:8) 非零才是真正错误。
|
||||||
g_abar[(base + PxCMD) / 4] = cmd;
|
* BUG-20260730-P14: 原 `tfd & 0xFF` 误判 STS=0x50 为错误,导致写入
|
||||||
|
* 实际成功却被报告为失败,FAT32 写路径提前返回错误码。 */
|
||||||
|
u32 tfd = g_abar[(base + PxTFD) / 4];
|
||||||
|
u32 is = g_abar[(base + PxIS) / 4];
|
||||||
|
u8 sts = (u8)(tfd & 0xFF);
|
||||||
|
u8 err_reg = (u8)((tfd >> 8) & 0xFF);
|
||||||
|
if ((sts & 0x01) || err_reg) {
|
||||||
|
g_log->warn("[ahci] write error: TFD ERR");
|
||||||
|
log_hex("[ahci] TFD=", tfd);
|
||||||
|
log_hex("[ahci] PxIS=", is);
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* timeout: clear PxCMD.W */
|
|
||||||
cmd = g_abar[(base + PxCMD) / 4];
|
|
||||||
cmd &= ~AHCI_CMD_W;
|
|
||||||
g_abar[(base + PxCMD) / 4] = cmd;
|
|
||||||
|
|
||||||
g_log->warn("[ahci] WRITE timeout");
|
g_log->warn("[ahci] WRITE timeout");
|
||||||
log_hex("[ahci] WRITE lba=", lba);
|
log_hex("[ahci] WRITE lba=", lba);
|
||||||
log_hex("[ahci] WRITE count=", count);
|
log_hex("[ahci] WRITE count=", count);
|
||||||
@@ -376,6 +401,7 @@ static int ahci_write_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm
|
|||||||
static int ahci_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
|
static int ahci_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1;
|
if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1;
|
||||||
|
DKM_STAT_INC(ahci_block_reads);
|
||||||
log_hex("[ahci] block_read lba=", lba);
|
log_hex("[ahci] block_read lba=", lba);
|
||||||
log_hex("[ahci] block_read count=", count);
|
log_hex("[ahci] block_read count=", count);
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
@@ -392,6 +418,7 @@ static int ahci_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
|
|||||||
static int ahci_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
|
static int ahci_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1;
|
if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1;
|
||||||
|
DKM_STAT_INC(ahci_block_writes);
|
||||||
/* ensure port is in ST+FRE state */
|
/* ensure port is in ST+FRE state */
|
||||||
u32 base = HBA_PORT_BASE + g_ready_port * HBA_PORT_SIZE;
|
u32 base = HBA_PORT_BASE + g_ready_port * HBA_PORT_SIZE;
|
||||||
u32 cmd = g_abar[(base + PxCMD) / 4];
|
u32 cmd = g_abar[(base + PxCMD) / 4];
|
||||||
@@ -445,8 +472,27 @@ static int ahci_identify_port(u32 port, const struct dkm_dma_api *dma) {
|
|||||||
struct dkm_dma_buffer data;
|
struct dkm_dma_buffer data;
|
||||||
if (dma->alloc_pages(1, 1024, 0x100000000ULL, &clb) != 0) return -2;
|
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, 256, 0x100000000ULL, &fis) != 0) return -3;
|
||||||
if (dma->alloc_pages(1, 128, 0x100000000ULL, &table) != 0) return -4;
|
/* BUG-P0-1: sizeof(ahci_cmd_table)=144, 128B 缓冲区越界 16 字节。
|
||||||
if (dma->alloc_pages(1, 512, 0x100000000ULL, &data) != 0) return -5;
|
* 改为 256B 满足对齐和大小要求,防止 ahci_zero 越界写。 */
|
||||||
|
if (dma->alloc_pages(1, 256, 0x100000000ULL, &table) != 0) return -4;
|
||||||
|
/* BUG-20260730-022: DMA data buffer must hold at least 8 sectors (4096B)
|
||||||
|
* for ahci_block_write chunked DMA. 512B caused 3584B out-of-bounds
|
||||||
|
* overwrite of adjacent kernel data on real hardware. */
|
||||||
|
if (dma->alloc_pages(1, 8192, 0x100000000ULL, &data) != 0) return -5;
|
||||||
|
|
||||||
|
/* P0 验证: DMA 缓冲区物理地址必须在 4GB 以下。
|
||||||
|
* AHCI 控制器 8086:2922 只支持 32 位 DMA,
|
||||||
|
* PRDT 地址超过 4GB 会导致 DMA 写入截断到错误物理地址。
|
||||||
|
* dma->alloc_pages 的上限参数 0x100000000ULL 已保证此约束,
|
||||||
|
* 但添加显式校验以防御 alloc_pages 实现变更。 */
|
||||||
|
if (clb.phys >= 0x100000000ULL || fis.phys >= 0x100000000ULL ||
|
||||||
|
table.phys >= 0x100000000ULL || data.phys >= 0x100000000ULL) {
|
||||||
|
g_log->warn("[ahci] DMA buffer above 4GB — 32-bit DMA will fail on this controller!");
|
||||||
|
log_hex("[ahci] clb.phys=", clb.phys);
|
||||||
|
log_hex("[ahci] fis.phys=", fis.phys);
|
||||||
|
log_hex("[ahci] table.phys=", table.phys);
|
||||||
|
log_hex("[ahci] data.phys=", data.phys);
|
||||||
|
}
|
||||||
|
|
||||||
if (ahci_stop_port(port) != 0) {
|
if (ahci_stop_port(port) != 0) {
|
||||||
g_log->warn("[ahci] port stop timeout");
|
g_log->warn("[ahci] port stop timeout");
|
||||||
@@ -476,7 +522,10 @@ static int ahci_identify_port(u32 port, const struct dkm_dma_api *dma) {
|
|||||||
ahci_start_port(port);
|
ahci_start_port(port);
|
||||||
|
|
||||||
g_abar[(base + PxCI) / 4] = 1;
|
g_abar[(base + PxCI) / 4] = 1;
|
||||||
for (u32 i = 0; i < 5000000; i++) {
|
/* BUG-P0-3: 固定 500 万次循环在 3GHz CPU 上仅 ~1-2ms,
|
||||||
|
* 不够 HDD 寻道。改为 TSC deadline 5s 超时,与 read/write 路径一致。 */
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 5000;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
|
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
|
||||||
log_hex("[ahci] IDENTIFY port=", port);
|
log_hex("[ahci] IDENTIFY port=", port);
|
||||||
log_hex("[ahci] IDENTIFY status=", g_abar[(base + PxTFD) / 4]);
|
log_hex("[ahci] IDENTIFY status=", g_abar[(base + PxTFD) / 4]);
|
||||||
@@ -492,6 +541,7 @@ static int ahci_identify_port(u32 port, const struct dkm_dma_api *dma) {
|
|||||||
g_data = data;
|
g_data = data;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
}
|
}
|
||||||
|
|
||||||
g_log->warn("[ahci] IDENTIFY timeout");
|
g_log->warn("[ahci] IDENTIFY timeout");
|
||||||
@@ -510,6 +560,7 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
|
|
||||||
if (!api || !api->log) return -1;
|
if (!api || !api->log) return -1;
|
||||||
g_log = api->log;
|
g_log = api->log;
|
||||||
|
dkm_instr_init(api);
|
||||||
|
|
||||||
g_log->info("[ahci] init begin");
|
g_log->info("[ahci] init begin");
|
||||||
|
|
||||||
@@ -536,7 +587,17 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
pci_write(bus, dev, func, PCI_COMMAND, command);
|
pci_write(bus, dev, func, PCI_COMMAND, command);
|
||||||
log_hex("[ahci] PCI command=", command);
|
log_hex("[ahci] PCI command=", command);
|
||||||
|
|
||||||
u64 abar_phys = (u64)(pci_read(bus, dev, func, PCI_BAR5) & 0xFFFFFFF0u);
|
/* 读取 ABAR: BAR5 可能是 64-bit MMIO BAR,高位在 BAR4 */
|
||||||
|
u32 bar5_lo = pci_read(bus, dev, func, PCI_BAR5);
|
||||||
|
u64 abar_phys;
|
||||||
|
if ((bar5_lo & 0x7) == 0x4) {
|
||||||
|
/* 64-bit MMIO BAR: 高 32 位在 BAR4 */
|
||||||
|
u32 bar5_hi = pci_read(bus, dev, func, PCI_BAR4);
|
||||||
|
abar_phys = ((u64)bar5_hi << 32) | (bar5_lo & 0xFFFFFFF0u);
|
||||||
|
g_log->info("[ahci] ABAR is 64-bit MMIO BAR");
|
||||||
|
} else {
|
||||||
|
abar_phys = bar5_lo & 0xFFFFFFF0u;
|
||||||
|
}
|
||||||
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
|
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
|
||||||
log_hex("[ahci] ABAR phys=", abar_phys);
|
log_hex("[ahci] ABAR phys=", abar_phys);
|
||||||
log_hex("[ahci] PCI IRQ line=", irq_line);
|
log_hex("[ahci] PCI IRQ line=", irq_line);
|
||||||
@@ -548,6 +609,7 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_abar = (volatile u32 *)(uintptr_t)(api->hhdm_offset + abar_phys);
|
g_abar = (volatile u32 *)(uintptr_t)(api->hhdm_offset + abar_phys);
|
||||||
|
DKM_TS_BEGIN(ts_abar);
|
||||||
log_hex("[ahci] HHDM offset=", api->hhdm_offset);
|
log_hex("[ahci] HHDM offset=", api->hhdm_offset);
|
||||||
|
|
||||||
u32 cap = g_abar[HBA_CAP / 4];
|
u32 cap = g_abar[HBA_CAP / 4];
|
||||||
@@ -565,13 +627,32 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
log_hex("[ahci] VS=", vs);
|
log_hex("[ahci] VS=", vs);
|
||||||
log_hex("[ahci] CAP2=", cap2);
|
log_hex("[ahci] CAP2=", cap2);
|
||||||
log_hex("[ahci] BOHC=", bohc);
|
log_hex("[ahci] BOHC=", bohc);
|
||||||
|
|
||||||
|
/* AHCI 1.3+ BOHC handoff: 如果 BIOS 仍拥有控制器, 请求所有权 */
|
||||||
|
if (bohc & 0x01) {
|
||||||
|
g_log->info("[ahci] BIOS owns HBA, requesting handoff");
|
||||||
|
g_abar[HBA_BOHC / 4] = bohc | 0x02; /* set OOS */
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
|
if (!(g_abar[HBA_BOHC / 4] & 0x01)) break;
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
if (g_abar[HBA_BOHC / 4] & 0x01) {
|
||||||
|
g_log->warn("[ahci] BIOS handoff timeout, proceeding anyway");
|
||||||
|
} else {
|
||||||
|
g_log->info("[ahci] BIOS handoff complete");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log_hex("[ahci] ports count=", (cap & 0x1f) + 1);
|
log_hex("[ahci] ports count=", (cap & 0x1f) + 1);
|
||||||
log_hex("[ahci] cmd slots=", ((cap >> 8) & 0x1f) + 1);
|
log_hex("[ahci] cmd slots=", ((cap >> 8) & 0x1f) + 1);
|
||||||
|
DKM_TS_END(ts_abar, g_log->info, "ahci_abar_mmio");
|
||||||
|
|
||||||
u32 implemented = 0;
|
u32 implemented = 0;
|
||||||
for (u32 port = 0; port < 32; port++) {
|
for (u32 port = 0; port < 32; port++) {
|
||||||
if (pi & (1u << port)) {
|
if (pi & (1u << port)) {
|
||||||
implemented++;
|
implemented++;
|
||||||
|
DKM_STAT_INC(ahci_port_found);
|
||||||
ahci_log_port(port);
|
ahci_log_port(port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -584,6 +665,7 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
int ident = ahci_identify_port(port, api->dma);
|
int ident = ahci_identify_port(port, api->dma);
|
||||||
if (ident > 0) {
|
if (ident > 0) {
|
||||||
identified++;
|
identified++;
|
||||||
|
DKM_STAT_INC(ahci_identify_ok);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1214
-11
@@ -1,7 +1,51 @@
|
|||||||
/* DKM APIC Driver — APIC discovery with PIC fallback
|
/* DKM APIC Driver — APIC discovery + IOAPIC init + LAPIC enable + 路由接管
|
||||||
* Stage 0, required, depends on "timer", provides "irq" and "apic".
|
* 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.
|
* 真机路径:
|
||||||
|
* 1. LAPIC 使能 + Spurious Interrupt Vector 注册
|
||||||
|
* 2. IOAPIC 重定向表初始化: 逐条目编程, 将 ISA IRQ 映射到 vector 0x20-0x2F
|
||||||
|
* (兼容 PIC 布局), 逐条目 mask
|
||||||
|
* 3. MADT ISO (Interrupt Source Override) 处理: 按 ACPI 规范修正 IRQ→GSI 映射
|
||||||
|
* 4. SIPI 多核启动框架 (预留, 未实现)
|
||||||
|
*
|
||||||
|
* B7 路由接管 (apic_route):
|
||||||
|
* FUCK [drivers] apic_route=1 时, 本驱动在初始化末尾尝试把中断路由
|
||||||
|
* 从 8259 PIC 切换到 IOAPIC→LAPIC 路径:
|
||||||
|
* a. 经 kernel_api.register_apic_eoi 向 UTSM 注册 LAPIC EOI 钩子
|
||||||
|
* (B7 阶段2 接口; idt_handler 两个 IRQ 分支在分发末尾调用之)
|
||||||
|
* b. 注册 PIT tick 计数 handler 于 vector 0x20 — 即 idt_handler 的
|
||||||
|
* PIC 分支 (0x20-0x2F), 与键盘 0x21/鼠标 0x2C/网卡 0x2B 同一代码
|
||||||
|
* 路径; handler 返回 0 由钩子完成 LAPIC EOI, 重复投递 >=2 即证明
|
||||||
|
* "legacy vector 分支 + 钩子 EOI" 全链路可持续 (阶段1 用 0xE0 自
|
||||||
|
* EOI 只证明了 APIC 分支)
|
||||||
|
* c. 按 ISO 表解析 PIT GSI (QEMU q35: IRQ0→GSI2), 解屏蔽该条目
|
||||||
|
* d. LAPIC TPR=0 全放行, PIC IMR 全 1 屏蔽 (QEMU 无功能 IMCR 寄存器,
|
||||||
|
* PIC 全屏蔽即等效切换; 8259 与 IOAPIC 同时监听 IRQ 线, 全屏蔽避免双投递)
|
||||||
|
* e. PIT ch0 mode3 100Hz 产生真实硬件中断, 500ms 窗口内计数 >=2 次
|
||||||
|
* 即证明 IOAPIC→LAPIC→IDT→handler→钩子EOI 全链路
|
||||||
|
* f. 验证失败完整降级: IOAPIC 全 mask + 恢复原 PIC IMR + 注销 handler
|
||||||
|
* 与 EOI 钩子, 系统行为与切换前完全一致
|
||||||
|
*
|
||||||
|
* B7 阶段2 (apic_route_legacy):
|
||||||
|
* EOI 钩子落地后, legacy IRQ1(键盘 0x21)/IRQ12(鼠标 0x2C)/IRQ11(e1000
|
||||||
|
* 0x2B) 重定向项可持续投递, 不再是 one-shot。ISA 线按 MADT ISO 极性/触发
|
||||||
|
* (缺省 edge/active-high); PCI 线 (IRQ11) 无 ISO 覆盖时强制 level/
|
||||||
|
* active-low (PCI INTx 规范; QEMU 内部 pci irq 电平语义与之匹配,
|
||||||
|
* Linux/Windows 同配置)。e1000 handler 读 ICR 清源后电平 deassert,
|
||||||
|
* 由钩子 EOI 完成 LAPIC 侧清扫。
|
||||||
|
*
|
||||||
|
* apic_diag=1 诊断:
|
||||||
|
* - HPET 绝对时基对拍: 解析 ACPI HPET 表 (QEMU q35 有, 100MHz 主计数器),
|
||||||
|
* 用主计数器实测 tsc_calibrate 的 10ms 窗口与 route 验证的 500ms 窗口,
|
||||||
|
* 定论 "tick 2x" 根因 (TSC 校准测半 vs PIT mode3 双触发)
|
||||||
|
* - PS/2 鼠标流: 初始化 AUX 端口 + 注册 IRQ12 诊断 handler, 配合
|
||||||
|
* QEMU monitor mouse_move 验证 IRQ12 持续投递 (计数 + 观察窗)
|
||||||
|
* diag 只增观测不改变量路由语义, 默认 0 关闭。
|
||||||
|
*
|
||||||
|
* QEMU 兼容:
|
||||||
|
* - QEMU 默认只提供 1 个 IOAPIC, 24 条 GSI
|
||||||
|
* - 实机可能有多个 IOAPIC, GSI 范围更大
|
||||||
|
* - 实机必须处理 MADT ISO (IRQ0→GSI2 等)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -47,6 +91,15 @@ struct dkm_kernel_api {
|
|||||||
const void *boot_modules_response;
|
const void *boot_modules_response;
|
||||||
int (*irq_register)(u8 irq, void *handler);
|
int (*irq_register)(u8 irq, void *handler);
|
||||||
u64 hhdm_offset;
|
u64 hhdm_offset;
|
||||||
|
const void *block;
|
||||||
|
const void *mmio;
|
||||||
|
void *(*mm_map_mmio)(u64 phys, u64 size);
|
||||||
|
void (*mm_unmap_mmio)(void *virt, u64 size);
|
||||||
|
/* B7 阶段2 尾部追加 (旧内核无此字段 -> 判空使用) */
|
||||||
|
void (*register_apic_eoi)(void (*eoi_fn)(void));
|
||||||
|
/* B7 阶段3 尾部追加 (本驱动不使用, 保持布局与 UTSM 一致) */
|
||||||
|
int (*irq_vector_alloc)(void);
|
||||||
|
void (*irq_vector_free)(int vector);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dkm_driver_handle;
|
struct dkm_driver_handle;
|
||||||
@@ -81,7 +134,7 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
.abi_version = DKM_ABI_VERSION,
|
.abi_version = DKM_ABI_VERSION,
|
||||||
.desc_size = sizeof(struct dkm_driver_desc),
|
.desc_size = sizeof(struct dkm_driver_desc),
|
||||||
.name = "apic",
|
.name = "apic",
|
||||||
.version = "0.1.0",
|
.version = "0.2.0",
|
||||||
.vendor = "Deshab",
|
.vendor = "Deshab",
|
||||||
.driver_class = 3, /* DKM_CLASS_INTERRUPT */
|
.driver_class = 3, /* DKM_CLASS_INTERRUPT */
|
||||||
.stage = 0,
|
.stage = 0,
|
||||||
@@ -135,15 +188,102 @@ typedef struct __attribute__((packed)) {
|
|||||||
u32 flags;
|
u32 flags;
|
||||||
} acpi_madt;
|
} acpi_madt;
|
||||||
|
|
||||||
|
/* ACPI HPET 表 (signature "HPET") — apic_diag 绝对时基对拍用 */
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
acpi_sdt_header hdr;
|
||||||
|
u32 event_timer_block_id;
|
||||||
|
/* ACPI Generic Address Structure (GAS) */
|
||||||
|
u8 addr_space_id; /* 0 = system memory */
|
||||||
|
u8 register_bit_width;
|
||||||
|
u8 register_bit_offset;
|
||||||
|
u8 access_size;
|
||||||
|
u64 base_addr; /* HPET MMIO 物理基址 (QEMU: 0xFED00000) */
|
||||||
|
u8 hpet_number;
|
||||||
|
u16 min_clock_tick;
|
||||||
|
u8 page_protection;
|
||||||
|
} acpi_hpet;
|
||||||
|
|
||||||
|
/* Limine boot module 最小类型 (与 bootfs.drv / UTSM limine.h 布局一致) */
|
||||||
|
struct limine_file_min {
|
||||||
|
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_min {
|
||||||
|
u64 revision;
|
||||||
|
u64 module_count;
|
||||||
|
struct limine_file_min **modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- IOAPIC 信息存储(真机多 IOAPIC 支持) ---- */
|
||||||
|
|
||||||
|
#define MAX_IOAPIC 4
|
||||||
|
#define MAX_ISO 16
|
||||||
|
#define MAX_LAPIC 256
|
||||||
|
|
||||||
|
struct ioapic_info {
|
||||||
|
u8 id;
|
||||||
|
u32 gsi_base;
|
||||||
|
u32 gsi_count; /* 从 IOAPIC VER 寄存器读取: max_redir_entry + 1 */
|
||||||
|
u64 mmio_phys; /* IOAPIC MMIO 物理基址 */
|
||||||
|
volatile u32 *mmio; /* HHDM/MMIO 窗口映射后的虚拟地址, 0=未映射 */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct iso_info {
|
||||||
|
u8 bus_source; /* ISA bus source IRQ (0-15) */
|
||||||
|
u32 gsi; /* 全局系统中断号 */
|
||||||
|
u16 flags; /* 极性/触发模式标志 */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lapic_entry {
|
||||||
|
u8 apic_id;
|
||||||
|
u32 flags; /* MADT LAPIC flags: bit0=enabled */
|
||||||
|
};
|
||||||
|
|
||||||
struct madt_info {
|
struct madt_info {
|
||||||
u64 lapic_phys;
|
u64 lapic_phys;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
u32 lapic_count;
|
u32 lapic_count;
|
||||||
u32 ioapic_count;
|
u32 ioapic_count;
|
||||||
u32 iso_count;
|
u32 iso_count;
|
||||||
|
|
||||||
|
struct ioapic_info ioapics[MAX_IOAPIC];
|
||||||
|
struct iso_info isos[MAX_ISO];
|
||||||
|
struct lapic_entry lapics[MAX_LAPIC];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- B7 路由接管配置 (FUCK [drivers]) ---- */
|
||||||
|
struct apic_config {
|
||||||
|
int lapic_enable; /* lapic_enable, 默认 1 */
|
||||||
|
int ioapic_init; /* ioapic_init, 默认 1 */
|
||||||
|
int apic_route; /* apic_route, 默认 0 (PIC 路由, 现状) */
|
||||||
|
int apic_route_legacy; /* apic_route_legacy, 默认 0 */
|
||||||
|
int apic_diag; /* apic_diag, 默认 0 (HPET 校准/投递计数/鼠标流诊断) */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct dkm_log_api *g_log;
|
static const struct dkm_log_api *g_log;
|
||||||
|
static struct apic_config g_cfg = { 1, 1, 0, 0, 0 };
|
||||||
|
|
||||||
|
static volatile u32 *g_lapic; /* LAPIC MMIO 映射基址, 0=未映射 */
|
||||||
|
static u32 g_lapic_id; /* BSP Local APIC ID (IOAPIC 目的 ID) */
|
||||||
|
static volatile u32 g_tick_count; /* PIT tick 计数 (路由验证) */
|
||||||
|
static int g_route_active; /* 1=IOAPIC 路由已接管 */
|
||||||
|
|
||||||
|
static __inline__ void apic_mb(void) {
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
static int memeq(const char *a, const char *b, u32 n) {
|
static int memeq(const char *a, const char *b, u32 n) {
|
||||||
for (u32 i = 0; i < n; i++) {
|
for (u32 i = 0; i < n; i++) {
|
||||||
@@ -152,6 +292,24 @@ static int memeq(const char *a, const char *b, u32 n) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 str_len(const char *s) {
|
||||||
|
u32 n = 0;
|
||||||
|
while (s[n]) n++;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 在 NUL 结尾字符串中查找子串 (cmdline 匹配用) */
|
||||||
|
static int str_contains(const char *hay, const char *needle) {
|
||||||
|
u32 nlen = str_len(needle);
|
||||||
|
if (!nlen) return 1;
|
||||||
|
for (u32 i = 0; hay[i]; i++) {
|
||||||
|
u32 j = 0;
|
||||||
|
while (j < nlen && hay[i + j] && hay[i + j] == needle[j]) j++;
|
||||||
|
if (j == nlen) return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static u8 checksum8(const u8 *data, u32 len) {
|
static u8 checksum8(const u8 *data, u32 len) {
|
||||||
u8 sum = 0;
|
u8 sum = 0;
|
||||||
for (u32 i = 0; i < len; i++) sum += data[i];
|
for (u32 i = 0; i < len; i++) sum += data[i];
|
||||||
@@ -172,6 +330,37 @@ static void log_hex(const char *prefix, u64 val) {
|
|||||||
g_log->info(buf);
|
g_log->info(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void log_dec(const char *prefix, u64 val) {
|
||||||
|
char buf[24];
|
||||||
|
u32 pos = 0;
|
||||||
|
if (!val) {
|
||||||
|
buf[pos++] = '0';
|
||||||
|
} else {
|
||||||
|
char tmp[20];
|
||||||
|
u32 n = 0;
|
||||||
|
while (val) { tmp[n++] = (char)('0' + (val % 10)); val /= 10; }
|
||||||
|
while (n) buf[pos++] = tmp[--n];
|
||||||
|
}
|
||||||
|
buf[pos] = 0;
|
||||||
|
g_log->info(prefix);
|
||||||
|
g_log->info(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- I/O 端口 (PIC / PIT) ---- */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __inline__ void io_wait(void) {
|
||||||
|
outb(0x80, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static void cpuid(u32 leaf, u32 *a, u32 *b, u32 *c, u32 *d) {
|
static void cpuid(u32 leaf, u32 *a, u32 *b, u32 *c, u32 *d) {
|
||||||
__asm__ volatile ("cpuid"
|
__asm__ volatile ("cpuid"
|
||||||
: "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
|
: "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
|
||||||
@@ -185,6 +374,12 @@ static u64 rdmsr(u32 msr) {
|
|||||||
return ((u64)hi << 32) | lo;
|
return ((u64)hi << 32) | lo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wrmsr(u32 msr, u64 val) {
|
||||||
|
u32 lo = (u32)(val & 0xffffffffu);
|
||||||
|
u32 hi = (u32)(val >> 32);
|
||||||
|
__asm__ volatile("wrmsr" :: "a"(lo), "d"(hi), "c"(msr));
|
||||||
|
}
|
||||||
|
|
||||||
static int cpu_has_lapic(void) {
|
static int cpu_has_lapic(void) {
|
||||||
u32 a, b, c, d;
|
u32 a, b, c, d;
|
||||||
cpuid(1, &a, &b, &c, &d);
|
cpuid(1, &a, &b, &c, &d);
|
||||||
@@ -194,6 +389,131 @@ static int cpu_has_lapic(void) {
|
|||||||
return (d & (1u << 9)) != 0;
|
return (d & (1u << 9)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- FUCK 配置自解析 (DKM 侧, 不依赖 UTSM ini_parser) ---- *
|
||||||
|
* UTSM 通过 Limine boot module (cmdline="fuck:config") 预加载 FUCK;
|
||||||
|
* kernel_api.boot_modules_response 对所有 DKM 驱动可见。
|
||||||
|
* 此处实现最小分区感知 INI 扫描: 只找 [drivers] 分区下的目标键。
|
||||||
|
* 缺失文件/缺失键一律回退默认值, 行为与无配置时一致。
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 解析值 token: 支持 0/1/true/false/on/off/yes/no 及十进制整数 */
|
||||||
|
static int parse_scalar(const char *line, u64 start, u64 line_len, int defval) {
|
||||||
|
u64 i = start;
|
||||||
|
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
|
||||||
|
u64 tok_start = i;
|
||||||
|
while (i < line_len && line[i] != ' ' && line[i] != '\t' &&
|
||||||
|
line[i] != '#' && line[i] != ';' && line[i] != '\r') i++;
|
||||||
|
u64 tok_len = i - tok_start;
|
||||||
|
const char *tok = line + tok_start;
|
||||||
|
if (!tok_len) return defval;
|
||||||
|
if (tok_len == 1 && tok[0] == '1') return 1;
|
||||||
|
if (tok_len == 1 && tok[0] == '0') return 0;
|
||||||
|
if (tok_len == 4 && memeq(tok, "true", 4)) return 1;
|
||||||
|
if (tok_len == 5 && memeq(tok, "false", 5)) return 0;
|
||||||
|
if (tok_len == 2 && memeq(tok, "on", 2)) return 1;
|
||||||
|
if (tok_len == 3 && memeq(tok, "off", 3)) return 0;
|
||||||
|
if (tok_len == 3 && memeq(tok, "yes", 3)) return 1;
|
||||||
|
if (tok_len == 2 && memeq(tok, "no", 2)) return 0;
|
||||||
|
/* 十进制整数 (允许前导 '-') */
|
||||||
|
int neg = 0;
|
||||||
|
u64 j = 0;
|
||||||
|
if (tok[0] == '-') { neg = 1; j = 1; }
|
||||||
|
int val = 0;
|
||||||
|
int any = 0;
|
||||||
|
for (; j < tok_len; j++) {
|
||||||
|
if (tok[j] < '0' || tok[j] > '9') return defval;
|
||||||
|
val = val * 10 + (tok[j] - '0');
|
||||||
|
any = 1;
|
||||||
|
}
|
||||||
|
if (!any) return defval;
|
||||||
|
return neg ? -val : val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuck_get_int(const char *text, u64 size,
|
||||||
|
const char *section, const char *key, int defval) {
|
||||||
|
u64 pos = 0;
|
||||||
|
int in_section = 0;
|
||||||
|
u32 slen = str_len(section);
|
||||||
|
u32 klen = str_len(key);
|
||||||
|
|
||||||
|
while (pos < size) {
|
||||||
|
u64 end = pos;
|
||||||
|
while (end < size && text[end] != '\n') end++;
|
||||||
|
u64 line_len = end - pos;
|
||||||
|
if (line_len > 0 && text[pos + line_len - 1] == '\r') line_len--;
|
||||||
|
const char *line = text + pos;
|
||||||
|
|
||||||
|
u64 i = 0;
|
||||||
|
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
|
||||||
|
if (i < line_len) {
|
||||||
|
char c = line[i];
|
||||||
|
if (c == '#' || c == ';') {
|
||||||
|
/* 注释行, 跳过 */
|
||||||
|
} else if (c == '[') {
|
||||||
|
in_section = 0;
|
||||||
|
if (line_len >= (u64)(i + 1 + slen + 1) &&
|
||||||
|
memeq(line + i + 1, section, slen) &&
|
||||||
|
line[i + 1 + slen] == ']') {
|
||||||
|
in_section = 1;
|
||||||
|
}
|
||||||
|
} else if (in_section) {
|
||||||
|
if (line_len >= (u64)(i + klen) &&
|
||||||
|
memeq(line + i, key, klen)) {
|
||||||
|
u64 j = i + klen;
|
||||||
|
while (j < line_len && (line[j] == ' ' || line[j] == '\t')) j++;
|
||||||
|
if (j < line_len && line[j] == '=') {
|
||||||
|
return parse_scalar(line, j + 1, line_len, defval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos = end + 1;
|
||||||
|
}
|
||||||
|
return defval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_fuck_config(const struct dkm_kernel_api *api) {
|
||||||
|
/* 编译期默认: 与 FUCK 注释承诺一致 (无配置时行为不变) */
|
||||||
|
g_cfg.lapic_enable = 1;
|
||||||
|
g_cfg.ioapic_init = 1;
|
||||||
|
g_cfg.apic_route = 0;
|
||||||
|
g_cfg.apic_route_legacy = 0;
|
||||||
|
g_cfg.apic_diag = 0;
|
||||||
|
|
||||||
|
const struct limine_module_response_min *resp =
|
||||||
|
(const struct limine_module_response_min *)api->boot_modules_response;
|
||||||
|
if (!resp || !resp->modules) {
|
||||||
|
g_log->warn("[apic] no boot modules; FUCK defaults in effect");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u64 i = 0; i < resp->module_count; i++) {
|
||||||
|
struct limine_file_min *f = resp->modules[i];
|
||||||
|
if (!f || !f->cmdline || !f->address || !f->size) continue;
|
||||||
|
if (!str_contains(f->cmdline, "fuck:config")) continue;
|
||||||
|
|
||||||
|
const char *text = (const char *)f->address;
|
||||||
|
u64 size = f->size;
|
||||||
|
g_log->info("[apic] FUCK config module found");
|
||||||
|
log_hex("[apic] FUCK size=", size);
|
||||||
|
|
||||||
|
g_cfg.lapic_enable = fuck_get_int(text, size, "drivers", "lapic_enable", 1);
|
||||||
|
g_cfg.ioapic_init = fuck_get_int(text, size, "drivers", "ioapic_init", 1);
|
||||||
|
g_cfg.apic_route = fuck_get_int(text, size, "drivers", "apic_route", 0);
|
||||||
|
g_cfg.apic_route_legacy = fuck_get_int(text, size, "drivers", "apic_route_legacy", 0);
|
||||||
|
g_cfg.apic_diag = fuck_get_int(text, size, "drivers", "apic_diag", 0);
|
||||||
|
|
||||||
|
log_dec("[apic] cfg lapic_enable=", (u64)g_cfg.lapic_enable);
|
||||||
|
log_dec("[apic] cfg ioapic_init=", (u64)g_cfg.ioapic_init);
|
||||||
|
log_dec("[apic] cfg apic_route=", (u64)g_cfg.apic_route);
|
||||||
|
log_dec("[apic] cfg apic_route_legacy=", (u64)g_cfg.apic_route_legacy);
|
||||||
|
log_dec("[apic] cfg apic_diag=", (u64)g_cfg.apic_diag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_log->warn("[apic] FUCK module (fuck:config) not found; defaults in effect");
|
||||||
|
}
|
||||||
|
|
||||||
static const acpi_sdt_header *find_table_from_rsdt(const acpi_sdt_header *rsdt, const char sig[4], u32 entry_size) {
|
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 (!rsdt || rsdt->length < sizeof(acpi_sdt_header)) return NULL;
|
||||||
if (checksum8((const u8 *)rsdt, rsdt->length) != 0) return NULL;
|
if (checksum8((const u8 *)rsdt, rsdt->length) != 0) return NULL;
|
||||||
@@ -241,6 +561,27 @@ static const acpi_madt *find_madt(const void *rsdp_addr) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 查找 HPET 表 (apic_diag 用; 与 find_madt 同构, 仅签名不同) */
|
||||||
|
static const acpi_hpet *find_hpet(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 *hpet = find_table_from_rsdt(xsdt, "HPET", 8);
|
||||||
|
if (hpet) return (const acpi_hpet *)hpet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rsdp->rsdt_addr) {
|
||||||
|
const acpi_sdt_header *rsdt = (const acpi_sdt_header *)(uintptr_t)(u64)rsdp->rsdt_addr;
|
||||||
|
const acpi_sdt_header *hpet = find_table_from_rsdt(rsdt, "HPET", 4);
|
||||||
|
if (hpet) return (const acpi_hpet *)hpet;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_madt_entries(const acpi_madt *madt, struct madt_info *info) {
|
static void parse_madt_entries(const acpi_madt *madt, struct madt_info *info) {
|
||||||
const u8 *base = (const u8 *)madt;
|
const u8 *base = (const u8 *)madt;
|
||||||
u32 off = (u32)sizeof(acpi_madt);
|
u32 off = (u32)sizeof(acpi_madt);
|
||||||
@@ -255,19 +596,36 @@ static void parse_madt_entries(const acpi_madt *madt, struct madt_info *info) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type == 0 && len >= 8) {
|
if (type == 0 && len >= 8) {
|
||||||
|
/* LAPIC entry */
|
||||||
u8 apic_id = entry[3];
|
u8 apic_id = entry[3];
|
||||||
u32 flags = *(const u32 *)(const void *)(entry + 4);
|
u32 flags = *(const u32 *)(const void *)(entry + 4);
|
||||||
if (flags & 1u) info->lapic_count++;
|
|
||||||
log_hex("[apic] CPU LAPIC id=", apic_id);
|
log_hex("[apic] CPU LAPIC id=", apic_id);
|
||||||
|
if (flags & 1u) {
|
||||||
|
info->lapic_count++;
|
||||||
|
if (info->lapic_count <= MAX_LAPIC) {
|
||||||
|
info->lapics[info->lapic_count - 1].apic_id = apic_id;
|
||||||
|
info->lapics[info->lapic_count - 1].flags = flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (type == 1 && len >= 12) {
|
} else if (type == 1 && len >= 12) {
|
||||||
|
/* IOAPIC entry */
|
||||||
u8 ioapic_id = entry[2];
|
u8 ioapic_id = entry[2];
|
||||||
u32 addr = *(const u32 *)(const void *)(entry + 4);
|
u32 addr = *(const u32 *)(const void *)(entry + 4);
|
||||||
u32 gsi = *(const u32 *)(const void *)(entry + 8);
|
u32 gsi = *(const u32 *)(const void *)(entry + 8);
|
||||||
info->ioapic_count++;
|
|
||||||
log_hex("[apic] IOAPIC id=", ioapic_id);
|
log_hex("[apic] IOAPIC id=", ioapic_id);
|
||||||
log_hex("[apic] IOAPIC addr=", addr);
|
log_hex("[apic] IOAPIC addr=", addr);
|
||||||
log_hex("[apic] IOAPIC gsi_base=", gsi);
|
log_hex("[apic] IOAPIC gsi_base=", gsi);
|
||||||
|
if (info->ioapic_count < MAX_IOAPIC) {
|
||||||
|
struct ioapic_info *io = &info->ioapics[info->ioapic_count];
|
||||||
|
io->id = ioapic_id;
|
||||||
|
io->gsi_base = gsi;
|
||||||
|
io->mmio_phys = (u64)addr;
|
||||||
|
io->mmio = (volatile u32 *)(uintptr_t)(u64)addr; /* HHDM 直映射, 后续映射修正 */
|
||||||
|
io->gsi_count = 0;
|
||||||
|
info->ioapic_count++;
|
||||||
|
}
|
||||||
} else if (type == 2 && len >= 10) {
|
} else if (type == 2 && len >= 10) {
|
||||||
|
/* ISO (Interrupt Source Override) entry */
|
||||||
u8 source = entry[3];
|
u8 source = entry[3];
|
||||||
u32 gsi = *(const u32 *)(const void *)(entry + 4);
|
u32 gsi = *(const u32 *)(const void *)(entry + 4);
|
||||||
u16 flags = *(const u16 *)(const void *)(entry + 8);
|
u16 flags = *(const u16 *)(const void *)(entry + 8);
|
||||||
@@ -275,6 +633,12 @@ static void parse_madt_entries(const acpi_madt *madt, struct madt_info *info) {
|
|||||||
log_hex("[apic] ISO source=", source);
|
log_hex("[apic] ISO source=", source);
|
||||||
log_hex("[apic] ISO gsi=", gsi);
|
log_hex("[apic] ISO gsi=", gsi);
|
||||||
log_hex("[apic] ISO flags=", flags);
|
log_hex("[apic] ISO flags=", flags);
|
||||||
|
if (info->iso_count <= MAX_ISO) {
|
||||||
|
struct iso_info *iso = &info->isos[info->iso_count - 1];
|
||||||
|
iso->bus_source = source;
|
||||||
|
iso->gsi = gsi;
|
||||||
|
iso->flags = flags;
|
||||||
|
}
|
||||||
} else if (type == 5 && len >= 12) {
|
} else if (type == 5 && len >= 12) {
|
||||||
u64 override = *(const u64 *)(const void *)(entry + 4);
|
u64 override = *(const u64 *)(const void *)(entry + 4);
|
||||||
info->lapic_phys = override;
|
info->lapic_phys = override;
|
||||||
@@ -285,6 +649,797 @@ static void parse_madt_entries(const acpi_madt *madt, struct madt_info *info) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- IOAPIC MMIO 访问 ---- *
|
||||||
|
* IOAPIC 寄存器通过 MMIO 访问, 地址空间为 IOREGSEL(0x00) + IOWIN(0x10).
|
||||||
|
* 真机 IOAPIC 地址通常在 0xFEC00000, HHDM 通常覆盖此低 4G 地址。
|
||||||
|
* 写入 IOREGSEL 选择寄存器号, 读/写 IOWIN 访问数据。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define IOAPIC_IOREGSEL 0x00
|
||||||
|
#define IOAPIC_IOWIN 0x10
|
||||||
|
|
||||||
|
static u32 ioapic_read(volatile u32 *base, u8 reg) {
|
||||||
|
base[IOAPIC_IOREGSEL / 4] = (u32)reg;
|
||||||
|
apic_mb(); /* 寄存器选择必须先于数据读取 */
|
||||||
|
return base[IOAPIC_IOWIN / 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ioapic_write(volatile u32 *base, u8 reg, u32 val) {
|
||||||
|
base[IOAPIC_IOREGSEL / 4] = (u32)reg;
|
||||||
|
apic_mb(); /* 寄存器选择必须先于数据写入 */
|
||||||
|
base[IOAPIC_IOWIN / 4] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IOAPIC 重定向表项 (64-bit, 分两次 32-bit 读写, 82093AA 规范布局)
|
||||||
|
* bits 0-7: interrupt vector (0x20-0xFF)
|
||||||
|
* bits 8-10: delivery mode: 0=fixed, 1=lowest, 2=SMI, 4=NMI, 5=INIT, 7=ExtINT
|
||||||
|
* bit 11: destination mode: 0=physical, 1=logical
|
||||||
|
* bit 12: delivery status (RO)
|
||||||
|
* bit 13: polarity: 0=active high, 1=active low
|
||||||
|
* bit 14: remote IRR (RO, level-triggered only)
|
||||||
|
* bit 15: trigger mode: 0=edge, 1=level
|
||||||
|
* bit 16: mask: 0=enabled, 1=masked
|
||||||
|
* bits 17-55: reserved
|
||||||
|
* bits 56-63: destination APIC ID (physical mode) or MDA (logical mode)
|
||||||
|
* 注: 历史版本曾将 polarity/trigger/mask 错置于 bit 11/13/14,
|
||||||
|
* 导致 mask 写入只读 remote IRR 位而从未生效 —— 勿回退。
|
||||||
|
*/
|
||||||
|
static u64 ioapic_read_redir(volatile u32 *base, u8 entry) {
|
||||||
|
u32 lo = ioapic_read(base, (u8)(0x10 + entry * 2));
|
||||||
|
u32 hi = ioapic_read(base, (u8)(0x10 + entry * 2 + 1));
|
||||||
|
return ((u64)hi << 32) | (u64)lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ioapic_write_redir(volatile u32 *base, u8 entry, u64 val) {
|
||||||
|
ioapic_write(base, (u8)(0x10 + entry * 2), (u32)(val & 0xffffffffu));
|
||||||
|
ioapic_write(base, (u8)(0x10 + entry * 2 + 1), (u32)(val >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- B7: GSI 路由辅助 ---- */
|
||||||
|
|
||||||
|
/* 合成重定向表项: vector + ISO 极性/触发 + 物理目的 APIC ID + mask 位 */
|
||||||
|
static u64 redir_compose(u32 vector, u16 iso_flags, u32 dest_apic_id, int masked) {
|
||||||
|
u64 entry = (u64)(vector & 0xFFu);
|
||||||
|
/* delivery mode = fixed (0), destination mode = physical (0) */
|
||||||
|
|
||||||
|
/* 极性: bit 13. ISA 默认 active-high (0).
|
||||||
|
* ISO flags bit 0-1: 0=conforms, 1=active-high, 2=reserved, 3=active-low */
|
||||||
|
if ((iso_flags & 0x03u) == 0x03u) entry |= (1ULL << 13);
|
||||||
|
|
||||||
|
/* 触发: bit 15. ISA 默认 edge (0).
|
||||||
|
* ISO flags bit 2-3: 0=conforms, 1=edge, 2=reserved, 3=level */
|
||||||
|
if ((iso_flags & 0x0Cu) == 0x0Cu) entry |= (1ULL << 15);
|
||||||
|
|
||||||
|
entry |= ((u64)dest_apic_id << 56);
|
||||||
|
if (masked) entry |= (1ULL << 16);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 找覆盖指定 GSI 的 IOAPIC (多 IOAPIC 按 GSI 范围划分) */
|
||||||
|
static struct ioapic_info *ioapic_for_gsi(struct madt_info *info, u32 gsi) {
|
||||||
|
for (u32 i = 0; i < info->ioapic_count; i++) {
|
||||||
|
struct ioapic_info *io = &info->ioapics[i];
|
||||||
|
if (gsi >= io->gsi_base && gsi < io->gsi_base + io->gsi_count) return io;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ISA IRQ → GSI 解析: 优先 MADT ISO override, 缺省恒等 (QEMU ISA 惯例) */
|
||||||
|
static u32 isa_irq_to_gsi(u8 isa_irq, u16 *flags_out,
|
||||||
|
const struct iso_info *isos, u32 iso_count) {
|
||||||
|
for (u32 i = 0; i < iso_count; i++) {
|
||||||
|
if (isos[i].bus_source == isa_irq) {
|
||||||
|
*flags_out = isos[i].flags;
|
||||||
|
return isos[i].gsi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*flags_out = 0;
|
||||||
|
return (u32)isa_irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 编程单个 GSI 的重定向项 (unmask=1 放开投递, 0 保持屏蔽) */
|
||||||
|
static int ioapic_program_gsi(struct madt_info *info, u32 gsi,
|
||||||
|
u32 vector, u16 iso_flags, int unmask) {
|
||||||
|
struct ioapic_info *io = ioapic_for_gsi(info, gsi);
|
||||||
|
if (!io || !io->mmio) return -1;
|
||||||
|
u8 pin = (u8)(gsi - io->gsi_base);
|
||||||
|
ioapic_write_redir(io->mmio, pin,
|
||||||
|
redir_compose(vector, iso_flags, g_lapic_id, !unmask));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 全 IOAPIC 全条目屏蔽 (降级路径) — 保留 vector 信息, 仅置 mask 位 */
|
||||||
|
static void ioapic_mask_all(struct madt_info *info) {
|
||||||
|
for (u32 i = 0; i < info->ioapic_count; i++) {
|
||||||
|
struct ioapic_info *io = &info->ioapics[i];
|
||||||
|
if (!io->mmio) continue;
|
||||||
|
for (u32 pin = 0; pin < io->gsi_count && pin < 64; pin++) {
|
||||||
|
u64 entry = ioapic_read_redir(io->mmio, (u8)pin);
|
||||||
|
entry |= (1ULL << 16);
|
||||||
|
ioapic_write_redir(io->mmio, (u8)pin, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 重定向表回读 dump (验证证据) */
|
||||||
|
static void ioapic_dump_table(struct madt_info *info) {
|
||||||
|
for (u32 i = 0; i < info->ioapic_count; i++) {
|
||||||
|
struct ioapic_info *io = &info->ioapics[i];
|
||||||
|
if (!io->mmio) continue;
|
||||||
|
g_log->info("[apic] IOREDTBL readback:");
|
||||||
|
for (u32 pin = 0; pin < io->gsi_count && pin < 24; pin++) {
|
||||||
|
u64 entry = ioapic_read_redir(io->mmio, (u8)pin);
|
||||||
|
u32 vec = (u32)(entry & 0xFFu);
|
||||||
|
u32 masked = (u32)((entry >> 16) & 1u);
|
||||||
|
log_dec("[apic] pin=", pin);
|
||||||
|
log_hex("[apic] vec=", vec);
|
||||||
|
log_dec("[apic] mask=", masked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- IOAPIC 初始化 ---- *
|
||||||
|
* 逐条目编程重定向表: ISA IRQ→vector 映射 + mask all。
|
||||||
|
* 真机关键: 处理 MADT ISO (IRQ0→GSI2 是最常见的 override)。
|
||||||
|
* PIC 兼容模式: vector = 0x20 + IRQ (与 PIC remap 布局一致)。
|
||||||
|
*
|
||||||
|
* **QEMU 无法测试但真机必需的代码路径**:
|
||||||
|
* 1. ISO 处理: 真机几乎总有 IRQ0→GSI2 override, QEMU 默认有 1 条
|
||||||
|
* 2. 极性标志: 真机 ISA IRQ0 (timer) 必须设 active-high,
|
||||||
|
* 但 ISO 可能指定 active-low (PCIe SCI 等)
|
||||||
|
* 3. 多 IOAPIC: 真机可能有 2+ IOAPIC, 每个 GSI 范围不同
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void ioapic_init_one(struct ioapic_info *io, u64 hhdm_offset,
|
||||||
|
const struct iso_info *isos, u32 iso_count,
|
||||||
|
void *(*mm_map_mmio_fn)(u64, u64)) {
|
||||||
|
/* 映射 IOAPIC MMIO — 真机地址通常在 0xFECxxxxx, HHDM 通常覆盖,
|
||||||
|
* 但使用 mm_map_mmio 更安全 (PCD|PWT 属性) */
|
||||||
|
if (mm_map_mmio_fn) {
|
||||||
|
void *mapped = mm_map_mmio_fn(io->mmio_phys, 0x1000);
|
||||||
|
if (mapped) {
|
||||||
|
io->mmio = (volatile u32 *)mapped;
|
||||||
|
g_log->info("[apic] IOAPIC mapped via mm_map_mmio");
|
||||||
|
} else {
|
||||||
|
g_log->warn("[apic] mm_map_mmio failed for IOAPIC; trying HHDM");
|
||||||
|
io->mmio = (volatile u32 *)(uintptr_t)(hhdm_offset + io->mmio_phys);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
io->mmio = (volatile u32 *)(uintptr_t)(hhdm_offset + io->mmio_phys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 读取 IOAPIC 版本寄存器获取最大重定向条目数 */
|
||||||
|
u32 ver = ioapic_read(io->mmio, 0x01);
|
||||||
|
u32 max_redir = ((ver >> 16) & 0xff) + 1;
|
||||||
|
io->gsi_count = max_redir;
|
||||||
|
log_hex("[apic] IOAPIC VER=", ver);
|
||||||
|
log_hex("[apic] IOAPIC max_redir=", max_redir);
|
||||||
|
|
||||||
|
/* 读取 IOAPIC ID 验证 */
|
||||||
|
u32 id_reg = ioapic_read(io->mmio, 0x00);
|
||||||
|
log_hex("[apic] IOAPIC ID reg=", id_reg);
|
||||||
|
|
||||||
|
/* 构建默认 ISA IRQ→GSI→vector 映射表 (identity map + ISO override)
|
||||||
|
* 默认: GSI = ISA_IRQ (0-15), vector = 0x20 + ISA_IRQ
|
||||||
|
* ISO override: 修改特定 IRQ 的 GSI 和极性/触发模式 */
|
||||||
|
u32 gsi_to_vector[256]; /* GSI → vector 映射 */
|
||||||
|
u32 gsi_flags[256]; /* GSI → polar/trigger flags from ISO */
|
||||||
|
for (u32 i = 0; i < 256; i++) {
|
||||||
|
gsi_to_vector[i] = 0;
|
||||||
|
gsi_flags[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 先建立 identity 映射: GSI 0-15 → vector 0x20-0x2F */
|
||||||
|
for (u32 gsi = 0; gsi < 16; gsi++) {
|
||||||
|
gsi_to_vector[gsi] = 0x20 + gsi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 应用 ISO override */
|
||||||
|
for (u32 i = 0; i < iso_count; i++) {
|
||||||
|
const struct iso_info *iso = &isos[i];
|
||||||
|
u8 src = iso->bus_source;
|
||||||
|
u32 gsi = iso->gsi;
|
||||||
|
if (gsi < 256) {
|
||||||
|
gsi_to_vector[gsi] = 0x20 + src; /* vector 仍基于 ISA IRQ 编号 */
|
||||||
|
gsi_flags[gsi] = iso->flags;
|
||||||
|
g_log->info("[apic] ISO: ISA IRQ -> GSI override");
|
||||||
|
log_hex("[apic] ISA IRQ=", src);
|
||||||
|
log_hex("[apic] GSI=", gsi);
|
||||||
|
log_hex("[apic] vector=", 0x20 + src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 编程重定向表: 全部 mask, 编入正确的 vector/极性/触发模式/目标 APIC */
|
||||||
|
for (u32 pin = 0; pin < max_redir && pin < 256; pin++) {
|
||||||
|
u32 gsi = io->gsi_base + pin;
|
||||||
|
u64 entry = 0;
|
||||||
|
|
||||||
|
if (gsi < 256 && gsi_to_vector[gsi] != 0) {
|
||||||
|
u32 vec = gsi_to_vector[gsi];
|
||||||
|
u16 flags = gsi_flags[gsi];
|
||||||
|
|
||||||
|
/* vector + ISO 极性/触发 + 真实 BSP LAPIC ID, 保持 mask */
|
||||||
|
entry = redir_compose(vec, flags, g_lapic_id, 1);
|
||||||
|
} else {
|
||||||
|
entry = (1ULL << 16); /* 无映射条目: 纯 mask (bit16, vector=0 无害) */
|
||||||
|
}
|
||||||
|
|
||||||
|
ioapic_write_redir(io->mmio, (u8)pin, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_log->info("[apic] IOAPIC redirect table programmed (all masked)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- LAPIC 使能 ---- *
|
||||||
|
* 真机必须:
|
||||||
|
* 1. 写 IA32_APIC_BASE MSR 启用 bit 11 (global enable)
|
||||||
|
* 2. 写 LAPIC SVR 寄存器设置 Spurious Interrupt Vector (建议 0xFF)
|
||||||
|
* 3. 设置 SVR bit 8 (APIC software enable)
|
||||||
|
*
|
||||||
|
* **QEMU 无法测试但真机必需**: LAPIC SVR 设置 — QEMU 默认已启用 LAPIC,
|
||||||
|
* 但实机 BIOS 可能留 LAPIC 为 software-disable 状态。
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void lapic_enable(void) {
|
||||||
|
volatile u32 *lapic = g_lapic;
|
||||||
|
|
||||||
|
/* 确保 IA32_APIC_BASE 的 global enable 和 BSP 位已设置 */
|
||||||
|
u64 apic_base = rdmsr(0x1B);
|
||||||
|
apic_base |= (1ULL << 11); /* global enable */
|
||||||
|
/* 保持 BSP 位不变 (bit 8) */
|
||||||
|
wrmsr(0x1B, apic_base);
|
||||||
|
g_log->info("[apic] LAPIC global enable set in MSR");
|
||||||
|
|
||||||
|
/* 读当前 SVR */
|
||||||
|
u32 svr = lapic[0xF0 / 4];
|
||||||
|
log_hex("[apic] LAPIC SVR before=", svr);
|
||||||
|
|
||||||
|
/* 设置 Spurious Interrupt Vector = 0xFF + APIC software enable (bit 8) */
|
||||||
|
svr = 0xFF | (1u << 8);
|
||||||
|
lapic[0xF0 / 4] = svr;
|
||||||
|
apic_mb();
|
||||||
|
|
||||||
|
/* 回读验证 */
|
||||||
|
svr = lapic[0xF0 / 4];
|
||||||
|
log_hex("[apic] LAPIC SVR after=", svr);
|
||||||
|
if (svr & (1u << 8)) {
|
||||||
|
g_log->info("[apic] LAPIC software enabled");
|
||||||
|
} else {
|
||||||
|
g_log->warn("[apic] LAPIC SVR bit8 not set — hardware may not support");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 读 LAPIC ID 和 version 做诊断 dump */
|
||||||
|
log_hex("[apic] LAPIC ID=", lapic[0x20 / 4]);
|
||||||
|
log_hex("[apic] LAPIC LDR=", lapic[0xD0 / 4]);
|
||||||
|
log_hex("[apic] LAPIC version=", lapic[0x30 / 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LAPIC EOI — 写 0 到 EOI 寄存器 (offset 0xB0)。
|
||||||
|
* B7 阶段2: 本函数作为 EOI 钩子经 kernel_api.register_apic_eoi 注册给 UTSM,
|
||||||
|
* idt_handler 在两个 IRQ 分支的分发末尾调用 (钩子签名 void(*)(void) 匹配)。
|
||||||
|
* 调用点: IRQ 上下文, 单核 BSP interrupt gate (IF=0), 无嵌套, 无需锁。 */
|
||||||
|
static __inline__ void lapic_eoi(void) {
|
||||||
|
if (g_lapic) {
|
||||||
|
g_lapic[0xB0 / 4] = 0;
|
||||||
|
apic_mb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- B7 diag: HPET 绝对时基 (tick 2x 定论实验) ---- *
|
||||||
|
* 选型理由: TSC 校准 (PIT mode0 回绕法) 与被测对象 (PIT mode3 投递频率)
|
||||||
|
* 共享 PIT 硬件, 循环论证无法定论 2x 根因。HPET 主计数器是独立时基
|
||||||
|
* (QEMU q35: 100MHz, period=10000fs), 32 位回绕周期 ~42.9s, 对 ms 级
|
||||||
|
* 窗口充裕。只设 GEN_CONF bit0 (overall enable); 绝不置 bit1
|
||||||
|
* (legacy replacement route 会抢 PIT IRQ0/RTC IRQ8 路由, 破坏被测链路)。
|
||||||
|
*/
|
||||||
|
|
||||||
|
static volatile u32 *g_hpet; /* HPET MMIO 基址 (HHDM), 0=不可用 */
|
||||||
|
static u32 g_hpet_period_fs; /* 主计数器周期 (飞秒), GCAP_ID[63:32] */
|
||||||
|
|
||||||
|
#define HPET_GCAP_ID 0x00
|
||||||
|
#define HPET_GEN_CONF 0x10
|
||||||
|
#define HPET_MAIN_CNT 0xF0
|
||||||
|
|
||||||
|
static u64 hpet_read64(u32 off) {
|
||||||
|
volatile u32 *p = (volatile u32 *)((volatile u8 *)g_hpet + off);
|
||||||
|
u32 lo = p[0];
|
||||||
|
u32 hi = p[1];
|
||||||
|
return ((u64)hi << 32) | lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hpet_write64(u32 off, u64 v) {
|
||||||
|
volatile u32 *p = (volatile u32 *)((volatile u8 *)g_hpet + off);
|
||||||
|
p[0] = (u32)(v & 0xffffffffu);
|
||||||
|
p[1] = (u32)(v >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __inline__ u32 hpet_main32(void) {
|
||||||
|
return *(volatile u32 *)((volatile u8 *)g_hpet + HPET_MAIN_CNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 初始化 HPET 时基; 成功返回 0 且 g_hpet/g_hpet_period_fs 有效 */
|
||||||
|
static int hpet_diag_init(const struct dkm_kernel_api *api) {
|
||||||
|
const acpi_hpet *hpet = find_hpet(api->rsdp_address);
|
||||||
|
if (!hpet) {
|
||||||
|
g_log->warn("[apic] diag: HPET table not found");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (hpet->addr_space_id != 0 || !hpet->base_addr) {
|
||||||
|
g_log->warn("[apic] diag: HPET not memory-mapped");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
g_hpet = (volatile u32 *)(uintptr_t)(api->hhdm_offset + hpet->base_addr);
|
||||||
|
u64 gcap = hpet_read64(HPET_GCAP_ID);
|
||||||
|
g_hpet_period_fs = (u32)(gcap >> 32);
|
||||||
|
if (!g_hpet_period_fs || g_hpet_period_fs > 100000000u) {
|
||||||
|
g_log->warn("[apic] diag: HPET period implausible");
|
||||||
|
g_hpet = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
log_hex("[apic] diag: HPET base=", hpet->base_addr);
|
||||||
|
log_dec("[apic] diag: HPET period(fs)=", g_hpet_period_fs);
|
||||||
|
|
||||||
|
/* overall enable (bit0); legacy replacement route (bit1) 保持 0 */
|
||||||
|
u64 conf = hpet_read64(HPET_GEN_CONF);
|
||||||
|
if (!(conf & 1u)) {
|
||||||
|
hpet_write64(HPET_GEN_CONF, conf | 1u);
|
||||||
|
apic_mb();
|
||||||
|
}
|
||||||
|
g_log->info("[apic] diag: HPET main counter enabled");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HPET 主计数器差值 → 微秒 (全程 u64 整数, -msoft-float 环境无浮点) */
|
||||||
|
static u64 hpet_delta_us(u32 h0, u32 h1) {
|
||||||
|
/* 无符号回绕安全: u32 减法自动回绕 (42.9s 周期, 窗口 << 此值) */
|
||||||
|
u64 ticks = (u64)(u32)(h1 - h0);
|
||||||
|
/* ticks * period_fs / 1e9 = us; period_fs 典型 10000, 500ms 窗口
|
||||||
|
* ticks≈5e7, 乘积 5e11 << 2^64, 溢出安全 */
|
||||||
|
return (ticks * (u64)g_hpet_period_fs) / 1000000000ull;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- B7 diag: PS/2 鼠标流 (IRQ12 持续投递验证) ---- *
|
||||||
|
* UTSM 阶段 IRQ12 无属主 (mouseInit 是 DSK 阶段模块), diag handler 独占;
|
||||||
|
* DSK mouseInit 之后经 irq_register(12) 覆盖本 handler 并完整复位鼠标,
|
||||||
|
* 无状态冲突。所有单步带超时, 失败只记日志不阻塞。 */
|
||||||
|
|
||||||
|
static volatile u32 g_mouse_pkt_count;
|
||||||
|
|
||||||
|
static int ps2_wait_ibf(void) {
|
||||||
|
u32 t = 100000;
|
||||||
|
while (t--) { if (!(inb(0x64) & 0x02)) return 0; }
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ps2_wait_obf(void) {
|
||||||
|
u32 t = 100000;
|
||||||
|
while (t--) { if (inb(0x64) & 0x01) return 0; }
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ps2_drain(void) {
|
||||||
|
while (inb(0x64) & 0x01) { (void)inb(0x60); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 经 0xD4 通道写鼠标命令并等 ACK(0xFA); 失败返回 -1 */
|
||||||
|
static int ps2_aux_cmd(u8 cmd) {
|
||||||
|
if (ps2_wait_ibf() != 0) return -1;
|
||||||
|
outb(0x64, 0xD4);
|
||||||
|
if (ps2_wait_ibf() != 0) return -1;
|
||||||
|
outb(0x60, cmd);
|
||||||
|
if (ps2_wait_obf() != 0) return -1;
|
||||||
|
return inb(0x60) == 0xFA ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IRQ12 诊断 handler: 只读数据计数, EOI 交给 idt_handler 分发路径
|
||||||
|
* (PIC 模式: 8259 EOI; route 模式: 钩子 LAPIC EOI + 无害 8259 EOI)。
|
||||||
|
* 返回 0 — 不自负 EOI, 与 ps2kbd/e1000 handler 同一约定。 */
|
||||||
|
static int apic_mouse_diag_handler(u8 vector) {
|
||||||
|
(void)vector;
|
||||||
|
u8 st = inb(0x64);
|
||||||
|
if ((st & 0x01) && (st & 0x20)) { /* OBF 且 AUX 位置位 = 鼠标数据 */
|
||||||
|
u8 data = inb(0x60); /* 必须读, 否则 OBF 保持阻塞后续包 */
|
||||||
|
g_mouse_pkt_count++;
|
||||||
|
if (g_mouse_pkt_count <= 6) {
|
||||||
|
g_log->info("[apic] diag: mouse packet byte");
|
||||||
|
log_hex("[apic] diag: data=", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IRQ1 诊断 handler (键盘流计数): ps2kbd demo 窗口只有 ~10ms 数量级,
|
||||||
|
* 自动化无法抓住; diag 在 stage0 自建 3s 观察窗验证 IRQ1 持续投递。
|
||||||
|
* stage3 ps2kbd 注册时覆盖本 handler, 无冲突。 */
|
||||||
|
static volatile u32 g_kbd_pkt_count;
|
||||||
|
|
||||||
|
static int apic_kbd_diag_handler(u8 vector) {
|
||||||
|
(void)vector;
|
||||||
|
u8 st = inb(0x64);
|
||||||
|
if ((st & 0x01) && !(st & 0x20)) { /* OBF 且 AUX 位清零 = 键盘数据 */
|
||||||
|
u8 data = inb(0x60); /* 读走扫描码 (窗内按键, 无消费者) */
|
||||||
|
g_kbd_pkt_count++;
|
||||||
|
if (g_kbd_pkt_count <= 6) {
|
||||||
|
g_log->info("[apic] diag: kbd scancode byte");
|
||||||
|
log_hex("[apic] diag: sc=", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AUX 端口初始化: 启用 AUX 时钟 + 采样率 100 + 数据报告, 最后才开 AUX IRQ。
|
||||||
|
* 顺序要点: AUX IRQ (cfg bit1) 必须在全部命令/ACK 交互完成后才使能 —
|
||||||
|
* 否则 ACK 字节 (0xFA) 到达即触发 IRQ12 被 diag handler 读走, 轮询侧
|
||||||
|
* 永远等不到 ACK (实测 F3 nack 即此竞态: handler 日志 data=0xfa)。
|
||||||
|
* 单步失败只记日志继续 (与 mouseInit 同一安全准则)。 */
|
||||||
|
static void ps2_aux_enable_stream(void) {
|
||||||
|
ps2_drain();
|
||||||
|
|
||||||
|
if (ps2_wait_ibf() != 0) { g_log->warn("[apic] diag: PS2 ibf timeout"); return; }
|
||||||
|
outb(0x64, 0x20); /* 读配置字节 */
|
||||||
|
if (ps2_wait_obf() != 0) { g_log->warn("[apic] diag: PS2 cfg read timeout"); return; }
|
||||||
|
u8 cfg = inb(0x60);
|
||||||
|
cfg &= (u8)~0x02; /* bit1=0: AUX IRQ 先关闭 (轮询期) */
|
||||||
|
cfg &= (u8)~0x20; /* bit5=0: AUX clock enable */
|
||||||
|
cfg |= 0x01; /* bit0=1: KBD IRQ 保持使能 (键盘流用) */
|
||||||
|
|
||||||
|
if (ps2_wait_ibf() != 0) return;
|
||||||
|
outb(0x64, 0x60); /* 写配置字节 */
|
||||||
|
if (ps2_wait_ibf() != 0) return;
|
||||||
|
outb(0x60, cfg);
|
||||||
|
log_hex("[apic] diag: PS2 cfg byte=", cfg);
|
||||||
|
|
||||||
|
if (ps2_wait_ibf() != 0) return;
|
||||||
|
outb(0x64, 0xA8); /* 启用 AUX 端口 */
|
||||||
|
|
||||||
|
if (ps2_aux_cmd(0xF3) != 0) { g_log->warn("[apic] diag: mouse F3 nack"); }
|
||||||
|
else if (ps2_aux_cmd(100) != 0) { g_log->warn("[apic] diag: mouse rate nack"); }
|
||||||
|
if (ps2_aux_cmd(0xF4) != 0) { g_log->warn("[apic] diag: mouse F4 nack"); return; }
|
||||||
|
g_log->info("[apic] diag: PS/2 mouse data reporting enabled");
|
||||||
|
|
||||||
|
ps2_drain(); /* 清 F4 ACK 后可能挂起的首个流字节 */
|
||||||
|
|
||||||
|
/* 全部交互完成, 现在才开 AUX IRQ — 之后鼠标字节全走 IRQ12 handler */
|
||||||
|
if (ps2_wait_ibf() != 0) return;
|
||||||
|
outb(0x64, 0x20);
|
||||||
|
if (ps2_wait_obf() != 0) return;
|
||||||
|
cfg = inb(0x60);
|
||||||
|
cfg |= 0x02; /* bit1=1: AUX IRQ enable */
|
||||||
|
if (ps2_wait_ibf() != 0) return;
|
||||||
|
outb(0x64, 0x60);
|
||||||
|
if (ps2_wait_ibf() != 0) return;
|
||||||
|
outb(0x60, cfg);
|
||||||
|
log_hex("[apic] diag: PS2 cfg byte(final)=", cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- B7 阶段2: PIT tick 路由验证 ---- *
|
||||||
|
* vector 0x20 落在 idt_handler 的 PIC 分支 (vector 32-47) — 与键盘 0x21/
|
||||||
|
* 鼠标 0x2C/网卡 0x2B 同一代码路径。分发顺序: handler(本函数) → 钩子
|
||||||
|
* LAPIC EOI → 8259 EOI (PIC 全屏蔽下为无害 no-op)。
|
||||||
|
* 本 handler 返回 0 不自负 EOI: 由 UTSM 钩子写 LAPIC EOI 寄存器清除
|
||||||
|
* ISR 位; 重复投递 (计数>=2) 即证明"legacy 向量分支 + 钩子 EOI"可持续,
|
||||||
|
* 这正是键盘/鼠标/网卡将要走的路径 (阶段1 的 0xE0+自EOI 无法证明此路径)。
|
||||||
|
* handler 是普通 C 函数 (SysV ABI), isr stub 已完成现场保存/iret。
|
||||||
|
*/
|
||||||
|
static int apic_tick_handler(u8 vector) {
|
||||||
|
(void)vector;
|
||||||
|
g_tick_count++;
|
||||||
|
if (g_tick_count <= 8) {
|
||||||
|
g_log->info("[apic] ioapic tick (PIT GSI -> LAPIC -> IDT vec 0x20, hook EOI)");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 rdtsc(void) {
|
||||||
|
u32 lo, hi;
|
||||||
|
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
|
||||||
|
return ((u64)hi << 32) | lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 g_tsc_per_ms;
|
||||||
|
|
||||||
|
/* PIT ch0 mode 0 单发 (~10ms, 11932 counts) 校准 TSC。
|
||||||
|
* 必须在把 PIT 改为 mode 3 周期模式之前调用。 */
|
||||||
|
static void tsc_calibrate(void) {
|
||||||
|
outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */
|
||||||
|
outb(0x40, 0x9C); /* 11932 low = ~10ms */
|
||||||
|
outb(0x40, 0x2E); /* 11932 high */
|
||||||
|
u64 tsc_start = rdtsc();
|
||||||
|
u16 prev = 0;
|
||||||
|
u64 loops = 0;
|
||||||
|
for (;;) {
|
||||||
|
outb(0x43, 0x00);
|
||||||
|
u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8);
|
||||||
|
if (cur > prev && loops > 10) break; /* 回绕 = 10ms 到 */
|
||||||
|
prev = cur;
|
||||||
|
loops++;
|
||||||
|
}
|
||||||
|
u64 tsc_end = rdtsc();
|
||||||
|
g_tsc_per_ms = (tsc_end - tsc_start) / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tsc_delay_ms(u32 ms) {
|
||||||
|
if (!g_tsc_per_ms) {
|
||||||
|
for (volatile u32 i = 0; i < 100000 * ms; i++) __asm__ volatile("pause");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
u64 target = g_tsc_per_ms * ms;
|
||||||
|
u64 start = rdtsc();
|
||||||
|
while (rdtsc() - start < target) __asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- B7 阶段2: 路由切换主流程 ---- *
|
||||||
|
* 成功: PIC IMR 全 1, IOAPIC 拥有路由, EOI 钩子保持注册, tick 验证后
|
||||||
|
* tick 线重新 mask (保留路由表内容), legacy 线按 apic_route_legacy。
|
||||||
|
* 失败: IOAPIC 全 mask + 恢复原 PIC IMR + 注销 tick handler + 注销 EOI
|
||||||
|
* 钩子, 与切换前状态完全一致 (现状不回归)。
|
||||||
|
*/
|
||||||
|
static void apic_route_switch(const struct dkm_kernel_api *api,
|
||||||
|
struct madt_info *info) {
|
||||||
|
g_log->info("[apic] apic_route=1: attempting IOAPIC routing takeover");
|
||||||
|
|
||||||
|
if (!g_lapic) {
|
||||||
|
g_log->warn("[apic] route abort: LAPIC MMIO unavailable; PIC retained");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (info->ioapic_count == 0) {
|
||||||
|
g_log->warn("[apic] route abort: no IOAPIC; PIC retained");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!api->irq_register) {
|
||||||
|
g_log->warn("[apic] route abort: irq_register missing; PIC retained");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 0) B7 阶段2 前提: EOI 钩子接口必须存在 (旧内核无此字段 → 判空中止)。
|
||||||
|
* 无钩子时 legacy 向量 (0x20-0x2F) 在 IOAPIC 路由下只能 one-shot,
|
||||||
|
* 路由接管不成立 — 宁可保留 PIC 也不进入半残状态。 */
|
||||||
|
if (!api->register_apic_eoi) {
|
||||||
|
g_log->warn("[apic] route abort: register_apic_eoi missing (old kernel); PIC retained");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
api->register_apic_eoi(lapic_eoi);
|
||||||
|
g_log->info("[apic] LAPIC EOI hook registered to UTSM");
|
||||||
|
|
||||||
|
/* 1) 保存 PIC IMR (降级恢复用) */
|
||||||
|
u8 imr1 = inb(0x21);
|
||||||
|
u8 imr2 = inb(0xA1);
|
||||||
|
log_hex("[apic] PIC IMR saved master=", imr1);
|
||||||
|
log_hex("[apic] PIC IMR saved slave=", imr2);
|
||||||
|
|
||||||
|
/* 2) TSC 校准 (PIT 尚在 firmware/driver 遗留状态, 先校准再改模式)。
|
||||||
|
* diag: HPET 绝对时基对拍本窗口 — 校准窗口标称 10ms, HPET 实测
|
||||||
|
* 若 ~20000us 则 PIT mode0 实际跑了 20ms (TSC 测半), ~10000us
|
||||||
|
* 则校准正确, tick 2x 根因另在他处。 */
|
||||||
|
int hpet_ok = 0;
|
||||||
|
if (g_cfg.apic_diag) {
|
||||||
|
hpet_ok = (hpet_diag_init(api) == 0);
|
||||||
|
}
|
||||||
|
u32 hc0 = 0;
|
||||||
|
if (hpet_ok) hc0 = hpet_main32();
|
||||||
|
tsc_calibrate();
|
||||||
|
if (hpet_ok) {
|
||||||
|
u32 hc1 = hpet_main32();
|
||||||
|
u64 calib_us = hpet_delta_us(hc0, hc1);
|
||||||
|
log_dec("[apic] diag: calib window(us)=", calib_us);
|
||||||
|
g_log->info("[apic] diag: (nominal 10000us; ~20000 => TSC half-measured)");
|
||||||
|
}
|
||||||
|
log_hex("[apic] tsc_per_ms=", g_tsc_per_ms);
|
||||||
|
|
||||||
|
/* 3) 注册 tick handler @ vector 0x20 — PIC 分支 (legacy 向量路径),
|
||||||
|
* handler 返回 0, LAPIC EOI 由 UTSM 钩子完成 */
|
||||||
|
if (api->irq_register(0x20, (void *)(uintptr_t)apic_tick_handler) != 0) {
|
||||||
|
g_log->warn("[apic] route abort: irq_register(0x20) failed");
|
||||||
|
api->register_apic_eoi(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_log->info("[apic] tick handler registered at vector 0x20 (PIC branch, hook EOI)");
|
||||||
|
|
||||||
|
/* 4) PIT tick GSI 解析: MADT ISO source 0 (QEMU q35: GSI2), 缺省恒等 */
|
||||||
|
u16 tick_flags = 0;
|
||||||
|
u32 tick_gsi = isa_irq_to_gsi(0, &tick_flags, info->isos, info->iso_count);
|
||||||
|
log_hex("[apic] tick GSI=", tick_gsi);
|
||||||
|
log_hex("[apic] tick GSI flags=", tick_flags);
|
||||||
|
|
||||||
|
/* 5) 编程 tick 重定向项 (vector 0x20, 放开) */
|
||||||
|
if (ioapic_program_gsi(info, tick_gsi, 0x20, tick_flags, 1) != 0) {
|
||||||
|
g_log->warn("[apic] route abort: tick GSI not covered by any IOAPIC");
|
||||||
|
api->irq_register(0x20, (void *)0);
|
||||||
|
api->register_apic_eoi(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_log->info("[apic] tick redirection unmasked (vec 0x20)");
|
||||||
|
|
||||||
|
/* 6) legacy 线 (apic_route_legacy): EOI 钩子已就位, 持续投递成立。
|
||||||
|
* ISA 线 (IRQ1/12) 按 ISO, 缺省 edge/active-high;
|
||||||
|
* PCI 线 (IRQ11 e1000) 无 ISO 覆盖时强制 level/active-low
|
||||||
|
* (PCI INTx 规范; flags=0xF = pol bit0-1=3 active-low + trig bit2-3=3
|
||||||
|
* level)。e1000 handler 读 ICR 清源 → 电平 deassert → 钩子 EOI。 */
|
||||||
|
if (g_cfg.apic_route_legacy) {
|
||||||
|
u16 f1 = 0, f12 = 0, f11 = 0;
|
||||||
|
u32 gsi1 = isa_irq_to_gsi(1, &f1, info->isos, info->iso_count);
|
||||||
|
u32 gsi12 = isa_irq_to_gsi(12, &f12, info->isos, info->iso_count);
|
||||||
|
u32 gsi11 = isa_irq_to_gsi(11, &f11, info->isos, info->iso_count);
|
||||||
|
if (!f11) f11 = 0x0F;
|
||||||
|
ioapic_program_gsi(info, gsi1, 0x21, f1, 1); /* 键盘 */
|
||||||
|
ioapic_program_gsi(info, gsi12, 0x2C, f12, 1); /* 鼠标 */
|
||||||
|
ioapic_program_gsi(info, gsi11, 0x2B, f11, 1); /* e1000 (QEMU 默认 IRQ11) */
|
||||||
|
g_log->info("[apic] legacy IRQ1/12/11 unmasked (sustained delivery via EOI hook)");
|
||||||
|
log_hex("[apic] kbd gsi=", gsi1);
|
||||||
|
log_hex("[apic] mouse gsi=", gsi12);
|
||||||
|
log_hex("[apic] e1000 gsi=", gsi11);
|
||||||
|
log_hex("[apic] e1000 flags=", f11);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 7) LAPIC TPR=0 全放行 */
|
||||||
|
g_lapic[0x80 / 4] = 0;
|
||||||
|
apic_mb();
|
||||||
|
log_hex("[apic] LAPIC TPR=", g_lapic[0x80 / 4]);
|
||||||
|
|
||||||
|
/* 8) PIC 全屏蔽 (先 slave 后 master, 避免 cascade 窗口; QEMU 无功能 IMCR) */
|
||||||
|
outb(0xA1, 0xFF);
|
||||||
|
io_wait();
|
||||||
|
outb(0x21, 0xFF);
|
||||||
|
io_wait();
|
||||||
|
g_log->info("[apic] PIC fully masked; IOAPIC owns IRQ routing");
|
||||||
|
|
||||||
|
/* 9) PIT ch0 mode 3 周期 ~100Hz (divisor 11932), 产生真实 tick 源 */
|
||||||
|
outb(0x43, 0x36);
|
||||||
|
outb(0x40, 0x9C);
|
||||||
|
outb(0x40, 0x2E);
|
||||||
|
g_log->info("[apic] PIT ch0 mode3 100Hz started");
|
||||||
|
|
||||||
|
/* 10) 500ms 验证窗口 (+ diag: HPET 实测窗口与 tick 频率定论) */
|
||||||
|
u32 hv0 = 0;
|
||||||
|
if (hpet_ok) hv0 = hpet_main32();
|
||||||
|
u32 before = g_tick_count;
|
||||||
|
tsc_delay_ms(500);
|
||||||
|
u32 ticks = g_tick_count - before;
|
||||||
|
if (hpet_ok) {
|
||||||
|
u32 hv1 = hpet_main32();
|
||||||
|
u64 win_us = hpet_delta_us(hv0, hv1);
|
||||||
|
log_dec("[apic] diag: verify window(us)=", win_us);
|
||||||
|
if (win_us) {
|
||||||
|
u64 hz_x10 = (u64)ticks * 10000000ull / win_us;
|
||||||
|
log_dec("[apic] diag: tick rate(Hz x10)=", hz_x10);
|
||||||
|
}
|
||||||
|
/* tick 2x 定论分支:
|
||||||
|
* win≈500ms 且 ticks≈100 → PIT mode3 在 IOAPIC edge 下双触发 (200Hz 真实)
|
||||||
|
* win≈1000ms 且 ticks≈100 → TSC 校准窗口翻倍 (tick 频率真实 100Hz)
|
||||||
|
* win≈500ms 且 ticks≈50 → 一切正常 */
|
||||||
|
if (win_us > 750000) {
|
||||||
|
g_log->info("[apic] diag: TICK2X VERDICT: TSC calib window doubled (PIT mode0 ran ~20ms); tick rate nominal");
|
||||||
|
} else if (ticks > 75) {
|
||||||
|
g_log->info("[apic] diag: TICK2X VERDICT: PIT mode3 double-clocked on IOAPIC edge (~200Hz real)");
|
||||||
|
} else {
|
||||||
|
g_log->info("[apic] diag: TICK2X VERDICT: nominal (~100Hz, no 2x)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_dec("[apic] route verify ticks=", ticks);
|
||||||
|
|
||||||
|
if (ticks >= 2) {
|
||||||
|
/* 成功: 重复投递证明钩子 EOI 正确清除 ISR 位, 路由链路完整 */
|
||||||
|
g_log->info("[apic] ROUTE VERIFY OK — IOAPIC->LAPIC->IDT(vec 0x20)->handler->hook EOI chain proven");
|
||||||
|
|
||||||
|
/* diag: 第二窗口持续投递证据 (排除窗口内偶发两次的弱证明) */
|
||||||
|
if (g_cfg.apic_diag) {
|
||||||
|
u32 before2 = g_tick_count;
|
||||||
|
tsc_delay_ms(500);
|
||||||
|
u32 ticks2 = g_tick_count - before2;
|
||||||
|
log_dec("[apic] diag: sustain window ticks=", ticks2);
|
||||||
|
if (ticks2 >= 2) {
|
||||||
|
g_log->info("[apic] diag: SUSTAINED delivery confirmed (2nd window)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tick 线重新 mask (保留路由表, vector 保持 0x20), 避免后台周期中断;
|
||||||
|
* PIT 恢复 mode 3 @ 18.2Hz (BIOS 惯例值, 已被 mask 不会投递)。
|
||||||
|
* EOI 钩子保持注册 — legacy 线的持续投递依赖它。 */
|
||||||
|
ioapic_program_gsi(info, tick_gsi, 0x20, tick_flags, 0);
|
||||||
|
outb(0x43, 0x36);
|
||||||
|
outb(0x40, 0x00);
|
||||||
|
outb(0x40, 0x00);
|
||||||
|
g_route_active = 1;
|
||||||
|
g_log->info("[apic] APIC ROUTING ACTIVE (PIC masked; EOI hook live; tick line re-masked)");
|
||||||
|
} else {
|
||||||
|
/* 失败: 完整降级回 PIC (含注销 EOI 钩子) */
|
||||||
|
g_log->error("[apic] ROUTE VERIFY FAILED — degrading to PIC routing");
|
||||||
|
ioapic_mask_all(info);
|
||||||
|
api->irq_register(0x20, (void *)0);
|
||||||
|
api->register_apic_eoi(NULL);
|
||||||
|
outb(0x43, 0x36);
|
||||||
|
outb(0x40, 0x00);
|
||||||
|
outb(0x40, 0x00);
|
||||||
|
outb(0x21, imr1);
|
||||||
|
io_wait();
|
||||||
|
outb(0xA1, imr2);
|
||||||
|
io_wait();
|
||||||
|
g_log->info("[apic] PIC routing restored (IMR restored, IOAPIC masked, hook cleared)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- B7 diag: IRQ1/IRQ12 键鼠流观察窗 ---- *
|
||||||
|
* 在 route switch 之后调用 (route 成功走 IOAPIC; 否则走 PIC IMR)。
|
||||||
|
* 打锚点日志供测试脚本/人工在窗口内用 QEMU monitor sendkey/mouse_move
|
||||||
|
* 注入输入; 窗口结束分别报告收包计数。计数 >1 即证明该线在当前路由
|
||||||
|
* 模式下可持续投递 (一次按键=make+break 两包, 一次移动=多包)。 */
|
||||||
|
static void apic_diag_mouse_window(const struct dkm_kernel_api *api,
|
||||||
|
struct madt_info *info) {
|
||||||
|
g_log->info("[apic] diag: IRQ1/IRQ12 input stream check begin");
|
||||||
|
|
||||||
|
/* 确保 TSC 延时可用 (route=0 时未校准: 现场校准并恢复 PIT 惯例状态) */
|
||||||
|
if (!g_tsc_per_ms) {
|
||||||
|
tsc_calibrate();
|
||||||
|
outb(0x43, 0x36);
|
||||||
|
outb(0x40, 0x00);
|
||||||
|
outb(0x40, 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!api->irq_register) return;
|
||||||
|
|
||||||
|
if (g_route_active) {
|
||||||
|
/* IOAPIC 路由: 确保 IRQ12/IRQ1 重定向项放开 (legacy=1 时幂等重编) */
|
||||||
|
u16 f12 = 0, f1 = 0;
|
||||||
|
u32 gsi12 = isa_irq_to_gsi(12, &f12, info->isos, info->iso_count);
|
||||||
|
u32 gsi1 = isa_irq_to_gsi(1, &f1, info->isos, info->iso_count);
|
||||||
|
ioapic_program_gsi(info, gsi12, 0x2C, f12, 1);
|
||||||
|
ioapic_program_gsi(info, gsi1, 0x21, f1, 1);
|
||||||
|
g_log->info("[apic] diag: IRQ12+IRQ1 via IOAPIC redirection");
|
||||||
|
} else {
|
||||||
|
/* PIC 路由: 解 slave IMR bit4 (IRQ12); master IMR bit1 (IRQ1)
|
||||||
|
* UTSM pic_remap 已留 0xFD (IRQ1 enable), 幂等确认。
|
||||||
|
* diag 属实验开关, 显式改变 PIC 状态并打日志; UTSM PIC 分支
|
||||||
|
* 会做 8259 EOI, diag handler 返回 0 即可。 */
|
||||||
|
outb(0xA1, (u8)(inb(0xA1) & (u8)~0x10u));
|
||||||
|
outb(0x21, (u8)(inb(0x21) & (u8)~0x02u));
|
||||||
|
g_log->info("[apic] diag: IRQ12+IRQ1 via PIC (IMR bits cleared)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 控制器交互必须全轮询完成: 期间到达的 IRQ (KBD bit0 一直开着)
|
||||||
|
* 因 handler 未注册只会被 EOI 丢弃, 不会偷走命令响应字节。
|
||||||
|
* (实测教训: 先注册 handler 时, 0x20 读 cfg 的响应被 kbd handler
|
||||||
|
* 当扫描码读走, 轮询侧 cfg read timeout) */
|
||||||
|
ps2_aux_enable_stream();
|
||||||
|
|
||||||
|
/* 交互全部完成, 现在才注册诊断 handler */
|
||||||
|
if (api->irq_register(0x2C, (void *)(uintptr_t)apic_mouse_diag_handler) != 0) {
|
||||||
|
g_log->warn("[apic] diag: irq_register(0x2C) failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (api->irq_register(0x21, (void *)(uintptr_t)apic_kbd_diag_handler) != 0) {
|
||||||
|
g_log->warn("[apic] diag: irq_register(0x21) failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 6000ms 窗口: 注入方需经 "串口日志落盘(块缓冲) → 测试脚本轮询" 的
|
||||||
|
* 可见性延迟 (~1s 量级), 3s 窗口实测会被注入延迟吃光 */
|
||||||
|
g_log->info("[apic] diag: INPUT OBSERVE WINDOW 6000ms — sendkey/mouse_move now");
|
||||||
|
u32 m_before = g_mouse_pkt_count;
|
||||||
|
u32 k_before = g_kbd_pkt_count;
|
||||||
|
tsc_delay_ms(6000);
|
||||||
|
u32 mpkts = g_mouse_pkt_count - m_before;
|
||||||
|
u32 kpkts = g_kbd_pkt_count - k_before;
|
||||||
|
log_dec("[apic] diag: mouse packets observed=", mpkts);
|
||||||
|
log_dec("[apic] diag: kbd packets observed=", kpkts);
|
||||||
|
if (mpkts > 1) {
|
||||||
|
g_log->info("[apic] diag: IRQ12 SUSTAINED delivery confirmed");
|
||||||
|
} else if (mpkts == 1) {
|
||||||
|
g_log->warn("[apic] diag: IRQ12 single packet only (check EOI path)");
|
||||||
|
} else {
|
||||||
|
g_log->info("[apic] diag: no mouse packets (inject via monitor mouse_move)");
|
||||||
|
}
|
||||||
|
if (kpkts > 1) {
|
||||||
|
g_log->info("[apic] diag: IRQ1 SUSTAINED delivery confirmed");
|
||||||
|
} else if (kpkts == 1) {
|
||||||
|
g_log->warn("[apic] diag: IRQ1 single packet only (check EOI path)");
|
||||||
|
} else {
|
||||||
|
g_log->info("[apic] diag: no kbd packets (inject via monitor sendkey)");
|
||||||
|
}
|
||||||
|
/* handler 保持注册: stage3 ps2kbd 经 irq_register(1) 覆盖键盘 handler,
|
||||||
|
* DSK mouseInit 经 irq_register(12) 覆盖鼠标 handler, 均无冲突;
|
||||||
|
* 重定向项/IMR 保持现状 (ps2kbd/mouseInit 有完整初始化序列)。 */
|
||||||
|
}
|
||||||
|
|
||||||
__attribute__((visibility("default")))
|
__attribute__((visibility("default")))
|
||||||
int driver_init(const struct dkm_kernel_api *api,
|
int driver_init(const struct dkm_kernel_api *api,
|
||||||
struct dkm_driver_handle *handle) {
|
struct dkm_driver_handle *handle) {
|
||||||
@@ -301,6 +1456,9 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
}
|
}
|
||||||
g_log->info("[apic] kernel IRQ backend present: PIC");
|
g_log->info("[apic] kernel IRQ backend present: PIC");
|
||||||
|
|
||||||
|
/* B7: FUCK 配置自解析 (lapic_enable/ioapic_init/apic_route/apic_route_legacy/apic_diag) */
|
||||||
|
load_fuck_config(api);
|
||||||
|
|
||||||
if (!cpu_has_lapic()) {
|
if (!cpu_has_lapic()) {
|
||||||
g_log->warn("[apic] CPUID local APIC not present; PIC fallback active");
|
g_log->warn("[apic] CPUID local APIC not present; PIC fallback active");
|
||||||
g_log->info("[apic] driver ready");
|
g_log->info("[apic] driver ready");
|
||||||
@@ -338,15 +1496,60 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (api->hhdm_offset && info.lapic_phys) {
|
if (api->hhdm_offset && info.lapic_phys) {
|
||||||
volatile u32 *lapic = (volatile u32 *)(uintptr_t)(api->hhdm_offset + info.lapic_phys);
|
g_lapic = (volatile u32 *)(uintptr_t)(api->hhdm_offset + info.lapic_phys);
|
||||||
log_hex("[apic] LAPIC ID reg=", lapic[0x20 / 4]);
|
g_lapic_id = g_lapic[0x20 / 4] >> 24;
|
||||||
log_hex("[apic] LAPIC version reg=", lapic[0x30 / 4]);
|
log_hex("[apic] LAPIC ID reg=", g_lapic[0x20 / 4]);
|
||||||
log_hex("[apic] LAPIC SVR reg=", lapic[0xF0 / 4]);
|
log_hex("[apic] LAPIC version reg=", g_lapic[0x30 / 4]);
|
||||||
|
log_hex("[apic] LAPIC SVR reg=", g_lapic[0xF0 / 4]);
|
||||||
|
log_hex("[apic] LAPIC TPR reg=", g_lapic[0x80 / 4]);
|
||||||
|
log_hex("[apic] BSP LAPIC ID=", g_lapic_id);
|
||||||
} else {
|
} else {
|
||||||
g_log->warn("[apic] HHDM unavailable; skip LAPIC MMIO read");
|
g_log->warn("[apic] HHDM unavailable; skip LAPIC MMIO read");
|
||||||
|
g_lapic = (volatile u32 *)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_log->info("[apic] discovery complete; PIC routing retained");
|
/* ---- LAPIC 使能(真机必需,QEMU 也可安全执行) ---- */
|
||||||
|
if (g_lapic && g_cfg.lapic_enable) {
|
||||||
|
lapic_enable();
|
||||||
|
} else if (!g_cfg.lapic_enable) {
|
||||||
|
g_log->info("[apic] lapic_enable=0: skip LAPIC software enable");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- IOAPIC 初始化(真机必需,QEMU 也可安全执行) ---- */
|
||||||
|
if (g_cfg.ioapic_init) {
|
||||||
|
for (u32 i = 0; i < info.ioapic_count; i++) {
|
||||||
|
g_log->info("[apic] initializing IOAPIC");
|
||||||
|
log_hex("[apic] index=", i);
|
||||||
|
ioapic_init_one(&info.ioapics[i], api->hhdm_offset,
|
||||||
|
info.isos, info.iso_count,
|
||||||
|
api->mm_map_mmio);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g_log->info("[apic] ioapic_init=0: skip IOAPIC programming (PIC mode)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- B7: 路由接管 (默认关, FUCK apic_route=1 启用) ---- */
|
||||||
|
if (g_cfg.apic_route && g_cfg.ioapic_init) {
|
||||||
|
apic_route_switch(api, &info);
|
||||||
|
} else {
|
||||||
|
g_log->info("[apic] apic_route=0: PIC routing retained (default)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- B7 diag: IRQ12 鼠标流观察窗 (apic_diag=1, 两种路由模式皆可测) ---- */
|
||||||
|
if (g_cfg.apic_diag) {
|
||||||
|
apic_diag_mouse_window(api, &info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 重定向表回读 (验证证据; 无 IOAPIC 时无输出) */
|
||||||
|
if (g_cfg.ioapic_init) {
|
||||||
|
ioapic_dump_table(&info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_route_active) {
|
||||||
|
g_log->info("[apic] discovery + init complete; APIC routing active");
|
||||||
|
} else {
|
||||||
|
g_log->info("[apic] discovery + init complete; PIC routing retained as fallback");
|
||||||
|
}
|
||||||
g_log->info("[apic] driver ready");
|
g_log->info("[apic] driver ready");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/* dkm_instr.h — DKM 驱动专用插桩宏(无 UTSM 头文件依赖)
|
||||||
|
*
|
||||||
|
* 设计原则:
|
||||||
|
* - 使用 dkm_shared.h 已有的 dkm_rdtsc()/dkm_tsc_per_ms
|
||||||
|
* - 日志通过 dkm_kernel_api->log 输出
|
||||||
|
* - 统计计数器是 file-local static
|
||||||
|
* - 禁用时全部宏展开为空(零开销)
|
||||||
|
* - 需在 driver_init 开头调用 dkm_instr_init(api) 初始化
|
||||||
|
*
|
||||||
|
* 使用方式:
|
||||||
|
* 1. #include "../dkm_instr.h"
|
||||||
|
* 2. 在 driver_init 开头: dkm_instr_init(api);
|
||||||
|
* 3. 使用 DKM_STAT_INC / DKM_TS / DKM_LOG_TRACE 宏
|
||||||
|
*/
|
||||||
|
#ifndef DKM_INSTR_H
|
||||||
|
#define DKM_INSTR_H
|
||||||
|
|
||||||
|
#include "dkm_shared.h"
|
||||||
|
|
||||||
|
/* ---- 全局开关(dkm_instr.c 定义,每个 .drv 引用同一符号) ----
|
||||||
|
* 注意: DKM .drv 是 ET_REL 可重定位对象,每个驱动有独立的数据段副本。
|
||||||
|
* 因此 dkm_instr_global_enable 在每个 .drv 中独立控制,由
|
||||||
|
* dkm_instr_init() 在 driver_init 中根据 kernel_api 设置。
|
||||||
|
*/
|
||||||
|
static u32 dkm_instr_global_enable __attribute__((unused)) = 0;
|
||||||
|
|
||||||
|
/* ---- DKM_STAT: 轻量级计数器 ----
|
||||||
|
* DKM_STAT_DECL(name) — 在函数/文件作用域声明计数器
|
||||||
|
* DKM_STAT_INC(name) — 自增(仅当 enabled)
|
||||||
|
* DKM_STAT_DUMP(name, log_info_fn) — 输出当前计数值
|
||||||
|
*/
|
||||||
|
#define DKM_STAT_DECL(name) static u64 name##_count = 0
|
||||||
|
|
||||||
|
#define DKM_STAT_INC(name) do { \
|
||||||
|
if (dkm_instr_global_enable) { name##_count++; } \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define DKM_STAT_DUMP(name, log_info_fn) do { \
|
||||||
|
if (dkm_instr_global_enable && name##_count > 0) { \
|
||||||
|
log_info_fn("[DKM:STAT] " #name " count="); \
|
||||||
|
/* 简单 u64 to hex 串口输出 */ \
|
||||||
|
static const char _hx[] = "0123456789abcdef"; \
|
||||||
|
char _buf[19]; _buf[0]='0'; _buf[1]='x'; \
|
||||||
|
for(int _i=0;_i<16;_i++) _buf[2+_i]=_hx[(name##_count>>((15-_i)*4))&0xf]; \
|
||||||
|
_buf[18]=0; log_info_fn(_buf); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* ---- DKM_TS: 时间戳测量 ----
|
||||||
|
* DKM_TS_DECL(name) — 声明时间戳变量
|
||||||
|
* DKM_TS_BEGIN(name) — 记录起始 TSC
|
||||||
|
* DKM_TS_END(name, log_info_fn, label) — 计算耗时并输出
|
||||||
|
*/
|
||||||
|
#define DKM_TS_DECL(name) static u64 name##_ts = 0
|
||||||
|
|
||||||
|
#define DKM_TS_BEGIN(name) do { name##_ts = dkm_rdtsc(); } while(0)
|
||||||
|
|
||||||
|
#define DKM_TS_END(name, log_info_fn, label) do { \
|
||||||
|
if (dkm_instr_global_enable && dkm_tsc_per_ms) { \
|
||||||
|
u64 _d = dkm_rdtsc() - name##_ts; \
|
||||||
|
u64 _ms = _d / dkm_tsc_per_ms; \
|
||||||
|
log_info_fn("[DKM:TS] " label " elapsed_ms="); \
|
||||||
|
static const char _hx[] = "0123456789abcdef"; \
|
||||||
|
char _buf[19]; _buf[0]='0'; _buf[1]='x'; \
|
||||||
|
for(int _i=0;_i<16;_i++) _buf[2+_i]=_hx[(_ms>>((15-_i)*4))&0xf]; \
|
||||||
|
_buf[18]=0; log_info_fn(_buf); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* ---- DKM_LOG_TRACE: 调试级别日志 ----
|
||||||
|
* 仅当插桩启用时通过 api->log->info 输出
|
||||||
|
*/
|
||||||
|
#define DKM_LOG_TRACE(log_api, msg) do { \
|
||||||
|
if (dkm_instr_global_enable && (log_api)) (log_api)->info(msg); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* ---- 初始化:读取 kernel_api 中的插桩标志 ----
|
||||||
|
* kernel_api->feature_bits bit0 = 插桩启用标志
|
||||||
|
* 由 UTSM dkm_fill_platform_info() 根据 FUCK debug.dkm_trace 设置
|
||||||
|
*/
|
||||||
|
static __inline__ void dkm_instr_init(const struct dkm_kernel_api *api) {
|
||||||
|
if (api && (api->feature_bits & 0x01)) {
|
||||||
|
dkm_instr_global_enable = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* DKM_INSTR_H */
|
||||||
+30
-1
@@ -109,6 +109,15 @@ struct dkm_net_device_desc {
|
|||||||
int (*is_wireless)(void *ctx);
|
int (*is_wireless)(void *ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dkm_net_stats {
|
||||||
|
u64 tx_ok;
|
||||||
|
u64 tx_err;
|
||||||
|
u64 rx_ok;
|
||||||
|
u64 rx_err;
|
||||||
|
u64 rx_overflow;
|
||||||
|
u64 link_changes;
|
||||||
|
};
|
||||||
|
|
||||||
struct dkm_net_api {
|
struct dkm_net_api {
|
||||||
int (*register_device)(const struct dkm_net_device_desc *desc);
|
int (*register_device)(const struct dkm_net_device_desc *desc);
|
||||||
u32 (*device_count)(void);
|
u32 (*device_count)(void);
|
||||||
@@ -119,6 +128,7 @@ struct dkm_net_api {
|
|||||||
int (*scan_count)(u32 index);
|
int (*scan_count)(u32 index);
|
||||||
int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out);
|
int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out);
|
||||||
int (*is_wireless)(u32 index);
|
int (*is_wireless)(u32 index);
|
||||||
|
int (*device_stats)(u32 index, struct dkm_net_stats *out);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int (*dkm_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer);
|
typedef int (*dkm_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer);
|
||||||
@@ -143,6 +153,16 @@ struct dkm_block_api {
|
|||||||
int (*set_write_fn)(u32 index, dkm_block_write_fn fn);
|
int (*set_write_fn)(u32 index, dkm_block_write_fn fn);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* 与 UTSM/include/utsm/paging.h 中的 dkm_mmio_api 布局一致 */
|
||||||
|
struct dkm_mmio_api {
|
||||||
|
/* 四级页表 walk。返回 0=未映射;1=4K PTE;2=2M PDE;3=1G PDPTE */
|
||||||
|
int (*is_mapped)(u64 vaddr);
|
||||||
|
/* 把物理区间映射到 hhdm_offset + phys(4KiB UC 页),返回 0 成功 */
|
||||||
|
int (*map_mmio)(u64 phys, u64 size);
|
||||||
|
/* 当前 CR3 物理地址(诊断用) */
|
||||||
|
u64 (*cr3)(void);
|
||||||
|
};
|
||||||
|
|
||||||
/* 与 UTSM/include/utsm/dkm.h 中的 dkm_kernel_api 布局完全一致.
|
/* 与 UTSM/include/utsm/dkm.h 中的 dkm_kernel_api 布局完全一致.
|
||||||
* 字段顺序/类型不可调整 — UTSM 端按此偏移填充. */
|
* 字段顺序/类型不可调整 — UTSM 端按此偏移填充. */
|
||||||
struct dkm_kernel_api {
|
struct dkm_kernel_api {
|
||||||
@@ -168,7 +188,16 @@ struct dkm_kernel_api {
|
|||||||
const void *boot_modules_response;
|
const void *boot_modules_response;
|
||||||
int (*irq_register)(u8 irq, void *handler);
|
int (*irq_register)(u8 irq, void *handler);
|
||||||
u64 hhdm_offset;
|
u64 hhdm_offset;
|
||||||
const struct dkm_block_api *block; /* 末尾字段, 不使用 block 的驱动可不访问 */
|
const struct dkm_block_api *block; /* 不使用 block 的驱动可不访问 */
|
||||||
|
const struct dkm_mmio_api *mmio; /* 页表/高位 MMIO 映射服务(HHDM 空洞原地补映射) */
|
||||||
|
/* —— 新增字段只允许追加在末尾(DSK 以 api+0xA8 硬偏移读取 block) —— */
|
||||||
|
void *(*mm_map_mmio)(u64 phys, u64 size); /* MMIO 独立窗口映射(PCD|PWT),返回虚拟地址,失败返回 0 */
|
||||||
|
void (*mm_unmap_mmio)(void *virt, u64 size); /* 解除 MMIO 独立窗口映射 */
|
||||||
|
/* B7 阶段2 尾部追加(旧内核无此字段 → 判空使用) */
|
||||||
|
void (*register_apic_eoi)(void (*eoi_fn)(void));
|
||||||
|
/* B7 阶段3 尾部追加:动态 IDT 向量分配器(MSI/MSI-X),池 0x40-0xDF */
|
||||||
|
int (*irq_vector_alloc)(void);
|
||||||
|
void (*irq_vector_free)(int vector);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dkm_driver_handle;
|
struct dkm_driver_handle;
|
||||||
|
|||||||
+445
-79
@@ -1,9 +1,16 @@
|
|||||||
/* DKM e1000 Driver — Intel 8254x Gigabit Ethernet
|
/* DKM e1000 Driver — Intel 8254x/8257x Gigabit Ethernet
|
||||||
* Stage 3, optional, depends on "pci" and "irq", provides "netdev".
|
* Stage 3, optional, depends on "pci" and "irq", provides "netdev".
|
||||||
* Finds e1000 via PCI, maps MMIO, reads MAC address.
|
* Supports classic e1000 (82540EM/82545/82546) and e1000e (82574L/I217/I218/I219).
|
||||||
|
* Finds device via PCI, maps MMIO, reads MAC (RAL/RAH or EEPROM),
|
||||||
|
* initializes RX/TX rings, provides link/speed/duplex diagnostics.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../dkm_shared.h"
|
#include "../dkm_shared.h"
|
||||||
|
#include "../dkm_instr.h"
|
||||||
|
|
||||||
|
DKM_STAT_DECL(e1k_irq_count);
|
||||||
|
DKM_STAT_DECL(e1k_rx_packets);
|
||||||
|
DKM_STAT_DECL(e1k_tx_packets);
|
||||||
|
|
||||||
static const char *const g_depends[] = {"pci","irq"};
|
static const char *const g_depends[] = {"pci","irq"};
|
||||||
static const char *const g_provides[] = {"netdev"};
|
static const char *const g_provides[] = {"netdev"};
|
||||||
@@ -11,7 +18,7 @@ __attribute__((visibility("default"), used))
|
|||||||
const struct dkm_driver_desc driver_desc = {
|
const struct dkm_driver_desc driver_desc = {
|
||||||
.magic=DKM_DRIVER_MAGIC,.abi_version=DKM_ABI_VERSION,
|
.magic=DKM_DRIVER_MAGIC,.abi_version=DKM_ABI_VERSION,
|
||||||
.desc_size=sizeof(struct dkm_driver_desc),
|
.desc_size=sizeof(struct dkm_driver_desc),
|
||||||
.name="e1000",.version="0.1.0",.vendor="Deshab",
|
.name="e1000",.version="0.2.0",.vendor="Deshab",
|
||||||
.driver_class=9,.stage=3,.flags=0,.priority=0,
|
.driver_class=9,.stage=3,.flags=0,.priority=0,
|
||||||
.depends=g_depends,.depends_count=2,
|
.depends=g_depends,.depends_count=2,
|
||||||
.provides=g_provides,.provides_count=1,.min_kernel_abi=1,
|
.provides=g_provides,.provides_count=1,.min_kernel_abi=1,
|
||||||
@@ -41,13 +48,33 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
#define E1000_TXDCTL 0x3828
|
#define E1000_TXDCTL 0x3828
|
||||||
#define E1000_TCTL 0x0400
|
#define E1000_TCTL 0x0400
|
||||||
#define E1000_TIPG 0x0410
|
#define E1000_TIPG 0x0410
|
||||||
|
#define E1000_MANC 0x0580
|
||||||
|
|
||||||
|
/* STATUS register bits (link/speed/duplex diagnostic) */
|
||||||
|
#define E1000_STATUS_FD (1u << 0) /* full duplex */
|
||||||
|
#define E1000_STATUS_LU (1u << 1) /* link up */
|
||||||
|
#define E1000_STATUS_FUNC_SHIFT 2 /* PCI function number (ro) */
|
||||||
|
#define E1000_STATUS_TXOFF (1u << 4) /* transmitter paused */
|
||||||
|
#define E1000_STATUS_SPEEDMASK (3u << 6)
|
||||||
|
#define E1000_STATUS_SPEED_10 (0u << 6)
|
||||||
|
#define E1000_STATUS_SPEED_100 (1u << 6)
|
||||||
|
#define E1000_STATUS_SPEED_1000 (2u << 6) /* classic e1000 only */
|
||||||
|
|
||||||
|
/* CTRL register bits */
|
||||||
|
#define E1000_CTRL_FD (1u << 0)
|
||||||
|
#define E1000_CTRL_SLU (1u << 6)
|
||||||
|
#define E1000_CTRL_RST (1u << 26) /* software reset */
|
||||||
|
#define E1000_CTRL_ASDE (1u << 11) /* auto-speed detect enable */
|
||||||
|
#define E1000_CTRL_FRCSPD (1u << 12) /* force speed */
|
||||||
|
#define E1000_CTRL_FRCDPLX (1u << 13) /* force duplex */
|
||||||
|
|
||||||
|
/* e1000e (82574L/I217/I219) speed encoding in STATUS differs */
|
||||||
|
#define E1000_STATUS_SPEED_1000_E1000E (3u << 6)
|
||||||
|
|
||||||
#define E1000_RX_DESC_COUNT 8u
|
#define E1000_RX_DESC_COUNT 8u
|
||||||
#define E1000_TX_DESC_COUNT 8u
|
#define E1000_TX_DESC_COUNT 8u
|
||||||
#define E1000_RX_BUF_SIZE 2048u
|
#define E1000_RX_BUF_SIZE 2048u
|
||||||
#define E1000_TX_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_EN (1u << 1)
|
||||||
#define E1000_RCTL_SBP (1u << 2)
|
#define E1000_RCTL_SBP (1u << 2)
|
||||||
@@ -67,8 +94,97 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
#define E1000_TX_CMD_RS (1u << 3)
|
#define E1000_TX_CMD_RS (1u << 3)
|
||||||
#define E1000_TX_STA_DD (1u << 0)
|
#define E1000_TX_STA_DD (1u << 0)
|
||||||
#define E1000_RX_STA_DD (1u << 0)
|
#define E1000_RX_STA_DD (1u << 0)
|
||||||
#define E1000_CTRL_FD (1u << 0)
|
|
||||||
#define E1000_CTRL_SLU (1u << 6)
|
/* ---- e1000 device ID table ---- */
|
||||||
|
/* 分类: 0=经典 e1000 (8254x), 1=e1000e (8257x/I217/I218/I219) */
|
||||||
|
struct e1000_device_id {
|
||||||
|
u16 did;
|
||||||
|
u8 family; /* 0=classic, 1=e1000e */
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct e1000_device_id g_e1000_ids[] = {
|
||||||
|
/* 经典 e1000 (8254x 系列) — EERD 接口兼容 */
|
||||||
|
{0x100E, 0, "82540EM"}, /* QEMU e1000 default */
|
||||||
|
{0x1004, 0, "82543GC"}, /* Copper */
|
||||||
|
{0x100F, 0, "82545EM"}, /* Copper */
|
||||||
|
{0x1010, 0, "82546EB"}, /* Copper dual-port */
|
||||||
|
{0x1011, 0, "82545EM-Fiber"},
|
||||||
|
{0x1012, 0, "82546EB-Fiber"},
|
||||||
|
{0x1013, 0, "82541EI"},
|
||||||
|
{0x1015, 0, "82540EM-LOM"},
|
||||||
|
{0x1016, 0, "82540EP"},
|
||||||
|
{0x1017, 0, "82540EP-LOM"},
|
||||||
|
{0x1018, 0, "82541EI-LOM"},
|
||||||
|
{0x1019, 0, "82547EI"},
|
||||||
|
{0x101A, 0, "82547EI-LOM"},
|
||||||
|
{0x101D, 0, "82546EB-Quad"},
|
||||||
|
{0x1026, 0, "82545GM"},
|
||||||
|
{0x1027, 0, "82545GM-Fiber"},
|
||||||
|
{0x1028, 0, "82545GM-SerDes"},
|
||||||
|
{0x1075, 0, "82547GI"},
|
||||||
|
{0x1076, 0, "82541GI"},
|
||||||
|
{0x1077, 0, "82541GI-LOM"},
|
||||||
|
{0x1078, 0, "82541ER"},
|
||||||
|
{0x1079, 0, "82546GB"},
|
||||||
|
{0x107A, 0, "82546GB-Fiber"},
|
||||||
|
{0x107B, 0, "82546GB-SerDes"},
|
||||||
|
{0x107C, 0, "82541GI-Mobile"},
|
||||||
|
{0x108B, 0, "82573E"},
|
||||||
|
{0x108C, 0, "82573E-IAMT"},
|
||||||
|
{0x109A, 0, "82573L"},
|
||||||
|
|
||||||
|
/* e1000e (8257x/I217/I218/I219) 系列 — 寄存器兼容但 EERD/STATUS 编码有差异 */
|
||||||
|
{0x10D3, 1, "82574L"}, /* QEMU e1000e default */
|
||||||
|
{0x10F5, 1, "82578DM"},
|
||||||
|
{0x10F6, 1, "82578DC"},
|
||||||
|
{0x1502, 1, "82579V"},
|
||||||
|
{0x1503, 1, "82579V-2"},
|
||||||
|
{0x1533, 1, "I217-V"},
|
||||||
|
{0x1534, 1, "I217-LM"},
|
||||||
|
{0x153A, 1, "I218-V"},
|
||||||
|
{0x153B, 1, "I218-LM"},
|
||||||
|
{0x156F, 1, "I219-V"},
|
||||||
|
{0x1570, 1, "I219-LM"},
|
||||||
|
{0x157B, 1, "I219-V-2"},
|
||||||
|
{0x157A, 1, "I219-LM-2"},
|
||||||
|
{0x15D3, 1, "I219-V-3"},
|
||||||
|
{0x15D8, 1, "I219-LM-3"},
|
||||||
|
{0x15E3, 1, "I219-V-4"},
|
||||||
|
{0x15D6, 1, "I219-V-5"},
|
||||||
|
{0x15BD, 1, "I219-V-6"},
|
||||||
|
{0x15B8, 1, "I219-LM-4"},
|
||||||
|
{0x15BB, 1, "I219-LM-5"},
|
||||||
|
{0x15DF, 1, "I219-LM-6"},
|
||||||
|
{0x15E0, 1, "I219-V-7"},
|
||||||
|
{0x15E1, 1, "I219-LM-7"},
|
||||||
|
{0x15E2, 1, "I219-LM-8"},
|
||||||
|
{0x15E4, 1, "I219-V-8"},
|
||||||
|
{0x15E5, 1, "I219-V-9"},
|
||||||
|
{0x15F9, 1, "I219-LM-9"},
|
||||||
|
{0x15FA, 1, "I219-V-10"},
|
||||||
|
{0x15FB, 1, "I219-LM-10"},
|
||||||
|
{0x15FC, 1, "I219-LM-11"},
|
||||||
|
{0x15FD, 1, "I219-V-11"},
|
||||||
|
{0x15FE, 1, "I219-LM-12"},
|
||||||
|
{0x15FF, 1, "I219-V-12"},
|
||||||
|
|
||||||
|
/* I210/I211 (igb 系列) — 与 e1000e MMIO 兼容 */
|
||||||
|
{0x1537, 1, "I210"},
|
||||||
|
{0x1536, 1, "I211-AT"},
|
||||||
|
|
||||||
|
/* 82575/82576/82580 (igb 系列, 多队列, 基本兼容) */
|
||||||
|
{0x10A7, 1, "82575EB"},
|
||||||
|
{0x10A9, 1, "82575EB-SerDes"},
|
||||||
|
{0x10C9, 1, "82576"},
|
||||||
|
{0x10E6, 1, "82576-Fiber"},
|
||||||
|
{0x10E7, 1, "82576-Quad"},
|
||||||
|
{0x150D, 1, "82580"},
|
||||||
|
{0x1516, 1, "82580-Fiber"},
|
||||||
|
{0x1526, 1, "82576-2"},
|
||||||
|
|
||||||
|
{0, 0, 0} /* 终止符 */
|
||||||
|
};
|
||||||
|
|
||||||
struct e1000_rx_desc {
|
struct e1000_rx_desc {
|
||||||
u64 addr;
|
u64 addr;
|
||||||
@@ -89,6 +205,36 @@ struct e1000_tx_desc {
|
|||||||
u16 special;
|
u16 special;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* ---- 统计计数器(用于真机诊断) ---- */
|
||||||
|
static u64 g_stat_tx_ok; /* TX 成功帧数 */
|
||||||
|
static u64 g_stat_tx_err; /* TX 失败帧数 */
|
||||||
|
static u64 g_stat_tx_timeout; /* TX 超时次数 */
|
||||||
|
static u64 g_stat_rx_ok; /* RX 成功帧数 */
|
||||||
|
static u64 g_stat_rx_err; /* RX 错误帧数 */
|
||||||
|
static u64 g_stat_rx_overflow; /* RX buffer 太小丢包 */
|
||||||
|
static u64 g_stat_irq_count; /* IRQ 中断次数 */
|
||||||
|
|
||||||
|
/* 前向声明 */
|
||||||
|
static void raw_log(const char *s);
|
||||||
|
static void raw_hex(const char *prefix, u64 v);
|
||||||
|
|
||||||
|
/* 每 256 次数据路径事件打印一行摘要(避免刷串口)。 */
|
||||||
|
static void net_stat_tick(void) {
|
||||||
|
u64 sum = g_stat_tx_ok + g_stat_tx_err + g_stat_tx_timeout +
|
||||||
|
g_stat_rx_ok + g_stat_rx_err + g_stat_rx_overflow;
|
||||||
|
if ((sum & 0xFF) != 0) return;
|
||||||
|
raw_log("[e1000.raw] stat tx_ok=");
|
||||||
|
raw_hex("", g_stat_tx_ok);
|
||||||
|
raw_log("[e1000.raw] stat tx_err=");
|
||||||
|
raw_hex("", g_stat_tx_err);
|
||||||
|
raw_log("[e1000.raw] stat rx_ok=");
|
||||||
|
raw_hex("", g_stat_rx_ok);
|
||||||
|
raw_log("[e1000.raw] stat rx_err=");
|
||||||
|
raw_hex("", g_stat_rx_err);
|
||||||
|
raw_log("[e1000.raw] stat rx_overflow=");
|
||||||
|
raw_hex("", g_stat_rx_overflow);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct dkm_log_api *g_log;
|
static const struct dkm_log_api *g_log;
|
||||||
static volatile u32 *g_mmio;
|
static volatile u32 *g_mmio;
|
||||||
static u64 g_bar0_phys;
|
static u64 g_bar0_phys;
|
||||||
@@ -103,6 +249,8 @@ static u64 g_tx_buf_phys[E1000_TX_DESC_COUNT];
|
|||||||
static u32 g_rx_tail;
|
static u32 g_rx_tail;
|
||||||
static u32 g_tx_tail;
|
static u32 g_tx_tail;
|
||||||
static int g_rings_ready;
|
static int g_rings_ready;
|
||||||
|
static u8 g_device_family; /* 0=classic e1000, 1=e1000e */
|
||||||
|
static u16 g_device_id; /* PCI device ID */
|
||||||
|
|
||||||
#define COM1 0x3F8
|
#define COM1 0x3F8
|
||||||
|
|
||||||
@@ -132,6 +280,23 @@ static void raw_hex(const char *prefix, u64 v) {
|
|||||||
raw_log("\n");
|
raw_log("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 打印 MAC 地址 */
|
||||||
|
static void raw_mac(const char *prefix, const u8 mac[6]) {
|
||||||
|
static const char h[] = "0123456789abcdef";
|
||||||
|
raw_log(prefix);
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
serial_wait_tx();
|
||||||
|
dkm_outb(COM1, (u8)h[(mac[i] >> 4) & 0xf]);
|
||||||
|
serial_wait_tx();
|
||||||
|
dkm_outb(COM1, (u8)h[mac[i] & 0xf]);
|
||||||
|
if (i < 5) {
|
||||||
|
serial_wait_tx();
|
||||||
|
dkm_outb(COM1, ':');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raw_log("\n");
|
||||||
|
}
|
||||||
|
|
||||||
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
||||||
return dkm_pci_read(bus, dev, func, reg);
|
return dkm_pci_read(bus, dev, func, reg);
|
||||||
}
|
}
|
||||||
@@ -140,40 +305,165 @@ static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 v) {
|
|||||||
dkm_pci_write(bus, dev, func, reg, v);
|
dkm_pci_write(bus, dev, func, reg, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int e1000_find(u8 *bus, u8 *dev) {
|
/* 根据 PCI Device ID 查找设备信息 */
|
||||||
for (u8 d=0; d<32; d++) {
|
static const struct e1000_device_id *e1000_lookup_id(u16 did) {
|
||||||
u32 vd = pci_read(0,d,0,PCI_VENDOR_ID);
|
for (int i = 0; g_e1000_ids[i].did != 0; i++) {
|
||||||
if ((vd&0xffff)==0xffff) continue;
|
if (g_e1000_ids[i].did == did) return &g_e1000_ids[i];
|
||||||
if ((vd&0xffff)!=0x8086) continue;
|
}
|
||||||
u16 did = (u16)(vd>>16);
|
return 0;
|
||||||
/* 82540EM(0x100E) / 82545 等经典 e1000,以及 82574L(0x10D3) e1000e */
|
}
|
||||||
if (did==0x100E || did==0x10D3 || did==0x1004 || did==0x100F ||
|
|
||||||
did==0x10D3 || did==0x153A || did==0x1533) {
|
/* 多总线扫描 e1000 设备。真机网卡可能在 bus>0 上。 */
|
||||||
*bus=0; *dev=d; return 0;
|
static int e1000_find(u8 *out_bus, u8 *out_dev, u8 *out_func,
|
||||||
|
const struct e1000_device_id **out_id) {
|
||||||
|
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;
|
||||||
|
if ((vd & 0xffff) != 0x8086) 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;
|
||||||
|
if ((vd2 & 0xffff) != 0x8086) continue;
|
||||||
|
u16 did = (u16)(vd2 >> 16);
|
||||||
|
|
||||||
|
const struct e1000_device_id *id = e1000_lookup_id(did);
|
||||||
|
if (id) {
|
||||||
|
*out_bus = bus;
|
||||||
|
*out_dev = dev;
|
||||||
|
*out_func = func;
|
||||||
|
if (out_id) *out_id = id;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 读取 EEPROM 一个 16-bit 字(经典 e1000 EERD 接口)。失败返回 0xffff。 */
|
/* 读取 EEPROM 一个 16-bit 字(经典 e1000 EERD 接口)。
|
||||||
|
* 经典 e1000: EERD bit0=START, addr<<8, 完成后 bit4=DONE, data 在高 16 位。
|
||||||
|
* e1000e: EERD 接口类似但地址字段位置不同 (addr<<2, bit1=START, bit2=DONE)。
|
||||||
|
* 失败返回 0xffff。 */
|
||||||
static u16 e1000_eeprom_read(u16 addr) {
|
static u16 e1000_eeprom_read(u16 addr) {
|
||||||
/* EERD: bit0=START, addr<<8, 完成后 bit4=DONE, data 在高 16 位 */
|
if (g_device_family == 1) {
|
||||||
g_mmio[E1000_EERD/4] = ((u32)addr << 8) | 1u;
|
/* e1000e: EERD addr<<2 | START(bit1), DONE=bit2, data=bits[31:16] */
|
||||||
/* 实机: 100ms TSC 超时 */
|
g_mmio[E1000_EERD/4] = ((u32)addr << 2) | 0x2u;
|
||||||
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
|
||||||
while (dkm_rdtsc() < deadline) {
|
while (dkm_rdtsc() < deadline) {
|
||||||
u32 v = g_mmio[E1000_EERD/4];
|
u32 v = g_mmio[E1000_EERD/4];
|
||||||
if (v & (1u<<4)) return (u16)(v >> 16);
|
if (v & (1u << 2)) return (u16)(v >> 16);
|
||||||
__asm__ volatile("pause");
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* 经典 e1000: EERD addr<<8 | START(bit0), DONE=bit4 */
|
||||||
|
g_mmio[E1000_EERD/4] = ((u32)addr << 8) | 1u;
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
|
u32 v = g_mmio[E1000_EERD/4];
|
||||||
|
if (v & (1u << 4)) return (u16)(v >> 16);
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0xffff;
|
return 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 检测链路状态,返回 0=down, 1=up */
|
||||||
|
static int e1000_link_up(void) {
|
||||||
|
if (!g_mmio) return 0;
|
||||||
|
return (g_mmio[E1000_STATUS/4] & E1000_STATUS_LU) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 读取当前速度 (Mbps),返回 10/100/1000/0(未知) */
|
||||||
|
static u32 e1000_link_speed(void) {
|
||||||
|
if (!g_mmio) return 0;
|
||||||
|
u32 status = g_mmio[E1000_STATUS/4];
|
||||||
|
u32 speed_bits = status & E1000_STATUS_SPEEDMASK;
|
||||||
|
if (g_device_family == 1) {
|
||||||
|
/* e1000e speed encoding: 00=reserved, 01=100, 10=1000, 11=1000 */
|
||||||
|
if (speed_bits == E1000_STATUS_SPEED_1000_E1000E ||
|
||||||
|
speed_bits == (2u << 6))
|
||||||
|
return 1000;
|
||||||
|
if (speed_bits == E1000_STATUS_SPEED_100) return 100;
|
||||||
|
if (speed_bits == E1000_STATUS_SPEED_10) return 10;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
/* 经典 e1000 speed encoding: 00=10, 01=100, 10=1000 */
|
||||||
|
if (speed_bits == E1000_STATUS_SPEED_1000) return 1000;
|
||||||
|
if (speed_bits == E1000_STATUS_SPEED_100) return 100;
|
||||||
|
if (speed_bits == E1000_STATUS_SPEED_10) return 10;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 读取当前双工模式,返回 1=全双工, 0=半双工 */
|
||||||
|
static int e1000_link_full_duplex(void) {
|
||||||
|
if (!g_mmio) return 0;
|
||||||
|
return (g_mmio[E1000_STATUS/4] & E1000_STATUS_FD) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 打印链路诊断信息 */
|
||||||
|
static void e1000_log_link_status(void) {
|
||||||
|
int up = e1000_link_up();
|
||||||
|
if (up) {
|
||||||
|
u32 speed = e1000_link_speed();
|
||||||
|
int fd = e1000_link_full_duplex();
|
||||||
|
const char *speed_str = "unknown";
|
||||||
|
char speed_buf[8];
|
||||||
|
if (speed == 1000) speed_str = "1000";
|
||||||
|
else if (speed == 100) speed_str = "100";
|
||||||
|
else if (speed == 10) speed_str = "10";
|
||||||
|
else {
|
||||||
|
/* 手动转数字 */
|
||||||
|
speed_buf[0] = '0' + (char)(speed / 1000 % 10);
|
||||||
|
speed_buf[1] = '0' + (char)(speed / 100 % 10);
|
||||||
|
speed_buf[2] = '0' + (char)(speed / 10 % 10);
|
||||||
|
speed_buf[3] = '0' + (char)(speed % 10);
|
||||||
|
speed_buf[4] = 0;
|
||||||
|
speed_str = speed_buf;
|
||||||
|
}
|
||||||
|
raw_log("[e1000] link=up speed=");
|
||||||
|
raw_log(speed_str);
|
||||||
|
raw_log("Mbps duplex=");
|
||||||
|
raw_log(fd ? "full" : "half");
|
||||||
|
raw_log("\n");
|
||||||
|
} else {
|
||||||
|
raw_log("[e1000] link=down\n");
|
||||||
|
}
|
||||||
|
raw_hex("[e1000] STATUS=", g_mmio ? g_mmio[E1000_STATUS/4] : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 等待链路就绪(最长 2 秒),实机 PHY 自动协商可能需要时间 */
|
||||||
|
static int e1000_wait_for_link(void) {
|
||||||
|
if (!g_mmio) return -1;
|
||||||
|
/* 设置 ASDE (Auto-Speed Detect Enable) 让 PHY 自动协商 */
|
||||||
|
if (g_device_family == 1) {
|
||||||
|
/* e1000e: 建议先清除 FRCSPD/FRCDPLX,让自动协商生效 */
|
||||||
|
u32 ctrl = g_mmio[E1000_CTRL/4];
|
||||||
|
ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPLX);
|
||||||
|
ctrl |= E1000_CTRL_SLU | E1000_CTRL_ASDE;
|
||||||
|
g_mmio[E1000_CTRL/4] = ctrl;
|
||||||
|
}
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
|
if (e1000_link_up()) return 0;
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
return -1; /* 链路未就绪但驱动继续初始化(后续可能 link up) */
|
||||||
|
}
|
||||||
|
|
||||||
static int irq_handler(u8 irq) {
|
static int irq_handler(u8 irq) {
|
||||||
(void)irq;
|
(void)irq;
|
||||||
if (!g_mmio) return 0;
|
if (!g_mmio) return 0;
|
||||||
u32 icr = g_mmio[E1000_ICR/4];
|
u32 icr = g_mmio[E1000_ICR/4];
|
||||||
if (icr) {
|
if (icr) {
|
||||||
|
g_stat_irq_count++;
|
||||||
|
DKM_STAT_INC(e1k_irq_count);
|
||||||
g_mmio[E1000_ICR/4] = icr; /* clear */
|
g_mmio[E1000_ICR/4] = icr; /* clear */
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -228,7 +518,7 @@ static int e1000_init_rings(const struct dkm_dma_api *dma) {
|
|||||||
g_tx_desc[i].status = E1000_TX_STA_DD;
|
g_tx_desc[i].status = E1000_TX_STA_DD;
|
||||||
}
|
}
|
||||||
dma_fence();
|
dma_fence();
|
||||||
raw_log("[e1000.raw] ring/buffers allocated\n");
|
raw_log("[e1000] ring/buffers allocated\n");
|
||||||
|
|
||||||
g_mmio[E1000_RCTL/4] = 0;
|
g_mmio[E1000_RCTL/4] = 0;
|
||||||
g_mmio[E1000_TCTL/4] = 0;
|
g_mmio[E1000_TCTL/4] = 0;
|
||||||
@@ -259,19 +549,23 @@ static int e1000_init_rings(const struct dkm_dma_api *dma) {
|
|||||||
mmio_flush();
|
mmio_flush();
|
||||||
|
|
||||||
g_rings_ready = 1;
|
g_rings_ready = 1;
|
||||||
raw_log("[e1000.raw] RX/TX rings initialized\n");
|
raw_log("[e1000] RX/TX rings initialized\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int e1000_tx(void *ctx, const void *packet, u32 length) {
|
static int e1000_tx(void *ctx, const void *packet, u32 length) {
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
raw_log("[e1000.raw] tx enter\n");
|
if (!g_rings_ready || !packet || length == 0 || length > E1000_TX_BUF_SIZE) {
|
||||||
if (!g_rings_ready || !packet || length == 0 || length > E1000_TX_BUF_SIZE) return -1;
|
g_stat_tx_err++;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
u32 index = g_tx_tail;
|
u32 index = g_tx_tail;
|
||||||
if (!(g_tx_desc[index].status & E1000_TX_STA_DD)) return -2;
|
if (!(g_tx_desc[index].status & E1000_TX_STA_DD)) {
|
||||||
|
g_stat_tx_err++;
|
||||||
|
return -2; /* TX ring full */
|
||||||
|
}
|
||||||
copy_bytes(g_tx_buf[index], packet, length);
|
copy_bytes(g_tx_buf[index], packet, length);
|
||||||
dma_fence();
|
dma_fence();
|
||||||
raw_log("[e1000.raw] tx copied\n");
|
|
||||||
g_tx_desc[index].length = (u16)length;
|
g_tx_desc[index].length = (u16)length;
|
||||||
g_tx_desc[index].cso = 0;
|
g_tx_desc[index].cso = 0;
|
||||||
g_tx_desc[index].cmd = E1000_TX_CMD_EOP | E1000_TX_CMD_IFCS | E1000_TX_CMD_RS;
|
g_tx_desc[index].cmd = E1000_TX_CMD_EOP | E1000_TX_CMD_IFCS | E1000_TX_CMD_RS;
|
||||||
@@ -287,17 +581,20 @@ static int e1000_tx(void *ctx, const void *packet, u32 length) {
|
|||||||
while (dkm_rdtsc() < deadline) {
|
while (dkm_rdtsc() < deadline) {
|
||||||
dma_fence();
|
dma_fence();
|
||||||
if (g_tx_desc[index].status & E1000_TX_STA_DD) {
|
if (g_tx_desc[index].status & E1000_TX_STA_DD) {
|
||||||
raw_log("[e1000.raw] tx complete\n");
|
g_stat_tx_ok++;
|
||||||
|
DKM_STAT_INC(e1k_tx_packets);
|
||||||
|
net_stat_tick();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
__asm__ volatile("pause");
|
__asm__ volatile("pause");
|
||||||
}
|
}
|
||||||
raw_log("[e1000.raw] tx timeout\n");
|
g_stat_tx_timeout++;
|
||||||
raw_hex("[e1000.raw] STATUS=", g_mmio[E1000_STATUS/4]);
|
g_stat_tx_err++;
|
||||||
raw_hex("[e1000.raw] TCTL=", g_mmio[E1000_TCTL/4]);
|
raw_log("[e1000] tx timeout\n");
|
||||||
raw_hex("[e1000.raw] TDH=", g_mmio[E1000_TDH/4]);
|
raw_hex("[e1000] STATUS=", g_mmio[E1000_STATUS/4]);
|
||||||
raw_hex("[e1000.raw] TDT=", g_mmio[E1000_TDT/4]);
|
raw_hex("[e1000] TCTL=", g_mmio[E1000_TCTL/4]);
|
||||||
raw_hex("[e1000.raw] desc.status=", g_tx_desc[index].status);
|
raw_hex("[e1000] TDH=", g_mmio[E1000_TDH/4]);
|
||||||
|
raw_hex("[e1000] TDT=", g_mmio[E1000_TDT/4]);
|
||||||
return -3;
|
return -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,9 +604,10 @@ static int e1000_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length)
|
|||||||
if (!g_rings_ready || !buffer || !out_length) return -1;
|
if (!g_rings_ready || !buffer || !out_length) return -1;
|
||||||
u32 next = (g_rx_tail + 1) % E1000_RX_DESC_COUNT;
|
u32 next = (g_rx_tail + 1) % E1000_RX_DESC_COUNT;
|
||||||
dma_fence();
|
dma_fence();
|
||||||
if (!(g_rx_desc[next].status & E1000_RX_STA_DD)) return 1;
|
if (!(g_rx_desc[next].status & E1000_RX_STA_DD)) return 1; /* no data */
|
||||||
u32 len = g_rx_desc[next].length;
|
u32 len = g_rx_desc[next].length;
|
||||||
if (len > capacity) {
|
if (len > capacity) {
|
||||||
|
g_stat_rx_overflow++;
|
||||||
g_rx_desc[next].status = 0;
|
g_rx_desc[next].status = 0;
|
||||||
dma_fence();
|
dma_fence();
|
||||||
g_rx_tail = next;
|
g_rx_tail = next;
|
||||||
@@ -324,6 +622,9 @@ static int e1000_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length)
|
|||||||
g_rx_tail = next;
|
g_rx_tail = next;
|
||||||
g_mmio[E1000_RDT/4] = g_rx_tail;
|
g_mmio[E1000_RDT/4] = g_rx_tail;
|
||||||
mmio_flush();
|
mmio_flush();
|
||||||
|
g_stat_rx_ok++;
|
||||||
|
DKM_STAT_INC(e1k_rx_packets);
|
||||||
|
net_stat_tick();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,58 +633,81 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
struct dkm_driver_handle *handle) {
|
struct dkm_driver_handle *handle) {
|
||||||
/* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */
|
/* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */
|
||||||
dkm_tsc_calibrate();
|
dkm_tsc_calibrate();
|
||||||
raw_log("[e1000.raw] driver_init entered\n");
|
raw_log("[e1000] driver_init entered\n");
|
||||||
(void)handle;
|
(void)handle;
|
||||||
if (!api||!api->log) return -1;
|
if (!api||!api->log) return -1;
|
||||||
g_log=api->log;
|
g_log=api->log;
|
||||||
|
dkm_instr_init(api);
|
||||||
|
|
||||||
raw_log("[e1000.raw] before find\n");
|
u8 bus, dev, func;
|
||||||
u8 bus, dev;
|
const struct e1000_device_id *dev_id = 0;
|
||||||
if (e1000_find(&bus,&dev)!=0) {
|
if (e1000_find(&bus, &dev, &func, &dev_id) != 0) {
|
||||||
raw_log("[e1000.raw] device not found\n");
|
raw_log("[e1000] device not found\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
raw_log("[e1000.raw] found device\n");
|
g_device_id = (u16)(pci_read(bus, dev, func, PCI_VENDOR_ID) >> 16);
|
||||||
|
g_device_family = dev_id ? dev_id->family : 0;
|
||||||
|
raw_log("[e1000] found device: ");
|
||||||
|
raw_log(dev_id ? dev_id->name : "unknown");
|
||||||
|
raw_log(g_device_family == 1 ? " (e1000e)" : " (classic)");
|
||||||
|
raw_log("\n");
|
||||||
|
raw_hex("[e1000] PCI bus:dev:func=", ((u64)bus << 16) | ((u64)dev << 8) | func);
|
||||||
|
|
||||||
u32 cmd = pci_read(bus, dev, 0, PCI_COMMAND);
|
/* 检查 PCI class code 确认是网络设备 (class=0x02) */
|
||||||
cmd |= 0x0007u; /* IO Space | Memory Space | Bus Master */
|
u32 class_reg = pci_read(bus, dev, func, 0x08);
|
||||||
pci_write(bus, dev, 0, PCI_COMMAND, cmd);
|
u8 class_code = (u8)(class_reg >> 24);
|
||||||
raw_log("[e1000.raw] PCI command enabled\n");
|
if (class_code != 0x02) {
|
||||||
|
raw_log("[e1000] WARNING: PCI class is not Network\n");
|
||||||
|
}
|
||||||
|
|
||||||
u32 bar0 = pci_read(bus,dev,0,PCI_BAR0);
|
u32 cmd = pci_read(bus, dev, func, PCI_COMMAND);
|
||||||
|
cmd |= PCI_CMD_IO | PCI_CMD_MEM | PCI_CMD_BUSM;
|
||||||
|
pci_write(bus, dev, func, PCI_COMMAND, cmd);
|
||||||
|
raw_log("[e1000] PCI command enabled\n");
|
||||||
|
|
||||||
|
u32 bar0 = pci_read(bus, dev, func, PCI_BAR0);
|
||||||
if (bar0 & 1u) {
|
if (bar0 & 1u) {
|
||||||
raw_log("[e1000.raw] IO BAR unsupported\n");
|
raw_log("[e1000] IO BAR unsupported\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
g_bar0_phys = (u64)(bar0 & 0xFFFFFFF0ULL);
|
g_bar0_phys = (u64)(bar0 & 0xFFFFFFF0ULL);
|
||||||
if ((bar0 & 0x6u) == 0x4u) {
|
if ((bar0 & 0x6u) == 0x4u) {
|
||||||
u32 bar1 = pci_read(bus,dev,0,PCI_BAR1);
|
u32 bar1 = pci_read(bus, dev, func, PCI_BAR1);
|
||||||
g_bar0_phys |= ((u64)bar1 << 32);
|
g_bar0_phys |= ((u64)bar1 << 32);
|
||||||
}
|
}
|
||||||
raw_hex("[e1000.raw] BAR0 raw=", bar0);
|
raw_hex("[e1000] BAR0 phys=", g_bar0_phys);
|
||||||
raw_hex("[e1000.raw] BAR0 phys=", g_bar0_phys);
|
|
||||||
if (!g_bar0_phys) {
|
if (!g_bar0_phys) {
|
||||||
raw_log("[e1000.raw] BAR0 unavailable\n");
|
raw_log("[e1000] BAR0 unavailable\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
raw_log("[e1000.raw] BAR0 ok\n");
|
|
||||||
|
|
||||||
|
/* 真机 BAR0 可能在 4G 以上(如 I219),需 mm_map_mmio 检查 */
|
||||||
u64 hhdm = api->hhdm_offset;
|
u64 hhdm = api->hhdm_offset;
|
||||||
g_mmio = (volatile u32*)(uintptr_t)(hhdm + g_bar0_phys);
|
g_mmio = (volatile u32*)(uintptr_t)(hhdm + g_bar0_phys);
|
||||||
(void)g_mmio[E1000_STATUS/4];
|
/* 尝试读取 STATUS 寄存器验证映射是否有效 */
|
||||||
raw_log("[e1000.raw] MMIO status read ok\n");
|
u32 status_val = g_mmio[E1000_STATUS/4];
|
||||||
|
(void)status_val;
|
||||||
|
raw_log("[e1000] MMIO status read ok\n");
|
||||||
|
|
||||||
/* 软件复位,确保 RX/TX/RAL/RAH 从已知状态开始配置。 */
|
/* 软件复位,确保 RX/TX/RAL/RAH 从已知状态开始配置。 */
|
||||||
g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | (1u << 26);
|
g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | E1000_CTRL_RST;
|
||||||
/* 实机: 1秒 TSC 超时等待复位完成 */
|
/* 实机: 1秒 TSC 超时等待复位完成 */
|
||||||
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
|
||||||
while (dkm_rdtsc() < deadline) {
|
while (dkm_rdtsc() < deadline) {
|
||||||
if (!(g_mmio[E1000_CTRL/4] & (1u << 26))) break;
|
if (!(g_mmio[E1000_CTRL/4] & E1000_CTRL_RST)) break;
|
||||||
__asm__ volatile("pause");
|
__asm__ volatile("pause");
|
||||||
}
|
}
|
||||||
raw_log("[e1000.raw] reset done\n");
|
if (g_mmio[E1000_CTRL/4] & E1000_CTRL_RST) {
|
||||||
|
raw_log("[e1000] WARNING: reset did not complete\n");
|
||||||
|
} else {
|
||||||
|
raw_log("[e1000] reset done\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 等待 EEPROM 完成(实机复位后 EEPROM 状态机需要时间) */
|
||||||
|
dkm_delay_ms(1);
|
||||||
|
|
||||||
|
/* 读 MAC 地址:先尝试 RAL/RAH,空时走 EEPROM */
|
||||||
u32 ral = g_mmio[E1000_RAL/4];
|
u32 ral = g_mmio[E1000_RAL/4];
|
||||||
u32 rah = g_mmio[E1000_RAH/4];
|
u32 rah = g_mmio[E1000_RAH/4];
|
||||||
u8 mac[6];
|
u8 mac[6];
|
||||||
@@ -403,60 +727,102 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
mac[0]=(u8)(w0&0xff); mac[1]=(u8)(w0>>8);
|
mac[0]=(u8)(w0&0xff); mac[1]=(u8)(w0>>8);
|
||||||
mac[2]=(u8)(w1&0xff); mac[3]=(u8)(w1>>8);
|
mac[2]=(u8)(w1&0xff); mac[3]=(u8)(w1>>8);
|
||||||
mac[4]=(u8)(w2&0xff); mac[5]=(u8)(w2>>8);
|
mac[4]=(u8)(w2&0xff); mac[5]=(u8)(w2>>8);
|
||||||
|
raw_log("[e1000] MAC from EEPROM\n");
|
||||||
|
} else {
|
||||||
|
raw_log("[e1000] EEPROM read failed (all 0xffff)\n");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
raw_log("[e1000] MAC from RAL/RAH\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 仍为全 0 时使用与虚拟机配置一致的回退 MAC,保证收发地址匹配 */
|
/* 仍为全 0 或多播位错误时使用与虚拟机配置一致的回退 MAC */
|
||||||
if (mac[0]==0&&mac[1]==0&&mac[2]==0&&mac[3]==0&&mac[4]==0&&mac[5]==0) {
|
if ((mac[0]==0&&mac[1]==0&&mac[2]==0&&mac[3]==0&&mac[4]==0&&mac[5]==0) ||
|
||||||
|
(mac[0] & 1)) {
|
||||||
mac[0]=0x52; mac[1]=0x54; mac[2]=0x00; mac[3]=0x12; mac[4]=0x34; mac[5]=0x56;
|
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");
|
raw_log("[e1000] using fallback MAC 52:54:00:12:34:56\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
raw_mac("[e1000] MAC=", mac);
|
||||||
|
|
||||||
/* 写入 RAL/RAH 并置 AV(bit31) 使能接收该单播地址 */
|
/* 写入 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_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);
|
g_mmio[E1000_RAH/4] = (u32)mac[4] | ((u32)mac[5]<<8) | (1u<<31);
|
||||||
raw_log("[e1000.raw] MAC read ok\n");
|
dma_fence();
|
||||||
|
|
||||||
u32 irq_line = pci_read(bus,dev,0,PCI_IRQ_LINE) & 0xff;
|
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
|
||||||
|
raw_hex("[e1000] IRQ line=", irq_line);
|
||||||
|
|
||||||
|
/* 设置链路:FD + SLU,并允许自动协商 */
|
||||||
g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | E1000_CTRL_FD | E1000_CTRL_SLU;
|
g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | E1000_CTRL_FD | E1000_CTRL_SLU;
|
||||||
raw_log("[e1000.raw] link setup\n");
|
mmio_flush();
|
||||||
|
|
||||||
g_mmio[E1000_IMC/4] = 0xffffffffu;
|
g_mmio[E1000_IMC/4] = 0xffffffffu;
|
||||||
(void)g_mmio[E1000_ICR/4];
|
(void)g_mmio[E1000_ICR/4];
|
||||||
raw_log("[e1000.raw] interrupts masked\n");
|
raw_log("[e1000] interrupts masked\n");
|
||||||
|
|
||||||
|
/* 等待链路就绪(实机 PHY 自动协商可能需要 1-2 秒) */
|
||||||
|
int link_rc = e1000_wait_for_link();
|
||||||
|
if (link_rc == 0) {
|
||||||
|
raw_log("[e1000] link up after auto-negotiation\n");
|
||||||
|
} else {
|
||||||
|
raw_log("[e1000] link not yet up (will retry during operation)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 打印链路诊断 */
|
||||||
|
e1000_log_link_status();
|
||||||
|
|
||||||
int ring_rc = e1000_init_rings(api->dma);
|
int ring_rc = e1000_init_rings(api->dma);
|
||||||
if (ring_rc != 0) raw_log("[e1000.raw] ring init failed\n");
|
if (ring_rc != 0) {
|
||||||
|
raw_log("[e1000] ring init failed\n");
|
||||||
|
raw_hex("[e1000] ring_rc=", (u64)(i32)ring_rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 根据链路状态设置 netdev flags */
|
||||||
|
u32 netdev_flags = 0;
|
||||||
|
if (e1000_link_up()) netdev_flags |= DKM_NET_F_LINK_UP;
|
||||||
|
if (g_rings_ready) netdev_flags |= DKM_NET_F_TX_READY | DKM_NET_F_RX_READY;
|
||||||
|
|
||||||
if (api->net && api->net->register_device) {
|
if (api->net && api->net->register_device) {
|
||||||
struct dkm_net_device_desc netdev;
|
struct dkm_net_device_desc netdev;
|
||||||
|
zero_bytes(&netdev, sizeof(netdev));
|
||||||
netdev.name = "e1000";
|
netdev.name = "e1000";
|
||||||
for (int i=0;i<6;i++) netdev.mac[i] = mac[i];
|
for (int i=0;i<6;i++) netdev.mac[i] = mac[i];
|
||||||
netdev.flags = DKM_NET_F_LINK_UP;
|
netdev.flags = netdev_flags;
|
||||||
if (g_rings_ready) netdev.flags |= DKM_NET_F_TX_READY | DKM_NET_F_RX_READY;
|
|
||||||
netdev.ctx = 0;
|
netdev.ctx = 0;
|
||||||
netdev.tx = g_rings_ready ? e1000_tx : 0;
|
netdev.tx = g_rings_ready ? e1000_tx : 0;
|
||||||
netdev.rx_poll = g_rings_ready ? e1000_rx_poll : 0;
|
netdev.rx_poll = g_rings_ready ? e1000_rx_poll : 0;
|
||||||
int net_index = api->net->register_device(&netdev);
|
int net_index = api->net->register_device(&netdev);
|
||||||
(void)net_index;
|
raw_hex("[e1000] netdev registered, index=", (u64)(i32)net_index);
|
||||||
raw_log("[e1000.raw] netdev registered\n");
|
|
||||||
} else {
|
} else {
|
||||||
raw_log("[e1000.raw] net API unavailable\n");
|
raw_log("[e1000] net API unavailable\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
g_mmio[E1000_IMC/4] = 0xffffffffu;
|
g_mmio[E1000_IMC/4] = 0xffffffffu;
|
||||||
|
|
||||||
if (api->irq_register && irq_line < 16) {
|
if (api->irq_register && irq_line < 16 && irq_line > 0) {
|
||||||
api->irq_register((u8)irq_line, (void*)(uintptr_t)irq_handler);
|
api->irq_register((u8)irq_line, (void*)(uintptr_t)irq_handler);
|
||||||
raw_log("[e1000.raw] IRQ registered\n");
|
raw_log("[e1000] IRQ registered\n");
|
||||||
|
} else {
|
||||||
|
raw_log("[e1000] IRQ not registered (polling mode)\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
raw_log("[e1000.raw] driver ready\n");
|
/* 最终诊断快照 */
|
||||||
|
e1000_log_link_status();
|
||||||
|
raw_log("[e1000] driver ready\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((visibility("default")))
|
__attribute__((visibility("default")))
|
||||||
int driver_exit(struct dkm_driver_handle *handle) {
|
int driver_exit(struct dkm_driver_handle *handle) {
|
||||||
(void)handle; return 0;
|
(void)handle;
|
||||||
|
/* 关闭 RX/TX,释放 rings */
|
||||||
|
if (g_mmio) {
|
||||||
|
g_mmio[E1000_RCTL/4] = 0;
|
||||||
|
g_mmio[E1000_TCTL/4] = 0;
|
||||||
|
g_mmio[E1000_IMC/4] = 0xffffffffu;
|
||||||
|
mmio_flush();
|
||||||
|
}
|
||||||
|
g_rings_ready = 0;
|
||||||
|
g_mmio = 0;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-52
@@ -6,60 +6,15 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "../dkm_shared.h"
|
||||||
|
#include "../dkm_instr.h"
|
||||||
|
|
||||||
|
DKM_STAT_DECL(fat0_reads);
|
||||||
|
DKM_STAT_DECL(fat0_fat_walks);
|
||||||
|
|
||||||
/* ---------------------------------------------------------------
|
/* ---------------------------------------------------------------
|
||||||
* DKM ABI types
|
* FAT32-specific types (DKM ABI types come from dkm_shared.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_block_api {
|
|
||||||
int (*register_device)(const void *desc);
|
|
||||||
u32 (*device_count)(void);
|
|
||||||
int (*read)(u32 index, u64 lba, u32 count, void *buffer);
|
|
||||||
int (*write)(u32 index, u64 lba, u32 count, const void *buffer);
|
|
||||||
u64 (*sector_size)(u32 index);
|
|
||||||
const char *(*device_name)(u32 index);
|
|
||||||
int (*set_write_fn)(u32 index, int (*fn)(void *ctx, u64 lba, u32 count, const void *buf));
|
|
||||||
};
|
|
||||||
|
|
||||||
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 {
|
struct limine_file {
|
||||||
u64 revision; void *address; u64 size; char *path; char *cmdline; u8 _pad[56];
|
u64 revision; void *address; u64 size; char *path; char *cmdline; u8 _pad[56];
|
||||||
@@ -237,6 +192,7 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
(void)handle;
|
(void)handle;
|
||||||
if (!api||!api->log) return -1;
|
if (!api||!api->log) return -1;
|
||||||
g_log=api->log;
|
g_log=api->log;
|
||||||
|
dkm_instr_init(api);
|
||||||
|
|
||||||
u64 disk_size = 0;
|
u64 disk_size = 0;
|
||||||
const u8 *disk = fat32_select_disk_image(api, &disk_size);
|
const u8 *disk = fat32_select_disk_image(api, &disk_size);
|
||||||
@@ -279,6 +235,7 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
|
|
||||||
/* try to read README.TXT content */
|
/* try to read README.TXT content */
|
||||||
if (strneq(dir[e].name,"README TXT",11)) {
|
if (strneq(dir[e].name,"README TXT",11)) {
|
||||||
|
DKM_STAT_INC(fat0_reads);
|
||||||
u32 f_clus = read_u16_le((const u8*)&dir[e].cluster_low);
|
u32 f_clus = read_u16_le((const u8*)&dir[e].cluster_low);
|
||||||
u32 f_off = data_start + (f_clus - 2)*cluster_size;
|
u32 f_off = data_start + (f_clus - 2)*cluster_size;
|
||||||
u32 f_size = dir[e].file_size;
|
u32 f_size = dir[e].file_size;
|
||||||
@@ -297,6 +254,7 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
/* read next cluster from FAT */
|
/* read next cluster from FAT */
|
||||||
u32 fat_ent_off = fat_start + clus * 4;
|
u32 fat_ent_off = fat_start + clus * 4;
|
||||||
clus = read_u32_le(disk + fat_ent_off) & 0x0FFFFFFF;
|
clus = read_u32_le(disk + fat_ent_off) & 0x0FFFFFFF;
|
||||||
|
DKM_STAT_INC(fat0_fat_walks);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_log->info("[fat32] driver ready");
|
g_log->info("[fat32] driver ready");
|
||||||
|
|||||||
+1494
-207
@@ -1,76 +1,36 @@
|
|||||||
/* DKM NVMe Driver — PCI NVMe discovery and controller register enumeration
|
/* DKM NVMe Driver — 高位 BAR MMIO 映射 + admin/IO queue + block provider
|
||||||
|
*
|
||||||
* Stage 1 optional storage driver, depends on "pci" and "irq", provides "block".
|
* 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.
|
* 实现要点:
|
||||||
|
* 1. 64-bit BAR 常位于 4G 以上(QEMU/OVMF 把 NVMe BAR0 放到 ~48GiB),
|
||||||
|
* HHDM 直映射不保证覆盖。访问前先用 kernel_api.mmio->is_mapped 做
|
||||||
|
* 四级页表 walk 核实;未映射则 map_mmio 原地补建 4KiB UC 页。
|
||||||
|
* 2. 完成模型: 单 outstanding 命令 + CQE phase bit。B7 阶段3 起 IO 队列
|
||||||
|
* 默认尝试 MSI/MSI-X 中断驱动(FUCK [drivers] nvme_msi, 默认 1 自动):
|
||||||
|
* 先 MSI cap 编程, 缺则 MSI-X (QEMU nvme 只暴露 MSI-X) — 表项
|
||||||
|
* addr=0xFEE00000|dest<<12, data=动态向量, entry unmask + cap Enable;
|
||||||
|
* Create IOCQ IEN=1/IV=0 + INTMC 解除 IV0; handler 摘 CQE 进完成槽、
|
||||||
|
* 推进 head、写 CQ doorbell, 自写 LAPIC EOI 并返回 1 (MSI 直投 LAPIC,
|
||||||
|
* 与 apic_route 开关无关, 不依赖阶段2 的 EOI 钩子注册状态)。
|
||||||
|
* 提交侧统一等待: 先查完成槽(中断路径), 再在 irqsave 临界区内手动
|
||||||
|
* 摘取(自适应轮询路径) — IF=0 (DSK 阶段) 或 MSI 未投递时零感知降级,
|
||||||
|
* 行为与旧版纯轮询一致。计数: g_msi_irq_count / g_msi_slot_hits /
|
||||||
|
* g_msi_poll_harvests / g_msi_timeouts, init 末尾与压测后打点。
|
||||||
|
* 3. 数据面: 32 页 (128KiB) 物理连续 DMA bounce buffer + 单页 PRP list。
|
||||||
|
* PRP 布局按传输页数分档: 1 页单 PRP1; 2 页 PRP2 直连第二页;
|
||||||
|
* >=3 页 PRP2 指向 PRP list 页 (512 项 u64, 上限 513 页 = 2052KiB)。
|
||||||
|
* chunk 上限 = min(buffer 页数, 2^MDTS 页, NLB 16-bit)。
|
||||||
|
* 读写一律 bounce: 调用方缓冲是任意虚拟地址 (DSK BSS / vmm dbuf),
|
||||||
|
* 物理底账未知, 统一经 DMA 缓冲中转, 一次命令读满 chunk 再 memcpy。
|
||||||
|
* WRITE 路径 memcpy 后 wbinvd (沿用 ahci BUG-P0-2 防陈旧 cache 先例),
|
||||||
|
* 每次写调用末尾随一个 FLUSH。admin/IO queue 各 64 项, 均每队列一页。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include "../dkm_shared.h"
|
||||||
|
#include "../dkm_instr.h"
|
||||||
|
|
||||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
DKM_STAT_DECL(nvm0_pci_found);
|
||||||
#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_depends[] = { "pci", "irq" };
|
||||||
static const char *const g_provides[] = { "block" };
|
static const char *const g_provides[] = { "block" };
|
||||||
@@ -81,7 +41,7 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
.abi_version = DKM_ABI_VERSION,
|
.abi_version = DKM_ABI_VERSION,
|
||||||
.desc_size = sizeof(struct dkm_driver_desc),
|
.desc_size = sizeof(struct dkm_driver_desc),
|
||||||
.name = "nvme",
|
.name = "nvme",
|
||||||
.version = "0.1.0",
|
.version = "0.4.0",
|
||||||
.vendor = "Deshab",
|
.vendor = "Deshab",
|
||||||
.driver_class = 6, /* DKM_CLASS_STORAGE */
|
.driver_class = 6, /* DKM_CLASS_STORAGE */
|
||||||
.stage = 1,
|
.stage = 1,
|
||||||
@@ -97,78 +57,87 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
.reserved1 = 0
|
.reserved1 = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PCI_ADDR 0xCF8
|
/* ---- NVMe 控制器寄存器 ---- */
|
||||||
#define PCI_DATA 0xCFC
|
#define NVME_CAP 0x00 /* u64 capabilities */
|
||||||
#define PCI_VENDOR_ID 0x00
|
#define NVME_VS 0x08 /* u32 version */
|
||||||
#define PCI_COMMAND 0x04
|
#define NVME_INTMS 0x0C /* interrupt mask set */
|
||||||
#define PCI_CLASS_REG 0x08
|
#define NVME_INTMC 0x10 /* interrupt mask clear */
|
||||||
#define PCI_HEADER 0x0E
|
#define NVME_CC 0x14 /* controller configuration */
|
||||||
#define PCI_BAR0 0x10
|
#define NVME_CSTS 0x1C /* controller status */
|
||||||
#define PCI_BAR1 0x14
|
#define NVME_AQA 0x24 /* admin queue attributes */
|
||||||
#define PCI_IRQ_LINE 0x3C
|
#define NVME_ASQ 0x28 /* u64 admin SQ base */
|
||||||
|
#define NVME_ACQ 0x30 /* u64 admin CQ base */
|
||||||
#define PCI_CMD_MEM (1u << 1)
|
#define NVME_DB_BASE 0x1000 /* doorbell 起始偏移 */
|
||||||
#define PCI_CMD_BUSM (1u << 2)
|
|
||||||
|
|
||||||
#define NVME_CLASS_STORAGE 0x01
|
#define NVME_CLASS_STORAGE 0x01
|
||||||
#define NVME_SUBCLASS_NVM 0x08
|
#define NVME_SUBCLASS_NVM 0x08
|
||||||
#define NVME_PROGIF_NVME 0x02
|
#define NVME_PROGIF_NVME 0x02
|
||||||
|
|
||||||
#define NVME_CAP 0x00
|
/* admin 命令 opcode */
|
||||||
#define NVME_VS 0x08
|
#define NVME_ADM_DELETE_IOSQ 0x00
|
||||||
#define NVME_INTMS 0x0C
|
#define NVME_ADM_CREATE_IOSQ 0x01
|
||||||
#define NVME_INTMC 0x10
|
#define NVME_ADM_DELETE_IOCQ 0x04
|
||||||
#define NVME_CC 0x14
|
#define NVME_ADM_CREATE_IOCQ 0x05
|
||||||
#define NVME_CSTS 0x1C
|
#define NVME_ADM_IDENTIFY 0x06
|
||||||
#define NVME_NSSR 0x20
|
/* IO 命令 opcode(NVM 命令集: 00h=Flush, 01h=Write, 02h=Read) */
|
||||||
#define NVME_AQA 0x24
|
#define NVME_IO_FLUSH 0x00
|
||||||
#define NVME_ASQ 0x28
|
#define NVME_IO_READ 0x02
|
||||||
#define NVME_ACQ 0x30
|
#define NVME_IO_WRITE 0x01
|
||||||
#define NVME_CMBLOC 0x38
|
|
||||||
#define NVME_CMBSZ 0x3C
|
#define ADMIN_QSIZE 64 /* entry 数(SQ 64*64B=4K 一页, CQ 64*16B=1K) */
|
||||||
|
#define IO_QSIZE 64
|
||||||
|
#define NVME_IDENT_BYTES 4096 /* identify 数据页 */
|
||||||
|
#define NVME_DATA_PAGES 32u /* IO bounce: 128KiB = 512B×256 扇区,
|
||||||
|
* 正好覆盖 DSK FAT32 的 256 扇区批量读 */
|
||||||
|
#define NVME_PRP_LIST_ENTRIES 512u /* 单 list 页 4KiB / 8B = 512 项 */
|
||||||
|
|
||||||
static const struct dkm_log_api *g_log;
|
static const struct dkm_log_api *g_log;
|
||||||
static volatile u8 *g_regs;
|
static volatile u8 *g_regs; /* MMIO 基址(虚拟) */
|
||||||
|
static u32 g_db_stride; /* doorbell 步长 = 4 << CAP.DSTRD */
|
||||||
|
static u64 g_timeout_ms; /* CAP.TO * 500ms */
|
||||||
|
|
||||||
static __inline__ void outl(u16 port, u32 value) {
|
static struct dkm_dma_buffer g_asq; /* admin SQ */
|
||||||
__asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port));
|
static struct dkm_dma_buffer g_acq; /* admin CQ */
|
||||||
}
|
static struct dkm_dma_buffer g_ident; /* identify 数据页 */
|
||||||
|
static struct dkm_dma_buffer g_data; /* IO 读写 bounce (NVME_DATA_PAGES 页连续) */
|
||||||
|
static struct dkm_dma_buffer g_prp; /* PRP list 页 */
|
||||||
|
static struct dkm_dma_buffer g_iosq; /* IO SQ (qid=1) */
|
||||||
|
static struct dkm_dma_buffer g_iocq; /* IO CQ (qid=1) */
|
||||||
|
|
||||||
static __inline__ u32 inl(u16 port) {
|
static u32 g_asq_tail;
|
||||||
u32 value;
|
static u32 g_acq_head;
|
||||||
__asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port));
|
static u32 g_acq_phase;
|
||||||
return value;
|
static u32 g_iosq_tail;
|
||||||
}
|
static u32 g_iocq_head;
|
||||||
|
static u32 g_iocq_phase;
|
||||||
|
static u16 g_cid;
|
||||||
|
|
||||||
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
static u64 g_sector_size;
|
||||||
u32 addr = (1u << 31)
|
static u64 g_sector_count;
|
||||||
| ((u32)bus << 16)
|
static u32 g_mdts; /* Identify Controller byte 77 */
|
||||||
| ((u32)dev << 11)
|
static u32 g_chunk_max_sectors; /* 单命令扇区上限 (buffer/MDTS/NLB 取小) */
|
||||||
| ((u32)func << 8)
|
static int g_io_ready;
|
||||||
| ((u32)reg & 0xFC);
|
static int g_test_disk; /* LBA0 带 DESHABNVME0 签名的测试盘 */
|
||||||
outl(PCI_ADDR, addr);
|
|
||||||
return inl(PCI_DATA);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
|
/* ---- B7 阶段3: MSI 状态 ---- */
|
||||||
u32 addr = (1u << 31)
|
static int g_cfg_nvme_msi = 1; /* FUCK [drivers] nvme_msi, 默认 1 自动尝试 */
|
||||||
| ((u32)bus << 16)
|
static int g_msi_enabled; /* MSI/MSI-X 编程成功且 handler 已注册 */
|
||||||
| ((u32)dev << 11)
|
static int g_msix_mode; /* 1=MSI-X 表项模式, 0=MSI cap 模式 */
|
||||||
| ((u32)func << 8)
|
static int g_msi_vector = -1; /* 动态分配的 IDT 向量 (0x40-0xDF 池) */
|
||||||
| ((u32)reg & 0xFC);
|
/* 完成槽: 单 outstanding 前提下, handler 是唯一写者、提交侧是唯一读者,
|
||||||
outl(PCI_ADDR, addr);
|
* 且下一命令提交前不可能再有新 CQE, 故读-清无需互斥; valid 最后写/最先读,
|
||||||
outl(PCI_DATA, value);
|
* x86 TSO store-store / load-load 天然有序, nvme_mb 只挡编译器重排。 */
|
||||||
}
|
static volatile u32 g_slot_valid;
|
||||||
|
static volatile u32 g_slot_status; /* sc | (sct << 8), 0 = 成功 */
|
||||||
|
static volatile u32 g_slot_dw0; /* CQE dw0 (命令特定返回) */
|
||||||
|
static volatile u32 g_slot_cid; /* CQE dw3 低 16 位 CID, 提交侧比对防错位 */
|
||||||
|
static u64 g_msi_irq_count; /* MSI handler 进入次数 (投递证明) */
|
||||||
|
static u64 g_msi_slot_hits; /* 提交侧经完成槽完成的命令数 (中断路径) */
|
||||||
|
static u64 g_msi_poll_harvests; /* 提交侧 irqsave 手动摘取数 (轮询路径) */
|
||||||
|
static u64 g_msi_timeouts; /* 完成超时计数 (真故障, 非降级) */
|
||||||
|
static u64 g_msi_cid_mismatch; /* 摘到 CID 与提交 cid 不符的 CQE 计数 (队列错位警报) */
|
||||||
|
|
||||||
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 void log_hex(const char *prefix, u64 value) {
|
||||||
static const char hex[] = "0123456789abcdef";
|
static const char hex[] = "0123456789abcdef";
|
||||||
@@ -184,19 +153,126 @@ static void log_hex(const char *prefix, u64 value) {
|
|||||||
g_log->info(buf);
|
g_log->info(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 打印有界 ASCII 串(identify 数据中的 SN/MN 等定长字段) */
|
||||||
|
static void log_str(const char *prefix, const u8 *src, u32 len) {
|
||||||
|
char buf[48];
|
||||||
|
if (len > 40) len = 40;
|
||||||
|
for (u32 i = 0; i < len; i++) {
|
||||||
|
char c = (char)src[i];
|
||||||
|
buf[i] = (c >= 32 && c < 127) ? c : ' ';
|
||||||
|
}
|
||||||
|
buf[len] = 0;
|
||||||
|
g_log->info(prefix);
|
||||||
|
g_log->info(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nvme_zero(void *ptr, u32 len) {
|
||||||
|
u8 *p = (u8 *)ptr;
|
||||||
|
for (u32 i = 0; i < len; i++) p[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __inline__ void nvme_mb(void) {
|
||||||
|
/* x86 TSO 保证 store-store 顺序, 这里只需阻断编译器重排 */
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 十进制打印 (MSI 计数器打点用, 比 hex 直观) */
|
||||||
|
static void log_dec(const char *prefix, u64 val) {
|
||||||
|
char buf[24];
|
||||||
|
u32 pos = 0;
|
||||||
|
if (!val) {
|
||||||
|
buf[pos++] = '0';
|
||||||
|
} else {
|
||||||
|
char tmp[20];
|
||||||
|
u32 n = 0;
|
||||||
|
while (val) { tmp[n++] = (char)('0' + (val % 10)); val /= 10; }
|
||||||
|
while (n) buf[pos++] = tmp[--n];
|
||||||
|
}
|
||||||
|
buf[pos] = 0;
|
||||||
|
g_log->info(prefix);
|
||||||
|
g_log->info(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 u32 str_len(const char *s) {
|
||||||
|
u32 n = 0;
|
||||||
|
while (s[n]) n++;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 在 NUL 结尾字符串中查找子串 (boot module cmdline 匹配用) */
|
||||||
|
static int str_contains(const char *hay, const char *needle) {
|
||||||
|
u32 nlen = str_len(needle);
|
||||||
|
if (!nlen) return 1;
|
||||||
|
for (u32 i = 0; hay[i]; i++) {
|
||||||
|
u32 j = 0;
|
||||||
|
while (j < nlen && hay[i + j] && hay[i + j] == needle[j]) j++;
|
||||||
|
if (j == nlen) return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nvme_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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* irqsave 临界区: 提交侧手动摘取 CQE 时屏蔽中断, 与 MSI handler 互斥。
|
||||||
|
* pushf/popf 配对恢复 IF — DSK 阶段 IF=0, 绝不能无条件 sti。 */
|
||||||
|
static __inline__ u64 nvme_irqsave(void) {
|
||||||
|
u64 flags;
|
||||||
|
__asm__ volatile("pushfq; popq %0; cli" : "=r"(flags) :: "memory");
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __inline__ void nvme_irqrestore(u64 flags) {
|
||||||
|
__asm__ volatile("pushq %0; popfq" :: "r"(flags) : "memory", "cc");
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 mmio_read32(u32 off) {
|
||||||
|
return *(volatile u32 *)(void *)(g_regs + off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mmio_write32(u32 off, u32 value) {
|
||||||
|
*(volatile u32 *)(void *)(g_regs + off) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 mmio_read64(u32 off) {
|
||||||
|
u32 lo = mmio_read32(off);
|
||||||
|
u32 hi = mmio_read32(off + 4);
|
||||||
|
return ((u64)hi << 32) | lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mmio_write64(u32 off, u64 value) {
|
||||||
|
mmio_write32(off, (u32)(value & 0xffffffffu));
|
||||||
|
mmio_write32(off + 4, (u32)(value >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 sq_tail_db(u16 qid) { return NVME_DB_BASE + (2u * qid) * g_db_stride; }
|
||||||
|
static u32 cq_head_db(u16 qid) { return NVME_DB_BASE + (2u * qid + 1u) * g_db_stride; }
|
||||||
|
|
||||||
|
/* ---- PCI ---- */
|
||||||
|
|
||||||
static int nvme_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
static int nvme_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
||||||
for (u8 bus = 0; bus < 16; bus++) {
|
for (u8 bus = 0; bus < 16; bus++) {
|
||||||
for (u8 dev = 0; dev < 32; dev++) {
|
for (u8 dev = 0; dev < 32; dev++) {
|
||||||
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
|
u32 vd = dkm_pci_read(bus, dev, 0, PCI_VENDOR_ID);
|
||||||
if ((vd & 0xffff) == 0xffff) continue;
|
if ((vd & 0xffff) == 0xffff) continue;
|
||||||
|
|
||||||
u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16);
|
u8 header = (u8)(dkm_pci_read(bus, dev, 0, PCI_HEADER) >> 16);
|
||||||
u8 func_count = (header & 0x80) ? 8 : 1;
|
u8 func_count = (header & 0x80) ? 8 : 1;
|
||||||
for (u8 func = 0; func < func_count; func++) {
|
for (u8 func = 0; func < func_count; func++) {
|
||||||
u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID);
|
u32 vd2 = dkm_pci_read(bus, dev, func, PCI_VENDOR_ID);
|
||||||
if ((vd2 & 0xffff) == 0xffff) continue;
|
if ((vd2 & 0xffff) == 0xffff) continue;
|
||||||
|
|
||||||
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
|
u32 class_reg = dkm_pci_read(bus, dev, func, 0x08);
|
||||||
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
|
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
|
||||||
u8 subclass = (u8)((class_reg >> 16) & 0xff);
|
u8 subclass = (u8)((class_reg >> 16) & 0xff);
|
||||||
u8 class_code = (u8)((class_reg >> 24) & 0xff);
|
u8 class_code = (u8)((class_reg >> 24) & 0xff);
|
||||||
@@ -215,37 +291,1070 @@ static int nvme_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void log_nvme_version(u32 vs) {
|
/* ---- FUCK 配置自解析 (与 apic.c 同一范式, 不依赖 UTSM ini_parser) ----
|
||||||
log_hex("[nvme] version major=", (vs >> 16) & 0xffff);
|
* UTSM 经 Limine boot module (cmdline="fuck:config") 预加载 FUCK;
|
||||||
log_hex("[nvme] version minor=", (vs >> 8) & 0xff);
|
* kernel_api.boot_modules_response 对所有 DKM 驱动可见。
|
||||||
log_hex("[nvme] version tertiary=", vs & 0xff);
|
* 此处只找 [drivers] 分区下的 nvme_msi 键; 缺失文件/键一律默认 1
|
||||||
|
* (自动尝试 MSI, 失败回轮询), 与无配置时行为一致。
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Limine boot module 最小类型 (与 apic.c / bootfs.c / UTSM limine.h 布局一致) */
|
||||||
|
struct limine_file_min {
|
||||||
|
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_min {
|
||||||
|
u64 revision;
|
||||||
|
u64 module_count;
|
||||||
|
struct limine_file_min **modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 解析值 token: 支持 0/1/true/false/on/off/yes/no 及十进制整数 */
|
||||||
|
static int parse_scalar(const char *line, u64 start, u64 line_len, int defval) {
|
||||||
|
u64 i = start;
|
||||||
|
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
|
||||||
|
u64 tok_start = i;
|
||||||
|
while (i < line_len && line[i] != ' ' && line[i] != '\t' &&
|
||||||
|
line[i] != '#' && line[i] != ';' && line[i] != '\r') i++;
|
||||||
|
u64 tok_len = i - tok_start;
|
||||||
|
const char *tok = line + tok_start;
|
||||||
|
if (!tok_len) return defval;
|
||||||
|
if (tok_len == 1 && tok[0] == '1') return 1;
|
||||||
|
if (tok_len == 1 && tok[0] == '0') return 0;
|
||||||
|
if (tok_len == 4 && memeq(tok, "true", 4)) return 1;
|
||||||
|
if (tok_len == 5 && memeq(tok, "false", 5)) return 0;
|
||||||
|
if (tok_len == 2 && memeq(tok, "on", 2)) return 1;
|
||||||
|
if (tok_len == 3 && memeq(tok, "off", 3)) return 0;
|
||||||
|
if (tok_len == 3 && memeq(tok, "yes", 3)) return 1;
|
||||||
|
if (tok_len == 2 && memeq(tok, "no", 2)) return 0;
|
||||||
|
int neg = 0;
|
||||||
|
u64 j = 0;
|
||||||
|
if (tok[0] == '-') { neg = 1; j = 1; }
|
||||||
|
int val = 0;
|
||||||
|
int any = 0;
|
||||||
|
for (; j < tok_len; j++) {
|
||||||
|
if (tok[j] < '0' || tok[j] > '9') return defval;
|
||||||
|
val = val * 10 + (tok[j] - '0');
|
||||||
|
any = 1;
|
||||||
|
}
|
||||||
|
if (!any) return defval;
|
||||||
|
return neg ? -val : val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void log_cap_fields(u64 cap) {
|
static int fuck_get_int(const char *text, u64 size,
|
||||||
log_hex("[nvme] CAP.MQES=", cap & 0xffff);
|
const char *section, const char *key, int defval) {
|
||||||
log_hex("[nvme] CAP.CQR=", (cap >> 16) & 1);
|
u64 pos = 0;
|
||||||
log_hex("[nvme] CAP.AMS=", (cap >> 17) & 3);
|
int in_section = 0;
|
||||||
log_hex("[nvme] CAP.TO=", (cap >> 24) & 0xff);
|
u32 slen = str_len(section);
|
||||||
log_hex("[nvme] CAP.DSTRD=", (cap >> 32) & 0xf);
|
u32 klen = str_len(key);
|
||||||
log_hex("[nvme] CAP.NSSRS=", (cap >> 36) & 1);
|
|
||||||
log_hex("[nvme] CAP.CSS=", (cap >> 37) & 0xff);
|
while (pos < size) {
|
||||||
log_hex("[nvme] CAP.MPSMIN=", (cap >> 48) & 0xf);
|
u64 end = pos;
|
||||||
log_hex("[nvme] CAP.MPSMAX=", (cap >> 52) & 0xf);
|
while (end < size && text[end] != '\n') end++;
|
||||||
|
u64 line_len = end - pos;
|
||||||
|
if (line_len > 0 && text[pos + line_len - 1] == '\r') line_len--;
|
||||||
|
const char *line = text + pos;
|
||||||
|
|
||||||
|
u64 i = 0;
|
||||||
|
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
|
||||||
|
if (i < line_len) {
|
||||||
|
char c = line[i];
|
||||||
|
if (c == '#' || c == ';') {
|
||||||
|
/* 注释行, 跳过 */
|
||||||
|
} else if (c == '[') {
|
||||||
|
in_section = 0;
|
||||||
|
if (line_len >= (u64)(i + 1 + slen + 1) &&
|
||||||
|
memeq(line + i + 1, section, slen) &&
|
||||||
|
line[i + 1 + slen] == ']') {
|
||||||
|
in_section = 1;
|
||||||
|
}
|
||||||
|
} else if (in_section) {
|
||||||
|
if (line_len >= (u64)(i + klen) &&
|
||||||
|
memeq(line + i, key, klen)) {
|
||||||
|
u64 j = i + klen;
|
||||||
|
while (j < line_len && (line[j] == ' ' || line[j] == '\t')) j++;
|
||||||
|
if (j < line_len && line[j] == '=') {
|
||||||
|
return parse_scalar(line, j + 1, line_len, defval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos = end + 1;
|
||||||
|
}
|
||||||
|
return defval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nvme_load_config(const struct dkm_kernel_api *api) {
|
||||||
|
/* 编译期默认: 自动尝试 MSI (任何前提缺失都会回退纯轮询, 零回归) */
|
||||||
|
g_cfg_nvme_msi = 1;
|
||||||
|
|
||||||
|
const struct limine_module_response_min *resp =
|
||||||
|
(const struct limine_module_response_min *)api->boot_modules_response;
|
||||||
|
if (!resp || !resp->modules) {
|
||||||
|
g_log->warn("[nvme] no boot modules; FUCK defaults in effect");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u64 i = 0; i < resp->module_count; i++) {
|
||||||
|
struct limine_file_min *f = resp->modules[i];
|
||||||
|
if (!f || !f->cmdline || !f->address || !f->size) continue;
|
||||||
|
if (!str_contains(f->cmdline, "fuck:config")) continue;
|
||||||
|
|
||||||
|
g_cfg_nvme_msi = fuck_get_int((const char *)f->address, f->size,
|
||||||
|
"drivers", "nvme_msi", 1);
|
||||||
|
g_log->info("[nvme] FUCK config module found");
|
||||||
|
log_dec("[nvme] cfg nvme_msi=", (u64)g_cfg_nvme_msi);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_log->warn("[nvme] FUCK module (fuck:config) not found; defaults in effect");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- CSTS 等待 ---- */
|
||||||
|
|
||||||
|
static int nvme_wait_csts(u32 mask, u32 expect, u64 timeout_ms) {
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * timeout_ms;
|
||||||
|
for (;;) {
|
||||||
|
u32 csts = mmio_read32(NVME_CSTS);
|
||||||
|
if (csts & (1u << 1)) { /* CFS: controller fatal status */
|
||||||
|
g_log->error("[nvme] CSTS.CFS set");
|
||||||
|
log_hex("[nvme] CSTS=", csts);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
if ((csts & mask) == expect) return 0;
|
||||||
|
if (dkm_rdtsc() > deadline) {
|
||||||
|
g_log->error("[nvme] CSTS wait timeout");
|
||||||
|
log_hex("[nvme] CSTS=", csts);
|
||||||
|
log_hex("[nvme] wait mask=", mask);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 前向声明: nvme_dump_regs 在 nvme_submit 中使用 */
|
||||||
|
static void nvme_dump_regs(const char *ctx);
|
||||||
|
|
||||||
|
/* ---- B7 阶段3: IO CQ 完成摘取 (MSI handler 与提交侧轮询共用) ----
|
||||||
|
* 返回 1 = 摘到一枚 CQE; 0 = 无新完成。
|
||||||
|
* out_cid 返回 CQE dw3 低 16 位的命令 ID, 供调用方与提交 cid 比对 —
|
||||||
|
* 单 outstanding 模型下 CQ 任何时刻只应有当前命令的一枚 CQE, CID 不匹配
|
||||||
|
* 即队列状态错位的硬证据, 必须显式报错而非静默当作当前命令完成
|
||||||
|
* (route=0 曾观测 stress readback MISMATCH @0x9000, 疑 CQE 提前消费)。
|
||||||
|
* 并发模型: 单生产者(控制器 DMA 写 CQ) + 单逻辑消费者, 消费者有两个入口 ——
|
||||||
|
* 1. MSI handler (IRQ 上下文): 同一 vector 在 EOI 前不会被 LAPIC 再次
|
||||||
|
* 投递, handler 对同向量天然不可重入, 无需自锁;
|
||||||
|
* 2. 提交侧统一等待 (主上下文): 必须在 nvme_irqsave 临界区内调用,
|
||||||
|
* 屏蔽中断期间 handler 不可能插入, 消除 "phase 判定后、head 推进前
|
||||||
|
* 被 handler 抢摘同一 CQE" 的双消费竞态。
|
||||||
|
* head 推进 + CQ doorbell 只在此函数内发生, 是两个入口的唯一汇合点。
|
||||||
|
*/
|
||||||
|
static int nvme_iocq_harvest(u32 *out_status, u32 *out_dw0, u32 *out_cid) {
|
||||||
|
volatile u32 *cq = (volatile u32 *)g_iocq.virt;
|
||||||
|
u32 dw3 = cq[g_iocq_head * 4 + 3];
|
||||||
|
if (((dw3 >> 16) & 1u) != g_iocq_phase) return 0;
|
||||||
|
|
||||||
|
if (out_dw0) *out_dw0 = cq[g_iocq_head * 4 + 0];
|
||||||
|
if (out_cid) *out_cid = dw3 & 0xffffu;
|
||||||
|
u32 sc = (dw3 >> 17) & 0xffu;
|
||||||
|
u32 sct = (dw3 >> 25) & 0x7u;
|
||||||
|
/* SC 与 SCT 联合编码 (与 nvme_submit 经典路径同一约定):
|
||||||
|
* SCT!=0 且 SC==0 时只返回 sc 会被误判成功。 */
|
||||||
|
*out_status = sc | (sct << 8);
|
||||||
|
|
||||||
|
g_iocq_head = (g_iocq_head + 1) % IO_QSIZE;
|
||||||
|
if (g_iocq_head == 0) g_iocq_phase ^= 1u;
|
||||||
|
nvme_mb(); /* head 先于 doorbell 可见 */
|
||||||
|
mmio_write32(cq_head_db(1), g_iocq_head);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MSI 中断 handler: 摘 CQE 进完成槽。
|
||||||
|
* 返回 1 = 自负 LAPIC EOI (抑制 idt_handler 的钩子补 EOI)。
|
||||||
|
* 为何自写 EOI 而不依赖钩子: MSI 是设备向 LAPIC 直投的消息中断,
|
||||||
|
* 投递路径不经过 IOAPIC/PIC, 与 apic_route 开关无关; 但 EOI 钩子只在
|
||||||
|
* route=1 时注册, route=0 下钩子为 NULL 会漏写 EOI — LAPIC ISR 位
|
||||||
|
* 残留将永久阻塞同优先级后续中断, MSI 静默退化为 one-shot。
|
||||||
|
* handler 内直写 LAPIC EOI 寄存器后返回 1, 两种路由模式下行为一致。
|
||||||
|
* LAPIC 未软件使能时该写为无害 no-op (此时 MSI 本就不会投递)。
|
||||||
|
* 地址取 x86 默认 LAPIC base 0xFEE00000 + EOI 偏移 0xB0 (QEMU/MADT
|
||||||
|
* 一致; SAS-R0 恒等映射覆盖 4G 以下, apic.drv 已实证可直访)。
|
||||||
|
* 时序说明: 即使提交侧轮询抢先摘走 CQE, 控制器中断信号早已发出,
|
||||||
|
* 本次 handler 仍会执行并计入 g_msi_irq_count — 投递证据不受竞态影响。 */
|
||||||
|
static int nvme_msi_handler(u8 vector) {
|
||||||
|
(void)vector;
|
||||||
|
g_msi_irq_count++;
|
||||||
|
u32 st = 0, dw0 = 0, cid = 0;
|
||||||
|
if (nvme_iocq_harvest(&st, &dw0, &cid)) {
|
||||||
|
g_slot_status = st;
|
||||||
|
g_slot_dw0 = dw0;
|
||||||
|
g_slot_cid = cid;
|
||||||
|
nvme_mb(); /* payload 先于 valid 对提交侧可见 */
|
||||||
|
g_slot_valid = 1;
|
||||||
|
}
|
||||||
|
*(volatile u32 *)(uintptr_t)0xFEE000B0u = 0; /* LAPIC EOI */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IO 命令统一完成等待 (MSI 模式): 完成槽(中断路径)优先, irqsave 手动摘取
|
||||||
|
* (自适应轮询路径)兜底。MSI 健康时 handler 在 CQE 落盘瞬间填槽; MSI 未投递
|
||||||
|
* (LAPIC 未启用/IF=0/信号丢失) 时手动摘取沿原 phase 语义直接完成, 调用方
|
||||||
|
* 零感知。超时语义与经典路径一致 (CAP.TO*500ms*10, 下限 5s)。 */
|
||||||
|
static int nvme_io_wait_completion(u16 cid, u32 opcode, u32 *out_cqe_dw0) {
|
||||||
|
u64 submit_timeout_ms = g_timeout_ms * 10;
|
||||||
|
if (submit_timeout_ms < 5000) submit_timeout_ms = 5000;
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * submit_timeout_ms;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
/* 中断路径: handler 已摘取 CQE 进完成槽 */
|
||||||
|
if (g_slot_valid) {
|
||||||
|
u32 st = g_slot_status;
|
||||||
|
u32 dw0 = g_slot_dw0;
|
||||||
|
u32 scid = g_slot_cid;
|
||||||
|
nvme_mb();
|
||||||
|
g_slot_valid = 0;
|
||||||
|
g_msi_slot_hits++;
|
||||||
|
if (scid != (u32)cid) {
|
||||||
|
/* CID 错位: 槽内 CQE 不属于本命令 — 队列状态已损,
|
||||||
|
* 显式报错并继续等真 CQE (超时兜底), 绝不静默当成功 */
|
||||||
|
g_msi_cid_mismatch++;
|
||||||
|
g_log->error("[nvme] CID mismatch (slot)");
|
||||||
|
log_hex("[nvme] expect cid=", cid);
|
||||||
|
log_hex("[nvme] got cid=", scid);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (out_cqe_dw0) *out_cqe_dw0 = dw0;
|
||||||
|
if (st) {
|
||||||
|
g_log->error("[nvme] command failed (msi slot)");
|
||||||
|
log_hex("[nvme] opcode=", opcode);
|
||||||
|
log_hex("[nvme] cid=", cid);
|
||||||
|
log_hex("[nvme] status=", st);
|
||||||
|
return (int)st;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自适应轮询路径: irqsave 临界区内手动摘取 (与 handler 互斥) */
|
||||||
|
u32 st = 0, dw0 = 0, hcid = 0;
|
||||||
|
u64 flags = nvme_irqsave();
|
||||||
|
int got = nvme_iocq_harvest(&st, &dw0, &hcid);
|
||||||
|
nvme_irqrestore(flags);
|
||||||
|
if (got) {
|
||||||
|
g_msi_poll_harvests++;
|
||||||
|
if (hcid != (u32)cid) {
|
||||||
|
g_msi_cid_mismatch++;
|
||||||
|
g_log->error("[nvme] CID mismatch (poll)");
|
||||||
|
log_hex("[nvme] expect cid=", cid);
|
||||||
|
log_hex("[nvme] got cid=", hcid);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (out_cqe_dw0) *out_cqe_dw0 = dw0;
|
||||||
|
if (st) {
|
||||||
|
g_log->error("[nvme] command failed");
|
||||||
|
log_hex("[nvme] opcode=", opcode);
|
||||||
|
log_hex("[nvme] cid=", cid);
|
||||||
|
log_hex("[nvme] status=", st);
|
||||||
|
return (int)st;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dkm_rdtsc() > deadline) {
|
||||||
|
g_msi_timeouts++;
|
||||||
|
g_log->error("[nvme] completion timeout (msi mode)");
|
||||||
|
log_hex("[nvme] opcode=", opcode);
|
||||||
|
log_hex("[nvme] cid=", cid);
|
||||||
|
nvme_dump_regs("completion timeout");
|
||||||
|
return -100;
|
||||||
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 命令提交(同步轮询, 单 outstanding) ----
|
||||||
|
* dw[16] 为完整 SQE(dw0 低 16 位不含 CID, 由本函数填入)。
|
||||||
|
* 返回 0 成功;>0 = NVMe status code;<0 超时/内部错误。
|
||||||
|
*/
|
||||||
|
static int nvme_submit(volatile u32 *sq, u32 *sq_tail, u16 sq_qid,
|
||||||
|
volatile u32 *cq, u32 *cq_head, u32 *cq_phase,
|
||||||
|
u16 cq_qid, u32 qsize, u32 *dw, u32 *out_cqe_dw0) {
|
||||||
|
dw[0] = (dw[0] & 0xffffu) | ((u32)g_cid << 16);
|
||||||
|
u32 cid = g_cid;
|
||||||
|
g_cid++;
|
||||||
|
/* CID=0 保留不用: QEMU 在 MSI-X unmask 边界会向 IO CQ 补投一枚
|
||||||
|
* CID=0/phase 正确/status 成功的幽灵 CQE (实测稳定复现), g_cid 回绕
|
||||||
|
* 跳过 0 可保证该幽灵 CQE 永远被 CID 校验拒绝, 绝无误判窗口。 */
|
||||||
|
if (g_cid == 0) g_cid = 1;
|
||||||
|
|
||||||
|
/* 逐 dword 写入 SQE(64B),避免 freestanding 下编译器生成 memcpy 调用 */
|
||||||
|
u32 pos = *sq_tail;
|
||||||
|
for (u32 i = 0; i < 16; i++) sq[pos * 16 + i] = dw[i];
|
||||||
|
|
||||||
|
*sq_tail = (pos + 1) % qsize;
|
||||||
|
nvme_mb(); /* SQ 内容先于 doorbell 可见 */
|
||||||
|
mmio_write32(sq_tail_db(sq_qid), *sq_tail);
|
||||||
|
|
||||||
|
/* B7 阶段3: IO 队列 MSI 模式 → 完成槽 + irqsave 摘取统一等待。
|
||||||
|
* admin 队列保持纯轮询 (仅 init 期使用, 且 INTMC 解除前 IV0 处于
|
||||||
|
* 屏蔽态, admin 完成不会触发 MSI)。 */
|
||||||
|
if (cq_qid == 1 && g_msi_enabled) {
|
||||||
|
return nvme_io_wait_completion(cid, dw[0] & 0xffu, out_cqe_dw0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 真机超时: 使用 CAP.TO * 500ms * 10 (留 10 倍余量), 下限 5s。
|
||||||
|
* 真机 NVMe 控制器在重负载下可能需要更长的完成时间。 */
|
||||||
|
u64 submit_timeout_ms = g_timeout_ms * 10;
|
||||||
|
if (submit_timeout_ms < 5000) submit_timeout_ms = 5000;
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * submit_timeout_ms;
|
||||||
|
for (;;) {
|
||||||
|
u32 dw3 = cq[*cq_head * 4 + 3];
|
||||||
|
if (((dw3 >> 16) & 1u) == *cq_phase) {
|
||||||
|
u32 sc = (dw3 >> 17) & 0xffu;
|
||||||
|
u32 sct = (dw3 >> 25) & 0x7u;
|
||||||
|
if (out_cqe_dw0) *out_cqe_dw0 = cq[*cq_head * 4 + 0];
|
||||||
|
|
||||||
|
*cq_head = (*cq_head + 1) % qsize;
|
||||||
|
if (*cq_head == 0) *cq_phase ^= 1u;
|
||||||
|
nvme_mb();
|
||||||
|
mmio_write32(cq_head_db(cq_qid), *cq_head);
|
||||||
|
|
||||||
|
if (sc || sct) {
|
||||||
|
g_log->error("[nvme] command failed");
|
||||||
|
log_hex("[nvme] opcode=", dw[0] & 0xffu);
|
||||||
|
log_hex("[nvme] cid=", cid);
|
||||||
|
log_hex("[nvme] SC=", sc);
|
||||||
|
log_hex("[nvme] SCT=", sct);
|
||||||
|
/* SC 与 SCT 必须联合编码: SCT!=0 且 SC==0 时只返回 sc
|
||||||
|
* 会返回 0 被调用方误判为成功(CQID_INVALID 正是此类)。 */
|
||||||
|
return (int)(sc | (sct << 8));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (dkm_rdtsc() > deadline) {
|
||||||
|
g_log->error("[nvme] completion timeout");
|
||||||
|
log_hex("[nvme] opcode=", dw[0] & 0xffu);
|
||||||
|
nvme_dump_regs("completion timeout");
|
||||||
|
return -100;
|
||||||
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nvme_admin_submit(u32 *dw, u32 *out_cqe_dw0) {
|
||||||
|
return nvme_submit((volatile u32 *)g_asq.virt, &g_asq_tail, 0,
|
||||||
|
(volatile u32 *)g_acq.virt, &g_acq_head, &g_acq_phase,
|
||||||
|
0, ADMIN_QSIZE, dw, out_cqe_dw0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nvme_io_submit(u32 *dw, u32 *out_cqe_dw0) {
|
||||||
|
return nvme_submit((volatile u32 *)g_iosq.virt, &g_iosq_tail, 1,
|
||||||
|
(volatile u32 *)g_iocq.virt, &g_iocq_head, &g_iocq_phase,
|
||||||
|
1, IO_QSIZE, dw, out_cqe_dw0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Identify ---- */
|
||||||
|
|
||||||
|
static int nvme_identify_controller(u32 *out_nn) {
|
||||||
|
u32 dw[16];
|
||||||
|
nvme_zero(dw, sizeof(dw));
|
||||||
|
nvme_zero(g_ident.virt, NVME_IDENT_BYTES);
|
||||||
|
|
||||||
|
dw[0] = NVME_ADM_IDENTIFY;
|
||||||
|
dw[1] = 0; /* NSID=0 for controller */
|
||||||
|
dw[6] = (u32)(g_ident.phys & 0xffffffffu);
|
||||||
|
dw[7] = (u32)(g_ident.phys >> 32);
|
||||||
|
dw[10] = 1; /* CNS=1: identify controller */
|
||||||
|
|
||||||
|
int rc = nvme_admin_submit(dw, 0);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
u8 *id = (u8 *)g_ident.virt;
|
||||||
|
log_str("[nvme] ctrl SN=", id + 4, 20);
|
||||||
|
log_str("[nvme] ctrl MN=", id + 24, 40);
|
||||||
|
log_str("[nvme] ctrl FR=", id + 64, 8);
|
||||||
|
/* Identify Controller 布局锚点核对(NVMe 1.4, CNS=1 数据结构):
|
||||||
|
* byte 77 MDTS u8 最大传输 = 2^MDTS × CAP.MPSMIN 页, 0=无限制
|
||||||
|
* byte 512 SQES/CQES/MAXCMD —— QEMU 实测 0x00004466, 证明偏移基正确
|
||||||
|
* byte 516 NN u32 Number of Namespaces
|
||||||
|
* NN 核实结论 (QEMU 11.0 串口实证): NN=0x100 不是解析错位。NN 的 spec
|
||||||
|
* 语义是"控制器支持的最大合法 NSID 数"而非已挂载数量; QEMU hw/nvme
|
||||||
|
* 填的是 NVME_MAX_NAMESPACES=256 (0x100)。实际挂载的 NSID=1 由后续
|
||||||
|
* Identify Namespace(CNS=0) 成功返回确认。解析偏移 516 与 spec 一致,
|
||||||
|
* 无需修正。 */
|
||||||
|
u32 anchor512 = (u32)id[512] | ((u32)id[513] << 8) |
|
||||||
|
((u32)id[514] << 16) | ((u32)id[515] << 24);
|
||||||
|
log_hex("[nvme] ID dword@512(SQES/CQES/MAXCMD)=", anchor512);
|
||||||
|
u32 nn = (u32)id[516] | ((u32)id[517] << 8) | ((u32)id[518] << 16) | ((u32)id[519] << 24);
|
||||||
|
log_hex("[nvme] NN(max valid NSID)=", nn);
|
||||||
|
log_hex("[nvme] MDTS=", id[77]);
|
||||||
|
g_mdts = id[77];
|
||||||
|
*out_nn = nn;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nvme_identify_namespace(u32 nsid, u64 *out_nsze, u32 *out_lbads) {
|
||||||
|
u32 dw[16];
|
||||||
|
nvme_zero(dw, sizeof(dw));
|
||||||
|
nvme_zero(g_ident.virt, NVME_IDENT_BYTES);
|
||||||
|
|
||||||
|
dw[0] = NVME_ADM_IDENTIFY;
|
||||||
|
dw[1] = nsid;
|
||||||
|
dw[6] = (u32)(g_ident.phys & 0xffffffffu);
|
||||||
|
dw[7] = (u32)(g_ident.phys >> 32);
|
||||||
|
dw[10] = 0; /* CNS=0: identify namespace */
|
||||||
|
|
||||||
|
int rc = nvme_admin_submit(dw, 0);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
u8 *id = (u8 *)g_ident.virt;
|
||||||
|
u64 nsze = 0;
|
||||||
|
for (u32 i = 0; i < 8; i++) nsze |= ((u64)id[i]) << (i * 8);
|
||||||
|
u8 flbas = id[26] & 0x0f;
|
||||||
|
u32 lbaf_off = 128 + (u32)flbas * 4;
|
||||||
|
u32 lbaf = (u32)id[lbaf_off] | ((u32)id[lbaf_off + 1] << 8) |
|
||||||
|
((u32)id[lbaf_off + 2] << 16) | ((u32)id[lbaf_off + 3] << 24);
|
||||||
|
u32 lbads = (lbaf >> 16) & 0xff;
|
||||||
|
|
||||||
|
log_hex("[nvme] NSZE=", nsze);
|
||||||
|
log_hex("[nvme] FLBAS=", flbas);
|
||||||
|
log_hex("[nvme] LBADS=", lbads);
|
||||||
|
|
||||||
|
*out_nsze = nsze;
|
||||||
|
*out_lbads = lbads;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- B7 阶段3: PCI MSI 能力编程 ----
|
||||||
|
* MSI cap (ID 0x05) 布局:
|
||||||
|
* +0x00 u8 Cap ID (=0x05) +0x01 u8 Next Ptr
|
||||||
|
* +0x02 u16 Message Control: bit0 Enable, bit[3:1] MMC(可申请向量数-1),
|
||||||
|
* bit[6:4] MME(已分配向量数-1), bit7 64-bit Capable, bit8 PVM
|
||||||
|
* +0x04 u32 Message Address
|
||||||
|
* 64-bit 时: +0x08 u32 Message Upper Address, +0x0C u16 Message Data
|
||||||
|
* 32-bit 时: +0x08 u16 Message Data
|
||||||
|
* PVM(bit8)=1 时另有 Mask/Pending 寄存器, 复位值 0 (全部 unmask)。
|
||||||
|
* x86 LAPIC 投递公式 (physical destination, fixed delivery):
|
||||||
|
* Address = 0xFEE00000 | (dest_apic_id << 12) (RH=0, DM=0)
|
||||||
|
* Data = vector | (0<<8 delivery=fixed) (level/trigger 对 MSI 无效)
|
||||||
|
*/
|
||||||
|
#define PCI_CAP_ID_MSI 0x05
|
||||||
|
#define PCI_CAP_ID_MSIX 0x11
|
||||||
|
#define PCI_STATUS_CAPLIST 0x10u
|
||||||
|
|
||||||
|
static int nvme_find_pci_cap(u8 bus, u8 dev, u8 func, u8 cap_id) {
|
||||||
|
u32 reg04 = dkm_pci_read(bus, dev, func, PCI_COMMAND);
|
||||||
|
if (!((reg04 >> 16) & PCI_STATUS_CAPLIST)) return -1; /* Status.CapList=0 */
|
||||||
|
u8 ptr = (u8)(dkm_pci_read(bus, dev, func, PCI_CAP_PTR) & 0xFC);
|
||||||
|
int guard = 0;
|
||||||
|
while (ptr && guard++ < 64) { /* guard 防配置空间坏链死循环 */
|
||||||
|
u32 dw = dkm_pci_read(bus, dev, func, ptr);
|
||||||
|
u8 id = (u8)(dw & 0xff);
|
||||||
|
u8 next = (u8)((dw >> 8) & 0xff);
|
||||||
|
log_hex("[nvme] pci cap id=", id);
|
||||||
|
log_hex("[nvme] pci cap at=", ptr);
|
||||||
|
if (id == cap_id) return (int)ptr;
|
||||||
|
ptr = next & 0xFC;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MSI cap (0x05) 编程路径: 成功返回 1, cap 缺失或失败返回 0。
|
||||||
|
* 失败路径完整回滚 (恢复 msg_ctl + 释放向量), 不留半编程状态。 */
|
||||||
|
static int nvme_msi_program(const struct dkm_kernel_api *api,
|
||||||
|
u8 bus, u8 dev, u8 func) {
|
||||||
|
int cap = nvme_find_pci_cap(bus, dev, func, PCI_CAP_ID_MSI);
|
||||||
|
if (cap < 0) {
|
||||||
|
g_log->info("[nvme] MSI capability not present");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 cdw = dkm_pci_read(bus, dev, func, (u8)cap);
|
||||||
|
u32 msg_ctl = (cdw >> 16) & 0xffffu;
|
||||||
|
int cap64 = (int)((msg_ctl >> 7) & 1u);
|
||||||
|
int pvm = (int)((msg_ctl >> 8) & 1u);
|
||||||
|
log_hex("[nvme] MSI msg_ctl=", msg_ctl);
|
||||||
|
log_hex("[nvme] MSI 64bit=", (u64)cap64);
|
||||||
|
log_hex("[nvme] MSI pvm=", (u64)pvm);
|
||||||
|
|
||||||
|
/* 动态向量: UTSM 位图池 0x40-0xDF, 与静态向量 (tick 0xE0/spurious 0xFF)
|
||||||
|
* 及 legacy PIC 段 (0x20-0x2F) 无交叠。 */
|
||||||
|
int vector = api->irq_vector_alloc();
|
||||||
|
if (vector < 0 || vector > 255) {
|
||||||
|
g_log->warn("[nvme] MSI: vector alloc failed; polling");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
log_dec("[nvme] MSI vector=", (u64)vector);
|
||||||
|
|
||||||
|
/* 目的 APIC ID: CPUID.1:EBX[31:24] = BSP initial APIC ID (QEMU=0) */
|
||||||
|
u32 ca, cb, cc_, cd;
|
||||||
|
nvme_cpuid(1, &ca, &cb, &cc_, &cd);
|
||||||
|
(void)ca; (void)cc_; (void)cd;
|
||||||
|
u32 dest = (cb >> 24) & 0xffu;
|
||||||
|
log_hex("[nvme] MSI dest apic id=", dest);
|
||||||
|
|
||||||
|
u32 addr = 0xFEE00000u | (dest << 12);
|
||||||
|
u32 data = (u32)vector & 0xffu; /* fixed delivery, edge */
|
||||||
|
|
||||||
|
/* 先写 Address/Data, 最后置 Enable — 避免半成品消息被发出 */
|
||||||
|
dkm_pci_write(bus, dev, func, (u8)(cap + 4), addr);
|
||||||
|
if (cap64) {
|
||||||
|
dkm_pci_write(bus, dev, func, (u8)(cap + 8), 0); /* upper addr */
|
||||||
|
u32 dw = dkm_pci_read(bus, dev, func, (u8)(cap + 12));
|
||||||
|
dw = (dw & 0xffff0000u) | data; /* data 为低 u16 */
|
||||||
|
dkm_pci_write(bus, dev, func, (u8)(cap + 12), dw);
|
||||||
|
} else {
|
||||||
|
u32 dw = dkm_pci_read(bus, dev, func, (u8)(cap + 8));
|
||||||
|
dw = (dw & 0xffff0000u) | data;
|
||||||
|
dkm_pci_write(bus, dev, func, (u8)(cap + 8), dw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Message Control: MME=0 (单向量), Enable=1; 低 u16 (id/next) 回写原值 */
|
||||||
|
u32 mc = msg_ctl;
|
||||||
|
mc &= ~(0x7u << 4); /* MME=0 → 1 vector */
|
||||||
|
mc |= 1u; /* MSI Enable */
|
||||||
|
cdw = (cdw & 0x0000ffffu) | (mc << 16);
|
||||||
|
dkm_pci_write(bus, dev, func, (u8)cap, cdw);
|
||||||
|
|
||||||
|
/* PVM: 显式清 mask bit0 (复位值即为 0, 此处防御性 RMW) */
|
||||||
|
if (pvm) {
|
||||||
|
int mask_off = cap64 ? cap + 16 : cap + 12;
|
||||||
|
u32 mask = dkm_pci_read(bus, dev, func, (u8)mask_off);
|
||||||
|
mask &= ~1u;
|
||||||
|
dkm_pci_write(bus, dev, func, (u8)mask_off, mask);
|
||||||
|
log_hex("[nvme] MSI mask cleared=", mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 回读验证: Enable 置位 + Address/Data 与编程值一致 */
|
||||||
|
u32 rb_ctl = (dkm_pci_read(bus, dev, func, (u8)cap) >> 16) & 0xffffu;
|
||||||
|
u32 rb_addr = dkm_pci_read(bus, dev, func, (u8)(cap + 4));
|
||||||
|
u32 rb_data = dkm_pci_read(bus, dev, func,
|
||||||
|
(u8)(cap64 ? cap + 12 : cap + 8)) & 0xffffu;
|
||||||
|
log_hex("[nvme] MSI readback ctl=", rb_ctl);
|
||||||
|
log_hex("[nvme] MSI readback addr=", rb_addr);
|
||||||
|
log_hex("[nvme] MSI readback data=", rb_data);
|
||||||
|
if (!(rb_ctl & 1u) || rb_addr != addr || (rb_data & 0xffu) != data) {
|
||||||
|
g_log->warn("[nvme] MSI: readback mismatch; rolling back");
|
||||||
|
u32 rb = dkm_pci_read(bus, dev, func, (u8)cap);
|
||||||
|
rb = (rb & 0x0000ffffu) | (msg_ctl << 16); /* 恢复原 msg_ctl */
|
||||||
|
dkm_pci_write(bus, dev, func, (u8)cap, rb);
|
||||||
|
api->irq_vector_free(vector);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 注册 handler (irq_register 已接受向量命名空间, >0x2F 直挂分发表) */
|
||||||
|
api->irq_register((u8)vector, (void *)(uintptr_t)nvme_msi_handler);
|
||||||
|
|
||||||
|
g_msi_enabled = 1;
|
||||||
|
g_msi_vector = vector;
|
||||||
|
g_log->info("[nvme] MSI enabled (IO CQ IEN=1, IV=0)");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按 BIR 读取 BAR 物理基址 (处理 64-bit BAR; IO BAR 返回 0) */
|
||||||
|
static u64 nvme_bar_phys(u8 bus, u8 dev, u8 func, u32 bir) {
|
||||||
|
u8 reg = (u8)(PCI_BAR0 + bir * 4u);
|
||||||
|
u32 lo = dkm_pci_read(bus, dev, func, reg);
|
||||||
|
if (lo & 1u) return 0; /* IO BAR */
|
||||||
|
if ((lo & 0x6u) == 0x4u) { /* 64-bit MMIO BAR */
|
||||||
|
u32 hi = dkm_pci_read(bus, dev, func, (u8)(reg + 4));
|
||||||
|
return ((u64)hi << 32) | (u64)(lo & 0xFFFFFFF0u);
|
||||||
|
}
|
||||||
|
return (u64)(lo & 0xFFFFFFF0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MSI-X cap (0x11) 编程路径: 成功返回 1, cap 缺失或失败返回 0。
|
||||||
|
* QEMU nvme 只暴露 MSI-X (无 0x05), 本路径是 QEMU 下中断驱动化的唯一通道。
|
||||||
|
* 与 MSI 的差异仅在消息存储位置: MSI-X 的 addr/data 存放在 BAR 内的
|
||||||
|
* 表项 (16B/项), 需先把表区 MMIO 映射进 CPU 地址空间再编程 entry 0。
|
||||||
|
* 失败路径完整回滚 (清 Enable + 表项重新 mask + 释放向量)。 */
|
||||||
|
static int nvme_msix_program(const struct dkm_kernel_api *api,
|
||||||
|
u8 bus, u8 dev, u8 func) {
|
||||||
|
int cap = nvme_find_pci_cap(bus, dev, func, PCI_CAP_ID_MSIX);
|
||||||
|
if (cap < 0) {
|
||||||
|
g_log->warn("[nvme] MSI-X capability not present; polling");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!api->mm_map_mmio) {
|
||||||
|
g_log->warn("[nvme] MSI-X: mm_map_mmio unavailable; polling");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 cdw = dkm_pci_read(bus, dev, func, (u8)cap);
|
||||||
|
u32 msg_ctl = (cdw >> 16) & 0xffffu;
|
||||||
|
u32 table_size = (msg_ctl & 0x7ffu) + 1u; /* N-1 encoded */
|
||||||
|
u32 tbl_dw = dkm_pci_read(bus, dev, func, (u8)(cap + 4));
|
||||||
|
u32 bir = tbl_dw & 7u;
|
||||||
|
u32 tbl_off = tbl_dw & ~7u;
|
||||||
|
log_hex("[nvme] MSI-X msg_ctl=", msg_ctl);
|
||||||
|
log_dec("[nvme] MSI-X table size=", table_size);
|
||||||
|
log_hex("[nvme] MSI-X table BIR=", bir);
|
||||||
|
log_hex("[nvme] MSI-X table offset=", tbl_off);
|
||||||
|
if (bir > 5u) {
|
||||||
|
g_log->warn("[nvme] MSI-X: invalid BIR; polling");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 bar_phys = nvme_bar_phys(bus, dev, func, bir);
|
||||||
|
if (!bar_phys) {
|
||||||
|
g_log->warn("[nvme] MSI-X: table BAR invalid; polling");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u64 tbl_phys = bar_phys + tbl_off;
|
||||||
|
log_hex("[nvme] MSI-X table BAR phys=", bar_phys);
|
||||||
|
log_hex("[nvme] MSI-X table phys=", tbl_phys);
|
||||||
|
|
||||||
|
/* 映射表区首页 (含 entry0 的 16B)。mm_map_mmio 返回含页内偏移的指针,
|
||||||
|
* 页属性 PCD|PWT (UC), MSI-X 表访问不要求 cache 一致性操作。 */
|
||||||
|
volatile u32 *tbl = (volatile u32 *)api->mm_map_mmio(tbl_phys, 4096);
|
||||||
|
if (!tbl) {
|
||||||
|
g_log->warn("[nvme] MSI-X: table MMIO map failed; polling");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vector = api->irq_vector_alloc();
|
||||||
|
if (vector < 0 || vector > 255) {
|
||||||
|
g_log->warn("[nvme] MSI-X: vector alloc failed; polling");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
log_dec("[nvme] MSI-X vector=", (u64)vector);
|
||||||
|
|
||||||
|
u32 ca, cb, cc_, cd;
|
||||||
|
nvme_cpuid(1, &ca, &cb, &cc_, &cd);
|
||||||
|
(void)ca; (void)cc_; (void)cd;
|
||||||
|
u32 dest = (cb >> 24) & 0xffu;
|
||||||
|
u32 addr = 0xFEE00000u | (dest << 12);
|
||||||
|
|
||||||
|
/* 先写表项 (addr/data/mask=0), 再置 cap Enable — 避免半成品消息 */
|
||||||
|
volatile u32 *e = tbl; /* entry 0 */
|
||||||
|
e[0] = addr; /* Msg Addr Lo */
|
||||||
|
e[1] = 0; /* Msg Addr Hi */
|
||||||
|
e[2] = (u32)vector & 0xffu; /* Msg Data = 向量 */
|
||||||
|
e[3] = 0; /* Vector Control: unmask */
|
||||||
|
nvme_mb();
|
||||||
|
|
||||||
|
u32 nc = (msg_ctl | 0x8000u) & ~(1u << 14); /* Enable=1, FunctionMask=0 */
|
||||||
|
cdw = (cdw & 0x0000ffffu) | (nc << 16);
|
||||||
|
dkm_pci_write(bus, dev, func, (u8)cap, cdw);
|
||||||
|
|
||||||
|
/* 回读验证: cap Enable + 表项 addr/data/mask 与编程值一致 */
|
||||||
|
u32 rb_ctl = (dkm_pci_read(bus, dev, func, (u8)cap) >> 16) & 0xffffu;
|
||||||
|
u32 rb_a0 = e[0], rb_a1 = e[1], rb_d = e[2], rb_vc = e[3];
|
||||||
|
log_hex("[nvme] MSI-X readback ctl=", rb_ctl);
|
||||||
|
log_hex("[nvme] MSI-X readback entry addr=", ((u64)rb_a1 << 32) | rb_a0);
|
||||||
|
log_hex("[nvme] MSI-X readback entry data=", rb_d);
|
||||||
|
log_hex("[nvme] MSI-X readback entry vctl=", rb_vc);
|
||||||
|
if (!(rb_ctl & 0x8000u) || rb_a0 != addr || rb_a1 != 0 ||
|
||||||
|
(rb_d & 0xffu) != ((u32)vector & 0xffu) || (rb_vc & 1u)) {
|
||||||
|
g_log->warn("[nvme] MSI-X: readback mismatch; rolling back");
|
||||||
|
u32 rb = dkm_pci_read(bus, dev, func, (u8)cap);
|
||||||
|
rb = (rb & 0x0000ffffu) | (msg_ctl << 16); /* 恢复原 msg_ctl */
|
||||||
|
dkm_pci_write(bus, dev, func, (u8)cap, rb);
|
||||||
|
e[3] = 1; /* 表项重新 mask */
|
||||||
|
api->irq_vector_free(vector);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
api->irq_register((u8)vector, (void *)(uintptr_t)nvme_msi_handler);
|
||||||
|
|
||||||
|
g_msi_enabled = 1;
|
||||||
|
g_msix_mode = 1;
|
||||||
|
g_msi_vector = vector;
|
||||||
|
g_log->info("[nvme] MSI-X enabled (table entry0, IO CQ IEN=1, IV=0)");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 中断驱动化入口: 先 MSI (0x05), 缺则 MSI-X (0x11), 皆不可则纯轮询。
|
||||||
|
* 前提检查集中在此; 两条编程路径各自保证失败完整回滚。 */
|
||||||
|
static int nvme_msi_setup(const struct dkm_kernel_api *api,
|
||||||
|
u8 bus, u8 dev, u8 func) {
|
||||||
|
if (!g_cfg_nvme_msi) {
|
||||||
|
g_log->info("[nvme] MSI disabled by FUCK (nvme_msi=0); polling mode");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!api->irq_vector_alloc || !api->irq_vector_free || !api->irq_register) {
|
||||||
|
g_log->warn("[nvme] MSI: kernel vector allocator unavailable; polling");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (nvme_msi_program(api, bus, dev, func)) return 1;
|
||||||
|
return nvme_msix_program(api, bus, dev, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- IO queue 创建 ---- */
|
||||||
|
|
||||||
|
static int nvme_create_io_queues(void) {
|
||||||
|
u32 dw[16];
|
||||||
|
|
||||||
|
/* Create IO CQ (qid=1): PC=1; MSI 就绪时 IEN=1 + IV=0
|
||||||
|
* CDW10 = QSIZE[31:16](0's based) | QID[15:0] —— QID 必须在低半字
|
||||||
|
* CDW11 = IV[31:16] | IEN[1] | PC[0] —— IV=0 与 INTMS/INTMC bit0 对应 */
|
||||||
|
nvme_zero(dw, sizeof(dw));
|
||||||
|
dw[0] = NVME_ADM_CREATE_IOCQ;
|
||||||
|
dw[6] = (u32)(g_iocq.phys & 0xffffffffu);
|
||||||
|
dw[7] = (u32)(g_iocq.phys >> 32);
|
||||||
|
dw[10] = ((u32)(IO_QSIZE - 1u) << 16) | 1u;
|
||||||
|
dw[11] = 1u;
|
||||||
|
if (g_msi_enabled) dw[11] = 1u | (1u << 1); /* PC=1, IEN=1, IV=0 */
|
||||||
|
log_hex("[nvme] create IOCQ CDW11=", dw[11]);
|
||||||
|
int rc = nvme_admin_submit(dw, 0);
|
||||||
|
if (rc != 0) {
|
||||||
|
g_log->error("[nvme] create IO CQ failed");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create IO SQ (qid=1, cqid=1): PC=1
|
||||||
|
* CDW10 同 Create IOCQ: QSIZE[31:16] | QID[15:0]; CDW11 = CQID[31:16] | PC */
|
||||||
|
nvme_zero(dw, sizeof(dw));
|
||||||
|
dw[0] = NVME_ADM_CREATE_IOSQ;
|
||||||
|
dw[6] = (u32)(g_iosq.phys & 0xffffffffu);
|
||||||
|
dw[7] = (u32)(g_iosq.phys >> 32);
|
||||||
|
dw[10] = ((u32)(IO_QSIZE - 1u) << 16) | 1u;
|
||||||
|
dw[11] = (1u << 16) | 1;
|
||||||
|
rc = nvme_admin_submit(dw, 0);
|
||||||
|
if (rc != 0) {
|
||||||
|
g_log->error("[nvme] create IO SQ failed");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_iosq_tail = 0;
|
||||||
|
g_iocq_head = 0;
|
||||||
|
g_iocq_phase = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- IO Read/Write(PRP1 + PRP2 直连 / PRP list 分档) ----
|
||||||
|
* buf_phys 必须 4KiB 对齐且物理连续(本驱动只传 g_data.phys)。
|
||||||
|
* 传输页数由 nlb × sector_size 推出,决定 PRP 布局:
|
||||||
|
* 1 页 : PRP2 = 0(单 PRP1,等价旧 bounce 路径)
|
||||||
|
* 2 页 : PRP2 = 第二页物理地址(spec 规定的两页直连形式)
|
||||||
|
* >=3 页: PRP2 = g_prp list 页物理地址, list[i] = 第 i+2 页物理地址
|
||||||
|
* 单 list 页 512 项, 上限 513 页 (2052KiB), 远大于 chunk 上限 32 页,
|
||||||
|
* 无需 list 链页。PRP list 由 CPU 写、控制器 DMA 读: x86 TSO + 编译屏障
|
||||||
|
* 保证 list 先于 doorbell 可见(与 SQE 提交路径同一约定)。
|
||||||
|
*/
|
||||||
|
static int nvme_io_rw(u8 opcode, u64 slba, u32 nlb, u64 buf_phys) {
|
||||||
|
u32 bytes = nlb * (u32)g_sector_size;
|
||||||
|
u32 pages = (bytes + 4095u) >> 12;
|
||||||
|
if (pages == 0 || (pages - 1u) > NVME_PRP_LIST_ENTRIES) return -3;
|
||||||
|
|
||||||
|
u32 dw[16];
|
||||||
|
nvme_zero(dw, sizeof(dw));
|
||||||
|
dw[0] = opcode;
|
||||||
|
dw[1] = 1; /* nsid=1 */
|
||||||
|
dw[6] = (u32)(buf_phys & 0xffffffffu);
|
||||||
|
dw[7] = (u32)(buf_phys >> 32);
|
||||||
|
if (pages == 2) {
|
||||||
|
u64 p2 = buf_phys + 4096u;
|
||||||
|
dw[8] = (u32)(p2 & 0xffffffffu);
|
||||||
|
dw[9] = (u32)(p2 >> 32);
|
||||||
|
} else if (pages >= 3) {
|
||||||
|
volatile u64 *list = (volatile u64 *)g_prp.virt;
|
||||||
|
for (u32 i = 0; i < pages - 1u; i++)
|
||||||
|
list[i] = buf_phys + 4096ULL * (u64)(i + 1u);
|
||||||
|
nvme_mb(); /* list 内容先于 doorbell 可见 */
|
||||||
|
dw[8] = (u32)(g_prp.phys & 0xffffffffu);
|
||||||
|
dw[9] = (u32)(g_prp.phys >> 32);
|
||||||
|
}
|
||||||
|
dw[10] = (u32)(slba & 0xffffffffu);
|
||||||
|
dw[11] = (u32)(slba >> 32);
|
||||||
|
dw[12] = nlb - 1u; /* NLB 0-based */
|
||||||
|
return nvme_io_submit(dw, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NVM Flush (opcode 0x00): 无数据字段, 把 volatile write cache 刷到介质 */
|
||||||
|
static int nvme_io_flush(void) {
|
||||||
|
u32 dw[16];
|
||||||
|
nvme_zero(dw, sizeof(dw));
|
||||||
|
dw[0] = NVME_IO_FLUSH;
|
||||||
|
dw[1] = 1; /* nsid=1 */
|
||||||
|
return nvme_io_submit(dw, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- block provider ----
|
||||||
|
* 调用方缓冲为任意虚拟地址(DSK 静态 BSS、vmm virtio_blk dbuf 等),
|
||||||
|
* 物理底账未知且不一定连续/对齐,不能直接进 PRP。一律 bounce 经
|
||||||
|
* g_data(物理连续 DMA 区): 一次命令读/写满 chunk,再 memcpy 中转。
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int nvme_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
|
||||||
|
(void)ctx;
|
||||||
|
if (!g_io_ready || !buffer || count == 0) return -1;
|
||||||
|
u8 *out = (u8 *)buffer;
|
||||||
|
while (count > 0) {
|
||||||
|
u32 n = count > g_chunk_max_sectors ? g_chunk_max_sectors : count;
|
||||||
|
int rc = nvme_io_rw(NVME_IO_READ, lba, n, g_data.phys);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
u32 bytes = n * (u32)g_sector_size;
|
||||||
|
u8 *src = (u8 *)g_data.virt;
|
||||||
|
for (u32 i = 0; i < bytes; i++) out[i] = src[i];
|
||||||
|
out += bytes;
|
||||||
|
lba += n;
|
||||||
|
count -= n;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nvme_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
|
||||||
|
(void)ctx;
|
||||||
|
if (!g_io_ready || !buffer || count == 0) return -1;
|
||||||
|
const u8 *in = (const u8 *)buffer;
|
||||||
|
while (count > 0) {
|
||||||
|
u32 n = count > g_chunk_max_sectors ? g_chunk_max_sectors : count;
|
||||||
|
u32 bytes = n * (u32)g_sector_size;
|
||||||
|
u8 *dst = (u8 *)g_data.virt;
|
||||||
|
for (u32 i = 0; i < bytes; i++) dst[i] = in[i];
|
||||||
|
/* 沿用 ahci BUG-P0-2 先例: 写前 flush CPU cache, 防 DMA 读到陈旧数据 */
|
||||||
|
__asm__ volatile("wbinvd" ::: "memory");
|
||||||
|
int rc = nvme_io_rw(NVME_IO_WRITE, lba, n, g_data.phys);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
in += bytes;
|
||||||
|
lba += n;
|
||||||
|
count -= n;
|
||||||
|
}
|
||||||
|
/* 每次写调用末尾 FLUSH 一次,保证 volatile write cache 落盘 */
|
||||||
|
return nvme_io_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 数据面自测(仅签名测试盘运行写测试,防实机盘数据损坏) ---- */
|
||||||
|
|
||||||
|
/* 读回 LBA0 并核对 DESHABNVME0 签名(写测试后复查无串扰用) */
|
||||||
|
static int nvme_lba0_sig_ok(void) {
|
||||||
|
static const char sig[] = "DESHABNVME0";
|
||||||
|
int rc = nvme_io_rw(NVME_IO_READ, 0, 1, g_data.phys);
|
||||||
|
if (rc != 0) return 0;
|
||||||
|
u8 *d = (u8 *)g_data.virt;
|
||||||
|
for (u32 i = 0; i < 11; i++) {
|
||||||
|
if (d[i] != (u8)sig[i]) return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 多页单命令读: LBA0 起 32 扇区 = 16KiB = 4 页 → PRP list 路径。
|
||||||
|
* 测试盘内容已知: LBA0 为 "DESHABNVME0" 重复填满 512B, LBA1..31 全零。 */
|
||||||
|
static void nvme_selftest_read_multipage(void) {
|
||||||
|
static u8 rbuf[16384];
|
||||||
|
static const char sig[] = "DESHABNVME0";
|
||||||
|
int rc = nvme_block_read(0, 0, 32, rbuf);
|
||||||
|
log_hex("[nvme] PRPLIST read 32sec rc=", (u64)(i64)rc);
|
||||||
|
if (rc != 0) return;
|
||||||
|
|
||||||
|
u32 bad = 0;
|
||||||
|
for (u32 i = 0; i < 512; i++) {
|
||||||
|
if (rbuf[i] != (u8)sig[i % 11]) {
|
||||||
|
bad = 1;
|
||||||
|
log_hex("[nvme] sig mismatch idx=", i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!bad) {
|
||||||
|
for (u32 i = 512; i < 16384; i++) {
|
||||||
|
if (rbuf[i] != 0) {
|
||||||
|
bad = 1;
|
||||||
|
log_hex("[nvme] nonzero idx=", i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bad) {
|
||||||
|
g_log->error("[nvme] PRPLIST read verify FAILED");
|
||||||
|
} else {
|
||||||
|
g_log->info("[nvme] PRPLIST read verify OK (LBA0 sig + LBA1-31 zero)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 写 → FLUSH → 读回逐字节校验。sectors=16 走 PRP2 直连(2页),
|
||||||
|
* sectors=32 走 PRP list(4页)。pattern 为首 8 字节 "NVMEWRIT" + 全缓冲
|
||||||
|
* 确定式伪随机,重跑幂等(与上次写入内容一致)。 */
|
||||||
|
static void nvme_selftest_rw(u64 lba, u32 sectors) {
|
||||||
|
static u8 wpat[16384];
|
||||||
|
static u8 rbuf[16384];
|
||||||
|
u32 bytes = sectors * (u32)g_sector_size;
|
||||||
|
if (bytes > sizeof(wpat)) return;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < bytes; i++) wpat[i] = (u8)(i * 31u + 17u);
|
||||||
|
wpat[0] = 'N'; wpat[1] = 'V'; wpat[2] = 'M'; wpat[3] = 'E';
|
||||||
|
wpat[4] = 'W'; wpat[5] = 'R'; wpat[6] = 'I'; wpat[7] = 'T';
|
||||||
|
|
||||||
|
log_hex("[nvme] selftest rw lba=", lba);
|
||||||
|
log_hex("[nvme] selftest rw sectors=", sectors);
|
||||||
|
int rc = nvme_block_write(0, lba, sectors, wpat);
|
||||||
|
log_hex("[nvme] write+flush rc=", (u64)(i64)rc);
|
||||||
|
if (rc != 0) return;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < bytes; i++) rbuf[i] = 0;
|
||||||
|
rc = nvme_block_read(0, lba, sectors, rbuf);
|
||||||
|
log_hex("[nvme] readback rc=", (u64)(i64)rc);
|
||||||
|
if (rc != 0) return;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < bytes; i++) {
|
||||||
|
if (rbuf[i] != wpat[i]) {
|
||||||
|
g_log->error("[nvme] readback MISMATCH");
|
||||||
|
log_hex("[nvme] first bad idx=", i);
|
||||||
|
log_hex("[nvme] expect=", wpat[i]);
|
||||||
|
log_hex("[nvme] actual=", rbuf[i]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_log->info("[nvme] write+flush+readback verify OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* B7 阶段3 压测: 128 扇区 (64KiB, PRP list 路径) × 50 次 写+读。
|
||||||
|
* 统计总耗时与 MSI 计数器增量, 供中断/轮询两模式粗粒度对比。
|
||||||
|
* 仅签名测试盘运行 (写 LBA3000 起的 50 个块, 不碰 LBA0 签名区)。 */
|
||||||
|
static void nvme_selftest_stress(void) {
|
||||||
|
static u8 wbuf[65536];
|
||||||
|
static u8 rbuf[65536];
|
||||||
|
for (u32 i = 0; i < sizeof(wbuf); i++) wbuf[i] = (u8)(i * 17u + 3u);
|
||||||
|
|
||||||
|
u64 irq0 = g_msi_irq_count, slot0 = g_msi_slot_hits, poll0 = g_msi_poll_harvests;
|
||||||
|
u64 t0 = dkm_rdtsc();
|
||||||
|
u32 fails = 0;
|
||||||
|
for (u32 i = 0; i < 50; i++) {
|
||||||
|
u64 lba = 3000 + (u64)i * 128;
|
||||||
|
if (nvme_block_write(0, lba, 128, wbuf) != 0) { fails++; break; }
|
||||||
|
if (nvme_block_read(0, lba, 128, rbuf) != 0) { fails++; break; }
|
||||||
|
}
|
||||||
|
u64 elapsed_ms = dkm_tsc_per_ms ? (dkm_rdtsc() - t0) / dkm_tsc_per_ms : 0;
|
||||||
|
|
||||||
|
/* 末次读回全量校验 (数据通路在 MSI 模式下完整性证明) */
|
||||||
|
if (!fails) {
|
||||||
|
for (u32 i = 0; i < sizeof(wbuf); i++) {
|
||||||
|
if (rbuf[i] != wbuf[i]) {
|
||||||
|
g_log->error("[nvme] stress readback MISMATCH");
|
||||||
|
log_hex("[nvme] first bad idx=", i);
|
||||||
|
log_hex("[nvme] expect=", wbuf[i]);
|
||||||
|
log_hex("[nvme] actual=", rbuf[i]);
|
||||||
|
log_hex("[nvme] cq head=", g_iocq_head);
|
||||||
|
log_hex("[nvme] cq phase=", g_iocq_phase);
|
||||||
|
log_hex("[nvme] slot valid=", g_slot_valid);
|
||||||
|
fails++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_dec("[nvme] stress 50x(128sec W+R) ms=", elapsed_ms);
|
||||||
|
log_dec("[nvme] stress fails=", fails);
|
||||||
|
log_dec("[nvme] stress irq delta=", g_msi_irq_count - irq0);
|
||||||
|
log_dec("[nvme] stress slot delta=", g_msi_slot_hits - slot0);
|
||||||
|
log_dec("[nvme] stress poll delta=", g_msi_poll_harvests - poll0);
|
||||||
|
if (!fails) g_log->info("[nvme] stress verify OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nvme_msi_stats_dump(const char *tag) {
|
||||||
|
g_log->info(tag);
|
||||||
|
log_dec("[nvme] msi enabled=", (u64)g_msi_enabled);
|
||||||
|
log_dec("[nvme] msix mode=", (u64)g_msix_mode);
|
||||||
|
log_dec("[nvme] msi vector=", (u64)(g_msi_vector < 0 ? 0 : g_msi_vector));
|
||||||
|
log_dec("[nvme] msi irq count=", g_msi_irq_count);
|
||||||
|
log_dec("[nvme] msi slot hits=", g_msi_slot_hits);
|
||||||
|
log_dec("[nvme] msi poll harvests=", g_msi_poll_harvests);
|
||||||
|
log_dec("[nvme] msi timeouts=", g_msi_timeouts);
|
||||||
|
log_dec("[nvme] msi cid mismatch=", g_msi_cid_mismatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 真机诊断: dump 关键寄存器 ---- */
|
||||||
|
|
||||||
|
static void nvme_dump_regs(const char *ctx) {
|
||||||
|
g_log->info("[nvme] --- register dump: ");
|
||||||
|
g_log->info(ctx);
|
||||||
|
log_hex("[nvme] CAP =", mmio_read64(NVME_CAP));
|
||||||
|
log_hex("[nvme] VS =", mmio_read32(NVME_VS));
|
||||||
|
log_hex("[nvme] CC =", mmio_read32(NVME_CC));
|
||||||
|
log_hex("[nvme] CSTS =", mmio_read32(NVME_CSTS));
|
||||||
|
log_hex("[nvme] AQA =", mmio_read32(NVME_AQA));
|
||||||
|
log_hex("[nvme] ASQ =", mmio_read64(NVME_ASQ));
|
||||||
|
log_hex("[nvme] ACQ =", mmio_read64(NVME_ACQ));
|
||||||
|
/* doorbell 区域首个 entry 诊断 */
|
||||||
|
log_hex("[nvme] SQ0DB=", mmio_read32(NVME_DB_BASE));
|
||||||
|
log_hex("[nvme] CQ0DB=", mmio_read32(NVME_DB_BASE + g_db_stride));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 高位 BAR MMIO 接入 ---- */
|
||||||
|
|
||||||
|
/* 真机 NVMe 控制器寄存器 + doorbell 空间可能远大于 8K。
|
||||||
|
* 典型实机: CAP.MQES 决定队列深度,doorbell 偏移 = DB_BASE + 2*QID*DSTRD*4,
|
||||||
|
* 需要映射足够大的范围覆盖至少 admin + 1 个 IO queue 对。
|
||||||
|
* 安全映射大小: 至少 64K(覆盖 QID 0-7 的 SQ/CQ doorbell)。 */
|
||||||
|
#define NVME_MAP_SIZE 0x10000
|
||||||
|
|
||||||
|
static volatile u8 *nvme_map_bar(const struct dkm_kernel_api *api, u64 bar_phys) {
|
||||||
|
u64 bar_virt = api->hhdm_offset + bar_phys;
|
||||||
|
|
||||||
|
/* 优先使用 mm_map_mmio(独立窗口映射, PCD|PWT, 不与 HHDM 空洞竞争) */
|
||||||
|
if (api->mm_map_mmio) {
|
||||||
|
void *mapped = api->mm_map_mmio(bar_phys, NVME_MAP_SIZE);
|
||||||
|
if (mapped) {
|
||||||
|
g_log->info("[nvme] BAR mapped via mm_map_mmio window");
|
||||||
|
return (volatile u8 *)mapped;
|
||||||
|
}
|
||||||
|
g_log->warn("[nvme] mm_map_mmio failed; trying HHDM fallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!api->mmio || !api->mmio->is_mapped) {
|
||||||
|
/* 内核无页表服务: 仅当 BAR 位于低 4G(HHDM 经验上覆盖)时冒险直用 */
|
||||||
|
if (bar_phys < 0x100000000ULL) {
|
||||||
|
g_log->warn("[nvme] no mmio api; assume HHDM covers low BAR");
|
||||||
|
return (volatile u8 *)(uintptr_t)bar_virt;
|
||||||
|
}
|
||||||
|
g_log->warn("[nvme] no mmio api and BAR above 4G; cannot access");
|
||||||
|
return (volatile u8 *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_hex("[nvme] CR3=", api->mmio->cr3 ? api->mmio->cr3() : 0);
|
||||||
|
|
||||||
|
/* 核实: HHDM 是否直接覆盖该 BAR(四级页表只读 walk, 无 #PF 风险) */
|
||||||
|
int level = api->mmio->is_mapped(bar_virt);
|
||||||
|
log_hex("[nvme] HHDM is_mapped(BAR)=", (u64)(i64)level);
|
||||||
|
|
||||||
|
if (level == 0) {
|
||||||
|
g_log->info("[nvme] HHDM does NOT cover BAR; mapping via page tables");
|
||||||
|
if (!api->mmio->map_mmio) return (volatile u8 *)0;
|
||||||
|
int rc = api->mmio->map_mmio(bar_phys, NVME_MAP_SIZE);
|
||||||
|
log_hex("[nvme] map_mmio rc=", (u64)(i64)rc);
|
||||||
|
if (rc != 0) return (volatile u8 *)0;
|
||||||
|
|
||||||
|
level = api->mmio->is_mapped(bar_virt);
|
||||||
|
log_hex("[nvme] post-map is_mapped=", (u64)(i64)level);
|
||||||
|
if (level == 0) {
|
||||||
|
g_log->error("[nvme] BAR still unmapped after map_mmio");
|
||||||
|
return (volatile u8 *)0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g_log->info("[nvme] HHDM already covers BAR; direct access");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (volatile u8 *)(uintptr_t)bar_virt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- driver entry ---- */
|
||||||
|
|
||||||
__attribute__((visibility("default")))
|
__attribute__((visibility("default")))
|
||||||
int driver_init(const struct dkm_kernel_api *api,
|
int driver_init(const struct dkm_kernel_api *api,
|
||||||
struct dkm_driver_handle *handle) {
|
struct dkm_driver_handle *handle) {
|
||||||
(void)handle;
|
(void)handle;
|
||||||
|
dkm_tsc_calibrate();
|
||||||
|
|
||||||
if (!api || !api->log) return -1;
|
if (!api || !api->log) return -1;
|
||||||
g_log = api->log;
|
g_log = api->log;
|
||||||
|
dkm_instr_init(api);
|
||||||
|
|
||||||
g_log->info("[nvme] init begin");
|
g_log->info("[nvme] init begin");
|
||||||
|
nvme_load_config(api);
|
||||||
|
|
||||||
u8 bus = 0;
|
u8 bus = 0, dev = 0, func = 0;
|
||||||
u8 dev = 0;
|
|
||||||
u8 func = 0;
|
|
||||||
if (nvme_find(&bus, &dev, &func) != 0) {
|
if (nvme_find(&bus, &dev, &func) != 0) {
|
||||||
g_log->warn("[nvme] NVMe controller not found");
|
g_log->warn("[nvme] NVMe controller not found");
|
||||||
g_log->info("[nvme] driver ready");
|
g_log->info("[nvme] driver ready");
|
||||||
@@ -253,89 +1362,267 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_log->info("[nvme] NVMe controller found");
|
g_log->info("[nvme] NVMe controller found");
|
||||||
|
DKM_STAT_INC(nvm0_pci_found);
|
||||||
log_hex("[nvme] bus=", bus);
|
log_hex("[nvme] bus=", bus);
|
||||||
log_hex("[nvme] dev=", dev);
|
log_hex("[nvme] dev=", dev);
|
||||||
log_hex("[nvme] func=", func);
|
log_hex("[nvme] func=", func);
|
||||||
|
|
||||||
u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID);
|
u32 vd = dkm_pci_read(bus, dev, func, PCI_VENDOR_ID);
|
||||||
log_hex("[nvme] vendor=", vd & 0xffff);
|
log_hex("[nvme] vendor=", vd & 0xffff);
|
||||||
log_hex("[nvme] device=", vd >> 16);
|
log_hex("[nvme] device=", vd >> 16);
|
||||||
|
|
||||||
u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
|
/* 启用 MEM + BUSM, 并禁用 INTx(轮询模式不需要中断) */
|
||||||
command |= PCI_CMD_MEM | PCI_CMD_BUSM;
|
u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
|
||||||
pci_write(bus, dev, func, PCI_COMMAND, command);
|
command |= PCI_CMD_MEM | PCI_CMD_BUSM | (1u << 10);
|
||||||
|
dkm_pci_write(bus, dev, func, PCI_COMMAND, command);
|
||||||
log_hex("[nvme] PCI command=", command);
|
log_hex("[nvme] PCI command=", command);
|
||||||
|
|
||||||
u32 bar0_lo = pci_read(bus, dev, func, PCI_BAR0);
|
u32 bar0_lo = dkm_pci_read(bus, dev, func, PCI_BAR0);
|
||||||
u32 bar1_hi = pci_read(bus, dev, func, PCI_BAR1);
|
u32 bar1_hi = dkm_pci_read(bus, dev, func, PCI_BAR1);
|
||||||
u64 bar_phys;
|
u64 bar_phys;
|
||||||
if (bar0_lo & 1u) {
|
if (bar0_lo & 1u) {
|
||||||
g_log->warn("[nvme] BAR0 is IO space; unsupported");
|
g_log->warn("[nvme] BAR0 is IO space; unsupported");
|
||||||
g_log->info("[nvme] driver ready");
|
g_log->info("[nvme] driver ready");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((bar0_lo & 0x6u) == 0x4u) {
|
if ((bar0_lo & 0x6u) == 0x4u) {
|
||||||
bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u);
|
bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u);
|
||||||
} else {
|
} else {
|
||||||
bar_phys = (u64)(bar0_lo & 0xFFFFFFF0u);
|
bar_phys = (u64)(bar0_lo & 0xFFFFFFF0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
|
u32 irq_line = dkm_pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
|
||||||
log_hex("[nvme] BAR0 phys=", bar_phys);
|
log_hex("[nvme] BAR0 phys=", bar_phys);
|
||||||
log_hex("[nvme] PCI IRQ line=", irq_line);
|
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);
|
log_hex("[nvme] HHDM offset=", api->hhdm_offset);
|
||||||
|
|
||||||
u64 cap = mmio_read64(NVME_CAP);
|
if (!api->hhdm_offset || !bar_phys) {
|
||||||
u32 vs = mmio_read32(NVME_VS);
|
g_log->warn("[nvme] missing HHDM or BAR0; skip");
|
||||||
u32 intms = mmio_read32(NVME_INTMS);
|
g_log->info("[nvme] driver ready");
|
||||||
u32 intmc = mmio_read32(NVME_INTMC);
|
return 0;
|
||||||
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_regs = nvme_map_bar(api, bar_phys);
|
||||||
|
if (!g_regs) {
|
||||||
|
g_log->error("[nvme] BAR MMIO unavailable; driver inactive");
|
||||||
|
g_log->info("[nvme] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 寄存器枚举(验证 MMIO 可读) ---- */
|
||||||
|
u64 cap = mmio_read64(NVME_CAP);
|
||||||
|
u32 vs = mmio_read32(NVME_VS);
|
||||||
|
u32 cc = mmio_read32(NVME_CC);
|
||||||
|
u32 csts = mmio_read32(NVME_CSTS);
|
||||||
|
|
||||||
|
log_hex("[nvme] CAP=", cap);
|
||||||
|
log_hex("[nvme] VS=", vs);
|
||||||
|
log_hex("[nvme] CC=", cc);
|
||||||
|
log_hex("[nvme] CSTS=", csts);
|
||||||
|
log_hex("[nvme] CAP.MQES=", (cap & 0xffff) + 1);
|
||||||
|
log_hex("[nvme] CAP.DSTRD=", (cap >> 32) & 0xf);
|
||||||
|
log_hex("[nvme] CAP.TO=", (cap >> 24) & 0xff);
|
||||||
|
log_hex("[nvme] CAP.CSS=", (cap >> 37) & 0xff);
|
||||||
|
log_hex("[nvme] CAP.MPSMIN=", (cap >> 48) & 0xf);
|
||||||
|
log_hex("[nvme] CAP.MPSMAX=", (cap >> 52) & 0xf);
|
||||||
|
|
||||||
|
if (cap == 0 || cap == 0xFFFFFFFFFFFFFFFFULL) {
|
||||||
|
g_log->error("[nvme] CAP invalid; MMIO read broken");
|
||||||
|
g_log->info("[nvme] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!((cap >> 37) & 1u)) {
|
||||||
|
g_log->error("[nvme] NVM command set not supported");
|
||||||
|
g_log->info("[nvme] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_db_stride = 4u << ((cap >> 32) & 0xf);
|
||||||
|
g_timeout_ms = ((cap >> 24) & 0xff) * 500;
|
||||||
|
if (!g_timeout_ms) g_timeout_ms = 500;
|
||||||
|
log_hex("[nvme] doorbell stride=", g_db_stride);
|
||||||
|
log_hex("[nvme] timeout ms=", g_timeout_ms);
|
||||||
|
|
||||||
|
if (!api->dma) {
|
||||||
|
g_log->warn("[nvme] DMA API unavailable; queues not configured");
|
||||||
|
g_log->info("[nvme] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 分配队列与数据缓冲(低 4G, 4K 对齐, 清零, 物理连续) ---- */
|
||||||
|
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_asq) != 0) goto dma_fail;
|
||||||
|
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_acq) != 0) goto dma_fail;
|
||||||
|
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_ident) != 0) goto dma_fail;
|
||||||
|
if (api->dma->alloc_pages(NVME_DATA_PAGES, 4096, 0x100000000ULL, &g_data) != 0) goto dma_fail;
|
||||||
|
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_prp) != 0) goto dma_fail;
|
||||||
|
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_iosq) != 0) goto dma_fail;
|
||||||
|
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_iocq) != 0) goto dma_fail;
|
||||||
|
goto dma_ok;
|
||||||
|
dma_fail:
|
||||||
|
g_log->error("[nvme] DMA alloc failed");
|
||||||
|
g_log->info("[nvme] driver ready");
|
||||||
|
return 0;
|
||||||
|
dma_ok:
|
||||||
|
log_hex("[nvme] ASQ phys=", g_asq.phys);
|
||||||
|
log_hex("[nvme] ACQ phys=", g_acq.phys);
|
||||||
|
log_hex("[nvme] DATA phys=", g_data.phys);
|
||||||
|
log_hex("[nvme] PRP list phys=", g_prp.phys);
|
||||||
|
log_hex("[nvme] IOSQ phys=", g_iosq.phys);
|
||||||
|
log_hex("[nvme] IOCQ phys=", g_iocq.phys);
|
||||||
|
|
||||||
|
/* ---- 控制器复位: EN=0 等 RDY=0 ---- */
|
||||||
|
mmio_write32(NVME_INTMS, 0xFFFFFFFFu); /* 屏蔽全部中断向量 */
|
||||||
|
cc = mmio_read32(NVME_CC);
|
||||||
|
if (cc & 1u) {
|
||||||
|
mmio_write32(NVME_CC, cc & ~1u);
|
||||||
|
}
|
||||||
|
if (nvme_wait_csts(1u, 0u, g_timeout_ms) != 0) {
|
||||||
|
g_log->error("[nvme] disable failed");
|
||||||
|
nvme_dump_regs("controller disable timeout");
|
||||||
|
g_log->info("[nvme] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
g_log->info("[nvme] controller disabled");
|
||||||
|
|
||||||
|
/* ---- 配置 admin queue ---- */
|
||||||
|
mmio_write32(NVME_AQA, ((u32)(ADMIN_QSIZE - 1) << 16) | (u32)(ADMIN_QSIZE - 1));
|
||||||
|
mmio_write64(NVME_ASQ, g_asq.phys);
|
||||||
|
mmio_write64(NVME_ACQ, g_acq.phys);
|
||||||
|
g_asq_tail = 0;
|
||||||
|
g_acq_head = 0;
|
||||||
|
g_acq_phase = 1;
|
||||||
|
g_cid = 1;
|
||||||
|
|
||||||
|
/* ---- 使能: EN=1, CSS=NVM, MPS=4K, AMS=RR, IOSQES=6, IOCQES=4 ---- */
|
||||||
|
mmio_write32(NVME_CC, 0x00460001u);
|
||||||
|
if (nvme_wait_csts(1u, 1u, g_timeout_ms) != 0) {
|
||||||
|
g_log->error("[nvme] enable failed");
|
||||||
|
nvme_dump_regs("controller enable timeout");
|
||||||
|
g_log->info("[nvme] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
g_log->info("[nvme] controller enabled, admin queue live");
|
||||||
|
|
||||||
|
/* ---- Identify ---- */
|
||||||
|
u32 nn = 0;
|
||||||
|
if (nvme_identify_controller(&nn) != 0) {
|
||||||
|
g_log->error("[nvme] identify controller failed");
|
||||||
|
g_log->info("[nvme] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (nn == 0) {
|
||||||
|
g_log->warn("[nvme] no namespace");
|
||||||
|
g_log->info("[nvme] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 nsze = 0;
|
||||||
|
u32 lbads = 0;
|
||||||
|
if (nvme_identify_namespace(1, &nsze, &lbads) != 0) {
|
||||||
|
g_log->error("[nvme] identify namespace failed");
|
||||||
|
g_log->info("[nvme] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (lbads < 9 || lbads > 12 || nsze == 0) {
|
||||||
|
g_log->warn("[nvme] unsupported LBA format or empty NS");
|
||||||
|
log_hex("[nvme] LBADS=", lbads);
|
||||||
|
g_log->info("[nvme] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
g_sector_size = 1ULL << lbads;
|
||||||
|
g_sector_count = nsze;
|
||||||
|
log_hex("[nvme] sector size=", g_sector_size);
|
||||||
|
log_hex("[nvme] sector count=", g_sector_count);
|
||||||
|
|
||||||
|
/* ---- 单命令扇区上限: min(bounce 缓冲, 2^MDTS 页, NLB 16-bit) ----
|
||||||
|
* QEMU MDTS=7 → 512KiB, bounce 32 页=128KiB 更紧, 故 chunk=256 扇区,
|
||||||
|
* 恰好覆盖 DSK FAT32 的 256 扇区批量读(一次命令读满)。 */
|
||||||
|
{
|
||||||
|
u64 buf_bytes = (u64)NVME_DATA_PAGES * 4096u;
|
||||||
|
u64 mdts_bytes = g_mdts ? ((1ULL << g_mdts) * 4096u) : buf_bytes;
|
||||||
|
u64 cap = buf_bytes < mdts_bytes ? buf_bytes : mdts_bytes;
|
||||||
|
g_chunk_max_sectors = (u32)(cap / g_sector_size);
|
||||||
|
if (g_chunk_max_sectors > 65536u) g_chunk_max_sectors = 65536u;
|
||||||
|
if (g_chunk_max_sectors == 0) g_chunk_max_sectors = 1;
|
||||||
|
log_hex("[nvme] chunk max sectors=", g_chunk_max_sectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- B7 阶段3: IO queue 创建前尝试 MSI (任何前提缺失回退纯轮询) ---- */
|
||||||
|
nvme_msi_setup(api, bus, dev, func);
|
||||||
|
|
||||||
|
/* ---- IO queue ---- */
|
||||||
|
if (nvme_create_io_queues() != 0) {
|
||||||
|
g_log->error("[nvme] IO queue creation failed");
|
||||||
|
g_log->info("[nvme] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
g_io_ready = 1;
|
||||||
|
g_log->info("[nvme] IO queue live");
|
||||||
|
|
||||||
|
/* MSI 就绪: INTMC 解除 IV0 屏蔽 (此前 IV0 保持屏蔽, admin 完成不产生
|
||||||
|
* MSI; 此后 IO CQ 每次完成经 MSI 投递到 g_msi_vector) */
|
||||||
|
if (g_msi_enabled) {
|
||||||
|
mmio_write32(NVME_INTMC, 1u);
|
||||||
|
log_hex("[nvme] INTMC unmask IV0, INTMS=", mmio_read32(NVME_INTMS));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- LBA0 读取验证 ---- */
|
||||||
|
{
|
||||||
|
int rc = nvme_io_rw(NVME_IO_READ, 0, 1, g_data.phys);
|
||||||
|
log_hex("[nvme] READ LBA0 rc=", (u64)(i64)rc);
|
||||||
|
if (rc == 0) {
|
||||||
|
u8 *d = (u8 *)g_data.virt;
|
||||||
|
u32 *w = (u32 *)g_data.virt;
|
||||||
|
log_hex("[nvme] LBA0 dword0=", w[0]);
|
||||||
|
log_hex("[nvme] LBA0 dword1=", w[1]);
|
||||||
|
log_hex("[nvme] LBA0 dword2=", w[2]);
|
||||||
|
log_hex("[nvme] LBA0 dword3=", w[3]);
|
||||||
|
static const char sig[] = "DESHABNVME0";
|
||||||
|
u32 match = 1;
|
||||||
|
for (u32 i = 0; i < 11; i++) {
|
||||||
|
if (d[i] != (u8)sig[i]) { match = 0; break; }
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
g_log->info("[nvme] LBA0 signature OK (DESHABNVME0)");
|
||||||
|
g_test_disk = 1;
|
||||||
|
} else {
|
||||||
|
g_log->info("[nvme] LBA0 data read (no test signature)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 注册 block provider(读 + 写) ---- */
|
||||||
|
if (api->block && api->block->register_device) {
|
||||||
|
struct dkm_block_device_desc desc;
|
||||||
|
desc.name = "nvme0";
|
||||||
|
desc.sector_size = g_sector_size;
|
||||||
|
desc.sector_count = g_sector_count;
|
||||||
|
desc.ctx = 0;
|
||||||
|
desc.read = nvme_block_read;
|
||||||
|
desc.write = nvme_block_write;
|
||||||
|
int index = api->block->register_device(&desc);
|
||||||
|
log_hex("[nvme] block provider index=", (u64)(i64)index);
|
||||||
|
} else {
|
||||||
|
g_log->warn("[nvme] block API unavailable; provider not registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 数据面自测: 写测试仅在签名测试盘上运行(防实机盘数据损坏) ---- */
|
||||||
|
if (g_test_disk) {
|
||||||
|
nvme_selftest_read_multipage(); /* 32 扇区 16KiB: PRP list 读 */
|
||||||
|
nvme_selftest_rw(1000, 16); /* 8KiB: PRP2 直连 (2 页) 写+读回 */
|
||||||
|
nvme_selftest_rw(2000, 32); /* 16KiB: PRP list (4 页) 写+读回 */
|
||||||
|
nvme_selftest_stress(); /* B7: 128 扇区×50 压测 + MSI 计数 */
|
||||||
|
if (nvme_lba0_sig_ok()) {
|
||||||
|
g_log->info("[nvme] LBA0 sig intact after write tests");
|
||||||
|
} else {
|
||||||
|
g_log->error("[nvme] LBA0 sig CORRUPTED after write tests");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g_log->info("[nvme] not a test disk; write self-test skipped");
|
||||||
|
}
|
||||||
|
|
||||||
|
nvme_msi_stats_dump("[nvme] --- MSI stats ---");
|
||||||
|
|
||||||
g_log->info("[nvme] driver ready");
|
g_log->info("[nvme] driver ready");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+177
-1
@@ -1,6 +1,11 @@
|
|||||||
/* DKM PCI Driver — PCI bus enumeration via config space (0xCF8/0xCFC)
|
/* DKM PCI Driver — PCI bus enumeration via config space (0xCF8/0xCFC)
|
||||||
* Stage 0, required, depends on "irq", provides "pci".
|
* Stage 0, required, depends on "irq", provides "pci".
|
||||||
* No IRQ needed for enumeration. Scans all buses/devices/functions.
|
* No IRQ needed for enumeration. Scans all buses/devices/functions.
|
||||||
|
*
|
||||||
|
* 真机增强:
|
||||||
|
* - 多总线扫描(桥接设备下游 bus > 0)
|
||||||
|
* - 网卡型号识别(常见 Intel/Realtek 网卡芯片自动标注)
|
||||||
|
* - PCI 桥检测和副总线号读取
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -82,6 +87,15 @@ struct dkm_driver_desc {
|
|||||||
#define PCI_HEADER_TYPE 0x0E
|
#define PCI_HEADER_TYPE 0x0E
|
||||||
#define PCI_BAR0 0x10
|
#define PCI_BAR0 0x10
|
||||||
|
|
||||||
|
/* PCI 桥配置 (header type 1) */
|
||||||
|
#define PCI_PRI_BUS 0x18
|
||||||
|
#define PCI_SEC_BUS 0x19
|
||||||
|
#define PCI_SUB_BUS 0x1A
|
||||||
|
|
||||||
|
#define PCI_CMD_IO (1u << 0)
|
||||||
|
#define PCI_CMD_MEM (1u << 1)
|
||||||
|
#define PCI_CMD_BUSM (1u << 2)
|
||||||
|
|
||||||
/* ---------------------------------------------------------------
|
/* ---------------------------------------------------------------
|
||||||
* Driver descriptor
|
* Driver descriptor
|
||||||
* --------------------------------------------------------------- */
|
* --------------------------------------------------------------- */
|
||||||
@@ -94,7 +108,7 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
.abi_version = DKM_ABI_VERSION,
|
.abi_version = DKM_ABI_VERSION,
|
||||||
.desc_size = sizeof(struct dkm_driver_desc),
|
.desc_size = sizeof(struct dkm_driver_desc),
|
||||||
.name = "pci",
|
.name = "pci",
|
||||||
.version = "0.1.0",
|
.version = "0.2.0",
|
||||||
.vendor = "Deshab",
|
.vendor = "Deshab",
|
||||||
.driver_class = 2, /* DKM_CLASS_BUS */
|
.driver_class = 2, /* DKM_CLASS_BUS */
|
||||||
.stage = 0,
|
.stage = 0,
|
||||||
@@ -164,6 +178,114 @@ static void log_hex(const char *prefix, u64 val) {
|
|||||||
g_log->info(buf);
|
g_log->info(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- 网卡型号识别 ---- */
|
||||||
|
struct netchip_id {
|
||||||
|
u16 vendor;
|
||||||
|
u16 device;
|
||||||
|
const char *name;
|
||||||
|
u8 driver_hint; /* 0=e1000, 1=e1000e, 2=r8169, 3=igb, 4=ath9k */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct netchip_id g_netchips[] = {
|
||||||
|
/* Intel e1000 经典 (8254x) */
|
||||||
|
{0x8086, 0x100E, "82540EM", 0},
|
||||||
|
{0x8086, 0x1004, "82543GC", 0},
|
||||||
|
{0x8086, 0x100F, "82545EM", 0},
|
||||||
|
{0x8086, 0x1010, "82546EB", 0},
|
||||||
|
{0x8086, 0x1013, "82541EI", 0},
|
||||||
|
{0x8086, 0x1019, "82547EI", 0},
|
||||||
|
{0x8086, 0x1026, "82545GM", 0},
|
||||||
|
{0x8086, 0x1075, "82547GI", 0},
|
||||||
|
{0x8086, 0x1076, "82541GI", 0},
|
||||||
|
{0x8086, 0x1079, "82546GB", 0},
|
||||||
|
{0x8086, 0x108B, "82573E", 0},
|
||||||
|
{0x8086, 0x109A, "82573L", 0},
|
||||||
|
|
||||||
|
/* Intel e1000e (8257x/I217/I218/I219) */
|
||||||
|
{0x8086, 0x10D3, "82574L", 1},
|
||||||
|
{0x8086, 0x10F5, "82578DM", 1},
|
||||||
|
{0x8086, 0x10F6, "82578DC", 1},
|
||||||
|
{0x8086, 0x1502, "82579V", 1},
|
||||||
|
{0x8086, 0x1533, "I217-V", 1},
|
||||||
|
{0x8086, 0x1534, "I217-LM", 1},
|
||||||
|
{0x8086, 0x153A, "I218-V", 1},
|
||||||
|
{0x8086, 0x153B, "I218-LM", 1},
|
||||||
|
{0x8086, 0x156F, "I219-V", 1},
|
||||||
|
{0x8086, 0x1570, "I219-LM", 1},
|
||||||
|
{0x8086, 0x157B, "I219-V-2", 1},
|
||||||
|
{0x8086, 0x15D3, "I219-V-3", 1},
|
||||||
|
{0x8086, 0x15D8, "I219-LM-3", 1},
|
||||||
|
{0x8086, 0x15BD, "I219-V-6", 1},
|
||||||
|
|
||||||
|
/* Intel I210/I211 (igb) */
|
||||||
|
{0x8086, 0x1537, "I210", 3},
|
||||||
|
{0x8086, 0x1536, "I211-AT", 3},
|
||||||
|
|
||||||
|
/* Intel 82575/82576/82580 (igb) */
|
||||||
|
{0x8086, 0x10A7, "82575EB", 3},
|
||||||
|
{0x8086, 0x10C9, "82576", 3},
|
||||||
|
{0x8086, 0x150D, "82580", 3},
|
||||||
|
{0x8086, 0x1526, "82576-2", 3},
|
||||||
|
|
||||||
|
/* Realtek RTL8111/8168/8125 (r8169) — 常见桌面/笔记本有线网卡 */
|
||||||
|
{0x10EC, 0x8167, "RTL8111/8168B", 2},
|
||||||
|
{0x10EC, 0x8168, "RTL8111/8168", 2},
|
||||||
|
{0x10EC, 0x8169, "RTL8169", 2},
|
||||||
|
{0x10EC, 0x8125, "RTL8125", 2},
|
||||||
|
{0x10EC, 0x3000, "RTL8169S", 2},
|
||||||
|
{0x10EC, 0x8136, "RTL8101E", 2},
|
||||||
|
|
||||||
|
/* Qualcomm Atheros WiFi (ath9k) */
|
||||||
|
{0x168C, 0x0023, "AR5416", 4},
|
||||||
|
{0x168C, 0x0024, "AR5418", 4},
|
||||||
|
{0x168C, 0x0027, "AR9160", 4},
|
||||||
|
{0x168C, 0x0029, "AR9280", 4},
|
||||||
|
{0x168C, 0x002A, "AR9285", 4},
|
||||||
|
{0x168C, 0x002B, "AR9287", 4},
|
||||||
|
{0x168C, 0x002D, "AR9287-2", 4},
|
||||||
|
{0x168C, 0x002E, "AR9287-3", 4},
|
||||||
|
{0x168C, 0x0030, "AR9300", 4},
|
||||||
|
{0x168C, 0x0032, "AR9485", 4},
|
||||||
|
{0x168C, 0x0034, "AR9462", 4},
|
||||||
|
{0x168C, 0x0036, "AR9485-2", 4},
|
||||||
|
{0x168C, 0x0037, "AR1111", 4},
|
||||||
|
|
||||||
|
/* Broadcom WiFi — 目前无驱动,标注识别 */
|
||||||
|
{0x14E4, 0x4315, "BCM4312-WiFi", 0xFF},
|
||||||
|
{0x14E4, 0x4365, "BCM4365-WiFi", 0xFF},
|
||||||
|
{0x14E4, 0x43A0, "BCM4360-WiFi", 0xFF},
|
||||||
|
{0x14E4, 0x43C3, "BCM4366-WiFi", 0xFF},
|
||||||
|
|
||||||
|
/* Qualcomm WiFi — 无驱动 */
|
||||||
|
{0x168C, 0x003E, "QCA6174-WiFi", 0xFF},
|
||||||
|
{0x168C, 0x0042, "QCA6390-WiFi", 0xFF},
|
||||||
|
|
||||||
|
/* Intel WiFi — 无驱动 */
|
||||||
|
{0x8086, 0x24FD, "Intel-8265-WiFi", 0xFF},
|
||||||
|
{0x8086, 0x2526, "Intel-AX200-WiFi",0xFF},
|
||||||
|
{0x8086, 0x2725, "Intel-AX210-WiFi",0xFF},
|
||||||
|
{0x8086, 0x51F0, "Intel-AX211-WiFi",0xFF},
|
||||||
|
{0x8086, 0x7A70, "Intel-BE200-WiFi",0xFF},
|
||||||
|
|
||||||
|
{0, 0, 0, 0} /* 终止符 */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *netchip_name(u16 vendor, u16 device) {
|
||||||
|
for (int i = 0; g_netchips[i].name != 0; i++) {
|
||||||
|
if (g_netchips[i].vendor == vendor && g_netchips[i].device == device)
|
||||||
|
return g_netchips[i].name;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 netchip_driver(u16 vendor, u16 device) {
|
||||||
|
for (int i = 0; g_netchips[i].name != 0; i++) {
|
||||||
|
if (g_netchips[i].vendor == vendor && g_netchips[i].device == device)
|
||||||
|
return g_netchips[i].driver_hint;
|
||||||
|
}
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
static const char *pci_class_name(u8 base, u8 sub) {
|
static const char *pci_class_name(u8 base, u8 sub) {
|
||||||
switch (base) {
|
switch (base) {
|
||||||
case 0x00: return "Legacy";
|
case 0x00: return "Legacy";
|
||||||
@@ -190,9 +312,26 @@ static const char *pci_class_name(u8 base, u8 sub) {
|
|||||||
(void)sub;
|
(void)sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PCI subclass names for Network class */
|
||||||
|
static const char *net_subclass_name(u8 sub) {
|
||||||
|
switch (sub) {
|
||||||
|
case 0x00: return "Ethernet";
|
||||||
|
case 0x01: return "TokenRing";
|
||||||
|
case 0x02: return "FDDI";
|
||||||
|
case 0x03: return "ATM";
|
||||||
|
case 0x04: return "ISDN";
|
||||||
|
case 0x05: return "WorldFIP";
|
||||||
|
case 0x06: return "PICMG";
|
||||||
|
case 0x80: return "Other";
|
||||||
|
default: return "Net-?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------
|
/* ---------------------------------------------------------------
|
||||||
* Enumeration
|
* Enumeration
|
||||||
* --------------------------------------------------------------- */
|
* --------------------------------------------------------------- */
|
||||||
|
static u32 g_device_count;
|
||||||
|
|
||||||
static void pci_scan_bus(u8 bus) {
|
static void pci_scan_bus(u8 bus) {
|
||||||
for (u8 dev = 0; dev < 32; dev++) {
|
for (u8 dev = 0; dev < 32; dev++) {
|
||||||
u32 vid_dev = pci_cfg_read(bus, dev, 0, PCI_VENDOR_ID);
|
u32 vid_dev = pci_cfg_read(bus, dev, 0, PCI_VENDOR_ID);
|
||||||
@@ -212,7 +351,9 @@ static void pci_scan_bus(u8 bus) {
|
|||||||
u32 cc = pci_cfg_read(bus, dev, func, PCI_CLASS);
|
u32 cc = pci_cfg_read(bus, dev, func, PCI_CLASS);
|
||||||
u8 class_code = (u8)(cc >> 24);
|
u8 class_code = (u8)(cc >> 24);
|
||||||
u8 subclass = (u8)((cc >> 16) & 0xff);
|
u8 subclass = (u8)((cc >> 16) & 0xff);
|
||||||
|
u8 prog_if = (u8)((cc >> 8) & 0xff);
|
||||||
|
|
||||||
|
g_device_count++;
|
||||||
g_log->info("[pci]");
|
g_log->info("[pci]");
|
||||||
log_hex("[pci] bus=", bus);
|
log_hex("[pci] bus=", bus);
|
||||||
log_hex("[pci] dev=", dev);
|
log_hex("[pci] dev=", dev);
|
||||||
@@ -223,6 +364,39 @@ static void pci_scan_bus(u8 bus) {
|
|||||||
g_log->info(pci_class_name(class_code, subclass));
|
g_log->info(pci_class_name(class_code, subclass));
|
||||||
log_hex("[pci] class_hex=", class_code);
|
log_hex("[pci] class_hex=", class_code);
|
||||||
log_hex("[pci] subclass=", subclass);
|
log_hex("[pci] subclass=", subclass);
|
||||||
|
|
||||||
|
/* 网卡自动识别与驱动匹配提示 */
|
||||||
|
if (class_code == 0x02) {
|
||||||
|
const char *chip_name = netchip_name(vendor2, device);
|
||||||
|
u8 drv = netchip_driver(vendor2, device);
|
||||||
|
g_log->info("[pci] NetChip");
|
||||||
|
if (chip_name) {
|
||||||
|
g_log->info(chip_name);
|
||||||
|
} else {
|
||||||
|
g_log->info("unknown");
|
||||||
|
}
|
||||||
|
g_log->info("[pci] NetSub");
|
||||||
|
g_log->info(net_subclass_name(subclass));
|
||||||
|
switch (drv) {
|
||||||
|
case 0: g_log->info("[pci] -> driver: e1000"); break;
|
||||||
|
case 1: g_log->info("[pci] -> driver: e1000 (e1000e compat)"); break;
|
||||||
|
case 2: g_log->info("[pci] -> driver: r8169 (NOT YET IMPLEMENTED)"); break;
|
||||||
|
case 3: g_log->info("[pci] -> driver: igb (e1000 compat)"); break;
|
||||||
|
case 4: g_log->info("[pci] -> driver: ath9k"); break;
|
||||||
|
case 0xFF: g_log->info("[pci] -> driver: NONE (unsupported)"); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PCI-PCI 桥检测: 递归扫描下游总线 */
|
||||||
|
if (class_code == 0x06 && (subclass == 0x04 || subclass == 0x09)) {
|
||||||
|
u8 sec_bus = (u8)pci_cfg_read(bus, dev, func, PCI_SEC_BUS);
|
||||||
|
if (sec_bus > bus && sec_bus < 255) {
|
||||||
|
g_log->info("[pci] bridge -> secondary bus");
|
||||||
|
log_hex("[pci] sec_bus=", sec_bus);
|
||||||
|
pci_scan_bus(sec_bus);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,8 +412,10 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
if (!api || !api->log) return -1;
|
if (!api || !api->log) return -1;
|
||||||
g_log = api->log;
|
g_log = api->log;
|
||||||
|
|
||||||
|
g_device_count = 0;
|
||||||
g_log->info("[pci] scanning PCI bus");
|
g_log->info("[pci] scanning PCI bus");
|
||||||
pci_scan_bus(0);
|
pci_scan_bus(0);
|
||||||
|
log_hex("[pci] total devices=", g_device_count);
|
||||||
g_log->info("[pci] driver ready");
|
g_log->info("[pci] driver ready");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,43 +5,10 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/* DKM ABI types */
|
#include "../dkm_shared.h"
|
||||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
#include "../dkm_instr.h"
|
||||||
#define DKM_ABI_VERSION 1u
|
|
||||||
|
|
||||||
typedef unsigned char u8;
|
DKM_STAT_DECL(ps2kbd_irq_count);
|
||||||
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 */
|
/* Driver descriptor */
|
||||||
static const char *const g_depends[] = {"irq"};
|
static const char *const g_depends[] = {"irq"};
|
||||||
@@ -86,6 +53,7 @@ static int kbd_handler(u8 irq) {
|
|||||||
(void)irq;
|
(void)irq;
|
||||||
g_last_sc = inb(0x60);
|
g_last_sc = inb(0x60);
|
||||||
g_has_key = 1;
|
g_has_key = 1;
|
||||||
|
DKM_STAT_INC(ps2kbd_irq_count);
|
||||||
return 0; /* let kernel send EOI */
|
return 0; /* let kernel send EOI */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +63,7 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
(void)handle;
|
(void)handle;
|
||||||
if (!api||!api->log) return -1;
|
if (!api||!api->log) return -1;
|
||||||
g_log=api->log;
|
g_log=api->log;
|
||||||
|
dkm_instr_init(api);
|
||||||
|
|
||||||
if (!api->irq_register) {
|
if (!api->irq_register) {
|
||||||
g_log->warn("[kbd] irq_register not available");
|
g_log->warn("[kbd] irq_register not available");
|
||||||
|
|||||||
@@ -1,7 +1,19 @@
|
|||||||
/* DKM virtio-net Driver — PCI virtio network discovery
|
/* DKM virtio-net Driver — virtio network device (legacy/transitional PCI)
|
||||||
* Stage 3 optional network driver, depends on "pci" and "irq", provides "netdev".
|
* 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.
|
* 数据路径实现(virtio spec 1.0 legacy interface,transitional device 1AF4:1001):
|
||||||
|
* - PCI 探测 → BAR0 legacy I/O 窗口
|
||||||
|
* - reset → ACKNOWLEDGE → DRIVER → feature negotiation(MAC/STATUS 子集)
|
||||||
|
* → FEATURES_OK → virtqueue 建立(RXQ0/TXQ1, legacy split-ring 布局)
|
||||||
|
* → DRIVER_OK
|
||||||
|
* - RX: 预填 32×2048B buffer 到 RXQ avail;rx_poll 收割 used ring,
|
||||||
|
* 剥 10B virtio_net_hdr 后把以太网帧交给上层协议栈,buffer 回收重填
|
||||||
|
* - TX: 8×2048B buffer 池(desc 索引与 buffer 1:1 映射),hdr+帧单描述符,
|
||||||
|
* QueueNotify kick,与 e1000 一致的同步等待完成(TSC 超时兜底)
|
||||||
|
* - boot 自测: 经真实 QEMU user-net 后端向 10.0.2.2 DHCP server 发 DISCOVER、
|
||||||
|
* 收 OFFER,一次跑通证明 TX/RX 全链路(DMA/notify/used ring)
|
||||||
|
* - IRQ 按 IDT vector(irq_line+0x20)注册做 ISR 统计;PIC 未解屏蔽时
|
||||||
|
* 自动退化为纯轮询,不依赖中断正确性
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../dkm_shared.h"
|
#include "../dkm_shared.h"
|
||||||
@@ -10,11 +22,7 @@
|
|||||||
#define PCI_CLASS_REG 0x08 /* 32-bit class/subclass/prog_if/revision */
|
#define PCI_CLASS_REG 0x08 /* 32-bit class/subclass/prog_if/revision */
|
||||||
|
|
||||||
#define PCI_VENDOR_VIRTIO 0x1AF4u
|
#define PCI_VENDOR_VIRTIO 0x1AF4u
|
||||||
#define VIRTIO_TRANS_DEVICE_MIN 0x1000u
|
/* PCI device ID 约定见 virtio_is_net_device(): transitional net=0x1000, modern net=0x1041 */
|
||||||
#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_COMMON_CFG 1u
|
||||||
#define VIRTIO_PCI_CAP_NOTIFY_CFG 2u
|
#define VIRTIO_PCI_CAP_NOTIFY_CFG 2u
|
||||||
@@ -22,6 +30,76 @@
|
|||||||
#define VIRTIO_PCI_CAP_DEVICE_CFG 4u
|
#define VIRTIO_PCI_CAP_DEVICE_CFG 4u
|
||||||
#define VIRTIO_PCI_CAP_PCI_CFG 5u
|
#define VIRTIO_PCI_CAP_PCI_CFG 5u
|
||||||
|
|
||||||
|
/* ---- legacy I/O 寄存器偏移(virtio spec 1.0 §4.1.4, BAR0 I/O space) ---- */
|
||||||
|
#define VP_HOST_FEATURES 0x00u /* u32 RO: device feature bits 0..31 */
|
||||||
|
#define VP_GUEST_FEATURES 0x04u /* u32 WO: driver 接受的 feature 子集 */
|
||||||
|
#define VP_QUEUE_PFN 0x08u /* u32 WO: 队列物理地址 >> 12 */
|
||||||
|
#define VP_QUEUE_NUM 0x0Cu /* u16 RO: 队列容量(2 的幂) */
|
||||||
|
#define VP_QUEUE_SEL 0x0Eu /* u16 WO: 选择操作的队列 */
|
||||||
|
#define VP_QUEUE_NOTIFY 0x10u /* u16 WO: kick 指定队列 */
|
||||||
|
#define VP_STATUS 0x12u /* u8 RW: device status */
|
||||||
|
#define VP_ISR 0x13u /* u8 RO: ISR,读即清除 */
|
||||||
|
#define VP_NET_CFG_MAC 0x14u /* 6B RO: MAC(device config 区起始) */
|
||||||
|
|
||||||
|
/* ---- device status bits ---- */
|
||||||
|
#define VS_ACKNOWLEDGE 0x01u
|
||||||
|
#define VS_DRIVER 0x02u
|
||||||
|
#define VS_DRIVER_OK 0x04u
|
||||||
|
#define VS_FEATURES_OK 0x08u
|
||||||
|
|
||||||
|
/* ---- virtio-net feature bits(spec §5.1.6),只取最小子集 ---- */
|
||||||
|
#define VNET_F_MAC (1u << 5) /* device config 提供 MAC */
|
||||||
|
#define VNET_F_STATUS (1u << 16) /* device config 提供 link status */
|
||||||
|
|
||||||
|
/* ---- vring ---- */
|
||||||
|
#define VRING_DESC_F_WRITE 2u /* descriptor 由 device 写(RX) */
|
||||||
|
#define VNET_HDR_SIZE 10u /* 未协商 MRG_RXBUF → virtio_net_hdr 为 10 字节 */
|
||||||
|
|
||||||
|
#define RX_BUF_COUNT 32u /* RX 预填 buffer 数(不必填满整个队列) */
|
||||||
|
#define TX_BUF_COUNT 8u /* TX buffer 池大小 */
|
||||||
|
#define VNET_BUF_SIZE 2048u /* 10B hdr + 最大 1518B 以太网帧 */
|
||||||
|
|
||||||
|
/* split-ring 结构(legacy,x86 上字段为 guest 原生小端序) */
|
||||||
|
struct vring_desc {
|
||||||
|
u64 addr;
|
||||||
|
u32 len;
|
||||||
|
u16 flags;
|
||||||
|
u16 next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vring_used_elem {
|
||||||
|
u32 id;
|
||||||
|
u32 len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vring_avail {
|
||||||
|
u16 flags;
|
||||||
|
u16 idx;
|
||||||
|
u16 ring[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vring_used {
|
||||||
|
u16 flags;
|
||||||
|
u16 idx;
|
||||||
|
struct vring_used_elem ring[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 单个 virtqueue 的运行时视图。
|
||||||
|
* legacy 布局(spec §2.4.2):
|
||||||
|
* base+0 : descriptor table(16B × size)
|
||||||
|
* base+16*size : avail ring(4 + 2×size + 2B used_event)
|
||||||
|
* align4K(16*size + 6 + 2*size): used ring(4 + 8×size + 2B avail_event)
|
||||||
|
* 整段必须物理连续且 4KiB 对齐(QueuePFN 只接受页帧号)。 */
|
||||||
|
struct virtq {
|
||||||
|
u16 qsel; /* queue select 索引(0=RX, 1=TX) */
|
||||||
|
u16 size; /* device 报告的队列容量 */
|
||||||
|
u64 phys; /* 队列内存物理基址(页对齐) */
|
||||||
|
struct vring_desc *desc;
|
||||||
|
struct vring_avail *avail;
|
||||||
|
struct vring_used *used;
|
||||||
|
u16 last_used_idx; /* driver 侧 used ring 收割游标 */
|
||||||
|
};
|
||||||
|
|
||||||
static const char *const g_depends[] = { "pci", "irq" };
|
static const char *const g_depends[] = { "pci", "irq" };
|
||||||
static const char *const g_provides[] = { "netdev" };
|
static const char *const g_provides[] = { "netdev" };
|
||||||
|
|
||||||
@@ -31,7 +109,7 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
.abi_version = DKM_ABI_VERSION,
|
.abi_version = DKM_ABI_VERSION,
|
||||||
.desc_size = sizeof(struct dkm_driver_desc),
|
.desc_size = sizeof(struct dkm_driver_desc),
|
||||||
.name = "virtio_net",
|
.name = "virtio_net",
|
||||||
.version = "0.1.0",
|
.version = "0.2.0",
|
||||||
.vendor = "Deshab",
|
.vendor = "Deshab",
|
||||||
.driver_class = 9, /* DKM_CLASS_NET */
|
.driver_class = 9, /* DKM_CLASS_NET */
|
||||||
.stage = 3,
|
.stage = 3,
|
||||||
@@ -49,6 +127,48 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
|
|
||||||
static const struct dkm_log_api *g_log;
|
static const struct dkm_log_api *g_log;
|
||||||
|
|
||||||
|
/* ---- 全局设备状态 ---- */
|
||||||
|
static u16 g_io_base; /* legacy I/O BAR 基址,0=未初始化 */
|
||||||
|
static struct virtq g_rxq;
|
||||||
|
static struct virtq g_txq;
|
||||||
|
static u8 *g_rx_buf[RX_BUF_COUNT];
|
||||||
|
static u64 g_rx_buf_phys[RX_BUF_COUNT];
|
||||||
|
static u8 *g_tx_buf[TX_BUF_COUNT];
|
||||||
|
static u64 g_tx_buf_phys[TX_BUF_COUNT];
|
||||||
|
static u8 g_tx_free[TX_BUF_COUNT]; /* 1=空闲可提交 */
|
||||||
|
static int g_ready; /* 数据路径就绪标志 */
|
||||||
|
static u8 g_mac[6];
|
||||||
|
|
||||||
|
/* ---- 统计计数器(串口可观测) ---- */
|
||||||
|
static u64 g_stat_tx_ok;
|
||||||
|
static u64 g_stat_tx_err;
|
||||||
|
static u64 g_stat_tx_timeout;
|
||||||
|
static u64 g_stat_rx_ok;
|
||||||
|
static u64 g_stat_rx_err;
|
||||||
|
static u64 g_stat_rx_overflow;
|
||||||
|
static u64 g_stat_irq_count;
|
||||||
|
|
||||||
|
/* ---- 自测缓冲区(静态 bss,避免内核栈压力) ---- */
|
||||||
|
static u8 g_st_frame[360]; /* DHCP DISCOVER 帧(342B) */
|
||||||
|
static u8 g_st_buf[2048]; /* RX 收割暂存 */
|
||||||
|
|
||||||
|
/* ---- 16-bit I/O port 访问(dkm_shared.h 只提供 8/32 位) ---- */
|
||||||
|
static __inline__ void vn_outw(u16 port, u16 value) {
|
||||||
|
__asm__ volatile ("outw %0, %1" :: "a"(value), "Nd"(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static __inline__ u16 vn_inw(u16 port) {
|
||||||
|
u16 value;
|
||||||
|
__asm__ volatile ("inw %1, %0" : "=a"(value) : "Nd"(port));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* device DMA 内存屏障:描述符/ring 写入必须先于 idx 更新对设备可见。
|
||||||
|
* x86 store-store 有序,compiler fence + mfence 双保险(控制路径,开销可忽略)。 */
|
||||||
|
static __inline__ void vn_fence(void) {
|
||||||
|
__asm__ volatile("mfence" ::: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
||||||
return dkm_pci_read(bus, dev, func, reg);
|
return dkm_pci_read(bus, dev, func, reg);
|
||||||
}
|
}
|
||||||
@@ -61,6 +181,17 @@ static u8 pci_read8(u8 bus, u8 dev, u8 func, u8 reg) {
|
|||||||
return dkm_pci_read8(bus, dev, func, reg);
|
return dkm_pci_read8(bus, dev, func, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 void zero_bytes(void *dst, u32 len) {
|
||||||
|
u8 *d = (u8 *)dst;
|
||||||
|
for (u32 i = 0; i < len; i++) d[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void log_hex(const char *prefix, u64 value) {
|
static void log_hex(const char *prefix, u64 value) {
|
||||||
static const char hex[] = "0123456789abcdef";
|
static const char hex[] = "0123456789abcdef";
|
||||||
char buf[19];
|
char buf[19];
|
||||||
@@ -75,14 +206,51 @@ static void log_hex(const char *prefix, u64 value) {
|
|||||||
g_log->info(buf);
|
g_log->info(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u16 virtio_subsystem_id(u16 device_id) {
|
static u32 dec_u32(u32 v, char *out) {
|
||||||
if (device_id >= VIRTIO_TRANS_DEVICE_MIN && device_id <= VIRTIO_TRANS_DEVICE_MAX) {
|
char tmp[10];
|
||||||
return (u16)(device_id - VIRTIO_TRANS_DEVICE_MIN);
|
u32 n = 0;
|
||||||
|
if (v == 0) { out[0] = '0'; return 1; }
|
||||||
|
while (v && n < 10) { tmp[n++] = (char)('0' + (v % 10)); v /= 10; }
|
||||||
|
for (u32 i = 0; i < n; i++) out[i] = tmp[n - 1 - i];
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 打印 IPv4 地址。ip 为主机序(高字节 = 第一个 octet,如 0x0A00020F = 10.0.2.15)。 */
|
||||||
|
static void log_ipv4(const char *prefix, u32 ip) {
|
||||||
|
char buf[24];
|
||||||
|
u32 p = 0;
|
||||||
|
for (u32 i = 0; i < 4; i++) {
|
||||||
|
u8 octet = (u8)(ip >> ((3 - i) * 8));
|
||||||
|
p += dec_u32(octet, buf + p);
|
||||||
|
if (i < 3) buf[p++] = '.';
|
||||||
}
|
}
|
||||||
if (device_id >= VIRTIO_MODERN_DEVICE_MIN && device_id <= VIRTIO_MODERN_DEVICE_MAX) {
|
buf[p] = 0;
|
||||||
return (u16)(device_id - VIRTIO_MODERN_DEVICE_MIN);
|
g_log->info(prefix);
|
||||||
|
g_log->info(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_mac(const char *prefix, const u8 mac[6]) {
|
||||||
|
static const char hex[] = "0123456789abcdef";
|
||||||
|
char buf[18];
|
||||||
|
u32 p = 0;
|
||||||
|
for (u32 i = 0; i < 6; i++) {
|
||||||
|
buf[p++] = hex[(mac[i] >> 4) & 0xf];
|
||||||
|
buf[p++] = hex[mac[i] & 0xf];
|
||||||
|
if (i < 5) buf[p++] = ':';
|
||||||
}
|
}
|
||||||
return 0xffff;
|
buf[p] = 0;
|
||||||
|
g_log->info(prefix);
|
||||||
|
g_log->info(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtio-net PCI device ID 判定。
|
||||||
|
* QEMU 编号约定(virtio 1.0 spec §4.1.2.1 + QEMU hw/virtio/virtio-pci.c):
|
||||||
|
* - transitional/legacy: 0x1000=net, 0x1001=blk, 0x1002=balloon, ...
|
||||||
|
* (历史 table,不是 0x1000+virtio_id;virtio device id 由 PCI Subsystem ID 携带)
|
||||||
|
* - modern-only: 0x1040+virtio_id → net = 0x1041
|
||||||
|
* class code (0x02=Network) 作为第二判据,与 virtio-blk(0x1001/Storage) 区分。 */
|
||||||
|
static int virtio_is_net_device(u16 device_id) {
|
||||||
|
return (device_id == 0x1000u || device_id == 0x1041u) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int virtio_net_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
static int virtio_net_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
||||||
@@ -99,10 +267,9 @@ static int virtio_net_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
|||||||
u16 device = (u16)(vd2 >> 16);
|
u16 device = (u16)(vd2 >> 16);
|
||||||
if (vendor != PCI_VENDOR_VIRTIO) continue;
|
if (vendor != PCI_VENDOR_VIRTIO) continue;
|
||||||
|
|
||||||
u16 virtio_id = virtio_subsystem_id(device);
|
|
||||||
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
|
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
|
||||||
u8 class_code = (u8)(class_reg >> 24);
|
u8 class_code = (u8)(class_reg >> 24);
|
||||||
if (virtio_id == VIRTIO_NET_DEVICE_ID && class_code == 0x02) {
|
if (virtio_is_net_device(device) && class_code == 0x02) {
|
||||||
*out_bus = bus;
|
*out_bus = bus;
|
||||||
*out_dev = dev;
|
*out_dev = dev;
|
||||||
*out_func = func;
|
*out_func = func;
|
||||||
@@ -178,28 +345,455 @@ static void log_virtio_caps(u8 bus, u8 dev, u8 func) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- tx/rx_poll stub: 安全占位, 不实际收发, 防止 netdev 调用 NULL ---- */
|
/* ================= legacy device 初始化序列 ================= */
|
||||||
static int virtio_net_tx(void *ctx, const void *packet, u32 length) {
|
|
||||||
(void)ctx;
|
/* device status 寄存器读写(8 位) */
|
||||||
(void)packet;
|
static void vdev_set_status(u8 status) {
|
||||||
/* 仅记录日志, 避免每次发包刷屏: 仅记录长度 */
|
dkm_outb((u16)(g_io_base + VP_STATUS), status);
|
||||||
static u32 s_tx_log_guard = 0;
|
}
|
||||||
if (s_tx_log_guard < 4) {
|
|
||||||
g_log->info("[virtio_net] tx stub: packet dropped (virtqueues not enabled)");
|
static u8 vdev_get_status(void) {
|
||||||
log_hex("[virtio_net] tx stub len=", length);
|
return dkm_inb((u16)(g_io_base + VP_STATUS));
|
||||||
s_tx_log_guard++;
|
}
|
||||||
|
|
||||||
|
/* reset → ACKNOWLEDGE → DRIVER → feature 协商 → FEATURES_OK。
|
||||||
|
* 返回 0 成功;失败返回负数(调用方回退 stub 注册)。 */
|
||||||
|
static int vdev_negotiate(void) {
|
||||||
|
/* 1. reset: 写 0 并等待设备读回 0(spec §4.1.4.3 要求轮询确认) */
|
||||||
|
vdev_set_status(0);
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
|
||||||
|
while (vdev_get_status() != 0) {
|
||||||
|
if (dkm_rdtsc() > deadline) {
|
||||||
|
g_log->error("[virtio_net] device reset timeout");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2/3. ACKNOWLEDGE → DRIVER(status 位按序累积写入) */
|
||||||
|
vdev_set_status(VS_ACKNOWLEDGE);
|
||||||
|
vdev_set_status(VS_ACKNOWLEDGE | VS_DRIVER);
|
||||||
|
|
||||||
|
/* 4. feature negotiation: 只取 MAC + STATUS 子集。
|
||||||
|
* 不接受 MRG_RXBUF/CTRL_VQ/CSUM/GSO 等复杂特性 →
|
||||||
|
* virtio_net_hdr 固定 10 字节、无控制队列、无卸载,数据路径最简。 */
|
||||||
|
u32 dev_feat = dkm_inl((u16)(g_io_base + VP_HOST_FEATURES));
|
||||||
|
log_hex("[virtio_net] device features=", dev_feat);
|
||||||
|
u32 subset = dev_feat & (VNET_F_MAC | VNET_F_STATUS);
|
||||||
|
dkm_outl((u16)(g_io_base + VP_GUEST_FEATURES), subset);
|
||||||
|
log_hex("[virtio_net] guest features=", subset);
|
||||||
|
|
||||||
|
/* 5. FEATURES_OK 并回读确认(spec §3.1.1)。
|
||||||
|
* 纯 legacy(pre-1.0)设备没有该位概念;QEMU transitional 实现 1.0 语义会置位。
|
||||||
|
* 未置位时按 legacy 兼容策略 warn 后继续(与 Linux legacy 驱动一致)。 */
|
||||||
|
vdev_set_status(VS_ACKNOWLEDGE | VS_DRIVER | VS_FEATURES_OK);
|
||||||
|
u8 st = vdev_get_status();
|
||||||
|
log_hex("[virtio_net] status after FEATURES_OK=", st);
|
||||||
|
if (!(st & VS_FEATURES_OK)) {
|
||||||
|
g_log->warn("[virtio_net] FEATURES_OK not echoed; continuing as pre-1.0 legacy device");
|
||||||
|
} else {
|
||||||
|
g_log->info("[virtio_net] FEATURES_OK confirmed");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* QueuePFN = 物理地址 >> 12(legacy 队列地址寄存器语义) */
|
||||||
|
static void vn_outl_pfn(const struct virtq *q) {
|
||||||
|
dkm_outl((u16)(g_io_base + VP_QUEUE_PFN), (u32)(q->phys >> 12));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 建立单个 virtqueue:读容量 → 计算 legacy 布局 → 分配连续页 → 写 QueuePFN。 */
|
||||||
|
static int vq_setup(const struct dkm_dma_api *dma, struct virtq *q, u16 qsel) {
|
||||||
|
q->qsel = qsel;
|
||||||
|
q->last_used_idx = 0;
|
||||||
|
|
||||||
|
vn_outw((u16)(g_io_base + VP_QUEUE_SEL), qsel);
|
||||||
|
q->size = vn_inw((u16)(g_io_base + VP_QUEUE_NUM));
|
||||||
|
if (q->size == 0) {
|
||||||
|
g_log->error("[virtio_net] queue size is 0");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* legacy 布局(spec §2.4.2):
|
||||||
|
* desc @ 0,avail @ 16*size,used @ 4KiB 对齐边界,整段物理连续。 */
|
||||||
|
u64 avail_off = 16ULL * q->size;
|
||||||
|
u64 used_off = (avail_off + 6ULL + 2ULL * q->size + 4095ULL) & ~4095ULL;
|
||||||
|
u64 total = used_off + 6ULL + 8ULL * q->size;
|
||||||
|
u64 pages = (total + 4095ULL) / 4096ULL;
|
||||||
|
|
||||||
|
struct dkm_dma_buffer buf;
|
||||||
|
/* 低 4G + 页对齐:QueuePFN 只接受 phys>>12,alloc_pages 返回已清零页 */
|
||||||
|
if (dma->alloc_pages(pages, 4096, 0x100000000ULL, &buf) != 0) {
|
||||||
|
g_log->error("[virtio_net] queue memory alloc failed");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->phys = buf.phys;
|
||||||
|
q->desc = (struct vring_desc *)buf.virt;
|
||||||
|
q->avail = (struct vring_avail *)((u8 *)buf.virt + avail_off);
|
||||||
|
q->used = (struct vring_used *)((u8 *)buf.virt + used_off);
|
||||||
|
|
||||||
|
vn_outl_pfn(q);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* kick 队列:写 queue index 到 QueueNotify */
|
||||||
|
static void vq_notify(const struct virtq *q) {
|
||||||
|
vn_outw((u16)(g_io_base + VP_QUEUE_NOTIFY), q->qsel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 把描述符挂到 avail ring 并推进 idx。
|
||||||
|
* 顺序约束:desc 字段必须先写好(调用方责任),ring 写入 → fence → idx 推进。 */
|
||||||
|
static void vq_push_avail(struct virtq *q, u16 desc_idx) {
|
||||||
|
u16 slot = (u16)(q->avail->idx % q->size);
|
||||||
|
q->avail->ring[slot] = desc_idx;
|
||||||
|
vn_fence();
|
||||||
|
q->avail->idx = (u16)(q->avail->idx + 1u);
|
||||||
|
vn_fence();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 从 used ring 取一个完成项。返回 0=取到,1=空。 */
|
||||||
|
static int vq_pop_used(struct virtq *q, u16 *out_id, u32 *out_len) {
|
||||||
|
vn_fence();
|
||||||
|
if (q->used->idx == q->last_used_idx) return 1;
|
||||||
|
struct vring_used_elem *e = &q->used->ring[q->last_used_idx % q->size];
|
||||||
|
*out_id = (u16)e->id;
|
||||||
|
*out_len = e->len;
|
||||||
|
vn_fence();
|
||||||
|
q->last_used_idx = (u16)(q->last_used_idx + 1u);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================= RX 路径 ================= */
|
||||||
|
|
||||||
|
/* 回收 RX buffer:重置描述符并重新挂回 avail ring,保持 RX 水位。 */
|
||||||
|
static void rx_requeue(u16 id) {
|
||||||
|
g_rxq.desc[id].addr = g_rx_buf_phys[id];
|
||||||
|
g_rxq.desc[id].len = VNET_BUF_SIZE;
|
||||||
|
g_rxq.desc[id].flags = VRING_DESC_F_WRITE;
|
||||||
|
g_rxq.desc[id].next = 0;
|
||||||
|
vq_push_avail(&g_rxq, id);
|
||||||
|
vq_notify(&g_rxq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 预填全部 RX buffer(单描述符覆盖 hdr+帧,device 可写)。 */
|
||||||
|
static int rx_prefill(const struct dkm_dma_api *dma) {
|
||||||
|
for (u32 i = 0; i < RX_BUF_COUNT; i++) {
|
||||||
|
struct dkm_dma_buffer b;
|
||||||
|
if (dma->alloc_pages(1, 4096, 0x100000000ULL, &b) != 0) {
|
||||||
|
g_log->error("[virtio_net] rx buffer alloc failed");
|
||||||
|
log_hex("[virtio_net] filled=", i);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
g_rx_buf[i] = (u8 *)b.virt;
|
||||||
|
g_rx_buf_phys[i] = b.phys;
|
||||||
|
|
||||||
|
g_rxq.desc[i].addr = b.phys;
|
||||||
|
g_rxq.desc[i].len = VNET_BUF_SIZE;
|
||||||
|
g_rxq.desc[i].flags = VRING_DESC_F_WRITE;
|
||||||
|
g_rxq.desc[i].next = 0;
|
||||||
|
g_rxq.avail->ring[i] = (u16)i;
|
||||||
|
}
|
||||||
|
vn_fence();
|
||||||
|
g_rxq.avail->idx = (u16)RX_BUF_COUNT;
|
||||||
|
vn_fence();
|
||||||
|
log_hex("[virtio_net] rx buffers prefilled=", RX_BUF_COUNT);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* netdev rx_poll 回调(契约与 e1000 一致):
|
||||||
|
* 返回 0 且 *out_length>0 = 收到一帧;1 = 无数据;负数 = 错误。 */
|
||||||
static int virtio_net_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
|
static int virtio_net_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
|
||||||
|
(void)ctx;
|
||||||
|
if (out_length) *out_length = 0;
|
||||||
|
if (!g_ready || !buffer || !out_length) return -1;
|
||||||
|
|
||||||
|
u16 id;
|
||||||
|
u32 total;
|
||||||
|
if (vq_pop_used(&g_rxq, &id, &total) != 0) return 1; /* 无完成项 */
|
||||||
|
|
||||||
|
/* 防御:id 越界或总长小于 hdr → 丢帧(id 无效时无法回收,只计数) */
|
||||||
|
if (id >= RX_BUF_COUNT) {
|
||||||
|
g_stat_rx_err++;
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
if (total < VNET_HDR_SIZE) {
|
||||||
|
g_stat_rx_err++;
|
||||||
|
rx_requeue(id);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 frame_len = total - VNET_HDR_SIZE; /* 剥掉 virtio_net_hdr */
|
||||||
|
if (frame_len > capacity) {
|
||||||
|
g_stat_rx_overflow++;
|
||||||
|
rx_requeue(id);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_bytes(buffer, g_rx_buf[id] + VNET_HDR_SIZE, frame_len);
|
||||||
|
*out_length = frame_len;
|
||||||
|
rx_requeue(id);
|
||||||
|
g_stat_rx_ok++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================= TX 路径 ================= */
|
||||||
|
|
||||||
|
/* 收割 TX used ring,把完成的 buffer 标回空闲。 */
|
||||||
|
static void tx_reclaim(void) {
|
||||||
|
u16 id;
|
||||||
|
u32 len;
|
||||||
|
while (vq_pop_used(&g_txq, &id, &len) == 0) {
|
||||||
|
(void)len; /* TX 的 used.len 无语义,仅 id 用于回收 */
|
||||||
|
if (id < TX_BUF_COUNT) g_tx_free[id] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tx_buffers_alloc(const struct dkm_dma_api *dma) {
|
||||||
|
for (u32 i = 0; i < TX_BUF_COUNT; i++) {
|
||||||
|
struct dkm_dma_buffer b;
|
||||||
|
if (dma->alloc_pages(1, 4096, 0x100000000ULL, &b) != 0) {
|
||||||
|
g_log->error("[virtio_net] tx buffer alloc failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
g_tx_buf[i] = (u8 *)b.virt;
|
||||||
|
g_tx_buf_phys[i] = b.phys;
|
||||||
|
g_tx_free[i] = 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* netdev tx 回调:hdr(10B 全零,无卸载) + 帧单描述符提交,kick 后同步等待完成。
|
||||||
|
* 与 e1000 行为一致:TSC 1 秒超时兜底,避免上层协议栈无限阻塞。 */
|
||||||
|
static int virtio_net_tx(void *ctx, const void *packet, u32 length) {
|
||||||
|
(void)ctx;
|
||||||
|
if (!g_ready || !packet || length == 0 || length + VNET_HDR_SIZE > VNET_BUF_SIZE) {
|
||||||
|
g_stat_tx_err++;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tx_reclaim();
|
||||||
|
int slot = -1;
|
||||||
|
for (u32 i = 0; i < TX_BUF_COUNT; i++) {
|
||||||
|
if (g_tx_free[i]) { slot = (int)i; break; }
|
||||||
|
}
|
||||||
|
if (slot < 0) {
|
||||||
|
g_stat_tx_err++;
|
||||||
|
g_log->warn("[virtio_net] tx ring full");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 *buf = g_tx_buf[slot];
|
||||||
|
zero_bytes(buf, VNET_HDR_SIZE); /* virtio_net_hdr: flags/gso 全 0 */
|
||||||
|
copy_bytes(buf + VNET_HDR_SIZE, packet, length);
|
||||||
|
|
||||||
|
/* desc 索引与 buffer 1:1 映射,回收时 used.id 即 buffer 索引 */
|
||||||
|
g_txq.desc[slot].addr = g_tx_buf_phys[slot];
|
||||||
|
g_txq.desc[slot].len = length + VNET_HDR_SIZE;
|
||||||
|
g_txq.desc[slot].flags = 0; /* device 只读 */
|
||||||
|
g_txq.desc[slot].next = 0;
|
||||||
|
g_tx_free[slot] = 0;
|
||||||
|
|
||||||
|
vq_push_avail(&g_txq, (u16)slot);
|
||||||
|
vq_notify(&g_txq);
|
||||||
|
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
|
tx_reclaim();
|
||||||
|
if (g_tx_free[slot]) {
|
||||||
|
g_stat_tx_ok++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
g_stat_tx_timeout++;
|
||||||
|
g_log->warn("[virtio_net] tx timeout");
|
||||||
|
log_hex("[virtio_net] txq used idx=", g_txq.used->idx);
|
||||||
|
log_hex("[virtio_net] txq last_used=", g_txq.last_used_idx);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================= IRQ(辅助统计,数据路径不依赖) ================= */
|
||||||
|
|
||||||
|
static int virtio_net_irq(u8 irq) {
|
||||||
|
(void)irq;
|
||||||
|
if (!g_io_base) return 0;
|
||||||
|
/* ISR 读即清除;bit0=queue used buffer 通知,bit1=config change */
|
||||||
|
u8 isr = dkm_inb((u16)(g_io_base + VP_ISR));
|
||||||
|
if (isr & 0x1u) g_stat_irq_count++;
|
||||||
|
return 0; /* 不 suppress EOI,由 IDT 发 PIC EOI */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================= boot 自测:DHCP DISCOVER/OFFER ================= */
|
||||||
|
|
||||||
|
#define ST_XID 0x4B1D3A5Fu
|
||||||
|
#define ST_FRAME_LEN 342u /* 14 eth + 20 ip + 8 udp + 300 bootp */
|
||||||
|
|
||||||
|
/* 构造 DHCP DISCOVER(全手工大端字段,避免 packed struct 字节序歧义)。
|
||||||
|
* BOOTP 载荷补齐到 300 字节(RFC 1542 最小值,slirp DHCP server 容忍度最高)。 */
|
||||||
|
static void st_build_discover(const u8 mac[6]) {
|
||||||
|
u8 *f = g_st_frame;
|
||||||
|
zero_bytes(f, ST_FRAME_LEN);
|
||||||
|
|
||||||
|
/* Ethernet: 广播 DISCOVER */
|
||||||
|
for (u32 i = 0; i < 6; i++) f[i] = 0xff;
|
||||||
|
for (u32 i = 0; i < 6; i++) f[6 + i] = mac[i];
|
||||||
|
f[12] = 0x08; f[13] = 0x00; /* IPv4 */
|
||||||
|
|
||||||
|
/* IPv4 @14 */
|
||||||
|
u8 *ip = f + 14;
|
||||||
|
ip[0] = 0x45; /* v4, ihl=5 */
|
||||||
|
ip[1] = 0x00;
|
||||||
|
ip[2] = 0x01; ip[3] = 0x48; /* total len = 328 */
|
||||||
|
ip[4] = 0x00; ip[5] = 0x00; /* id */
|
||||||
|
ip[6] = 0x00; ip[7] = 0x00; /* flags/frag */
|
||||||
|
ip[8] = 64; /* ttl */
|
||||||
|
ip[9] = 17; /* UDP */
|
||||||
|
ip[10] = 0x00; ip[11] = 0x00; /* cksum 占位 */
|
||||||
|
/* src 0.0.0.0 @12..15 保持 0 */
|
||||||
|
ip[16] = 0xff; ip[17] = 0xff; ip[18] = 0xff; ip[19] = 0xff;
|
||||||
|
|
||||||
|
/* IP header checksum(标准 1's complement,大端语义直接按字节对累加) */
|
||||||
|
u32 sum = 0;
|
||||||
|
for (u32 i = 0; i < 20; i += 2) sum += ((u32)ip[i] << 8) | ip[i + 1];
|
||||||
|
while (sum >> 16) sum = (sum & 0xffffu) + (sum >> 16);
|
||||||
|
u16 ck = (u16)(~sum & 0xffffu);
|
||||||
|
ip[10] = (u8)(ck >> 8); ip[11] = (u8)ck;
|
||||||
|
|
||||||
|
/* UDP @34: 68 → 67,校验和置 0(IPv4 下合法) */
|
||||||
|
u8 *udp = f + 34;
|
||||||
|
udp[0] = 0x00; udp[1] = 68;
|
||||||
|
udp[2] = 0x00; udp[3] = 67;
|
||||||
|
udp[4] = 0x01; udp[5] = 0x34; /* len = 308 */
|
||||||
|
udp[6] = 0x00; udp[7] = 0x00;
|
||||||
|
|
||||||
|
/* BOOTP @42(固定 236B + magic 4B + options,pad 到 300B) */
|
||||||
|
u8 *bp = f + 42;
|
||||||
|
bp[0] = 1; /* op = BOOTREQUEST */
|
||||||
|
bp[1] = 1; /* htype = Ethernet */
|
||||||
|
bp[2] = 6; /* hlen */
|
||||||
|
bp[3] = 0; /* hops */
|
||||||
|
bp[4] = (u8)(ST_XID >> 24);
|
||||||
|
bp[5] = (u8)(ST_XID >> 16);
|
||||||
|
bp[6] = (u8)(ST_XID >> 8);
|
||||||
|
bp[7] = (u8)ST_XID;
|
||||||
|
/* secs @8..9 = 0 */
|
||||||
|
bp[10] = 0x80; bp[11] = 0x00; /* flags = broadcast */
|
||||||
|
/* ciaddr/yiaddr/siaddr/giaddr @12..27 = 0 */
|
||||||
|
for (u32 i = 0; i < 6; i++) bp[28 + i] = mac[i]; /* chaddr(16B,后 10B 为 0) */
|
||||||
|
/* sname @44..107, file @108..235 = 0 */
|
||||||
|
bp[236] = 0x63; bp[237] = 0x82; bp[238] = 0x53; bp[239] = 0x63; /* magic cookie */
|
||||||
|
u8 *opt = bp + 240;
|
||||||
|
opt[0] = 53; opt[1] = 1; opt[2] = 1; /* DHCP msg type = DISCOVER */
|
||||||
|
opt[3] = 55; opt[4] = 4; /* param request list */
|
||||||
|
opt[5] = 1; opt[6] = 3; opt[7] = 6; opt[8] = 15; /* subnet/router/dns/domain */
|
||||||
|
opt[9] = 255; /* end */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 解析帧是否为匹配 XID 的 DHCP OFFER。
|
||||||
|
* 返回 2=OFFER(*out_yiaddr 有效,主机序),5=ACK,0=无关帧。 */
|
||||||
|
static int st_parse_reply(const u8 *f, u32 len, u32 *out_yiaddr) {
|
||||||
|
if (len < 14 + 20 + 8 + 240) return 0;
|
||||||
|
if (f[12] != 0x08 || f[13] != 0x00) return 0; /* 非 IPv4 */
|
||||||
|
u32 ihl = (u32)(f[14] & 0x0f) * 4u;
|
||||||
|
if (ihl < 20) return 0;
|
||||||
|
if (f[14 + 9] != 17) return 0; /* 非 UDP */
|
||||||
|
u32 udp = 14 + ihl;
|
||||||
|
if (len < udp + 8 + 240) return 0;
|
||||||
|
u16 dport = (u16)(((u16)f[udp + 2] << 8) | f[udp + 3]);
|
||||||
|
if (dport != 68) return 0; /* 非 DHCP 回包 */
|
||||||
|
|
||||||
|
u32 bp = udp + 8;
|
||||||
|
if (f[bp] != 2) return 0; /* 非 BOOTREPLY */
|
||||||
|
u32 xid = ((u32)f[bp + 4] << 24) | ((u32)f[bp + 5] << 16) |
|
||||||
|
((u32)f[bp + 6] << 8) | f[bp + 7];
|
||||||
|
if (xid != ST_XID) return 0; /* XID 不匹配 */
|
||||||
|
if (f[bp + 236] != 0x63 || f[bp + 237] != 0x82 ||
|
||||||
|
f[bp + 238] != 0x53 || f[bp + 239] != 0x63) return 0;
|
||||||
|
|
||||||
|
u32 yiaddr = ((u32)f[bp + 16] << 24) | ((u32)f[bp + 17] << 16) |
|
||||||
|
((u32)f[bp + 18] << 8) | f[bp + 19];
|
||||||
|
|
||||||
|
/* options 区扫描 msg type(53) */
|
||||||
|
u32 i = bp + 240;
|
||||||
|
while (i < len) {
|
||||||
|
u8 o = f[i];
|
||||||
|
if (o == 255) break;
|
||||||
|
if (o == 0) { i++; continue; }
|
||||||
|
if (i + 1 >= len) break;
|
||||||
|
u8 olen = f[i + 1];
|
||||||
|
if (o == 53 && olen == 1 && i + 2 < len) {
|
||||||
|
u8 mtype = f[i + 2];
|
||||||
|
if (mtype == 2 || mtype == 5) {
|
||||||
|
*out_yiaddr = yiaddr;
|
||||||
|
return mtype;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
i += 2u + olen;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* boot 自测:经真实网络后端验证 TX(desc+notify+used 回收)与
|
||||||
|
* RX(avail 预填 + used 收割 + hdr 剥离)全链路。
|
||||||
|
* 任何失败只记日志,绝不阻塞/崩溃启动流程。 */
|
||||||
|
static void virtio_net_selftest(void) {
|
||||||
|
if (!g_ready) return;
|
||||||
|
g_log->info("[virtio_net] selftest begin: DHCP DISCOVER -> expect OFFER from backend");
|
||||||
|
|
||||||
|
st_build_discover(g_mac);
|
||||||
|
int txrc = virtio_net_tx(0, g_st_frame, ST_FRAME_LEN);
|
||||||
|
log_hex("[virtio_net] selftest tx rc=", (u64)(i64)txrc);
|
||||||
|
if (txrc != 0) {
|
||||||
|
g_log->warn("[virtio_net] selftest: TX path FAILED");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_log->info("[virtio_net] selftest: TX path ok (descriptor consumed by device)");
|
||||||
|
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
|
||||||
|
u32 frames = 0;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
|
u32 flen = 0;
|
||||||
|
int rc = virtio_net_rx_poll(0, g_st_buf, sizeof(g_st_buf), &flen);
|
||||||
|
if (rc == 0 && flen > 0) {
|
||||||
|
frames++;
|
||||||
|
u32 ethertype = ((u32)g_st_buf[12] << 8) | g_st_buf[13];
|
||||||
|
log_hex("[virtio_net] selftest rx frame len=", flen);
|
||||||
|
log_hex("[virtio_net] ethertype=", ethertype);
|
||||||
|
u32 yiaddr = 0;
|
||||||
|
int t = st_parse_reply(g_st_buf, flen, &yiaddr);
|
||||||
|
if (t == 2) {
|
||||||
|
log_ipv4("[virtio_net] selftest: DHCP OFFER yiaddr=", yiaddr);
|
||||||
|
g_log->info("[virtio_net] selftest PASS: RX path ok (frame from real backend)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
g_log->warn("[virtio_net] selftest: no DHCP OFFER within 2000ms");
|
||||||
|
log_hex("[virtio_net] selftest rx frames seen=", frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================= stub(初始化失败时的安全占位,与历史行为一致) ================= */
|
||||||
|
|
||||||
|
static int virtio_net_tx_stub(void *ctx, const void *packet, u32 length) {
|
||||||
|
(void)ctx;
|
||||||
|
(void)packet;
|
||||||
|
log_hex("[virtio_net] tx stub: packet dropped, len=", length);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int virtio_net_rx_poll_stub(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
(void)buffer;
|
(void)buffer;
|
||||||
(void)capacity;
|
(void)capacity;
|
||||||
if (out_length) *out_length = 0;
|
if (out_length) *out_length = 0;
|
||||||
return 0; /* 无数据, virtqueues 未启用 */
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================= driver 入口 ================= */
|
||||||
|
|
||||||
__attribute__((visibility("default")))
|
__attribute__((visibility("default")))
|
||||||
int driver_init(const struct dkm_kernel_api *api,
|
int driver_init(const struct dkm_kernel_api *api,
|
||||||
struct dkm_driver_handle *handle) {
|
struct dkm_driver_handle *handle) {
|
||||||
@@ -230,19 +824,11 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID);
|
u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID);
|
||||||
u16 vendor = (u16)(vd & 0xffff);
|
u16 vendor = (u16)(vd & 0xffff);
|
||||||
u16 device = (u16)(vd >> 16);
|
u16 device = (u16)(vd >> 16);
|
||||||
u16 virtio_id = virtio_subsystem_id(device);
|
|
||||||
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
|
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
|
||||||
u8 revision = (u8)(class_reg & 0xff);
|
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] vendor=", vendor);
|
||||||
log_hex("[virtio_net] device=", device);
|
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);
|
log_hex("[virtio_net] revision=", revision);
|
||||||
|
|
||||||
u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
|
u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
|
||||||
@@ -256,21 +842,96 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
log_bars(bus, dev, func);
|
log_bars(bus, dev, func);
|
||||||
log_virtio_caps(bus, dev, func);
|
log_virtio_caps(bus, dev, func);
|
||||||
|
|
||||||
|
/* ---- legacy I/O BAR 检查:transitional 设备 BAR0 必须是 I/O space ---- */
|
||||||
|
u32 bar0 = pci_read(bus, dev, func, PCI_BAR0);
|
||||||
|
int datapath_ok = 0;
|
||||||
|
|
||||||
|
if (!(bar0 & 1u)) {
|
||||||
|
g_log->warn("[virtio_net] BAR0 is not I/O space (modern-only device?); legacy path unavailable");
|
||||||
|
} else if (!api->dma || !api->dma->alloc_pages) {
|
||||||
|
g_log->warn("[virtio_net] dma API unavailable");
|
||||||
|
} else {
|
||||||
|
g_io_base = (u16)(bar0 & 0xfffcU);
|
||||||
|
log_hex("[virtio_net] legacy io_base=", g_io_base);
|
||||||
|
|
||||||
|
if (vdev_negotiate() != 0) {
|
||||||
|
g_log->warn("[virtio_net] feature negotiation failed");
|
||||||
|
} else if (vq_setup(api->dma, &g_rxq, 0) != 0) {
|
||||||
|
g_log->warn("[virtio_net] RXQ setup failed");
|
||||||
|
} else if (vq_setup(api->dma, &g_txq, 1) != 0) {
|
||||||
|
g_log->warn("[virtio_net] TXQ setup failed");
|
||||||
|
} else if (rx_prefill(api->dma) != 0) {
|
||||||
|
g_log->warn("[virtio_net] RX prefill failed");
|
||||||
|
} else if (tx_buffers_alloc(api->dma) != 0) {
|
||||||
|
g_log->warn("[virtio_net] TX buffer alloc failed");
|
||||||
|
} else {
|
||||||
|
log_hex("[virtio_net] rxq size=", g_rxq.size);
|
||||||
|
log_hex("[virtio_net] rxq phys=", g_rxq.phys);
|
||||||
|
log_hex("[virtio_net] txq size=", g_txq.size);
|
||||||
|
log_hex("[virtio_net] txq phys=", g_txq.phys);
|
||||||
|
|
||||||
|
/* MAC: device config @ io_base+0x14(协商 MAC feature 后有效) */
|
||||||
|
for (u32 i = 0; i < 6; i++) {
|
||||||
|
g_mac[i] = dkm_inb((u16)(g_io_base + VP_NET_CFG_MAC + i));
|
||||||
|
}
|
||||||
|
u8 mac_sum = 0;
|
||||||
|
for (u32 i = 0; i < 6; i++) mac_sum |= g_mac[i];
|
||||||
|
if (mac_sum == 0 || (g_mac[0] & 1u)) {
|
||||||
|
/* 全 0 或组播位异常 → fallback */
|
||||||
|
g_mac[0] = 0x52; g_mac[1] = 0x54; g_mac[2] = 0x00;
|
||||||
|
g_mac[3] = 0x12; g_mac[4] = 0x34; g_mac[5] = 0x57;
|
||||||
|
g_log->warn("[virtio_net] invalid device MAC, using fallback");
|
||||||
|
}
|
||||||
|
log_mac("[virtio_net] MAC=", g_mac);
|
||||||
|
|
||||||
|
/* DRIVER_OK:设备开始处理队列;随后 kick RXQ 让设备吃掉预填 buffer */
|
||||||
|
vdev_set_status(VS_ACKNOWLEDGE | VS_DRIVER | VS_FEATURES_OK | VS_DRIVER_OK);
|
||||||
|
vq_notify(&g_rxq);
|
||||||
|
g_ready = 1;
|
||||||
|
datapath_ok = 1;
|
||||||
|
g_log->info("[virtio_net] DRIVER_OK set; virtqueues live");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- IRQ 注册(辅助统计;数据路径为纯轮询,不依赖中断) ----
|
||||||
|
* 注意: kernel irq_register 按 IDT vector 索引(PIC IRQn → vector 0x20+n),
|
||||||
|
* 传裸 IRQ 号会落到异常向量槽位永不触发(e1000 当前如此)。
|
||||||
|
* PIC 未解屏蔽该线时自动退化为纯轮询。 */
|
||||||
|
if (datapath_ok && api->irq_register && irq_line > 0 && irq_line < 16) {
|
||||||
|
api->irq_register((u8)(irq_line + 0x20u), (void *)(uintptr_t)virtio_net_irq);
|
||||||
|
g_log->info("[virtio_net] IRQ handler registered (vector = irq + 0x20)");
|
||||||
|
} else {
|
||||||
|
g_log->info("[virtio_net] IRQ not registered (polling mode)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 注册 netdev ---- */
|
||||||
if (api->net && api->net->register_device) {
|
if (api->net && api->net->register_device) {
|
||||||
struct dkm_net_device_desc netdev;
|
struct dkm_net_device_desc netdev;
|
||||||
|
zero_bytes(&netdev, sizeof(netdev));
|
||||||
netdev.name = "virtio-net";
|
netdev.name = "virtio-net";
|
||||||
for (int i = 0; i < 6; i++) netdev.mac[i] = 0;
|
for (u32 i = 0; i < 6; i++) netdev.mac[i] = g_mac[i];
|
||||||
netdev.flags = DKM_NET_F_LINK_UP;
|
|
||||||
netdev.ctx = 0;
|
netdev.ctx = 0;
|
||||||
netdev.tx = virtio_net_tx; /* stub: 安全占位, 不 NULL */
|
if (datapath_ok) {
|
||||||
netdev.rx_poll = virtio_net_rx_poll;
|
netdev.flags = DKM_NET_F_LINK_UP | DKM_NET_F_TX_READY | DKM_NET_F_RX_READY;
|
||||||
|
netdev.tx = virtio_net_tx;
|
||||||
|
netdev.rx_poll = virtio_net_rx_poll;
|
||||||
|
} else {
|
||||||
|
/* 安全占位:flags 仅 LINK_UP,netman 不会选它跑 DHCP(与历史行为一致) */
|
||||||
|
netdev.flags = DKM_NET_F_LINK_UP;
|
||||||
|
netdev.tx = virtio_net_tx_stub;
|
||||||
|
netdev.rx_poll = virtio_net_rx_poll_stub;
|
||||||
|
}
|
||||||
int net_index = api->net->register_device(&netdev);
|
int net_index = api->net->register_device(&netdev);
|
||||||
log_hex("[virtio_net] netdev register rc=", (u64)(i64)net_index);
|
log_hex("[virtio_net] netdev registered, index=", (u64)(i64)net_index);
|
||||||
} else {
|
} else {
|
||||||
g_log->warn("[virtio_net] net API unavailable; netdev not registered");
|
g_log->warn("[virtio_net] net API unavailable; netdev not registered");
|
||||||
}
|
}
|
||||||
|
|
||||||
g_log->info("[virtio_net] discovery only; virtqueues disabled");
|
/* ---- boot 自测(证明 TX/RX 全链路,失败仅记日志) ---- */
|
||||||
|
virtio_net_selftest();
|
||||||
|
|
||||||
|
log_hex("[virtio_net] stat tx_ok=", g_stat_tx_ok);
|
||||||
|
log_hex("[virtio_net] stat rx_ok=", g_stat_rx_ok);
|
||||||
g_log->info("[virtio_net] driver ready");
|
g_log->info("[virtio_net] driver ready");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -278,5 +939,11 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
__attribute__((visibility("default")))
|
__attribute__((visibility("default")))
|
||||||
int driver_exit(struct dkm_driver_handle *handle) {
|
int driver_exit(struct dkm_driver_handle *handle) {
|
||||||
(void)handle;
|
(void)handle;
|
||||||
|
/* reset 设备:停止一切 DMA 与队列活动 */
|
||||||
|
if (g_io_base) {
|
||||||
|
vdev_set_status(0);
|
||||||
|
}
|
||||||
|
g_ready = 0;
|
||||||
|
g_io_base = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,1783 @@
|
|||||||
|
/* DKM xHCI Driver — USB xHCI host controller + Mass Storage Class (BOT/SCSI)
|
||||||
|
*
|
||||||
|
* Stage 1 optional storage driver, depends on "pci" and "irq", provides "block".
|
||||||
|
*
|
||||||
|
* Architecture:
|
||||||
|
* 1. PCI discovery: class=0x0C/03/30 (Serial Bus / USB / xHCI)
|
||||||
|
* 2. MMIO mapping: 64-bit BAR0 via mm_map_mmio or HHDM fallback
|
||||||
|
* 3. Controller init: BIOS handoff, HC reset, parse capabilities
|
||||||
|
* 4. Ring setup: Command Ring, Event Ring, ERST, DCBAA, Scratchpad
|
||||||
|
* 5. Port scan: detect connected devices, port reset
|
||||||
|
* 6. USB enumeration: Enable Slot, Address Device, GET_DESCRIPTOR
|
||||||
|
* 7. MSC protocol: BOT (Bulk-Only Transport) + SCSI commands
|
||||||
|
* 8. Block provider: register usb0 via kernel_api.block
|
||||||
|
*
|
||||||
|
* Synchronous polling model (no IRQ/MSI in first version).
|
||||||
|
* All hardware waits use TSC deadline — no infinite loops.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../dkm_shared.h"
|
||||||
|
#include "../dkm_instr.h"
|
||||||
|
|
||||||
|
DKM_STAT_DECL(xhci_pci_found);
|
||||||
|
DKM_STAT_DECL(xhci_port_reset_ok);
|
||||||
|
DKM_STAT_DECL(xhci_msc_found);
|
||||||
|
DKM_STAT_DECL(xhci_block_reads);
|
||||||
|
DKM_STAT_DECL(xhci_block_writes);
|
||||||
|
|
||||||
|
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 = "xhci",
|
||||||
|
.version = "0.1.0",
|
||||||
|
.vendor = "Deshab",
|
||||||
|
.driver_class = 6, /* DKM_CLASS_STORAGE */
|
||||||
|
.stage = 1,
|
||||||
|
.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
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Constants — PCI, xHCI registers, TRB types, USB
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
#define XHCI_CLASS_SERIAL 0x0C
|
||||||
|
#define XHCI_SUBCLASS_USB 0x03
|
||||||
|
#define XHCI_PROGIF_XHCI 0x30
|
||||||
|
|
||||||
|
/* Capability Registers (MMIO + 0x00) */
|
||||||
|
#define XHCI_CAP_HCSPARAMS1 0x00
|
||||||
|
#define XHCI_CAP_HCSPARAMS2 0x04
|
||||||
|
#define XHCI_CAP_HCSPARAMS3 0x08
|
||||||
|
#define XHCI_CAP_HCCPARAMS1 0x0C
|
||||||
|
#define XHCI_CAP_DBOFF 0x14
|
||||||
|
#define XHCI_CAP_RTSOFF 0x18
|
||||||
|
|
||||||
|
/* Operational Registers (MMIO + cap_length) */
|
||||||
|
#define XHCI_OP_USBCMD 0x00
|
||||||
|
#define XHCI_OP_USBSTS 0x04
|
||||||
|
#define XHCI_OP_PAGESIZE 0x08
|
||||||
|
#define XHCI_OP_DNCTRL 0x14
|
||||||
|
#define XHCI_OP_CRCR_LO 0x18
|
||||||
|
#define XHCI_OP_CRCR_HI 0x1C
|
||||||
|
#define XHCI_OP_DCBAAP_LO 0x30
|
||||||
|
#define XHCI_OP_DCBAAP_HI 0x34
|
||||||
|
#define XHCI_OP_CONFIG 0x38
|
||||||
|
|
||||||
|
/* Port Registers (MMIO + 0x400 + port * 0x10) */
|
||||||
|
#define XHCI_PORT_BASE_OFF 0x400
|
||||||
|
#define XHCI_PORT_STRIDE 0x10
|
||||||
|
#define XHCI_PORT_SC 0x00
|
||||||
|
|
||||||
|
/* Runtime Registers (MMIO + rtsoff) */
|
||||||
|
#define XHCI_RT_MFINDEX 0x00
|
||||||
|
#define XHCI_RT_IR0_IMAN 0x20
|
||||||
|
#define XHCI_RT_IR0_IMOD 0x24
|
||||||
|
#define XHCI_RT_IR0_ERSTSZ 0x28
|
||||||
|
#define XHCI_RT_IR0_ERSTBA_LO 0x30
|
||||||
|
#define XHCI_RT_IR0_ERSTBA_HI 0x34
|
||||||
|
#define XHCI_RT_IR0_ERDP_LO 0x38
|
||||||
|
#define XHCI_RT_IR0_ERDP_HI 0x3C
|
||||||
|
|
||||||
|
/* USBCMD bits */
|
||||||
|
#define USBCMD_RS (1u << 0)
|
||||||
|
#define USBCMD_RST (1u << 1)
|
||||||
|
#define USBCMD_INTE (1u << 2)
|
||||||
|
|
||||||
|
/* USBSTS bits */
|
||||||
|
#define USBSTS_HCH (1u << 0)
|
||||||
|
#define USBSTS_EINT (1u << 3)
|
||||||
|
#define USBSTS_CNR (1u << 11)
|
||||||
|
|
||||||
|
/* PORTSC bits */
|
||||||
|
#define PORTSC_CCS (1u << 0)
|
||||||
|
#define PORTSC_PED (1u << 1)
|
||||||
|
#define PORTSC_OCA (1u << 3)
|
||||||
|
#define PORTSC_PR (1u << 4)
|
||||||
|
#define PORTSC_PLS_SHIFT 5
|
||||||
|
#define PORTSC_PLS_MASK 0xF
|
||||||
|
#define PORTSC_SPEED_SHIFT 17
|
||||||
|
#define PORTSC_SPEED_MASK 0x7
|
||||||
|
|
||||||
|
/* TRB types */
|
||||||
|
#define TRB_TYPE_NORMAL 0x01
|
||||||
|
#define TRB_TYPE_SETUP_STAGE 0x02
|
||||||
|
#define TRB_TYPE_DATA_STAGE 0x03
|
||||||
|
#define TRB_TYPE_STATUS_STAGE 0x04
|
||||||
|
#define TRB_TYPE_LINK 0x06
|
||||||
|
#define TRB_TYPE_ENABLE_SLOT 0x09
|
||||||
|
#define TRB_TYPE_DISABLE_SLOT 0x0A
|
||||||
|
#define TRB_TYPE_ADDRESS_DEV 0x0B
|
||||||
|
#define TRB_TYPE_CONFIGURE_EP 0x0C
|
||||||
|
#define TRB_TYPE_NOOP 0x17
|
||||||
|
|
||||||
|
/* Event TRB types — same namespace as command/transfer TRB types.
|
||||||
|
* Per xHCI spec §6.4: these are the TRB Type field values in the
|
||||||
|
* Event TRB control dword [15:10]. */
|
||||||
|
#define EVT_TYPE_TRANSFER 0x01 /* Transfer Event */
|
||||||
|
#define EVT_TYPE_CMD_COMPLETE 0x02 /* Command Completion Event */
|
||||||
|
#define EVT_TYPE_PORT_CHANGE 0x21 /* Port Status Change Event */
|
||||||
|
|
||||||
|
/* TRB control field helpers */
|
||||||
|
#define TRB_TYPE_SHIFT 10
|
||||||
|
#define TRB_TYPE_MASK 0x3F
|
||||||
|
#define TRB_IOC (1u << 5) /* Interrupt On Completion */
|
||||||
|
#define TRB_CH (1u << 6) /* Chain bit */
|
||||||
|
#define TRB_ENT (1u << 7) /* Evaluate Next TRB (for Link) */
|
||||||
|
#define TRB_ISP (1u << 8) /* Interrupt on Short Packet */
|
||||||
|
#define TRB_CYCLE (1u << 0)
|
||||||
|
#define TRB_TC (1u << 1) /* Toggle Cycle (for Link) */
|
||||||
|
|
||||||
|
/* Setup Stage TRB direction */
|
||||||
|
#define SETUP_DIR_OUT 0
|
||||||
|
#define SETUP_DIR_IN 1
|
||||||
|
|
||||||
|
/* USB speeds */
|
||||||
|
#define USB_SPEED_FULL 1
|
||||||
|
#define USB_SPEED_LOW 2
|
||||||
|
#define USB_SPEED_HIGH 3
|
||||||
|
#define USB_SPEED_SUPER 4
|
||||||
|
|
||||||
|
/* Endpoint types for EP Context */
|
||||||
|
#define EP_TYPE_CTRL_OUT 0
|
||||||
|
#define EP_TYPE_CTRL_IN 4
|
||||||
|
#define EP_TYPE_BULK_OUT 2
|
||||||
|
#define EP_TYPE_BULK_IN 6
|
||||||
|
|
||||||
|
/* Slot states */
|
||||||
|
#define SLOT_STATE_DISABLED 0
|
||||||
|
#define SLOT_STATE_ENABLED 1
|
||||||
|
#define SLOT_STATE_ADDRESSED 2
|
||||||
|
#define SLOT_STATE_CONFIGURED 3
|
||||||
|
|
||||||
|
/* Ring sizes */
|
||||||
|
#define CMD_RING_SIZE 256
|
||||||
|
#define EVT_RING_SIZE 256
|
||||||
|
#define XFER_RING_SIZE 256
|
||||||
|
|
||||||
|
/* xHCI MMIO mapping size (64 KB covers most controllers) */
|
||||||
|
#define XHCI_MAP_SIZE 0x10000
|
||||||
|
|
||||||
|
/* BOT (Bulk-Only Transport) signatures */
|
||||||
|
#define BOT_CBW_SIG 0x43425355u /* "USBC" */
|
||||||
|
#define BOT_CSW_SIG 0x53425355u /* "USBS" */
|
||||||
|
|
||||||
|
/* SCSI opcodes */
|
||||||
|
#define SCSI_TEST_UNIT_READY 0x00
|
||||||
|
#define SCSI_INQUIRY 0x12
|
||||||
|
#define SCSI_READ_CAPACITY10 0x25
|
||||||
|
#define SCSI_READ10 0x28
|
||||||
|
#define SCSI_WRITE10 0x2A
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Data structures
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u64 parameter; /* DW0-DW1 */
|
||||||
|
u32 status; /* DW2 */
|
||||||
|
u32 control; /* DW3 */
|
||||||
|
} xhci_trb;
|
||||||
|
|
||||||
|
/* TRB Ring */
|
||||||
|
typedef struct {
|
||||||
|
struct dkm_dma_buffer dma;
|
||||||
|
u32 size; /* TRB count */
|
||||||
|
u32 enqueue; /* producer index */
|
||||||
|
u32 ccs; /* producer Cycle State */
|
||||||
|
} xhci_ring;
|
||||||
|
|
||||||
|
/* ERST entry */
|
||||||
|
typedef struct {
|
||||||
|
u64 seg_addr;
|
||||||
|
u32 seg_size;
|
||||||
|
u32 rsvd;
|
||||||
|
} xhci_erst_entry;
|
||||||
|
|
||||||
|
/* MSC device state */
|
||||||
|
typedef struct {
|
||||||
|
u8 slot_id;
|
||||||
|
u8 speed;
|
||||||
|
u8 bulk_out_ep; /* EP number for Bulk OUT */
|
||||||
|
u8 bulk_in_ep; /* EP number for Bulk IN */
|
||||||
|
u16 bulk_out_mps; /* Max Packet Size */
|
||||||
|
u16 bulk_in_mps; /* Max Packet Size */
|
||||||
|
u32 cbw_tag; /* BOT tag counter */
|
||||||
|
u64 sector_count;
|
||||||
|
u32 sector_size;
|
||||||
|
int ready;
|
||||||
|
} xhci_msc_dev;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Global state
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static const struct dkm_kernel_api *g_api;
|
||||||
|
static const struct dkm_log_api *g_log;
|
||||||
|
static volatile u8 *g_mmio; /* MMIO base virtual address */
|
||||||
|
static u32 g_cap_length; /* Capability register space length */
|
||||||
|
static u32 g_doorbell_off; /* Doorbell register offset */
|
||||||
|
static u32 g_runtime_off; /* Runtime register space offset */
|
||||||
|
static u32 g_max_slots;
|
||||||
|
static u32 g_max_ports;
|
||||||
|
static u32 g_max_intrs;
|
||||||
|
static u32 g_ctx_size; /* 32 or 64 bytes */
|
||||||
|
static u32 g_page_size; /* controller page size in bytes */
|
||||||
|
static u32 g_spb_count; /* scratchpad buffer count */
|
||||||
|
static u32 g_port_base; /* absolute MMIO offset of port register set (cap_length + 0x400) */
|
||||||
|
static u32 g_ppc; /* Per-Port Power Control (HCCPARAMS1 bit 3) */
|
||||||
|
|
||||||
|
static xhci_ring g_cmd_ring;
|
||||||
|
static xhci_ring g_evt_ring;
|
||||||
|
static u32 g_evt_ccs; /* consumer cycle state for event ring */
|
||||||
|
|
||||||
|
static struct dkm_dma_buffer g_erst; /* ERST DMA buffer */
|
||||||
|
static struct dkm_dma_buffer g_dcbaa; /* DCBAA DMA buffer */
|
||||||
|
static struct dkm_dma_buffer g_sp_bufs; /* Scratchpad buffer array */
|
||||||
|
/* Scratchpad individual pages — max 32 scratchpads for safety */
|
||||||
|
static struct dkm_dma_buffer g_sp_pages[32];
|
||||||
|
|
||||||
|
/* Per-device DMA buffers */
|
||||||
|
static struct dkm_dma_buffer g_input_ctx; /* Input Context */
|
||||||
|
static struct dkm_dma_buffer g_dev_ctx; /* Device Context (output) */
|
||||||
|
static xhci_ring g_ep0_ring; /* EP0 control transfer ring */
|
||||||
|
static xhci_ring g_bulk_out_ring;
|
||||||
|
static xhci_ring g_bulk_in_ring;
|
||||||
|
static struct dkm_dma_buffer g_xfer_buf; /* Data transfer bounce buffer */
|
||||||
|
static struct dkm_dma_buffer g_csw_buf; /* CSW receive buffer */
|
||||||
|
|
||||||
|
static xhci_msc_dev g_msc;
|
||||||
|
|
||||||
|
/* PCI device location */
|
||||||
|
static u8 g_pci_bus, g_pci_dev, g_pci_func;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Utility functions
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
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 xhci_zero(void *ptr, u32 len) {
|
||||||
|
u8 *p = (u8 *)ptr;
|
||||||
|
for (u32 i = 0; i < len; i++) p[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xhci_memcpy(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];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MMIO read/write */
|
||||||
|
static u32 mmio_read32(u32 off) {
|
||||||
|
return *(volatile u32 *)(void *)(g_mmio + off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mmio_write32(u32 off, u32 value) {
|
||||||
|
*(volatile u32 *)(void *)(g_mmio + off) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 mmio_read64(u32 off) {
|
||||||
|
u32 lo = mmio_read32(off);
|
||||||
|
u32 hi = mmio_read32(off + 4);
|
||||||
|
return ((u64)hi << 32) | lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mmio_write64(u32 off, u64 value) {
|
||||||
|
mmio_write32(off, (u32)(value & 0xffffffffu));
|
||||||
|
mmio_write32(off + 4, (u32)(value >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Memory barrier (compiler only — x86 TSO guarantees HW ordering) */
|
||||||
|
static __inline__ void xhci_mb(void) {
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 1: PCI discovery + MMIO mapping
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
||||||
|
for (u16 bus = 0; bus < 256; bus++) {
|
||||||
|
for (u8 dev = 0; dev < 32; dev++) {
|
||||||
|
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
|
||||||
|
if ((vd & 0xffff) == 0xffff) continue;
|
||||||
|
|
||||||
|
u8 header = (u8)(dkm_pci_read((u8)bus, dev, 0, PCI_HEADER) >> 16);
|
||||||
|
u8 func_count = (header & 0x80) ? 8 : 1;
|
||||||
|
for (u8 func = 0; func < func_count; func++) {
|
||||||
|
u32 vd2 = dkm_pci_read((u8)bus, dev, func, PCI_VENDOR_ID);
|
||||||
|
if ((vd2 & 0xffff) == 0xffff) continue;
|
||||||
|
|
||||||
|
u32 class_reg = dkm_pci_read((u8)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 == XHCI_CLASS_SERIAL &&
|
||||||
|
subclass == XHCI_SUBCLASS_USB &&
|
||||||
|
prog_if == XHCI_PROGIF_XHCI) {
|
||||||
|
*out_bus = (u8)bus;
|
||||||
|
*out_dev = dev;
|
||||||
|
*out_func = func;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static volatile u8 *xhci_map_bar(const struct dkm_kernel_api *api, u64 bar_phys) {
|
||||||
|
u64 bar_virt = api->hhdm_offset + bar_phys;
|
||||||
|
|
||||||
|
/* Prefer mm_map_mmio independent window */
|
||||||
|
if (api->mm_map_mmio) {
|
||||||
|
void *mapped = api->mm_map_mmio(bar_phys, XHCI_MAP_SIZE);
|
||||||
|
if (mapped) {
|
||||||
|
g_log->info("[xhci] BAR mapped via mm_map_mmio");
|
||||||
|
return (volatile u8 *)mapped;
|
||||||
|
}
|
||||||
|
g_log->warn("[xhci] mm_map_mmio failed; trying HHDM fallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if HHDM covers this BAR */
|
||||||
|
if (api->mmio && api->mmio->is_mapped) {
|
||||||
|
int level = api->mmio->is_mapped(bar_virt);
|
||||||
|
if (level > 0) {
|
||||||
|
g_log->info("[xhci] HHDM covers BAR; direct access");
|
||||||
|
return (volatile u8 *)(uintptr_t)bar_virt;
|
||||||
|
}
|
||||||
|
/* Map via page tables */
|
||||||
|
if (api->mmio->map_mmio) {
|
||||||
|
int rc = api->mmio->map_mmio(bar_phys, XHCI_MAP_SIZE);
|
||||||
|
if (rc == 0 && api->mmio->is_mapped(bar_virt) > 0) {
|
||||||
|
g_log->info("[xhci] BAR mapped via page tables");
|
||||||
|
return (volatile u8 *)(uintptr_t)bar_virt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Low BAR fallback */
|
||||||
|
if (bar_phys < 0x100000000ULL) {
|
||||||
|
g_log->warn("[xhci] assuming HHDM covers low BAR");
|
||||||
|
return (volatile u8 *)(uintptr_t)bar_virt;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_log->error("[xhci] BAR MMIO unavailable");
|
||||||
|
return (volatile u8 *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 1: BIOS Handoff via Extended Capabilities
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static void xhci_bios_handoff(void) {
|
||||||
|
u32 hcc1 = mmio_read32(XHCI_CAP_HCCPARAMS1);
|
||||||
|
u32 ext_ptr = (hcc1 >> 16) & 0xFFFF;
|
||||||
|
if (!ext_ptr) return;
|
||||||
|
|
||||||
|
/* Walk extended capabilities (offsets in 32-bit words from MMIO base) */
|
||||||
|
u32 off = ext_ptr;
|
||||||
|
for (int i = 0; i < 16 && off != 0; i++) {
|
||||||
|
u32 dw0 = mmio_read32(off * 4);
|
||||||
|
u8 id = (u8)(dw0 & 0xFF);
|
||||||
|
u8 next = (u8)((dw0 >> 8) & 0xFF);
|
||||||
|
|
||||||
|
if (id == 1) {
|
||||||
|
/* USB Legacy Support Capability */
|
||||||
|
u32 legsup = mmio_read32(off * 4 + 4);
|
||||||
|
log_hex("[xhci] USBLEGSUP=", legsup);
|
||||||
|
|
||||||
|
if (legsup & 0x0000FFFFu) {
|
||||||
|
/* BIOS owns controller — request ownership */
|
||||||
|
mmio_write32(off * 4 + 4, legsup | (1u << 16));
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
|
u32 cur = mmio_read32(off * 4 + 4);
|
||||||
|
if (!(cur & 0x0000FFFFu)) {
|
||||||
|
g_log->info("[xhci] BIOS handoff complete");
|
||||||
|
/* Clear SMI triggers */
|
||||||
|
mmio_write32(off * 4 + 8, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
off = next ? (off + next) : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 1: Controller reset and init
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_controller_reset(void) {
|
||||||
|
u32 usbcmd = mmio_read32(g_cap_length + XHCI_OP_USBCMD);
|
||||||
|
u32 usbsts = mmio_read32(g_cap_length + XHCI_OP_USBSTS);
|
||||||
|
|
||||||
|
/* If controller is running, halt it first */
|
||||||
|
if (!(usbsts & USBSTS_HCH)) {
|
||||||
|
mmio_write32(g_cap_length + XHCI_OP_USBCMD, usbcmd & ~USBCMD_RS);
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
|
if (mmio_read32(g_cap_length + XHCI_OP_USBSTS) & USBSTS_HCH) break;
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for CNR to clear */
|
||||||
|
{
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
|
if (!(mmio_read32(g_cap_length + XHCI_OP_USBSTS) & USBSTS_CNR)) break;
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assert HC reset */
|
||||||
|
usbcmd = mmio_read32(g_cap_length + XHCI_OP_USBCMD);
|
||||||
|
mmio_write32(g_cap_length + XHCI_OP_USBCMD, usbcmd | USBCMD_RST);
|
||||||
|
|
||||||
|
/* Wait for reset to complete (RST bit clears) */
|
||||||
|
{
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
|
usbcmd = mmio_read32(g_cap_length + XHCI_OP_USBCMD);
|
||||||
|
if (!(usbcmd & USBCMD_RST)) {
|
||||||
|
g_log->info("[xhci] controller reset complete");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_log->error("[xhci] controller reset timeout");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xhci_parse_caps(void) {
|
||||||
|
/* Read CAPLENGTH (low byte of MMIO+0) */
|
||||||
|
g_cap_length = mmio_read32(0) & 0xFF;
|
||||||
|
|
||||||
|
u32 hcs1 = mmio_read32(XHCI_CAP_HCSPARAMS1);
|
||||||
|
u32 hcs2 = mmio_read32(XHCI_CAP_HCSPARAMS2);
|
||||||
|
u32 hcc1 = mmio_read32(XHCI_CAP_HCCPARAMS1);
|
||||||
|
|
||||||
|
g_max_slots = hcs1 & 0xFF;
|
||||||
|
g_max_intrs = (hcs1 >> 8) & 0x7FF;
|
||||||
|
g_max_ports = (hcs1 >> 24) & 0xFF;
|
||||||
|
|
||||||
|
/* Context size: CSZ bit4 of HCCPARAMS1 */
|
||||||
|
g_ctx_size = (hcc1 & (1u << 4)) ? 64 : 32;
|
||||||
|
|
||||||
|
/* Per-Port Power Control: PPC bit3 of HCCPARAMS1 */
|
||||||
|
g_ppc = (hcc1 >> 3) & 1;
|
||||||
|
|
||||||
|
/* Scratchpad buffer count from HCSPARAMS2 */
|
||||||
|
g_spb_count = ((hcs2 >> 21) & 0x1F) | ((hcs2 >> 27) & 0x1F);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Port Register Set offset: absolute from MMIO base.
|
||||||
|
* xHCI spec: "Port Register Sets are located at offset 0x400
|
||||||
|
* from the beginning of the Operational Register Space."
|
||||||
|
* Operational Register Space starts at offset CAPLENGTH.
|
||||||
|
* Therefore absolute port offset = CAPLENGTH + 0x400.
|
||||||
|
* Verified against QEMU source (XHCI_REGS_OFFSET_PORT =
|
||||||
|
* XHCI_REGS_OFFSET_OPER + XHCI_REGS_LENGTH_OPER) and Linux
|
||||||
|
* kernel (op_regs + PORTSC_OFFSET where op_regs = base + cap_length).
|
||||||
|
*/
|
||||||
|
g_port_base = g_cap_length + XHCI_PORT_BASE_OFF;
|
||||||
|
|
||||||
|
/* Doorbell and Runtime offsets — full 32-bit, clear reserved low bits */
|
||||||
|
g_doorbell_off = mmio_read32(XHCI_CAP_DBOFF) & 0xFFFFFFF0u;
|
||||||
|
g_runtime_off = mmio_read32(XHCI_CAP_RTSOFF) & 0xFFFFFFE0u;
|
||||||
|
|
||||||
|
/* Page size */
|
||||||
|
u32 ps_reg = mmio_read32(g_cap_length + XHCI_OP_PAGESIZE);
|
||||||
|
g_page_size = 1u << ((ps_reg & 0xF) + 12);
|
||||||
|
|
||||||
|
log_hex("[xhci] CAPLEN=", g_cap_length);
|
||||||
|
log_hex("[xhci] MaxSlots=", g_max_slots);
|
||||||
|
log_hex("[xhci] MaxPorts=", g_max_ports);
|
||||||
|
log_hex("[xhci] MaxIntrs=", g_max_intrs);
|
||||||
|
log_hex("[xhci] ContextSize=", g_ctx_size);
|
||||||
|
log_hex("[xhci] PPC=", g_ppc);
|
||||||
|
log_hex("[xhci] PortBaseOff=", g_port_base);
|
||||||
|
log_hex("[xhci] ScratchpadBufs=", g_spb_count);
|
||||||
|
log_hex("[xhci] DBOFF=", g_doorbell_off);
|
||||||
|
log_hex("[xhci] RTSOFF=", g_runtime_off);
|
||||||
|
log_hex("[xhci] PageSize=", g_page_size);
|
||||||
|
|
||||||
|
if (g_max_slots == 0 || g_max_ports == 0) {
|
||||||
|
g_log->error("[xhci] invalid capability parameters");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (g_max_intrs == 0) {
|
||||||
|
g_log->warn("[xhci] MaxIntrs=0 (no interrupt register sets); polling mode only");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 2: TRB Ring management
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_ring_init(xhci_ring *ring, u32 size) {
|
||||||
|
if (!g_api->dma) return -1;
|
||||||
|
/* Allocate one page (must be 4K aligned for DMA) */
|
||||||
|
if (g_api->dma->alloc_pages(1, 4096, 0x100000000ULL, &ring->dma) != 0)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
xhci_zero(ring->dma.virt, ring->dma.size);
|
||||||
|
ring->size = size;
|
||||||
|
ring->enqueue = 0;
|
||||||
|
ring->ccs = 1; /* Producer Cycle State starts at 1 */
|
||||||
|
|
||||||
|
/* Append Link TRB at end of ring */
|
||||||
|
xhci_trb *trbs = (xhci_trb *)ring->dma.virt;
|
||||||
|
u32 link_idx = size - 1;
|
||||||
|
trbs[link_idx].parameter = ring->dma.phys;
|
||||||
|
trbs[link_idx].status = 0;
|
||||||
|
trbs[link_idx].control = (TRB_TYPE_LINK << TRB_TYPE_SHIFT) | TRB_TC | TRB_CYCLE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enqueue a TRB onto a ring and return the enqueue index before advance */
|
||||||
|
static u32 xhci_ring_enqueue(xhci_ring *ring, u64 param, u32 status, u32 control) {
|
||||||
|
xhci_trb *trbs = (xhci_trb *)ring->dma.virt;
|
||||||
|
u32 idx = ring->enqueue;
|
||||||
|
|
||||||
|
/* Set cycle bit in control */
|
||||||
|
control &= ~TRB_CYCLE;
|
||||||
|
if (ring->ccs) control |= TRB_CYCLE;
|
||||||
|
|
||||||
|
trbs[idx].parameter = param;
|
||||||
|
trbs[idx].status = status;
|
||||||
|
trbs[idx].control = control;
|
||||||
|
|
||||||
|
/* Advance enqueue */
|
||||||
|
ring->enqueue = (idx + 1) % (ring->size - 1); /* -1 to skip Link TRB */
|
||||||
|
/* If we wrapped, toggle CCS */
|
||||||
|
if (idx + 1 >= ring->size - 1)
|
||||||
|
ring->ccs ^= 1;
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ring a doorbell: slot_id=0 for command, otherwise endpoint doorbell */
|
||||||
|
static void xhci_ring_doorbell(u32 slot_id, u32 target) {
|
||||||
|
mmio_write32(g_doorbell_off + slot_id * 0x20, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 2: Event Ring polling
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
/* Poll event ring for a specific TRB type with TSC timeout.
|
||||||
|
* Returns 0 on success, fills out_param/out_status/out_control.
|
||||||
|
* Returns -1 on timeout, -2 on error completion code. */
|
||||||
|
static int xhci_poll_event(u32 expected_type, u64 timeout_ms,
|
||||||
|
u64 *out_param, u32 *out_status, u32 *out_control) {
|
||||||
|
xhci_trb *trbs = (xhci_trb *)g_evt_ring.dma.virt;
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * timeout_ms;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
xhci_trb *evt = &trbs[g_evt_ring.enqueue];
|
||||||
|
u32 ctrl = evt->control;
|
||||||
|
u8 cycle = (u8)(ctrl & TRB_CYCLE);
|
||||||
|
|
||||||
|
/* Check if this TRB is owned by us (consumer) */
|
||||||
|
if (cycle == (u8)g_evt_ccs) {
|
||||||
|
u8 type = (u8)((ctrl >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK);
|
||||||
|
|
||||||
|
/* Process this event */
|
||||||
|
if (type == expected_type) {
|
||||||
|
if (out_param) *out_param = evt->parameter;
|
||||||
|
if (out_status) *out_status = evt->status;
|
||||||
|
if (out_control)*out_control= evt->control;
|
||||||
|
|
||||||
|
/* Advance dequeue */
|
||||||
|
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
|
||||||
|
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
|
||||||
|
|
||||||
|
/* Update ERDP */
|
||||||
|
u64 erdp = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
|
||||||
|
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | g_evt_ccs);
|
||||||
|
|
||||||
|
/* Check completion code */
|
||||||
|
u32 cc = (evt->status >> 24) & 0xFF;
|
||||||
|
if (cc != 1) { /* 1 = Success */
|
||||||
|
if (cc != 0) {
|
||||||
|
log_hex("[xhci] event completion code=", cc);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unexpected event type — handle or skip */
|
||||||
|
if (type == EVT_TYPE_PORT_CHANGE) {
|
||||||
|
/* Port Status Change Event: clear PORTSC change bits */
|
||||||
|
u32 port_id = (u32)((evt->parameter >> 24) & 0xFF);
|
||||||
|
if (port_id > 0 && port_id <= g_max_ports) {
|
||||||
|
u32 port_off = g_port_base + (port_id - 1) * XHCI_PORT_STRIDE + XHCI_PORT_SC;
|
||||||
|
/* Read PORTSC and write back to clear change bits (W1C) */
|
||||||
|
u32 portsc = mmio_read32(port_off);
|
||||||
|
mmio_write32(port_off, portsc);
|
||||||
|
log_hex("[xhci] port change event, port=", port_id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_hex("[xhci] unexpected event type=", type);
|
||||||
|
}
|
||||||
|
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
|
||||||
|
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
|
||||||
|
u64 erdp2 = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
|
||||||
|
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp2 | g_evt_ccs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dkm_rdtsc() > deadline) {
|
||||||
|
g_log->warn("[xhci] event poll timeout");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 2: Command submission
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_cmd_submit(u64 param, u32 status, u32 control,
|
||||||
|
u64 timeout_ms,
|
||||||
|
u64 *out_param, u32 *out_status) {
|
||||||
|
/* Clear Event Ring interrupt pending */
|
||||||
|
mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN,
|
||||||
|
mmio_read32(g_runtime_off + XHCI_RT_IR0_IMAN) | 2u);
|
||||||
|
|
||||||
|
/* Enqueue command TRB */
|
||||||
|
xhci_ring_enqueue(&g_cmd_ring, param, status, control);
|
||||||
|
xhci_mb();
|
||||||
|
|
||||||
|
/* Debug: verify CRCR and command ring state */
|
||||||
|
log_hex("[xhci] CRCR=", mmio_read64(g_cap_length + XHCI_OP_CRCR_LO));
|
||||||
|
log_hex("[xhci] cmd ring enqueue=", g_cmd_ring.enqueue);
|
||||||
|
log_hex("[xhci] cmd ring ccs=", g_cmd_ring.ccs);
|
||||||
|
|
||||||
|
xhci_ring_doorbell(0, 0); /* Command ring doorbell */
|
||||||
|
|
||||||
|
/* Debug: check USBSTS after doorbell */
|
||||||
|
log_hex("[xhci] USBSTS after db=", mmio_read32(g_cap_length + XHCI_OP_USBSTS));
|
||||||
|
|
||||||
|
/* Poll for Command Completion Event */
|
||||||
|
/* Debug: dump event ring head entry */
|
||||||
|
{
|
||||||
|
xhci_trb *evts = (xhci_trb *)g_evt_ring.dma.virt;
|
||||||
|
log_hex("[xhci] evt ring head=", g_evt_ring.enqueue);
|
||||||
|
log_hex("[xhci] evt[0] ctrl=", evts[g_evt_ring.enqueue].control);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = xhci_poll_event(EVT_TYPE_CMD_COMPLETE, timeout_ms,
|
||||||
|
out_param, out_status, 0);
|
||||||
|
|
||||||
|
/* Clear EINT */
|
||||||
|
mmio_write32(g_cap_length + XHCI_OP_USBSTS,
|
||||||
|
mmio_read32(g_cap_length + XHCI_OP_USBSTS) | USBSTS_EINT);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 2: Setup controller rings + DCBAA + Scratchpad
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_setup_rings(void) {
|
||||||
|
if (!g_api->dma) {
|
||||||
|
g_log->warn("[xhci] DMA API unavailable");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Command Ring */
|
||||||
|
if (xhci_ring_init(&g_cmd_ring, CMD_RING_SIZE) != 0) {
|
||||||
|
g_log->error("[xhci] cmd ring alloc failed");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
log_hex("[xhci] cmd ring phys=", g_cmd_ring.dma.phys);
|
||||||
|
|
||||||
|
/* Event Ring */
|
||||||
|
if (xhci_ring_init(&g_evt_ring, EVT_RING_SIZE) != 0) {
|
||||||
|
g_log->error("[xhci] evt ring alloc failed");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
g_evt_ccs = 1; /* Consumer Cycle State starts at 1 */
|
||||||
|
log_hex("[xhci] evt ring phys=", g_evt_ring.dma.phys);
|
||||||
|
|
||||||
|
/* ERST (Event Ring Segment Table) */
|
||||||
|
if (g_api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_erst) != 0) {
|
||||||
|
g_log->error("[xhci] ERST alloc failed");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
xhci_erst_entry *erst = (xhci_erst_entry *)g_erst.virt;
|
||||||
|
xhci_zero(erst, sizeof(xhci_erst_entry));
|
||||||
|
erst->seg_addr = g_evt_ring.dma.phys;
|
||||||
|
erst->seg_size = EVT_RING_SIZE;
|
||||||
|
|
||||||
|
/* DCBAA (Device Context Base Address Array) */
|
||||||
|
u32 dcbaa_size = (g_max_slots + 1) * 8;
|
||||||
|
u32 dcbaa_pages = (dcbaa_size + 4095) / 4096;
|
||||||
|
if (dcbaa_pages == 0) dcbaa_pages = 1;
|
||||||
|
if (g_api->dma->alloc_pages(dcbaa_pages, 4096, 0x100000000ULL, &g_dcbaa) != 0) {
|
||||||
|
g_log->error("[xhci] DCBAA alloc failed");
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
xhci_zero(g_dcbaa.virt, g_dcbaa.size);
|
||||||
|
log_hex("[xhci] DCBAA phys=", g_dcbaa.phys);
|
||||||
|
|
||||||
|
/* Scratchpad Buffer Array (if needed) */
|
||||||
|
if (g_spb_count > 0) {
|
||||||
|
u32 sp_arr_size = g_spb_count * 8;
|
||||||
|
u32 sp_arr_pages = (sp_arr_size + 4095) / 4096;
|
||||||
|
if (sp_arr_pages == 0) sp_arr_pages = 1;
|
||||||
|
if (g_api->dma->alloc_pages(sp_arr_pages, 4096, 0x100000000ULL, &g_sp_bufs) != 0) {
|
||||||
|
g_log->error("[xhci] scratchpad array alloc failed");
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
xhci_zero(g_sp_bufs.virt, g_sp_bufs.size);
|
||||||
|
|
||||||
|
/* Allocate individual scratchpad buffer pages */
|
||||||
|
u32 sp_count = g_spb_count;
|
||||||
|
if (sp_count > 32) sp_count = 32; /* safety cap */
|
||||||
|
for (u32 i = 0; i < sp_count; i++) {
|
||||||
|
if (g_api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_sp_pages[i]) != 0) {
|
||||||
|
g_log->error("[xhci] scratchpad page alloc failed");
|
||||||
|
return -7;
|
||||||
|
}
|
||||||
|
xhci_zero(g_sp_pages[i].virt, g_sp_pages[i].size);
|
||||||
|
/* Fill scratchpad buffer array with physical addresses */
|
||||||
|
u64 *sp_arr = (u64 *)g_sp_bufs.virt;
|
||||||
|
sp_arr[i] = g_sp_pages[i].phys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DCBAA slot 0 points to scratchpad buffer array */
|
||||||
|
u64 *dcbaa_entries = (u64 *)g_dcbaa.virt;
|
||||||
|
dcbaa_entries[0] = g_sp_bufs.phys;
|
||||||
|
log_hex("[xhci] SPB array phys=", g_sp_bufs.phys);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xhci_controller_start(void) {
|
||||||
|
u32 cap_off = g_cap_length;
|
||||||
|
|
||||||
|
/* Configure Command Ring */
|
||||||
|
u64 crcr = g_cmd_ring.dma.phys | (u64)g_cmd_ring.ccs;
|
||||||
|
mmio_write64(cap_off + XHCI_OP_CRCR_LO, crcr);
|
||||||
|
|
||||||
|
/* Configure DCBAAP */
|
||||||
|
mmio_write64(cap_off + XHCI_OP_DCBAAP_LO, g_dcbaa.phys);
|
||||||
|
|
||||||
|
/* Configure MaxSlotsEn */
|
||||||
|
mmio_write32(cap_off + XHCI_OP_CONFIG, g_max_slots);
|
||||||
|
|
||||||
|
/* Configure Event Ring */
|
||||||
|
mmio_write32(g_runtime_off + XHCI_RT_IR0_ERSTSZ, 1);
|
||||||
|
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERSTBA_LO, g_erst.phys);
|
||||||
|
u64 erdp = g_evt_ring.dma.phys | (u64)g_evt_ccs;
|
||||||
|
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp);
|
||||||
|
|
||||||
|
/* Enable interrupt (IMAN.IE=1) */
|
||||||
|
mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN,
|
||||||
|
mmio_read32(g_runtime_off + XHCI_RT_IR0_IMAN) | (1u << 1));
|
||||||
|
|
||||||
|
/* Enable controller interrupts + start controller */
|
||||||
|
u32 usbcmd = mmio_read32(cap_off + XHCI_OP_USBCMD);
|
||||||
|
usbcmd |= USBCMD_INTE | USBCMD_RS;
|
||||||
|
mmio_write32(cap_off + XHCI_OP_USBCMD, usbcmd);
|
||||||
|
|
||||||
|
/* Wait for HCH to clear (controller running) */
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
|
u32 usbsts = mmio_read32(cap_off + XHCI_OP_USBSTS);
|
||||||
|
if (!(usbsts & USBSTS_HCH)) {
|
||||||
|
g_log->info("[xhci] controller started");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
g_log->error("[xhci] controller start timeout");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 3: Port detection and reset
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_reset_port(u32 port_idx) {
|
||||||
|
u32 port_off = g_port_base + port_idx * XHCI_PORT_STRIDE + XHCI_PORT_SC;
|
||||||
|
u32 portsc = mmio_read32(port_off);
|
||||||
|
|
||||||
|
/* Check if device is connected */
|
||||||
|
if (!(portsc & PORTSC_CCS)) return -1;
|
||||||
|
|
||||||
|
/* Already enabled? */
|
||||||
|
if (portsc & PORTSC_PED) return 0;
|
||||||
|
|
||||||
|
/* Clear any change bits (W1C), then assert port reset */
|
||||||
|
mmio_write32(port_off, portsc | PORTSC_PR);
|
||||||
|
|
||||||
|
/* Wait for PED (Port Enabled) with 500ms timeout */
|
||||||
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 500;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
|
portsc = mmio_read32(port_off);
|
||||||
|
if (portsc & PORTSC_PED) {
|
||||||
|
/* Clear port change bits (W1C: write 1 to clear) */
|
||||||
|
mmio_write32(port_off, portsc);
|
||||||
|
DKM_STAT_INC(xhci_port_reset_ok);
|
||||||
|
|
||||||
|
/* Drain any Port Status Change Events from the event ring.
|
||||||
|
* Port reset generates a Port Status Change Event that must
|
||||||
|
* be consumed before submitting new commands, otherwise the
|
||||||
|
* event ring gets congested and command completion events
|
||||||
|
* may not be delivered. */
|
||||||
|
dkm_delay_ms(10); /* Allow controller to post the event */
|
||||||
|
xhci_trb *evts = (xhci_trb *)g_evt_ring.dma.virt;
|
||||||
|
for (int drain = 0; drain < 16; drain++) {
|
||||||
|
xhci_trb *evt = &evts[g_evt_ring.enqueue];
|
||||||
|
u32 ctrl = evt->control;
|
||||||
|
u8 cycle = (u8)(ctrl & TRB_CYCLE);
|
||||||
|
if (cycle != (u8)g_evt_ccs) break; /* No more events */
|
||||||
|
u8 type = (u8)((ctrl >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK);
|
||||||
|
if (type == EVT_TYPE_PORT_CHANGE) {
|
||||||
|
u32 port_id = (u32)((evt->parameter >> 24) & 0xFF);
|
||||||
|
log_hex("[xhci] drained port change event, port=", port_id);
|
||||||
|
}
|
||||||
|
/* Advance dequeue */
|
||||||
|
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
|
||||||
|
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
|
||||||
|
u64 erdp = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
|
||||||
|
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | g_evt_ccs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Check if device disconnected during reset */
|
||||||
|
if (!(portsc & PORTSC_CCS)) return -2;
|
||||||
|
__asm__ volatile("pause");
|
||||||
|
}
|
||||||
|
g_log->warn("[xhci] port reset timeout");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 xhci_get_port_speed(u32 port_idx) {
|
||||||
|
u32 port_off = g_port_base + port_idx * XHCI_PORT_STRIDE + XHCI_PORT_SC;
|
||||||
|
u32 portsc = mmio_read32(port_off);
|
||||||
|
return (u8)((portsc >> PORTSC_SPEED_SHIFT) & PORTSC_SPEED_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 3: Enable Slot
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_enable_slot(u8 *out_slot_id) {
|
||||||
|
/* Submit Enable Slot Command TRB */
|
||||||
|
u32 control = (TRB_TYPE_ENABLE_SLOT << TRB_TYPE_SHIFT);
|
||||||
|
u64 out_param = 0;
|
||||||
|
u32 out_status = 0;
|
||||||
|
|
||||||
|
int rc = xhci_cmd_submit(0, 0, control, 5000, &out_param, &out_status);
|
||||||
|
if (rc != 0) {
|
||||||
|
g_log->error("[xhci] Enable Slot failed");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slot ID is in the upper bits of the completion event parameter */
|
||||||
|
*out_slot_id = (u8)((out_param >> 24) & 0xFF);
|
||||||
|
log_hex("[xhci] slot_id=", *out_slot_id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 3: Address Device
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_address_device(u8 slot_id, int bsr) {
|
||||||
|
/* Input Context physical address */
|
||||||
|
u64 input_ctx_phys = g_input_ctx.phys;
|
||||||
|
|
||||||
|
u32 control = (TRB_TYPE_ADDRESS_DEV << TRB_TYPE_SHIFT);
|
||||||
|
if (bsr) control |= (1u << 9); /* BSR (Block Set Address) */
|
||||||
|
|
||||||
|
/* Slot ID goes in control bits [31:24] */
|
||||||
|
control |= ((u32)slot_id << 24);
|
||||||
|
|
||||||
|
u64 out_param = 0;
|
||||||
|
u32 out_status = 0;
|
||||||
|
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status);
|
||||||
|
if (rc != 0) {
|
||||||
|
g_log->error("[xhci] Address Device failed");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 3: Configure Endpoint
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_configure_endpoint(u8 slot_id) {
|
||||||
|
u64 input_ctx_phys = g_input_ctx.phys;
|
||||||
|
u32 control = (TRB_TYPE_CONFIGURE_EP << TRB_TYPE_SHIFT) | ((u32)slot_id << 24);
|
||||||
|
|
||||||
|
u64 out_param = 0;
|
||||||
|
u32 out_status = 0;
|
||||||
|
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status);
|
||||||
|
if (rc != 0) {
|
||||||
|
g_log->error("[xhci] Configure Endpoint failed");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 3: Control Transfer (EP0)
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_control_transfer(const u8 *setup_pkt, u8 *data, u32 data_len,
|
||||||
|
u8 direction) {
|
||||||
|
/* Setup Stage TRB */
|
||||||
|
u64 setup_val = ((u64)setup_pkt[4] << 32) | ((u64)setup_pkt[5] << 40) |
|
||||||
|
((u64)setup_pkt[6] << 48) | ((u64)setup_pkt[7] << 56);
|
||||||
|
setup_val |= (u64)setup_pkt[0] | ((u64)setup_pkt[1] << 8) |
|
||||||
|
((u64)setup_pkt[2] << 16) | ((u64)setup_pkt[3] << 24);
|
||||||
|
|
||||||
|
u32 setup_status = 8; /* Setup packet length */
|
||||||
|
u32 setup_control = (TRB_TYPE_SETUP_STAGE << TRB_TYPE_SHIFT) | TRB_IOC;
|
||||||
|
/* Set direction in TRB: bit 16 of Setup Stage TRB */
|
||||||
|
if (direction) setup_control |= (1u << 16);
|
||||||
|
|
||||||
|
xhci_ring_enqueue(&g_ep0_ring, setup_val, setup_status, setup_control);
|
||||||
|
|
||||||
|
/* Data Stage TRB (if data_len > 0) */
|
||||||
|
if (data_len > 0 && data) {
|
||||||
|
/* Copy data to transfer buffer for OUT direction */
|
||||||
|
if (direction == 0) {
|
||||||
|
xhci_memcpy(g_xfer_buf.virt, data, data_len);
|
||||||
|
__asm__ volatile("wbinvd" ::: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 data_phys = direction ? g_xfer_buf.phys : g_xfer_buf.phys;
|
||||||
|
u32 data_status = data_len;
|
||||||
|
u32 data_control = (TRB_TYPE_DATA_STAGE << TRB_TYPE_SHIFT) | TRB_IOC;
|
||||||
|
if (direction) data_control |= (1u << 16); /* DIR=IN */
|
||||||
|
|
||||||
|
xhci_ring_enqueue(&g_ep0_ring, data_phys, data_status, data_control);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status Stage TRB */
|
||||||
|
u32 status_dir = direction ? 0 : 1; /* opposite of data direction */
|
||||||
|
u32 status_control = (TRB_TYPE_STATUS_STAGE << TRB_TYPE_SHIFT) | TRB_IOC;
|
||||||
|
if (status_dir) status_control |= (1u << 16);
|
||||||
|
|
||||||
|
xhci_ring_enqueue(&g_ep0_ring, 0, 0, status_control);
|
||||||
|
|
||||||
|
xhci_mb();
|
||||||
|
xhci_ring_doorbell(g_msc.slot_id, 1); /* EP1 = EP0+1 */
|
||||||
|
|
||||||
|
/* Poll for Transfer Event */
|
||||||
|
u64 timeout_ms = 5000;
|
||||||
|
u64 out_param = 0;
|
||||||
|
u32 out_status = 0;
|
||||||
|
int rc = xhci_poll_event(EVT_TYPE_TRANSFER, timeout_ms,
|
||||||
|
&out_param, &out_status, 0);
|
||||||
|
if (rc != 0) {
|
||||||
|
g_log->warn("[xhci] control transfer failed");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For IN direction, copy data from DMA buffer */
|
||||||
|
if (direction && data_len > 0 && data) {
|
||||||
|
xhci_memcpy(data, g_xfer_buf.virt, data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear EINT */
|
||||||
|
mmio_write32(g_cap_length + XHCI_OP_USBSTS,
|
||||||
|
mmio_read32(g_cap_length + XHCI_OP_USBSTS) | USBSTS_EINT);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 3: USB Descriptor reads
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_get_device_descriptor(u8 *out_desc) {
|
||||||
|
u8 setup[8];
|
||||||
|
setup[0] = 0x80; /* bmRequestType: Device-to-Host, Standard, Device */
|
||||||
|
setup[1] = 0x06; /* bRequest: GET_DESCRIPTOR */
|
||||||
|
setup[2] = 0x00; /* wValue low: Descriptor Index */
|
||||||
|
setup[3] = 0x01; /* wValue high: Device Descriptor */
|
||||||
|
setup[4] = 0x00; /* wIndex low */
|
||||||
|
setup[5] = 0x00; /* wIndex high */
|
||||||
|
setup[6] = 0x12; /* wLength low: 18 bytes */
|
||||||
|
setup[7] = 0x00; /* wLength high */
|
||||||
|
|
||||||
|
xhci_zero(out_desc, 18);
|
||||||
|
return xhci_control_transfer(setup, out_desc, 18, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xhci_get_config_descriptor(u8 *out_desc, u32 max_len) {
|
||||||
|
u8 setup[8];
|
||||||
|
setup[0] = 0x80; /* Device-to-Host, Standard, Device */
|
||||||
|
setup[1] = 0x06; /* GET_DESCRIPTOR */
|
||||||
|
setup[2] = 0x00; /* Index */
|
||||||
|
setup[3] = 0x02; /* Configuration Descriptor */
|
||||||
|
setup[4] = 0x00;
|
||||||
|
setup[5] = 0x00;
|
||||||
|
setup[6] = (u8)(max_len & 0xFF);
|
||||||
|
setup[7] = (u8)((max_len >> 8) & 0xFF);
|
||||||
|
|
||||||
|
xhci_zero(out_desc, max_len);
|
||||||
|
return xhci_control_transfer(setup, out_desc, max_len, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 4: Bulk Transfer
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_bulk_transfer(xhci_ring *ring, u8 ep_num,
|
||||||
|
u8 direction, /* 0=OUT, 1=IN */
|
||||||
|
const void *data, u32 data_len) {
|
||||||
|
if (data_len == 0) return -1;
|
||||||
|
|
||||||
|
/* For OUT, copy data to DMA buffer */
|
||||||
|
if (direction == 0) {
|
||||||
|
xhci_memcpy(g_xfer_buf.virt, data, data_len);
|
||||||
|
__asm__ volatile("wbinvd" ::: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enqueue Normal TRB(s) — single TRB for now (up to 64KB) */
|
||||||
|
u32 control = (TRB_TYPE_NORMAL << TRB_TYPE_SHIFT) | TRB_IOC;
|
||||||
|
xhci_ring_enqueue(ring, g_xfer_buf.phys, data_len, control);
|
||||||
|
|
||||||
|
xhci_mb();
|
||||||
|
xhci_ring_doorbell(g_msc.slot_id, ep_num);
|
||||||
|
|
||||||
|
/* Poll for Transfer Event */
|
||||||
|
u64 out_param = 0;
|
||||||
|
u32 out_status = 0;
|
||||||
|
int rc = xhci_poll_event(EVT_TYPE_TRANSFER, 10000,
|
||||||
|
&out_param, &out_status, 0);
|
||||||
|
if (rc != 0) {
|
||||||
|
g_log->warn("[xhci] bulk transfer failed");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For IN, copy data from DMA buffer */
|
||||||
|
if (direction == 1) {
|
||||||
|
xhci_memcpy((void *)data, g_xfer_buf.virt, data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear EINT */
|
||||||
|
mmio_write32(g_cap_length + XHCI_OP_USBSTS,
|
||||||
|
mmio_read32(g_cap_length + XHCI_OP_USBSTS) | USBSTS_EINT);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 4: BOT (Bulk-Only Transport) + SCSI
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_bot_send_cbw(u8 lun, u8 flags, u32 data_len,
|
||||||
|
const u8 *cb, u8 cb_len) {
|
||||||
|
u8 cbw[31];
|
||||||
|
xhci_zero(cbw, 31);
|
||||||
|
|
||||||
|
/* Signature */
|
||||||
|
cbw[0] = 0x55; cbw[1] = 0x53; cbw[2] = 0x42; cbw[3] = 0x43;
|
||||||
|
/* Tag (little-endian) */
|
||||||
|
u32 tag = g_msc.cbw_tag++;
|
||||||
|
cbw[4] = (u8)(tag & 0xFF);
|
||||||
|
cbw[5] = (u8)((tag >> 8) & 0xFF);
|
||||||
|
cbw[6] = (u8)((tag >> 16) & 0xFF);
|
||||||
|
cbw[7] = (u8)((tag >> 24) & 0xFF);
|
||||||
|
/* Data Transfer Length (little-endian) */
|
||||||
|
cbw[8] = (u8)(data_len & 0xFF);
|
||||||
|
cbw[9] = (u8)((data_len >> 8) & 0xFF);
|
||||||
|
cbw[10] = (u8)((data_len >> 16) & 0xFF);
|
||||||
|
cbw[11] = (u8)((data_len >> 24) & 0xFF);
|
||||||
|
/* Flags */
|
||||||
|
cbw[12] = flags;
|
||||||
|
/* LUN */
|
||||||
|
cbw[13] = lun;
|
||||||
|
/* CB Length */
|
||||||
|
cbw[14] = cb_len;
|
||||||
|
/* Command Block */
|
||||||
|
for (u8 i = 0; i < cb_len && i < 16; i++)
|
||||||
|
cbw[15 + i] = cb[i];
|
||||||
|
|
||||||
|
return xhci_bulk_transfer(&g_bulk_out_ring, g_msc.bulk_out_ep,
|
||||||
|
0, cbw, 31);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xhci_bot_receive_csw(void) {
|
||||||
|
int rc = xhci_bulk_transfer(&g_bulk_in_ring, g_msc.bulk_in_ep,
|
||||||
|
1, g_csw_buf.virt, 13);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
/* Verify CSW signature */
|
||||||
|
u8 *csw = (u8 *)g_csw_buf.virt;
|
||||||
|
if (csw[0] != 0x55 || csw[1] != 0x53 || csw[2] != 0x42 || csw[3] != 0x53) {
|
||||||
|
g_log->error("[xhci] CSW signature mismatch");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CSW status: byte 12, 0=OK */
|
||||||
|
u8 status = csw[12];
|
||||||
|
if (status != 0) {
|
||||||
|
log_hex("[xhci] CSW status=", status);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SCSI INQUIRY */
|
||||||
|
static int xhci_scsi_inquiry(void) {
|
||||||
|
u8 cb[16];
|
||||||
|
xhci_zero(cb, 16);
|
||||||
|
cb[0] = SCSI_INQUIRY;
|
||||||
|
cb[4] = 36; /* allocation length */
|
||||||
|
|
||||||
|
int rc = xhci_bot_send_cbw(0, 0x80, 36, cb, 6);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
/* Data phase: IN 36 bytes */
|
||||||
|
u8 inq_data[36];
|
||||||
|
rc = xhci_bulk_transfer(&g_bulk_in_ring, g_msc.bulk_in_ep,
|
||||||
|
1, inq_data, 36);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
/* CSW */
|
||||||
|
rc = xhci_bot_receive_csw();
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
/* Parse INQUIRY data */
|
||||||
|
u8 dev_type = inq_data[0] & 0x1F;
|
||||||
|
log_hex("[xhci] INQ PeripheralType=", dev_type);
|
||||||
|
g_log->info("[xhci] INQ Vendor:");
|
||||||
|
g_log->info((const char *)(inq_data + 8)); /* Not null-terminated but safe for log */
|
||||||
|
|
||||||
|
return (dev_type == 0x00) ? 0 : -1; /* 0 = Direct Access Block */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SCSI READ CAPACITY(10) */
|
||||||
|
static int xhci_scsi_read_capacity(u64 *out_sectors, u32 *out_blk_size) {
|
||||||
|
u8 cb[16];
|
||||||
|
xhci_zero(cb, 16);
|
||||||
|
cb[0] = SCSI_READ_CAPACITY10;
|
||||||
|
|
||||||
|
int rc = xhci_bot_send_cbw(0, 0x80, 8, cb, 10);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
/* Data phase: IN 8 bytes */
|
||||||
|
u8 cap_data[8];
|
||||||
|
rc = xhci_bulk_transfer(&g_bulk_in_ring, g_msc.bulk_in_ep,
|
||||||
|
1, cap_data, 8);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
/* CSW */
|
||||||
|
rc = xhci_bot_receive_csw();
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
/* Parse big-endian */
|
||||||
|
u32 max_lba = ((u32)cap_data[0] << 24) | ((u32)cap_data[1] << 16) |
|
||||||
|
((u32)cap_data[2] << 8) | (u32)cap_data[3];
|
||||||
|
u32 blk_size = ((u32)cap_data[4] << 24) | ((u32)cap_data[5] << 16) |
|
||||||
|
((u32)cap_data[6] << 8) | (u32)cap_data[7];
|
||||||
|
|
||||||
|
*out_sectors = (u64)max_lba + 1; /* READ CAPACITY returns max LBA */
|
||||||
|
*out_blk_size = blk_size;
|
||||||
|
|
||||||
|
log_hex("[xhci] capacity sectors=", *out_sectors);
|
||||||
|
log_hex("[xhci] block size=", blk_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SCSI TEST UNIT READY */
|
||||||
|
static int xhci_scsi_test_unit_ready(void) {
|
||||||
|
u8 cb[16];
|
||||||
|
xhci_zero(cb, 16);
|
||||||
|
cb[0] = SCSI_TEST_UNIT_READY;
|
||||||
|
|
||||||
|
int rc = xhci_bot_send_cbw(0, 0x00, 0, cb, 6);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
return xhci_bot_receive_csw();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SCSI READ(10) */
|
||||||
|
static int xhci_scsi_read10(u64 lba, u32 count, void *buffer) {
|
||||||
|
u8 cb[16];
|
||||||
|
xhci_zero(cb, 16);
|
||||||
|
cb[0] = SCSI_READ10;
|
||||||
|
cb[2] = (u8)((lba >> 24) & 0xFF);
|
||||||
|
cb[3] = (u8)((lba >> 16) & 0xFF);
|
||||||
|
cb[4] = (u8)((lba >> 8) & 0xFF);
|
||||||
|
cb[5] = (u8)(lba & 0xFF);
|
||||||
|
cb[7] = (u8)((count >> 8) & 0xFF);
|
||||||
|
cb[8] = (u8)(count & 0xFF);
|
||||||
|
|
||||||
|
u32 data_len = count * 512;
|
||||||
|
|
||||||
|
int rc = xhci_bot_send_cbw(0, 0x80, data_len, cb, 10);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
/* Data phase: IN */
|
||||||
|
rc = xhci_bulk_transfer(&g_bulk_in_ring, g_msc.bulk_in_ep,
|
||||||
|
1, buffer, data_len);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
return xhci_bot_receive_csw();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SCSI WRITE(10) */
|
||||||
|
static int xhci_scsi_write10(u64 lba, u32 count, const void *buffer) {
|
||||||
|
u8 cb[16];
|
||||||
|
xhci_zero(cb, 16);
|
||||||
|
cb[0] = SCSI_WRITE10;
|
||||||
|
cb[2] = (u8)((lba >> 24) & 0xFF);
|
||||||
|
cb[3] = (u8)((lba >> 16) & 0xFF);
|
||||||
|
cb[4] = (u8)((lba >> 8) & 0xFF);
|
||||||
|
cb[5] = (u8)(lba & 0xFF);
|
||||||
|
cb[7] = (u8)((count >> 8) & 0xFF);
|
||||||
|
cb[8] = (u8)(count & 0xFF);
|
||||||
|
|
||||||
|
u32 data_len = count * 512;
|
||||||
|
|
||||||
|
int rc = xhci_bot_send_cbw(0, 0x00, data_len, cb, 10);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
/* Data phase: OUT */
|
||||||
|
rc = xhci_bulk_transfer(&g_bulk_out_ring, g_msc.bulk_out_ep,
|
||||||
|
0, buffer, data_len);
|
||||||
|
if (rc != 0) return rc;
|
||||||
|
|
||||||
|
return xhci_bot_receive_csw();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 4+5: Block device interface
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
|
||||||
|
(void)ctx;
|
||||||
|
if (!g_msc.ready || !buffer || count == 0) return -1;
|
||||||
|
DKM_STAT_INC(xhci_block_reads);
|
||||||
|
|
||||||
|
u8 *buf = (u8 *)buffer;
|
||||||
|
while (count > 0) {
|
||||||
|
u32 chunk = count > 16 ? 16 : count; /* max 16 sectors (8KB) per SCSI cmd */
|
||||||
|
int rc = xhci_scsi_read10(lba, chunk, g_xfer_buf.virt);
|
||||||
|
if (rc != 0) {
|
||||||
|
log_hex("[xhci] READ10 failed lba=", lba);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
xhci_memcpy(buf, g_xfer_buf.virt, chunk * 512);
|
||||||
|
buf += chunk * 512;
|
||||||
|
lba += chunk;
|
||||||
|
count -= chunk;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xhci_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
|
||||||
|
(void)ctx;
|
||||||
|
if (!g_msc.ready || !buffer || count == 0) return -1;
|
||||||
|
DKM_STAT_INC(xhci_block_writes);
|
||||||
|
|
||||||
|
const u8 *buf = (const u8 *)buffer;
|
||||||
|
while (count > 0) {
|
||||||
|
u32 chunk = count > 16 ? 16 : count;
|
||||||
|
xhci_memcpy(g_xfer_buf.virt, buf, chunk * 512);
|
||||||
|
__asm__ volatile("wbinvd" ::: "memory"); /* Cache flush for DMA */
|
||||||
|
int rc = xhci_scsi_write10(lba, chunk, g_xfer_buf.virt);
|
||||||
|
if (rc != 0) {
|
||||||
|
log_hex("[xhci] WRITE10 failed lba=", lba);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
buf += chunk * 512;
|
||||||
|
lba += chunk;
|
||||||
|
count -= chunk;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 3: Build Input Context for Address Device
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static void xhci_build_input_ctx_slot(u8 slot_id, u8 speed, u16 mps0) {
|
||||||
|
(void)slot_id;
|
||||||
|
u32 ctx_sz = g_ctx_size;
|
||||||
|
u8 *ic = (u8 *)g_input_ctx.virt;
|
||||||
|
xhci_zero(ic, g_input_ctx.size);
|
||||||
|
|
||||||
|
/* Input Control Context: enable Slot Context (bit 0) and EP0 (bit 1) */
|
||||||
|
u32 *icc = (u32 *)ic;
|
||||||
|
icc[0] = (1u << 1) | (1u << 0); /* A0=1 (Slot), A1=1 (EP0) */
|
||||||
|
|
||||||
|
/* Slot Context starts at offset ctx_sz */
|
||||||
|
u32 *slot_ctx = (u32 *)(ic + ctx_sz);
|
||||||
|
u32 route_string = 0; /* direct-connect, no hub */
|
||||||
|
slot_ctx[0] = route_string | ((u32)speed << 29);
|
||||||
|
slot_ctx[1] = (u32)mps0; /* Max Packet Size for EP0 */
|
||||||
|
slot_ctx[2] = (1u << 27); /* Context Entries = 1 (only EP0) */
|
||||||
|
|
||||||
|
/* EP0 Context starts at offset ctx_sz * 2 */
|
||||||
|
u32 *ep0_ctx = (u32 *)(ic + ctx_sz * 2);
|
||||||
|
/* EP Type = Control, Max Packet Size, etc. */
|
||||||
|
ep0_ctx[1] = (4u << 3) | ((u32)mps0 << 16); /* EP Type=4 (Control IN), MPS */
|
||||||
|
ep0_ctx[4] = 8; /* Average TRB Length */
|
||||||
|
/* Dequeue Pointer: EP0 transfer ring */
|
||||||
|
u64 ep0_deq = g_ep0_ring.dma.phys | (u64)g_ep0_ring.ccs;
|
||||||
|
ep0_ctx[6] = (u32)(ep0_deq & 0xFFFFFFFFu);
|
||||||
|
ep0_ctx[7] = (u32)(ep0_deq >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xhci_build_input_ctx_bulk_eps(u8 bulk_out_ep, u8 bulk_in_ep,
|
||||||
|
u16 out_mps, u16 in_mps) {
|
||||||
|
u32 ctx_sz = g_ctx_size;
|
||||||
|
u8 *ic = (u8 *)g_input_ctx.virt;
|
||||||
|
|
||||||
|
/* Update Input Control Context: enable EP0, Bulk OUT, Bulk IN */
|
||||||
|
u32 *icc = (u32 *)ic;
|
||||||
|
icc[0] = (1u << 1) | (1u << 0) |
|
||||||
|
(1u << bulk_out_ep) | (1u << bulk_in_ep);
|
||||||
|
|
||||||
|
/* Update Slot Context: Context Entries = max(bulk_in_ep, bulk_out_ep) */
|
||||||
|
u32 *slot_ctx = (u32 *)(ic + ctx_sz);
|
||||||
|
u32 max_ep = bulk_out_ep > bulk_in_ep ? bulk_out_ep : bulk_in_ep;
|
||||||
|
slot_ctx[2] = (max_ep << 27);
|
||||||
|
|
||||||
|
/* Bulk OUT Endpoint Context (at offset ctx_sz * (1 + bulk_out_ep)) */
|
||||||
|
u32 *out_ctx = (u32 *)(ic + ctx_sz * (1 + bulk_out_ep));
|
||||||
|
out_ctx[1] = (EP_TYPE_BULK_OUT << 3) | ((u32)out_mps << 16);
|
||||||
|
out_ctx[4] = 512; /* Average TRB Length */
|
||||||
|
u64 out_deq = g_bulk_out_ring.dma.phys | (u64)g_bulk_out_ring.ccs;
|
||||||
|
out_ctx[6] = (u32)(out_deq & 0xFFFFFFFFu);
|
||||||
|
out_ctx[7] = (u32)(out_deq >> 32);
|
||||||
|
|
||||||
|
/* Bulk IN Endpoint Context (at offset ctx_sz * (1 + bulk_in_ep)) */
|
||||||
|
u32 *in_ctx = (u32 *)(ic + ctx_sz * (1 + bulk_in_ep));
|
||||||
|
in_ctx[1] = (EP_TYPE_BULK_IN << 3) | ((u32)in_mps << 16);
|
||||||
|
in_ctx[4] = 512; /* Average TRB Length */
|
||||||
|
u64 in_deq = g_bulk_in_ring.dma.phys | (u64)g_bulk_in_ring.ccs;
|
||||||
|
in_ctx[6] = (u32)(in_deq & 0xFFFFFFFFu);
|
||||||
|
in_ctx[7] = (u32)(in_deq >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Phase 3: Full device enumeration
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int xhci_enum_msc_device(u32 port_idx) {
|
||||||
|
u8 speed = xhci_get_port_speed(port_idx);
|
||||||
|
log_hex("[xhci] port speed=", speed);
|
||||||
|
|
||||||
|
/* Default MaxPacketSize for EP0 based on speed */
|
||||||
|
u16 mps0 = 64; /* High/Super-Speed default */
|
||||||
|
if (speed == USB_SPEED_FULL) mps0 = 64;
|
||||||
|
if (speed == USB_SPEED_LOW) mps0 = 8;
|
||||||
|
|
||||||
|
/* Step 1: Enable Slot */
|
||||||
|
u8 slot_id = 0;
|
||||||
|
if (xhci_enable_slot(&slot_id) != 0) return -1;
|
||||||
|
g_msc.slot_id = slot_id;
|
||||||
|
g_msc.speed = speed;
|
||||||
|
|
||||||
|
/* Step 2: Allocate per-device DMA buffers */
|
||||||
|
if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_input_ctx) != 0) return -2;
|
||||||
|
if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_dev_ctx) != 0) return -2;
|
||||||
|
if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_xfer_buf) != 0) return -2;
|
||||||
|
if (g_api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_csw_buf) != 0) return -2;
|
||||||
|
if (xhci_ring_init(&g_ep0_ring, XFER_RING_SIZE) != 0) return -2;
|
||||||
|
if (xhci_ring_init(&g_bulk_out_ring, XFER_RING_SIZE) != 0) return -2;
|
||||||
|
if (xhci_ring_init(&g_bulk_in_ring, XFER_RING_SIZE) != 0) return -2;
|
||||||
|
|
||||||
|
/* Step 3: Build Input Context and Address Device (BSR=1 first) */
|
||||||
|
xhci_build_input_ctx_slot(slot_id, speed, mps0);
|
||||||
|
if (xhci_address_device(slot_id, 1) != 0) {
|
||||||
|
/* BSR=1 failed, try without BSR */
|
||||||
|
xhci_build_input_ctx_slot(slot_id, speed, mps0);
|
||||||
|
if (xhci_address_device(slot_id, 0) != 0) return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 4: Get Device Descriptor */
|
||||||
|
u8 dev_desc[18];
|
||||||
|
if (xhci_get_device_descriptor(dev_desc) != 0) {
|
||||||
|
g_log->warn("[xhci] GET_DESCRIPTOR(Device) failed");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 dev_class = dev_desc[4];
|
||||||
|
u16 vid = (u16)dev_desc[8] | ((u16)dev_desc[9] << 8);
|
||||||
|
u16 pid = (u16)dev_desc[10] | ((u16)dev_desc[11] << 8);
|
||||||
|
u8 real_mps0 = dev_desc[7];
|
||||||
|
|
||||||
|
log_hex("[xhci] dev class=", dev_class);
|
||||||
|
log_hex("[xhci] VID=", vid);
|
||||||
|
log_hex("[xhci] PID=", pid);
|
||||||
|
log_hex("[xhci] bMaxPacketSize0=", real_mps0);
|
||||||
|
|
||||||
|
/* Step 5: Re-address with real MPS if different */
|
||||||
|
if (real_mps0 != mps0 && real_mps0 >= 8) {
|
||||||
|
mps0 = real_mps0;
|
||||||
|
xhci_build_input_ctx_slot(slot_id, speed, mps0);
|
||||||
|
if (xhci_address_device(slot_id, 0) != 0) {
|
||||||
|
g_log->warn("[xhci] re-address failed");
|
||||||
|
/* Continue anyway — device may still work */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 6: Get Configuration Descriptor */
|
||||||
|
u8 cfg_desc[255];
|
||||||
|
if (xhci_get_config_descriptor(cfg_desc, 255) != 0) {
|
||||||
|
g_log->warn("[xhci] GET_DESCRIPTOR(Config) failed");
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse configuration descriptor to find MSC interface and endpoints */
|
||||||
|
u16 total_len = (u16)cfg_desc[2] | ((u16)cfg_desc[3] << 8);
|
||||||
|
if (total_len < 9) return -5;
|
||||||
|
|
||||||
|
u8 bulk_out_ep = 0, bulk_in_ep = 0;
|
||||||
|
u16 bulk_out_mps = 512, bulk_in_mps = 512;
|
||||||
|
int msc_found = 0;
|
||||||
|
|
||||||
|
/* Walk through descriptors: config(9) + interface(9) + endpoints(7*N) */
|
||||||
|
u32 pos = 9; /* skip config descriptor */
|
||||||
|
while (pos + 2 <= (u32)total_len && pos + cfg_desc[pos] <= (u32)total_len) {
|
||||||
|
u8 desc_len = cfg_desc[pos];
|
||||||
|
u8 desc_type = cfg_desc[pos + 1];
|
||||||
|
|
||||||
|
if (desc_type == 4 && desc_len >= 9) {
|
||||||
|
/* Interface Descriptor */
|
||||||
|
u8 iface_class = cfg_desc[pos + 5];
|
||||||
|
u8 iface_subclass = cfg_desc[pos + 6];
|
||||||
|
u8 iface_protocol = cfg_desc[pos + 7];
|
||||||
|
|
||||||
|
if (iface_class == 0x08 && iface_subclass == 0x06 && iface_protocol == 0x50) {
|
||||||
|
msc_found = 1;
|
||||||
|
g_log->info("[xhci] MSC BOT interface found");
|
||||||
|
|
||||||
|
/* Scan following endpoint descriptors */
|
||||||
|
u32 ep_pos = pos + desc_len;
|
||||||
|
while (ep_pos + 7 <= (u32)total_len &&
|
||||||
|
cfg_desc[ep_pos + 1] == 5) { /* endpoint descriptor */
|
||||||
|
u8 ep_addr = cfg_desc[ep_pos + 2];
|
||||||
|
u8 ep_attr = cfg_desc[ep_pos + 3];
|
||||||
|
u16 ep_mps = (u16)cfg_desc[ep_pos + 4] | ((u16)cfg_desc[ep_pos + 5] << 8);
|
||||||
|
|
||||||
|
if ((ep_attr & 0x03) == 0x02) { /* Bulk */
|
||||||
|
if (ep_addr & 0x80) {
|
||||||
|
bulk_in_ep = ep_addr & 0x0F;
|
||||||
|
bulk_in_mps = ep_mps;
|
||||||
|
} else {
|
||||||
|
bulk_out_ep = ep_addr & 0x0F;
|
||||||
|
bulk_out_mps = ep_mps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ep_pos += cfg_desc[ep_pos];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos += desc_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!msc_found || bulk_out_ep == 0 || bulk_in_ep == 0) {
|
||||||
|
g_log->warn("[xhci] no MSC BOT interface found");
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_hex("[xhci] Bulk OUT EP=", bulk_out_ep);
|
||||||
|
log_hex("[xhci] Bulk IN EP=", bulk_in_ep);
|
||||||
|
log_hex("[xhci] Bulk OUT MPS=", bulk_out_mps);
|
||||||
|
log_hex("[xhci] Bulk IN MPS=", bulk_in_mps);
|
||||||
|
|
||||||
|
g_msc.bulk_out_ep = bulk_out_ep;
|
||||||
|
g_msc.bulk_in_ep = bulk_in_ep;
|
||||||
|
g_msc.bulk_out_mps = bulk_out_mps;
|
||||||
|
g_msc.bulk_in_mps = bulk_in_mps;
|
||||||
|
|
||||||
|
/* Step 7: Configure Endpoint */
|
||||||
|
xhci_zero(g_input_ctx.virt, g_input_ctx.size);
|
||||||
|
xhci_build_input_ctx_bulk_eps(bulk_out_ep, bulk_in_ep,
|
||||||
|
bulk_out_mps, bulk_in_mps);
|
||||||
|
if (xhci_configure_endpoint(slot_id) != 0) {
|
||||||
|
g_log->warn("[xhci] Configure Endpoint failed");
|
||||||
|
return -7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 8: SCSI INQUIRY to verify MSC device */
|
||||||
|
if (xhci_scsi_inquiry() != 0) {
|
||||||
|
g_log->warn("[xhci] INQUIRY failed or not block device");
|
||||||
|
/* Continue anyway — some devices return non-zero peripheral type */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 9: TEST UNIT READY (retry up to 5 times) */
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
if (xhci_scsi_test_unit_ready() == 0) break;
|
||||||
|
dkm_delay_ms(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 10: READ CAPACITY */
|
||||||
|
u64 sectors = 0;
|
||||||
|
u32 blk_size = 512;
|
||||||
|
if (xhci_scsi_read_capacity(§ors, &blk_size) != 0) {
|
||||||
|
g_log->warn("[xhci] READ CAPACITY failed");
|
||||||
|
/* Use defaults */
|
||||||
|
sectors = 0;
|
||||||
|
blk_size = 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_msc.sector_count = sectors;
|
||||||
|
g_msc.sector_size = blk_size;
|
||||||
|
g_msc.cbw_tag = 1;
|
||||||
|
g_msc.ready = 1;
|
||||||
|
|
||||||
|
DKM_STAT_INC(xhci_msc_found);
|
||||||
|
log_hex("[xhci] MSC device ready, sectors=", sectors);
|
||||||
|
log_hex("[xhci] MSC device block_size=", blk_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* driver_init — main entry point
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
__attribute__((visibility("default")))
|
||||||
|
int driver_init(const struct dkm_kernel_api *api,
|
||||||
|
struct dkm_driver_handle *handle) {
|
||||||
|
(void)handle;
|
||||||
|
dkm_tsc_calibrate();
|
||||||
|
|
||||||
|
if (!api || !api->log) return -1;
|
||||||
|
g_api = api;
|
||||||
|
g_log = api->log;
|
||||||
|
dkm_instr_init(api);
|
||||||
|
|
||||||
|
g_log->info("[xhci] init begin");
|
||||||
|
|
||||||
|
/* ---- Phase 1: PCI discovery ---- */
|
||||||
|
u8 bus = 0, dev = 0, func = 0;
|
||||||
|
if (xhci_find(&bus, &dev, &func) != 0) {
|
||||||
|
g_log->info("[xhci] xHCI controller not found");
|
||||||
|
g_log->info("[xhci] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pci_bus = bus; g_pci_dev = dev; g_pci_func = func;
|
||||||
|
DKM_STAT_INC(xhci_pci_found);
|
||||||
|
g_log->info("[xhci] xHCI controller found");
|
||||||
|
log_hex("[xhci] bus=", bus);
|
||||||
|
log_hex("[xhci] dev=", dev);
|
||||||
|
log_hex("[xhci] func=", func);
|
||||||
|
|
||||||
|
u32 vd = dkm_pci_read(bus, dev, func, PCI_VENDOR_ID);
|
||||||
|
log_hex("[xhci] vendor=", vd & 0xffff);
|
||||||
|
log_hex("[xhci] device=", vd >> 16);
|
||||||
|
|
||||||
|
/* Enable PCI MEM + BUSM, disable INTx (polling mode) */
|
||||||
|
u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
|
||||||
|
command |= PCI_CMD_MEM | PCI_CMD_BUSM | (1u << 10);
|
||||||
|
dkm_pci_write(bus, dev, func, PCI_COMMAND, command);
|
||||||
|
log_hex("[xhci] PCI command=", command);
|
||||||
|
|
||||||
|
/* Read BAR0 (64-bit) */
|
||||||
|
u32 bar0_lo = dkm_pci_read(bus, dev, func, PCI_BAR0);
|
||||||
|
u32 bar1_hi = dkm_pci_read(bus, dev, func, PCI_BAR1);
|
||||||
|
u64 bar_phys;
|
||||||
|
if (bar0_lo & 1u) {
|
||||||
|
g_log->warn("[xhci] BAR0 is IO space; unsupported");
|
||||||
|
g_log->info("[xhci] 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 = dkm_pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
|
||||||
|
log_hex("[xhci] BAR0 phys=", bar_phys);
|
||||||
|
log_hex("[xhci] IRQ line=", irq_line);
|
||||||
|
log_hex("[xhci] HHDM offset=", api->hhdm_offset);
|
||||||
|
|
||||||
|
if (!api->hhdm_offset || !bar_phys) {
|
||||||
|
g_log->warn("[xhci] missing HHDM or BAR0; skip");
|
||||||
|
g_log->info("[xhci] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Phase 1: MMIO mapping ---- */
|
||||||
|
g_mmio = xhci_map_bar(api, bar_phys);
|
||||||
|
if (!g_mmio) {
|
||||||
|
g_log->error("[xhci] BAR MMIO unavailable; driver inactive");
|
||||||
|
g_log->info("[xhci] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Phase 1: Parse capabilities ---- */
|
||||||
|
if (xhci_parse_caps() != 0) {
|
||||||
|
g_log->error("[xhci] capability parse failed");
|
||||||
|
g_log->info("[xhci] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Phase 1: BIOS Handoff ---- */
|
||||||
|
xhci_bios_handoff();
|
||||||
|
|
||||||
|
/* ---- Phase 1: Controller Reset ---- */
|
||||||
|
if (xhci_controller_reset() != 0) {
|
||||||
|
g_log->error("[xhci] controller reset failed");
|
||||||
|
g_log->info("[xhci] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Phase 2: Setup Rings + DCBAA + Scratchpad ---- */
|
||||||
|
if (!api->dma) {
|
||||||
|
g_log->warn("[xhci] DMA API unavailable");
|
||||||
|
g_log->info("[xhci] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xhci_setup_rings() != 0) {
|
||||||
|
g_log->error("[xhci] ring setup failed");
|
||||||
|
g_log->info("[xhci] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Phase 2: Start Controller ---- */
|
||||||
|
if (xhci_controller_start() != 0) {
|
||||||
|
g_log->error("[xhci] controller start failed");
|
||||||
|
g_log->info("[xhci] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Phase 3: Port Scan + Device Enumeration ---- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait 100ms after controller start for QEMU to complete USB device
|
||||||
|
* attachment and update port status. Without this delay, PORTSC.CCS
|
||||||
|
* may read 0 even though a device is physically connected.
|
||||||
|
*/
|
||||||
|
g_log->info("[xhci] waiting 100ms for port settle...");
|
||||||
|
dkm_delay_ms(100);
|
||||||
|
|
||||||
|
g_msc.ready = 0;
|
||||||
|
for (u32 port = 0; port < g_max_ports; port++) {
|
||||||
|
u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC;
|
||||||
|
u32 portsc = mmio_read32(port_off);
|
||||||
|
|
||||||
|
/* Always log PORTSC for debugging, even if no device is connected */
|
||||||
|
log_hex("[xhci] port=", port);
|
||||||
|
log_hex("[xhci] PORTSC=", portsc);
|
||||||
|
|
||||||
|
if (!(portsc & PORTSC_CCS)) {
|
||||||
|
/*
|
||||||
|
* If PPC (Per-Port Power Control) is supported, the port may
|
||||||
|
* need to be powered on before CCS reflects device connection.
|
||||||
|
* Set PP (Port Power) bit in PORTSC to enable port power.
|
||||||
|
* If PPC=0, ports are always powered and this write is a no-op.
|
||||||
|
*/
|
||||||
|
if (g_ppc && !(portsc & (1u << 9))) {
|
||||||
|
g_log->info("[xhci] enabling port power");
|
||||||
|
mmio_write32(port_off, portsc | (1u << 9));
|
||||||
|
/* Wait briefly for power to stabilize */
|
||||||
|
dkm_delay_ms(20);
|
||||||
|
portsc = mmio_read32(port_off);
|
||||||
|
log_hex("[xhci] PORTSC after PP=", portsc);
|
||||||
|
}
|
||||||
|
if (!(portsc & PORTSC_CCS)) continue; /* Still no device */
|
||||||
|
}
|
||||||
|
|
||||||
|
g_log->info("[xhci] device connected");
|
||||||
|
|
||||||
|
/* Port Reset */
|
||||||
|
if (xhci_reset_port(port) != 0) {
|
||||||
|
g_log->warn("[xhci] port reset failed; skipping");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
g_log->info("[xhci] port reset OK");
|
||||||
|
|
||||||
|
/* Try to enumerate as MSC device */
|
||||||
|
if (xhci_enum_msc_device(port) == 0) {
|
||||||
|
g_log->info("[xhci] MSC enumeration successful");
|
||||||
|
break; /* First MSC device is enough for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
g_log->warn("[xhci] MSC enumeration failed; trying next port");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Phase 5: Register Block Device ---- */
|
||||||
|
if (g_msc.ready) {
|
||||||
|
if (api->block && api->block->register_device) {
|
||||||
|
struct dkm_block_device_desc desc;
|
||||||
|
desc.name = "usb0";
|
||||||
|
desc.sector_size = g_msc.sector_size;
|
||||||
|
desc.sector_count = g_msc.sector_count;
|
||||||
|
desc.ctx = 0;
|
||||||
|
desc.read = xhci_block_read;
|
||||||
|
desc.write = xhci_block_write;
|
||||||
|
int index = api->block->register_device(&desc);
|
||||||
|
log_hex("[xhci] block provider index=", (u64)(i64)index);
|
||||||
|
} else {
|
||||||
|
g_log->warn("[xhci] block API unavailable; provider not registered");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g_log->info("[xhci] no MSC device found; block provider not registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
g_log->info("[xhci] driver ready");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((visibility("default")))
|
||||||
|
int driver_exit(struct dkm_driver_handle *handle) {
|
||||||
|
(void)handle;
|
||||||
|
/* Halt controller if it was started */
|
||||||
|
if (g_mmio) {
|
||||||
|
u32 usbcmd = mmio_read32(g_cap_length + XHCI_OP_USBCMD);
|
||||||
|
mmio_write32(g_cap_length + XHCI_OP_USBCMD, usbcmd & ~USBCMD_RS);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Build for Windows PE64 without any MSVC toolchain:
|
||||||
|
# - lld-link (LLVM) links directly in MSVC flavor
|
||||||
|
# - all Win32 imports use raw-dylib (no import .lib files needed)
|
||||||
|
# - no CRT: we provide mainCRTStartup ourselves and link /NODEFAULTLIB
|
||||||
|
[build]
|
||||||
|
target = "x86_64-pc-windows-msvc"
|
||||||
|
|
||||||
|
[target.x86_64-pc-windows-msvc]
|
||||||
|
linker = "C:/Program Files/LLVM/bin/lld-link.exe"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=/NODEFAULTLIB",
|
||||||
|
"-C", "link-arg=/SUBSYSTEM:CONSOLE",
|
||||||
|
"-C", "link-arg=/ENTRY:mainCRTStartup",
|
||||||
|
"-C", "link-arg=/STACK:4194304",
|
||||||
|
"-C", "link-arg=/DEBUG:NONE",
|
||||||
|
]
|
||||||
Generated
+240
@@ -0,0 +1,240 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ab_glyph"
|
||||||
|
version = "0.2.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2"
|
||||||
|
dependencies = [
|
||||||
|
"ab_glyph_rasterizer",
|
||||||
|
"owned_ttf_parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ab_glyph_rasterizer"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deaicup-pe"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"egui",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecolor"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"epaint",
|
||||||
|
"nohash-hasher",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "emath"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "epaint"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b381f8b149657a4acf837095351839f32cd5c4aec1817fc4df84e18d76334176"
|
||||||
|
dependencies = [
|
||||||
|
"ab_glyph",
|
||||||
|
"ahash",
|
||||||
|
"ecolor",
|
||||||
|
"emath",
|
||||||
|
"nohash-hasher",
|
||||||
|
"parking_lot",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.189"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nohash-hasher"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "owned_ttf_parser"
|
||||||
|
version = "0.25.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
|
||||||
|
dependencies = [
|
||||||
|
"ttf-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.107"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.47"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.119"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ttf-parser"
|
||||||
|
version = "0.25.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.55"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.55"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "deaicup-pe"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# Deaicup Software — Windows PE64 target for Deshab UTSM PE compat layer.
|
||||||
|
# egui UI logic is kept; the eframe runtime is replaced by a hand-written
|
||||||
|
# Win32 backend (user32 window + GDI DIB blit) plus a pure-Rust software
|
||||||
|
# rasterizer for egui tessellated primitives.
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "deaicup"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
egui = { version = "=0.27.2", default-features = false, features = ["default_fonts"] }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 2
|
||||||
|
debug = false
|
||||||
|
debug-assertions = false
|
||||||
|
lto = "thin"
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
@@ -0,0 +1,438 @@
|
|||||||
|
//! Deaicup Software — Windows PE64 entry point.
|
||||||
|
//!
|
||||||
|
//! The original application runs on eframe/egui. For the Deshab UTSM PE
|
||||||
|
//! compatibility layer we keep the egui UI logic but replace the eframe
|
||||||
|
//! runtime with a minimal hand-written Win32 backend:
|
||||||
|
//! user32 window + message pump -> egui RawInput events
|
||||||
|
//! egui tessellation -> SoftRaster (pure Rust, raster.rs)
|
||||||
|
//! 32bpp DIB backbuffer -> GDI BitBlt onto the window DC
|
||||||
|
//!
|
||||||
|
//! No CRT: the PE entry is `mainCRTStartup` (see .cargo/config.toml) and the
|
||||||
|
//! process ends via ExitProcess.
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
#![allow(static_mut_refs)]
|
||||||
|
|
||||||
|
mod raster;
|
||||||
|
#[allow(dead_code)]
|
||||||
|
mod rt;
|
||||||
|
mod win32;
|
||||||
|
|
||||||
|
use egui::{self, Color32, Context, Event, Key, Modifiers, PointerButton, Pos2, Rect};
|
||||||
|
|
||||||
|
use raster::SoftRaster;
|
||||||
|
use win32::*;
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* UI 逻辑(移植自原 eframe 应用 CODE/Deaicup Software/src)
|
||||||
|
* ========================================================== */
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy, Default)]
|
||||||
|
enum Page {
|
||||||
|
#[default]
|
||||||
|
Home,
|
||||||
|
Tool,
|
||||||
|
Game,
|
||||||
|
LinuxWare,
|
||||||
|
WindowsWare,
|
||||||
|
Set,
|
||||||
|
About,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct DeaicupSoftware {
|
||||||
|
current_page: Page,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeaicupSoftware {
|
||||||
|
fn ui(&mut self, ctx: &Context) {
|
||||||
|
// 原应用顶部是远程图片 https://deaicup.com/.../top.png。
|
||||||
|
// PE 环境无网络,用标题栏替代,保留 75px 高度布局。
|
||||||
|
egui::TopBottomPanel::top("top_image_panel").show(ctx, |ui| {
|
||||||
|
ui.set_min_height(75.0);
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
|
ui.add_space(15.0);
|
||||||
|
ui.label(egui::RichText::new("Deaicup Software").size(30.0).strong());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// main
|
||||||
|
egui::SidePanel::left("navigation_panel")
|
||||||
|
.default_width(200.0)
|
||||||
|
.resizable(true)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.heading("Menu");
|
||||||
|
ui.separator();
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
if ui.selectable_label(self.current_page == Page::Home, "HOME").clicked() {
|
||||||
|
self.current_page = Page::Home;
|
||||||
|
}
|
||||||
|
if ui.selectable_label(self.current_page == Page::Tool, "TOOL").clicked() {
|
||||||
|
self.current_page = Page::Tool;
|
||||||
|
}
|
||||||
|
if ui.selectable_label(self.current_page == Page::Game, "GAME").clicked() {
|
||||||
|
self.current_page = Page::Game;
|
||||||
|
}
|
||||||
|
if ui
|
||||||
|
.selectable_label(self.current_page == Page::LinuxWare, "LINUXWARE")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.current_page = Page::LinuxWare;
|
||||||
|
}
|
||||||
|
if ui
|
||||||
|
.selectable_label(self.current_page == Page::WindowsWare, "WINDOWSWARE")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.current_page = Page::WindowsWare;
|
||||||
|
}
|
||||||
|
if ui.selectable_label(self.current_page == Page::Set, "SET").clicked() {
|
||||||
|
self.current_page = Page::Set;
|
||||||
|
}
|
||||||
|
if ui
|
||||||
|
.selectable_label(self.current_page == Page::About, "ABOUT")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.current_page = Page::About;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.separator();
|
||||||
|
match self.current_page {
|
||||||
|
Page::Home => ui.label("Home"),
|
||||||
|
Page::Tool => ui.label("Tool"),
|
||||||
|
Page::Game => ui.label("Game"),
|
||||||
|
Page::LinuxWare => ui.label("LinuxWare"),
|
||||||
|
Page::WindowsWare => ui.label("WindowsWare"),
|
||||||
|
Page::Set => ui.label("Set"),
|
||||||
|
Page::About => ui.label("About"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 全局输入状态(单线程:wnd_proc 只被 DispatchMessageW 同步调用)
|
||||||
|
* ========================================================== */
|
||||||
|
|
||||||
|
static mut EVENTS: Vec<Event> = Vec::new();
|
||||||
|
static mut POINTER: Pos2 = Pos2::ZERO;
|
||||||
|
static mut MOD_ALT: bool = false;
|
||||||
|
static mut MOD_CTRL: bool = false;
|
||||||
|
static mut MOD_SHIFT: bool = false;
|
||||||
|
static mut QUIT: bool = false;
|
||||||
|
|
||||||
|
fn current_modifiers() -> Modifiers {
|
||||||
|
unsafe {
|
||||||
|
Modifiers {
|
||||||
|
alt: MOD_ALT,
|
||||||
|
ctrl: MOD_CTRL,
|
||||||
|
shift: MOD_SHIFT,
|
||||||
|
mac_cmd: false,
|
||||||
|
command: MOD_CTRL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn lparam_x(l: LPARAM) -> i32 {
|
||||||
|
(l as i32 & 0xFFFF) as i16 as i32
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn lparam_y(l: LPARAM) -> i32 {
|
||||||
|
((l as i32 >> 16) & 0xFFFF) as i16 as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_pointer_button(button: PointerButton, pressed: bool, l: LPARAM) -> LRESULT {
|
||||||
|
unsafe {
|
||||||
|
POINTER = Pos2::new(lparam_x(l) as f32, lparam_y(l) as f32);
|
||||||
|
EVENTS.push(Event::PointerButton {
|
||||||
|
pos: POINTER,
|
||||||
|
button,
|
||||||
|
pressed,
|
||||||
|
modifiers: current_modifiers(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vk_to_egui_key(vk: usize) -> Option<Key> {
|
||||||
|
// 字符键不需要映射为 egui::Key —— 文本输入走 WM_CHAR -> Event::Text。
|
||||||
|
Some(match vk {
|
||||||
|
VK_LEFT => Key::ArrowLeft,
|
||||||
|
VK_UP => Key::ArrowUp,
|
||||||
|
VK_RIGHT => Key::ArrowRight,
|
||||||
|
VK_DOWN => Key::ArrowDown,
|
||||||
|
VK_BACK => Key::Backspace,
|
||||||
|
VK_DELETE => Key::Delete,
|
||||||
|
VK_RETURN => Key::Enter,
|
||||||
|
VK_TAB => Key::Tab,
|
||||||
|
VK_ESCAPE => Key::Escape,
|
||||||
|
VK_SPACE => Key::Space,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_key(vk: usize, pressed: bool) -> LRESULT {
|
||||||
|
unsafe {
|
||||||
|
match vk {
|
||||||
|
VK_SHIFT => MOD_SHIFT = pressed,
|
||||||
|
VK_CONTROL => MOD_CTRL = pressed,
|
||||||
|
VK_MENU => MOD_ALT = pressed,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
if let Some(key) = vk_to_egui_key(vk) {
|
||||||
|
EVENTS.push(Event::Key {
|
||||||
|
key,
|
||||||
|
physical_key: None,
|
||||||
|
pressed,
|
||||||
|
repeat: false,
|
||||||
|
modifiers: current_modifiers(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "system" fn wnd_proc(hwnd: HWND, msg: u32, w: WPARAM, l: LPARAM) -> LRESULT {
|
||||||
|
unsafe {
|
||||||
|
match msg {
|
||||||
|
WM_CLOSE | WM_DESTROY => {
|
||||||
|
QUIT = true;
|
||||||
|
PostQuitMessage(0);
|
||||||
|
0
|
||||||
|
}
|
||||||
|
WM_MOUSEMOVE => {
|
||||||
|
POINTER = Pos2::new(lparam_x(l) as f32, lparam_y(l) as f32);
|
||||||
|
EVENTS.push(Event::PointerMoved(POINTER));
|
||||||
|
0
|
||||||
|
}
|
||||||
|
WM_LBUTTONDOWN => push_pointer_button(PointerButton::Primary, true, l),
|
||||||
|
WM_LBUTTONUP => push_pointer_button(PointerButton::Primary, false, l),
|
||||||
|
WM_RBUTTONDOWN => push_pointer_button(PointerButton::Secondary, true, l),
|
||||||
|
WM_RBUTTONUP => push_pointer_button(PointerButton::Secondary, false, l),
|
||||||
|
WM_MBUTTONDOWN => push_pointer_button(PointerButton::Middle, true, l),
|
||||||
|
WM_MBUTTONUP => push_pointer_button(PointerButton::Middle, false, l),
|
||||||
|
WM_KEYDOWN => on_key(w, true),
|
||||||
|
WM_KEYUP => on_key(w, false),
|
||||||
|
WM_CHAR => {
|
||||||
|
if let Some(c) = char::from_u32(w as u32) {
|
||||||
|
if c >= ' ' && c != '\x7f' {
|
||||||
|
EVENTS.push(Event::Text(c.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
WM_ERASEBKGND => 1, // 背景由 egui 全量绘制
|
||||||
|
_ => DefWindowProcW(hwnd, msg, w, l),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 计时
|
||||||
|
* ========================================================== */
|
||||||
|
|
||||||
|
fn now_secs() -> f64 {
|
||||||
|
unsafe {
|
||||||
|
static mut FREQ: i64 = 0;
|
||||||
|
if FREQ == 0 {
|
||||||
|
QueryPerformanceFrequency(&mut FREQ);
|
||||||
|
if FREQ == 0 {
|
||||||
|
FREQ = 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut c: i64 = 0;
|
||||||
|
QueryPerformanceCounter(&mut c);
|
||||||
|
c as f64 / FREQ as f64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* PE 入口
|
||||||
|
* ========================================================== */
|
||||||
|
|
||||||
|
/// ExitProcess wrapper: restore the kernel GS base before leaving.
|
||||||
|
fn exit_process(code: u32) -> ! {
|
||||||
|
unsafe {
|
||||||
|
rt::restore_tls();
|
||||||
|
ExitProcess(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn mainCRTStartup() {
|
||||||
|
unsafe {
|
||||||
|
// Fake TEB/TLS must precede any std facility that might touch
|
||||||
|
// thread locals (parking_lot, std::io, etc.).
|
||||||
|
rt::install_fake_tls();
|
||||||
|
}
|
||||||
|
log("[deaicup] mainCRTStartup\n");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let hinst = GetModuleHandleW(core::ptr::null());
|
||||||
|
|
||||||
|
let class_name = wide("DeaicupWindow");
|
||||||
|
let wc = WNDCLASSEXW {
|
||||||
|
cbSize: core::mem::size_of::<WNDCLASSEXW>() as u32,
|
||||||
|
style: 0,
|
||||||
|
lpfnWndProc: wnd_proc,
|
||||||
|
cbClsExtra: 0,
|
||||||
|
cbWndExtra: 0,
|
||||||
|
hInstance: hinst,
|
||||||
|
hIcon: 0,
|
||||||
|
hCursor: LoadCursorW(0, IDC_ARROW),
|
||||||
|
hbrBackground: 0,
|
||||||
|
lpszMenuName: core::ptr::null(),
|
||||||
|
lpszClassName: class_name.as_ptr(),
|
||||||
|
hIconSm: 0,
|
||||||
|
};
|
||||||
|
RegisterClassExW(&wc);
|
||||||
|
|
||||||
|
// 屏幕尺寸(shim 报 framebuffer 尺寸),窗口 800x600,超出则收缩
|
||||||
|
let sw = GetSystemMetrics(SM_CXSCREEN);
|
||||||
|
let sh = GetSystemMetrics(SM_CYSCREEN);
|
||||||
|
let mut ww = 800;
|
||||||
|
let mut wh = 600;
|
||||||
|
if sw > 0 && ww > sw {
|
||||||
|
ww = sw;
|
||||||
|
}
|
||||||
|
if sh > 0 && wh > sh {
|
||||||
|
wh = sh;
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = wide("Deaicup Software");
|
||||||
|
let hwnd = CreateWindowExW(
|
||||||
|
0,
|
||||||
|
class_name.as_ptr(),
|
||||||
|
title.as_ptr(),
|
||||||
|
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
ww,
|
||||||
|
wh,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
hinst,
|
||||||
|
core::ptr::null_mut(),
|
||||||
|
);
|
||||||
|
if hwnd == 0 {
|
||||||
|
log("[deaicup] CreateWindowExW failed\n");
|
||||||
|
exit_process(1);
|
||||||
|
}
|
||||||
|
ShowWindow(hwnd, SW_SHOW);
|
||||||
|
UpdateWindow(hwnd);
|
||||||
|
|
||||||
|
// 32bpp top-down DIB 后台缓冲
|
||||||
|
let hdc = GetDC(hwnd);
|
||||||
|
let memdc = CreateCompatibleDC(hdc);
|
||||||
|
let mut bmi = BITMAPINFO::default();
|
||||||
|
bmi.bmiHeader.biSize = core::mem::size_of::<BITMAPINFOHEADER>() as u32;
|
||||||
|
bmi.bmiHeader.biWidth = ww;
|
||||||
|
bmi.bmiHeader.biHeight = -wh; // top-down
|
||||||
|
bmi.bmiHeader.biPlanes = 1;
|
||||||
|
bmi.bmiHeader.biBitCount = 32;
|
||||||
|
bmi.bmiHeader.biCompression = BI_RGB;
|
||||||
|
let mut bits: *mut core::ffi::c_void = core::ptr::null_mut();
|
||||||
|
let hbmp = CreateDIBSection(memdc, &bmi, DIB_RGB_COLORS, &mut bits, 0, 0);
|
||||||
|
if hbmp == 0 || bits.is_null() {
|
||||||
|
log("[deaicup] CreateDIBSection failed\n");
|
||||||
|
exit_process(1);
|
||||||
|
}
|
||||||
|
SelectObject(memdc, hbmp);
|
||||||
|
log("[dc] SelectObject ok\n");
|
||||||
|
|
||||||
|
/* 二分探针:逐项验证 std 设施,定位 Context::default 崩溃点 */
|
||||||
|
{
|
||||||
|
let mut v: Vec<u8> = Vec::with_capacity(16);
|
||||||
|
for i in 0..64u8 { v.push(i); }
|
||||||
|
log("[dc] vec ok\n");
|
||||||
|
let s = String::from("hello");
|
||||||
|
let _ = s.len();
|
||||||
|
log("[dc] string ok\n");
|
||||||
|
let mut h: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
|
||||||
|
h.insert(1, 2); h.insert(3, 4);
|
||||||
|
let _ = h.get(&1);
|
||||||
|
log("[dc] hashmap ok\n");
|
||||||
|
let a = std::sync::Arc::new(42u32);
|
||||||
|
let b = a.clone();
|
||||||
|
let _ = *b;
|
||||||
|
log("[dc] arc ok\n");
|
||||||
|
let m = std::sync::Mutex::new(7u32);
|
||||||
|
*m.lock().unwrap() = 8;
|
||||||
|
log("[dc] mutex ok\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
let ctx = Context::default();
|
||||||
|
log("[dc] Context::default ok\n");
|
||||||
|
ctx.set_pixels_per_point(1.0);
|
||||||
|
|
||||||
|
let mut app = DeaicupSoftware::default();
|
||||||
|
let mut raster = SoftRaster::new();
|
||||||
|
let mut msg = MSG::default();
|
||||||
|
let mut frame: u64 = 0;
|
||||||
|
log("[dc] app+raster ok\n");
|
||||||
|
|
||||||
|
while !QUIT {
|
||||||
|
// 消息泵:把 Win32 消息转成 egui 事件(由 wnd_proc 推入 EVENTS)
|
||||||
|
while PeekMessageW(&mut msg, 0, 0, 0, PM_REMOVE) != 0 {
|
||||||
|
if msg.message == WM_QUIT {
|
||||||
|
QUIT = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessageW(&msg);
|
||||||
|
}
|
||||||
|
if QUIT {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if frame < 3 { log("[dc] msgpump ok\n"); }
|
||||||
|
|
||||||
|
let events = core::mem::take(&mut *core::ptr::addr_of_mut!(EVENTS));
|
||||||
|
let had_events = !events.is_empty();
|
||||||
|
|
||||||
|
let input = egui::RawInput {
|
||||||
|
screen_rect: Some(Rect::from_min_size(
|
||||||
|
Pos2::ZERO,
|
||||||
|
egui::vec2(ww as f32, wh as f32),
|
||||||
|
)),
|
||||||
|
time: Some(now_secs()),
|
||||||
|
modifiers: current_modifiers(),
|
||||||
|
events,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
if frame < 3 { log("[dc] rawinput ok\n"); }
|
||||||
|
|
||||||
|
let full = ctx.run(input, |ctx| app.ui(&ctx));
|
||||||
|
if frame < 3 { log("[dc] ctx.run ok\n"); }
|
||||||
|
let repaint_after = full
|
||||||
|
.viewport_output
|
||||||
|
.values()
|
||||||
|
.next()
|
||||||
|
.map(|v| v.repaint_delay)
|
||||||
|
.unwrap_or(std::time::Duration::from_millis(16));
|
||||||
|
|
||||||
|
raster.apply_delta(&full.textures_delta);
|
||||||
|
if frame < 3 { log("[dc] apply_delta ok\n"); }
|
||||||
|
let prims = ctx.tessellate(full.shapes, full.pixels_per_point);
|
||||||
|
if frame < 3 { log("[dc] tessellate ok\n"); }
|
||||||
|
|
||||||
|
let dst = core::slice::from_raw_parts_mut(bits as *mut u32, (ww * wh) as usize);
|
||||||
|
raster::clear(dst, Color32::from_rgb(0x30, 0x34, 0x3d));
|
||||||
|
raster.paint(dst, ww, wh, &prims);
|
||||||
|
if frame < 3 { log("[dc] paint ok\n"); }
|
||||||
|
|
||||||
|
BitBlt(hdc, 0, 0, ww, wh, memdc, 0, 0, SRCCOPY);
|
||||||
|
if frame < 3 { log("[dc] bitblt ok\n"); }
|
||||||
|
|
||||||
|
frame += 1;
|
||||||
|
if frame > 2 && !had_events && repaint_after > std::time::Duration::from_millis(5) {
|
||||||
|
let ms = repaint_after.as_millis().min(50) as u32;
|
||||||
|
Sleep(if ms == 0 { 5 } else { ms });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log("[deaicup] quit, ExitProcess(0)\n");
|
||||||
|
exit_process(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
//! Pure-Rust software rasterizer for egui tessellated primitives.
|
||||||
|
//! Renders `ClippedPrimitive` meshes (font atlas + color triangles) into a
|
||||||
|
//! 32bpp 0xAARRGGBB backbuffer that is later blitted via GDI BitBlt.
|
||||||
|
|
||||||
|
use egui::epaint::Primitive;
|
||||||
|
use egui::{ClippedPrimitive, Color32, Rect, TextureFilter, TextureId, TexturesDelta};
|
||||||
|
|
||||||
|
struct Texture {
|
||||||
|
w: usize,
|
||||||
|
h: usize,
|
||||||
|
/// RGBA8, row-major.
|
||||||
|
rgba: Vec<u8>,
|
||||||
|
linear: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SoftRaster {
|
||||||
|
/// Small texture store (font atlas + user images). Vec-based to avoid
|
||||||
|
/// std HashMap RandomState (which would import an OS RNG API).
|
||||||
|
textures: Vec<(TextureId, Texture)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SoftRaster {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
SoftRaster {
|
||||||
|
textures: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, id: TextureId) -> Option<&Texture> {
|
||||||
|
self.textures.iter().find(|(tid, _)| *tid == id).map(|(_, t)| t)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply egui's texture updates (font atlas etc).
|
||||||
|
pub fn apply_delta(&mut self, delta: &TexturesDelta) {
|
||||||
|
for (id, img_delta) in &delta.set {
|
||||||
|
let size = img_delta.image.size();
|
||||||
|
let mut rgba = Vec::with_capacity(size[0] * size[1] * 4);
|
||||||
|
match &img_delta.image {
|
||||||
|
egui::ImageData::Color(color) => {
|
||||||
|
for p in &color.pixels {
|
||||||
|
rgba.extend_from_slice(&p.to_array());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
egui::ImageData::Font(font) => {
|
||||||
|
// Coverage -> straight-alpha white texel (255,255,255,a).
|
||||||
|
for c in font.srgba_pixels(None) {
|
||||||
|
let a = c.a();
|
||||||
|
rgba.extend_from_slice(&[255, 255, 255, a]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let linear = img_delta.options.magnification == TextureFilter::Linear;
|
||||||
|
let tex = Texture {
|
||||||
|
w: size[0],
|
||||||
|
h: size[1],
|
||||||
|
rgba,
|
||||||
|
linear,
|
||||||
|
};
|
||||||
|
if let Some(slot) = self.textures.iter_mut().find(|(tid, _)| tid == id) {
|
||||||
|
slot.1 = tex;
|
||||||
|
} else {
|
||||||
|
self.textures.push((*id, tex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for id in &delta.free {
|
||||||
|
self.textures.retain(|(tid, _)| tid != id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Paint all clipped primitives into `dst` (0xAARRGGBB pixels, dw*dh).
|
||||||
|
pub fn paint(&self, dst: &mut [u32], dw: i32, dh: i32, prims: &[ClippedPrimitive]) {
|
||||||
|
for cp in prims {
|
||||||
|
if let Primitive::Mesh(mesh) = &cp.primitive {
|
||||||
|
let Some(tex) = self.get(mesh.texture_id) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let clip = clip_rect_px(&cp.clip_rect, dw, dh);
|
||||||
|
if clip.2 <= clip.0 || clip.3 <= clip.1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for tri in mesh.indices.chunks_exact(3) {
|
||||||
|
let v0 = &mesh.vertices[tri[0] as usize];
|
||||||
|
let v1 = &mesh.vertices[tri[1] as usize];
|
||||||
|
let v2 = &mesh.vertices[tri[2] as usize];
|
||||||
|
raster_tri(dst, dw, dh, clip, v0, v1, v2, tex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (x0, y0, x1, y1) integer clip bounds from an egui rect (points == pixels here).
|
||||||
|
fn clip_rect_px(r: &Rect, dw: i32, dh: i32) -> (i32, i32, i32, i32) {
|
||||||
|
let x0 = (r.min.x.floor() as i32).clamp(0, dw);
|
||||||
|
let y0 = (r.min.y.floor() as i32).clamp(0, dh);
|
||||||
|
let x1 = (r.max.x.ceil() as i32).clamp(0, dw);
|
||||||
|
let y1 = (r.max.y.ceil() as i32).clamp(0, dh);
|
||||||
|
(x0, y0, x1, y1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn edge(ax: f32, ay: f32, bx: f32, by: f32, px: f32, py: f32) -> f32 {
|
||||||
|
(px - ax) * (by - ay) - (py - ay) * (bx - ax)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn raster_tri(
|
||||||
|
dst: &mut [u32],
|
||||||
|
dw: i32,
|
||||||
|
_dh: i32,
|
||||||
|
clip: (i32, i32, i32, i32),
|
||||||
|
v0: &egui::epaint::Vertex,
|
||||||
|
v1: &egui::epaint::Vertex,
|
||||||
|
v2: &egui::epaint::Vertex,
|
||||||
|
tex: &Texture,
|
||||||
|
) {
|
||||||
|
let x0 = v0.pos.x;
|
||||||
|
let y0 = v0.pos.y;
|
||||||
|
let x1 = v1.pos.x;
|
||||||
|
let y1 = v1.pos.y;
|
||||||
|
let x2 = v2.pos.x;
|
||||||
|
let y2 = v2.pos.y;
|
||||||
|
|
||||||
|
let area = edge(x0, y0, x1, y1, x2, y2);
|
||||||
|
if area.abs() < 1e-6 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let inv_area = 1.0 / area;
|
||||||
|
|
||||||
|
let min_x = ((x0.min(x1).min(x2)).floor() as i32).max(clip.0);
|
||||||
|
let min_y = ((y0.min(y1).min(y2)).floor() as i32).max(clip.1);
|
||||||
|
let max_x = ((x0.max(x1).max(x2)).ceil() as i32).min(clip.2);
|
||||||
|
let max_y = ((y0.max(y1).max(y2)).ceil() as i32).min(clip.3);
|
||||||
|
if min_x >= max_x || min_y >= max_y {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for py in min_y..max_y {
|
||||||
|
let fy = py as f32 + 0.5;
|
||||||
|
let mut px = min_x;
|
||||||
|
// Row start barycentrics
|
||||||
|
let mut w0 = edge(x1, y1, x2, y2, px as f32 + 0.5, fy) * inv_area;
|
||||||
|
let mut w1 = edge(x2, y2, x0, y0, px as f32 + 0.5, fy) * inv_area;
|
||||||
|
let mut w2 = 1.0 - w0 - w1;
|
||||||
|
// Per-pixel increments (dx = +1).
|
||||||
|
// d/dpx edge(a, b, P) = (b.y - a.y):
|
||||||
|
// w0 = edge(p1, p2, P) -> (y2 - y1)
|
||||||
|
// w1 = edge(p2, p0, P) -> (y0 - y2)
|
||||||
|
// 符号写反会让 u/v/顶点色沿 +x 反向插值:文字逐行起始像素正确、
|
||||||
|
// 向右逐渐反向采样图集(字形水平镜像/竖条碎裂),纯色面板因
|
||||||
|
// 采样恒白区域不可见而掩盖了该 bug。
|
||||||
|
let dw0 = (y2 - y1) * inv_area;
|
||||||
|
let dw1 = (y0 - y2) * inv_area;
|
||||||
|
|
||||||
|
while px < max_x {
|
||||||
|
if w0 >= 0.0 && w1 >= 0.0 && w2 >= 0.0 {
|
||||||
|
let u = w0 * v0.uv.x + w1 * v1.uv.x + w2 * v2.uv.x;
|
||||||
|
let v = w0 * v0.uv.y + w1 * v1.uv.y + w2 * v2.uv.y;
|
||||||
|
let t = sample(tex, u, v);
|
||||||
|
let c0 = v0.color;
|
||||||
|
let c1 = v1.color;
|
||||||
|
let c2 = v2.color;
|
||||||
|
let cr = (w0 * c0.r() as f32 + w1 * c1.r() as f32 + w2 * c2.r() as f32) as u32;
|
||||||
|
let cg = (w0 * c0.g() as f32 + w1 * c1.g() as f32 + w2 * c2.g() as f32) as u32;
|
||||||
|
let cb = (w0 * c0.b() as f32 + w1 * c1.b() as f32 + w2 * c2.b() as f32) as u32;
|
||||||
|
let ca = (w0 * c0.a() as f32 + w1 * c1.a() as f32 + w2 * c2.a() as f32) as u32;
|
||||||
|
|
||||||
|
// src color = vertex * texture (straight), alpha = va * ta
|
||||||
|
let sa = (ca * t[3] as u32 + 127) / 255;
|
||||||
|
if sa != 0 {
|
||||||
|
let sr = (cr * t[0] as u32 + 127) / 255;
|
||||||
|
let sg = (cg * t[1] as u32 + 127) / 255;
|
||||||
|
let sb = (cb * t[2] as u32 + 127) / 255;
|
||||||
|
let idx = (py * dw + px) as usize;
|
||||||
|
let d = dst[idx];
|
||||||
|
let dr = (d >> 16) & 0xFF;
|
||||||
|
let dg = (d >> 8) & 0xFF;
|
||||||
|
let db = d & 0xFF;
|
||||||
|
let inv = 255 - sa;
|
||||||
|
let or = (sr * sa + dr * inv + 127) / 255;
|
||||||
|
let og = (sg * sa + dg * inv + 127) / 255;
|
||||||
|
let ob = (sb * sa + db * inv + 127) / 255;
|
||||||
|
dst[idx] = 0xFF00_0000 | (or << 16) | (og << 8) | ob;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
px += 1;
|
||||||
|
w0 += dw0;
|
||||||
|
w1 += dw1;
|
||||||
|
w2 = 1.0 - w0 - w1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sample(tex: &Texture, u: f32, v: f32) -> [u8; 4] {
|
||||||
|
if tex.linear {
|
||||||
|
sample_linear(tex, u, v)
|
||||||
|
} else {
|
||||||
|
sample_nearest(tex, u, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sample_nearest(tex: &Texture, u: f32, v: f32) -> [u8; 4] {
|
||||||
|
let x = (u * tex.w as f32 - 0.5).round() as i32;
|
||||||
|
let y = (v * tex.h as f32 - 0.5).round() as i32;
|
||||||
|
texel(tex, x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sample_linear(tex: &Texture, u: f32, v: f32) -> [u8; 4] {
|
||||||
|
let fx = u * tex.w as f32 - 0.5;
|
||||||
|
let fy = v * tex.h as f32 - 0.5;
|
||||||
|
let x0 = fx.floor() as i32;
|
||||||
|
let y0 = fy.floor() as i32;
|
||||||
|
let tx = ((fx - x0 as f32) * 256.0) as i32;
|
||||||
|
let ty = ((fy - y0 as f32) * 256.0) as i32;
|
||||||
|
let p00 = texel(tex, x0, y0);
|
||||||
|
let p10 = texel(tex, x0 + 1, y0);
|
||||||
|
let p01 = texel(tex, x0, y0 + 1);
|
||||||
|
let p11 = texel(tex, x0 + 1, y0 + 1);
|
||||||
|
let mut out = [0u8; 4];
|
||||||
|
for i in 0..4 {
|
||||||
|
let top = (p00[i] as i32 * (256 - tx) + p10[i] as i32 * tx) >> 8;
|
||||||
|
let bot = (p01[i] as i32 * (256 - tx) + p11[i] as i32 * tx) >> 8;
|
||||||
|
out[i] = ((top * (256 - ty) + bot * ty) >> 8) as u8;
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn texel(tex: &Texture, x: i32, y: i32) -> [u8; 4] {
|
||||||
|
let x = x.clamp(0, tex.w as i32 - 1) as usize;
|
||||||
|
let y = y.clamp(0, tex.h as i32 - 1) as usize;
|
||||||
|
let i = (y * tex.w + x) * 4;
|
||||||
|
[tex.rgba[i], tex.rgba[i + 1], tex.rgba[i + 2], tex.rgba[i + 3]]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the whole backbuffer to an opaque color.
|
||||||
|
pub fn clear(dst: &mut [u32], c: Color32) {
|
||||||
|
let px = 0xFF00_0000 | ((c.r() as u32) << 16) | ((c.g() as u32) << 8) | c.b() as u32;
|
||||||
|
dst.fill(px);
|
||||||
|
}
|
||||||
@@ -0,0 +1,600 @@
|
|||||||
|
//! Minimal CRT / libm runtime for the no-CRT PE64 build.
|
||||||
|
//!
|
||||||
|
//! The UTSM PE shim only resolves DLL imports (kernel32/user32/gdi32).
|
||||||
|
//! Symbols below are *link-time* references emitted by LLVM/std, so they must
|
||||||
|
//! live inside the PE image itself:
|
||||||
|
//! - string intrinsics: memcpy / memmove / memset / memcmp
|
||||||
|
//! - MSVC runtime: __chkstk, _fltused, _tls_index, _tls_used,
|
||||||
|
//! __CxxFrameHandler3 (never called, panic = abort)
|
||||||
|
//! - f32 libm: floorf/ceilf/roundf, sinf/cosf, atan2f, powf,
|
||||||
|
//! exp2f, acosf, cbrtf, _hypotf
|
||||||
|
//!
|
||||||
|
//! Math routines are ported from musl-libm (MIT) where accuracy matters
|
||||||
|
//! (sin/cos/atan) and use simple polynomial/series elsewhere. Visual use by
|
||||||
|
//! egui only needs ~1e-5 relative accuracy.
|
||||||
|
//!
|
||||||
|
//! TLS: Rust std on windows-msvc checks `_tls_used` and (if nonzero) accesses
|
||||||
|
//! thread locals through gs:[0x58] -> slots[_tls_index]. We are Ring0 with no
|
||||||
|
//! TEB, so mainCRTStartup installs a fake GS base pointing at a zeroed TLS
|
||||||
|
//! block (install_fake_tls) and restores it before ExitProcess.
|
||||||
|
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use core::arch::asm;
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* string intrinsics
|
||||||
|
* ========================================================== */
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// Standard C contract.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *const u8, n: usize) -> *mut u8 {
|
||||||
|
let orig = dst;
|
||||||
|
asm!(
|
||||||
|
"rep movsb",
|
||||||
|
inout("rcx") n => _,
|
||||||
|
inout("rsi") src => _,
|
||||||
|
inout("rdi") dst => _,
|
||||||
|
options(nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
orig
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// Standard C contract.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn memmove(dst: *mut u8, src: *const u8, n: usize) -> *mut u8 {
|
||||||
|
let d = dst as usize;
|
||||||
|
let s = src as usize;
|
||||||
|
if d < s || d >= s.wrapping_add(n) {
|
||||||
|
memcpy(dst, src, n)
|
||||||
|
} else {
|
||||||
|
// Backward copy without touching DF (ABI requires DF=0).
|
||||||
|
let mut i = n;
|
||||||
|
while i > 0 {
|
||||||
|
i -= 1;
|
||||||
|
core::ptr::write_volatile(dst.add(i), core::ptr::read_volatile(src.add(i)));
|
||||||
|
}
|
||||||
|
dst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// Standard C contract.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn memset(dst: *mut u8, val: i32, n: usize) -> *mut u8 {
|
||||||
|
let orig = dst;
|
||||||
|
asm!(
|
||||||
|
"rep stosb",
|
||||||
|
inout("rcx") n => _,
|
||||||
|
inout("rdi") dst => _,
|
||||||
|
in("al") val as u8,
|
||||||
|
options(nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
orig
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// Standard C contract.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn memcmp(a: *const u8, b: *const u8, n: usize) -> i32 {
|
||||||
|
// Volatile reads keep LLVM from idiom-recognizing this back into memcmp.
|
||||||
|
for i in 0..n {
|
||||||
|
let x = core::ptr::read_volatile(a.add(i));
|
||||||
|
let y = core::ptr::read_volatile(b.add(i));
|
||||||
|
if x != y {
|
||||||
|
return x as i32 - y as i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* MSVC runtime bits
|
||||||
|
* ========================================================== */
|
||||||
|
|
||||||
|
/// Stack probe. Our stack is one large committed region (see /STACK flag),
|
||||||
|
/// so probing pages is unnecessary. An empty body compiles to `ret`,
|
||||||
|
/// which trivially satisfies the preserve-everything contract.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn __chkstk() {}
|
||||||
|
|
||||||
|
/// Referenced by std's unwind tables (.pdata personality). With
|
||||||
|
/// panic = "abort" it is never invoked; loop loudly if it ever is.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn __CxxFrameHandler3() -> ! {
|
||||||
|
loop {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CRT float-usage marker (data symbol only).
|
||||||
|
#[no_mangle]
|
||||||
|
pub static _fltused: i32 = 0x9875;
|
||||||
|
|
||||||
|
/// TLS slot index of this module. We use slot 0 of the fake TLS array.
|
||||||
|
#[no_mangle]
|
||||||
|
pub static _tls_index: u32 = 0;
|
||||||
|
|
||||||
|
/// IMAGE_TLS_DIRECTORY64 stand-in (all zeros: no raw template, no callbacks).
|
||||||
|
/// std only checks that its address is nonzero to decide TLS is supported.
|
||||||
|
#[no_mangle]
|
||||||
|
pub static _tls_used: [u8; 0x30] = [0; 0x30];
|
||||||
|
|
||||||
|
/* ---- fake TEB / TLS so std thread_local access can't fault ---- */
|
||||||
|
|
||||||
|
const IA32_GS_BASE: u32 = 0xC000_0101;
|
||||||
|
|
||||||
|
static mut OLD_GS_BASE: u64 = 0;
|
||||||
|
static mut TLS_READY: bool = false;
|
||||||
|
|
||||||
|
fn rdmsr(msr: u32) -> u64 {
|
||||||
|
let lo: u32;
|
||||||
|
let hi: u32;
|
||||||
|
unsafe {
|
||||||
|
asm!("rdmsr", in("ecx") msr, out("eax") lo, out("edx") hi,
|
||||||
|
options(nomem, nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
((hi as u64) << 32) | lo as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrmsr(msr: u32, val: u64) {
|
||||||
|
unsafe {
|
||||||
|
asm!("wrmsr", in("ecx") msr, in("eax") val as u32,
|
||||||
|
in("edx") (val >> 32) as u32, options(nomem, nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Point GS at a minimal fake TEB whose ThreadLocalStoragePointer (0x58)
|
||||||
|
/// references a zeroed slots array; slot 0 covers `_tls_index == 0`.
|
||||||
|
/// All const-init thread locals in std are zero-valid (None/0).
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Ring0 only; call once at PE entry before touching std facilities.
|
||||||
|
pub unsafe fn install_fake_tls() {
|
||||||
|
static mut TEB: [u8; 0x100] = [0; 0x100];
|
||||||
|
static mut SLOTS: [usize; 64] = [0; 64];
|
||||||
|
static mut TLS_BLOCK: [u8; 8192] = [0; 8192];
|
||||||
|
|
||||||
|
if TLS_READY {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let teb = core::ptr::addr_of_mut!(TEB);
|
||||||
|
let slots = core::ptr::addr_of_mut!(SLOTS);
|
||||||
|
let block = core::ptr::addr_of_mut!(TLS_BLOCK);
|
||||||
|
(*slots)[0] = block as usize;
|
||||||
|
*((*teb).as_mut_ptr().add(0x58) as *mut usize) = slots as usize;
|
||||||
|
|
||||||
|
OLD_GS_BASE = rdmsr(IA32_GS_BASE);
|
||||||
|
wrmsr(IA32_GS_BASE, teb as u64);
|
||||||
|
TLS_READY = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restore the kernel's GS base (call before ExitProcess).
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Ring0 only; pair with install_fake_tls.
|
||||||
|
pub unsafe fn restore_tls() {
|
||||||
|
if TLS_READY {
|
||||||
|
wrmsr(IA32_GS_BASE, OLD_GS_BASE);
|
||||||
|
TLS_READY = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* f32 libm
|
||||||
|
* ========================================================== */
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn asu32(x: f32) -> u32 {
|
||||||
|
x.to_bits()
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn asf32(x: u32) -> f32 {
|
||||||
|
f32::from_bits(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn floorf(x: f32) -> f32 {
|
||||||
|
// |x| >= 2^23 is already integral; NaN passes the compare false and
|
||||||
|
// degrades to 0.0, which egui never feeds us anyway.
|
||||||
|
if x.abs() >= 8388608.0 {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
let i = x as i32; // truncate toward zero
|
||||||
|
let f = i as f32;
|
||||||
|
if f > x {
|
||||||
|
f - 1.0
|
||||||
|
} else {
|
||||||
|
f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn ceilf(x: f32) -> f32 {
|
||||||
|
if x.abs() >= 8388608.0 {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
let i = x as i32;
|
||||||
|
let f = i as f32;
|
||||||
|
if f < x {
|
||||||
|
f + 1.0
|
||||||
|
} else {
|
||||||
|
f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// C semantics: round half away from zero.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn roundf(x: f32) -> f32 {
|
||||||
|
if x >= 0.0 {
|
||||||
|
floorf(x + 0.5)
|
||||||
|
} else {
|
||||||
|
ceilf(x - 0.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn truncf(x: f32) -> f32 {
|
||||||
|
if x.abs() >= 8388608.0 {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
x as i32 as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// f64 truncation toward zero (LLVM libcall on SSE2-only targets).
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn trunc(x: f64) -> f64 {
|
||||||
|
// 2^52: every f64 with |x| >= 2^52 is already integral.
|
||||||
|
if x.abs() >= 4503599627370496.0 {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
x as i64 as f64
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- sin/cos: musl __sindf/__cosdf + medium Cody-Waite reduction ---- */
|
||||||
|
|
||||||
|
// |sin(x)/x - s(x)| < 2**-27.5 on the reduced range.
|
||||||
|
const S1: f64 = -0.166666666416265235595;
|
||||||
|
const S2: f64 = 0.0083333293858894631756;
|
||||||
|
const S3: f64 = -0.000198393348360966317347;
|
||||||
|
const S4: f64 = 0.0000027183114939898219064;
|
||||||
|
|
||||||
|
const C0: f64 = -0.499999997251031003120;
|
||||||
|
const C1: f64 = 0.0416666233237390631894;
|
||||||
|
const C2: f64 = -0.00138867637746099294692;
|
||||||
|
const C3: f64 = 0.0000243904487962774090654;
|
||||||
|
|
||||||
|
const PIO2_HI: f64 = 1.57079625129699707031e+00;
|
||||||
|
const PIO2_LO: f64 = 7.54978941586159635335e-08;
|
||||||
|
const INV_PIO2: f64 = 6.36619772367581382433e-01; // 2/pi
|
||||||
|
const TOINT: f64 = 1.5 / f64::EPSILON;
|
||||||
|
const TWO_PI: f64 = 6.28318530717958647693;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sindf(x: f64) -> f64 {
|
||||||
|
let z = x * x;
|
||||||
|
let w = z * z;
|
||||||
|
let r = S3 + z * S4;
|
||||||
|
let s = z * x;
|
||||||
|
(x + s * (S1 + z * S2)) + s * w * r
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn cosdf(x: f64) -> f64 {
|
||||||
|
let z = x * x;
|
||||||
|
let w = z * z;
|
||||||
|
let r = C2 + z * C3;
|
||||||
|
((1.0 + z * C0) + w * C1) + (w * z) * r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reduce x modulo pi/2 (Cody-Waite, double precision). Returns (n, y)
|
||||||
|
/// with y = x - n*pi/2 accurate for moderate |x|.
|
||||||
|
#[inline]
|
||||||
|
fn rem_pio2_medium(x: f64) -> (i32, f64) {
|
||||||
|
// Coarse pre-reduction keeps the accurate path valid for any angle egui
|
||||||
|
// can produce (ellipse/bezier angles are already tiny).
|
||||||
|
let x = if x.abs() > 105.0 {
|
||||||
|
x - (x / TWO_PI).trunc() * TWO_PI
|
||||||
|
} else {
|
||||||
|
x
|
||||||
|
};
|
||||||
|
let fn_ = x * INV_PIO2 + TOINT - TOINT;
|
||||||
|
let n = fn_ as i32;
|
||||||
|
(n, x - fn_ * PIO2_HI - fn_ * PIO2_LO)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn sinf(x: f32) -> f32 {
|
||||||
|
if x.abs() < 1.0e-30 {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
let (n, y) = rem_pio2_medium(x as f64);
|
||||||
|
(match n & 3 {
|
||||||
|
0 => sindf(y),
|
||||||
|
1 => cosdf(y),
|
||||||
|
2 => sindf(-y),
|
||||||
|
_ => -cosdf(y),
|
||||||
|
}) as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn cosf(x: f32) -> f32 {
|
||||||
|
let (n, y) = rem_pio2_medium(x as f64);
|
||||||
|
(match n & 3 {
|
||||||
|
0 => cosdf(y),
|
||||||
|
1 => sindf(-y),
|
||||||
|
2 => -cosdf(y),
|
||||||
|
_ => sindf(y),
|
||||||
|
}) as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- atan2f: musl atanf + quadrant fix ---- */
|
||||||
|
|
||||||
|
const ATAN_HI: [f32; 4] = [
|
||||||
|
4.6364760399e-01,
|
||||||
|
7.8539812565e-01,
|
||||||
|
9.8279371262e-01,
|
||||||
|
1.5707962513e+00,
|
||||||
|
];
|
||||||
|
const ATAN_LO: [f32; 4] = [
|
||||||
|
8.8023035407e-08,
|
||||||
|
1.2093354704e-08,
|
||||||
|
1.2587686365e-08,
|
||||||
|
7.5497894159e-08,
|
||||||
|
];
|
||||||
|
const AT: [f32; 5] = [
|
||||||
|
3.3333328366e-01,
|
||||||
|
-1.9999158382e-01,
|
||||||
|
1.4253635705e-01,
|
||||||
|
-1.0648017377e-01,
|
||||||
|
6.1687607318e-02,
|
||||||
|
];
|
||||||
|
const PI_F: f32 = 3.1415927410e+00;
|
||||||
|
|
||||||
|
fn atanf(x: f32) -> f32 {
|
||||||
|
let mut x = x;
|
||||||
|
let ix = asu32(x);
|
||||||
|
let sign = ix >> 31;
|
||||||
|
let aix = ix & 0x7fffffff;
|
||||||
|
if aix >= 0x4c800000 {
|
||||||
|
// |x| >= 2^26
|
||||||
|
if x.is_nan() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
let z = ATAN_HI[3] + 7.5231638e-37;
|
||||||
|
return if sign != 0 { -z } else { z };
|
||||||
|
}
|
||||||
|
let id: i32;
|
||||||
|
if aix < 0x3ee00000 {
|
||||||
|
// |x| < 0.4375
|
||||||
|
if aix < 0x39800000 {
|
||||||
|
return x; // |x| < 2^-12: atan(x) ~ x
|
||||||
|
}
|
||||||
|
id = -1;
|
||||||
|
} else {
|
||||||
|
x = x.abs();
|
||||||
|
if aix < 0x3f980000 {
|
||||||
|
if aix < 0x3f300000 {
|
||||||
|
id = 0;
|
||||||
|
x = (2.0 * x - 1.0) / (2.0 + x);
|
||||||
|
} else {
|
||||||
|
id = 1;
|
||||||
|
x = (x - 1.0) / (x + 1.0);
|
||||||
|
}
|
||||||
|
} else if aix < 0x401c0000 {
|
||||||
|
id = 2;
|
||||||
|
x = (x - 1.5) / (1.0 + 1.5 * x);
|
||||||
|
} else {
|
||||||
|
id = 3;
|
||||||
|
x = -1.0 / x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let z = x * x;
|
||||||
|
let w = z * z;
|
||||||
|
let s1 = z * (AT[0] + w * (AT[2] + w * AT[4]));
|
||||||
|
let s2 = w * (AT[1] + w * AT[3]);
|
||||||
|
if id < 0 {
|
||||||
|
return x - x * (s1 + s2);
|
||||||
|
}
|
||||||
|
let i = id as usize;
|
||||||
|
let z2 = ATAN_HI[i] - ((x * (s1 + s2) - ATAN_LO[i]) - x);
|
||||||
|
if sign != 0 {
|
||||||
|
-z2
|
||||||
|
} else {
|
||||||
|
z2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn atan2f(y: f32, x: f32) -> f32 {
|
||||||
|
if y == 0.0 {
|
||||||
|
return if x < 0.0 { PI_F } else { y }; // crude, matches C for our uses
|
||||||
|
}
|
||||||
|
if x == 0.0 {
|
||||||
|
return if y < 0.0 { -PI_F / 2.0 } else { PI_F / 2.0 };
|
||||||
|
}
|
||||||
|
let ax = x.abs();
|
||||||
|
let ay = y.abs();
|
||||||
|
let z = if ax > ay {
|
||||||
|
atanf(ay / ax)
|
||||||
|
} else {
|
||||||
|
PI_F / 2.0 - atanf(ax / ay)
|
||||||
|
};
|
||||||
|
if x < 0.0 {
|
||||||
|
if y < 0.0 {
|
||||||
|
z - PI_F
|
||||||
|
} else {
|
||||||
|
PI_F - z
|
||||||
|
}
|
||||||
|
} else if y < 0.0 {
|
||||||
|
-z
|
||||||
|
} else {
|
||||||
|
z
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- exp2f / log2f / powf ---- */
|
||||||
|
|
||||||
|
/// ldexp-style scaling by 2^n (n clamped to f32 range by callers).
|
||||||
|
fn scalbnf(mut r: f32, mut n: i32) -> f32 {
|
||||||
|
while n > 127 {
|
||||||
|
r *= asf32(0x7F00_0000); // 2^127
|
||||||
|
n -= 127;
|
||||||
|
}
|
||||||
|
while n < -126 {
|
||||||
|
r *= asf32(0x0080_0000); // 2^-126
|
||||||
|
n += 126;
|
||||||
|
}
|
||||||
|
r * asf32(((127 + n) as u32) << 23)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn exp2f(x: f32) -> f32 {
|
||||||
|
if x > 128.0 {
|
||||||
|
return f32::INFINITY;
|
||||||
|
}
|
||||||
|
if x < -150.0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
// n = round-to-nearest-even(x) via the magic-number trick.
|
||||||
|
let t = x + 12582912.0; // 1.5 * 2^23
|
||||||
|
let nf = t - 12582912.0;
|
||||||
|
let n = nf as i32;
|
||||||
|
let f = x - nf; // in [-0.5, 0.5]
|
||||||
|
// 2^f: Taylor in ln2, degree 6 -> ~1e-10 rel error on [-0.5, 0.5].
|
||||||
|
let p = 1.0
|
||||||
|
+ f * (0.6931471805599453
|
||||||
|
+ f * (0.24022650695910070
|
||||||
|
+ f * (0.05550410866482158
|
||||||
|
+ f * (0.009618129107628477
|
||||||
|
+ f * (0.0013333558146428443 + f * 0.00015403530393381609)))));
|
||||||
|
scalbnf(p, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// log2(x) for x > 0; accuracy ~1e-7 via atanh series in double.
|
||||||
|
fn log2f(x: f32) -> f32 {
|
||||||
|
let ix = asu32(x);
|
||||||
|
let e = ((ix >> 23) as i32) - 127;
|
||||||
|
let m = asf32((ix & 0x007F_FFFF) | 0x3F80_0000); // [1, 2)
|
||||||
|
let t = ((m - 1.0) / (m + 1.0)) as f64;
|
||||||
|
let t2 = t * t;
|
||||||
|
let mut sum = 0.0f64;
|
||||||
|
let mut term = t;
|
||||||
|
for k in 0..8 {
|
||||||
|
sum += term / (2 * k + 1) as f64;
|
||||||
|
term *= t2;
|
||||||
|
}
|
||||||
|
let ln_m = 2.0 * sum;
|
||||||
|
(e as f64 + ln_m * 1.4426950408889634) as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn powf(x: f32, y: f32) -> f32 {
|
||||||
|
if y == 0.0 {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
if x == 1.0 {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
if x == 0.0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
if x == 2.0 {
|
||||||
|
return exp2f(y);
|
||||||
|
}
|
||||||
|
if x < 0.0 {
|
||||||
|
let yi = y as i32;
|
||||||
|
if yi as f32 == y {
|
||||||
|
let r = exp2f(y * log2f(-x));
|
||||||
|
return if yi & 1 != 0 { -r } else { r };
|
||||||
|
}
|
||||||
|
return f32::NAN;
|
||||||
|
}
|
||||||
|
exp2f(y * log2f(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- acosf: musl ---- */
|
||||||
|
|
||||||
|
const PIO2_HI_F: f32 = 1.5707962513e+00;
|
||||||
|
const PIO2_LO_F: f32 = 7.5497894159e-08;
|
||||||
|
const PS0: f32 = 1.6666586697e-01;
|
||||||
|
const PS1: f32 = -4.2743422091e-02;
|
||||||
|
const PS2: f32 = -8.6563630030e-03;
|
||||||
|
const QS1: f32 = -7.0662963390e-01;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn acos_r(z: f32) -> f32 {
|
||||||
|
z * (PS0 + z * (PS1 + z * PS2)) / (1.0 + z * QS1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn acosf(x: f32) -> f32 {
|
||||||
|
let hx = asu32(x);
|
||||||
|
let ix = hx & 0x7fffffff;
|
||||||
|
if ix >= 0x3f800000 {
|
||||||
|
// |x| >= 1
|
||||||
|
if ix == 0x3f800000 {
|
||||||
|
if hx >> 31 != 0 {
|
||||||
|
return 2.0 * PIO2_HI_F; // acos(-1) = pi
|
||||||
|
}
|
||||||
|
return 0.0; // acos(1) = 0
|
||||||
|
}
|
||||||
|
return f32::NAN;
|
||||||
|
}
|
||||||
|
if ix < 0x3f000000 {
|
||||||
|
// |x| < 0.5
|
||||||
|
if ix <= 0x32800000 {
|
||||||
|
return PIO2_HI_F; // |x| < 2^-26
|
||||||
|
}
|
||||||
|
return PIO2_HI_F - (x - (PIO2_LO_F - x * acos_r(x * x)));
|
||||||
|
}
|
||||||
|
if hx >> 31 != 0 {
|
||||||
|
// x < -0.5
|
||||||
|
let z = (1.0 + x) * 0.5;
|
||||||
|
let s = z.sqrt();
|
||||||
|
let w = acos_r(z) * s - PIO2_LO_F;
|
||||||
|
return 2.0 * PIO2_HI_F - 2.0 * (s + w);
|
||||||
|
}
|
||||||
|
// x > 0.5
|
||||||
|
let z = (1.0 - x) * 0.5;
|
||||||
|
let s = z.sqrt();
|
||||||
|
let df = asf32(asu32(s) & 0xFFFFF000); // keep df*df rounding-exact
|
||||||
|
let c = (z - df * df) / (s + df);
|
||||||
|
let w = acos_r(z) * s + c;
|
||||||
|
2.0 * (df + w)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- cbrtf / hypotf ---- */
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn cbrtf(x: f32) -> f32 {
|
||||||
|
if x == 0.0 {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
let r = exp2f(log2f(x.abs()) / 3.0);
|
||||||
|
if x < 0.0 {
|
||||||
|
-r
|
||||||
|
} else {
|
||||||
|
r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn _hypotf(x: f32, y: f32) -> f32 {
|
||||||
|
let ax = x.abs();
|
||||||
|
let ay = y.abs();
|
||||||
|
let (a, b) = if ax > ay { (ax, ay) } else { (ay, ax) };
|
||||||
|
if a == 0.0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
let r = b / a;
|
||||||
|
a * (1.0 + r * r).sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Plain alias in case LLVM references the C99 spelling.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn hypotf(x: f32, y: f32) -> f32 {
|
||||||
|
_hypotf(x, y)
|
||||||
|
}
|
||||||
@@ -0,0 +1,315 @@
|
|||||||
|
//! Minimal Win32 bindings used by the Deaicup PE backend.
|
||||||
|
//! Only the API subset that the Deshab UTSM PE shim implements is declared here.
|
||||||
|
//! All imports use raw-dylib so rust-lld can emit the import table without
|
||||||
|
//! any MSVC import libraries.
|
||||||
|
|
||||||
|
#![allow(non_snake_case, non_camel_case_types, dead_code)]
|
||||||
|
|
||||||
|
pub type HWND = isize;
|
||||||
|
pub type HDC = isize;
|
||||||
|
pub type HINSTANCE = isize;
|
||||||
|
pub type HICON = isize;
|
||||||
|
pub type HCURSOR = isize;
|
||||||
|
pub type HBRUSH = isize;
|
||||||
|
pub type HMENU = isize;
|
||||||
|
pub type HGDIOBJ = isize;
|
||||||
|
pub type HBITMAP = isize;
|
||||||
|
pub type HANDLE = isize;
|
||||||
|
pub type ATOM = u16;
|
||||||
|
pub type WPARAM = usize;
|
||||||
|
pub type LPARAM = isize;
|
||||||
|
pub type LRESULT = isize;
|
||||||
|
pub type BOOL = i32;
|
||||||
|
pub type UINT = u32;
|
||||||
|
pub type DWORD = u32;
|
||||||
|
pub type WORD = u16;
|
||||||
|
pub type LPCWSTR = *const u16;
|
||||||
|
|
||||||
|
pub const CW_USEDEFAULT: i32 = 0x80000000u32 as i32;
|
||||||
|
|
||||||
|
pub const WS_OVERLAPPED: u32 = 0x00000000;
|
||||||
|
pub const WS_CAPTION: u32 = 0x00C00000;
|
||||||
|
pub const WS_SYSMENU: u32 = 0x00080000;
|
||||||
|
pub const WS_THICKFRAME: u32 = 0x00040000;
|
||||||
|
pub const WS_MINIMIZEBOX: u32 = 0x00020000;
|
||||||
|
pub const WS_MAXIMIZEBOX: u32 = 0x00010000;
|
||||||
|
pub const WS_VISIBLE: u32 = 0x10000000;
|
||||||
|
pub const WS_OVERLAPPEDWINDOW: u32 = WS_OVERLAPPED
|
||||||
|
| WS_CAPTION
|
||||||
|
| WS_SYSMENU
|
||||||
|
| WS_THICKFRAME
|
||||||
|
| WS_MINIMIZEBOX
|
||||||
|
| WS_MAXIMIZEBOX;
|
||||||
|
|
||||||
|
pub const SW_SHOW: i32 = 5;
|
||||||
|
|
||||||
|
pub const WM_CREATE: u32 = 0x0001;
|
||||||
|
pub const WM_DESTROY: u32 = 0x0002;
|
||||||
|
pub const WM_SIZE: u32 = 0x0005;
|
||||||
|
pub const WM_PAINT: u32 = 0x000F;
|
||||||
|
pub const WM_CLOSE: u32 = 0x0010;
|
||||||
|
pub const WM_QUIT: u32 = 0x0012;
|
||||||
|
pub const WM_ERASEBKGND: u32 = 0x0014;
|
||||||
|
pub const WM_KEYDOWN: u32 = 0x0100;
|
||||||
|
pub const WM_KEYUP: u32 = 0x0101;
|
||||||
|
pub const WM_CHAR: u32 = 0x0102;
|
||||||
|
pub const WM_TIMER: u32 = 0x0113;
|
||||||
|
pub const WM_MOUSEMOVE: u32 = 0x0200;
|
||||||
|
pub const WM_LBUTTONDOWN: u32 = 0x0201;
|
||||||
|
pub const WM_LBUTTONUP: u32 = 0x0202;
|
||||||
|
pub const WM_RBUTTONDOWN: u32 = 0x0204;
|
||||||
|
pub const WM_RBUTTONUP: u32 = 0x0205;
|
||||||
|
pub const WM_MBUTTONDOWN: u32 = 0x0207;
|
||||||
|
pub const WM_MBUTTONUP: u32 = 0x0208;
|
||||||
|
|
||||||
|
pub const PM_NOREMOVE: u32 = 0x0000;
|
||||||
|
pub const PM_REMOVE: u32 = 0x0001;
|
||||||
|
|
||||||
|
pub const VK_BACK: usize = 0x08;
|
||||||
|
pub const VK_TAB: usize = 0x09;
|
||||||
|
pub const VK_RETURN: usize = 0x0D;
|
||||||
|
pub const VK_SHIFT: usize = 0x10;
|
||||||
|
pub const VK_CONTROL: usize = 0x11;
|
||||||
|
pub const VK_MENU: usize = 0x12;
|
||||||
|
pub const VK_ESCAPE: usize = 0x1B;
|
||||||
|
pub const VK_SPACE: usize = 0x20;
|
||||||
|
pub const VK_LEFT: usize = 0x25;
|
||||||
|
pub const VK_UP: usize = 0x26;
|
||||||
|
pub const VK_RIGHT: usize = 0x27;
|
||||||
|
pub const VK_DOWN: usize = 0x28;
|
||||||
|
pub const VK_DELETE: usize = 0x2E;
|
||||||
|
|
||||||
|
pub const IDC_ARROW: usize = 32512;
|
||||||
|
pub const COLOR_WINDOW: u32 = 5;
|
||||||
|
|
||||||
|
pub const GWLP_USERDATA: i32 = -21;
|
||||||
|
pub const GWLP_WNDPROC: i32 = -4;
|
||||||
|
|
||||||
|
pub const SM_CXSCREEN: i32 = 0;
|
||||||
|
pub const SM_CYSCREEN: i32 = 1;
|
||||||
|
|
||||||
|
pub const SRCCOPY: u32 = 0x00CC0020;
|
||||||
|
pub const DIB_RGB_COLORS: u32 = 0;
|
||||||
|
pub const BI_RGB: u32 = 0;
|
||||||
|
pub const WHITE_BRUSH: i32 = 0;
|
||||||
|
|
||||||
|
pub const STD_OUTPUT_HANDLE: u32 = 0xFFFFFFF5;
|
||||||
|
pub const STD_ERROR_HANDLE: u32 = 0xFFFFFFF4;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Clone, Copy)]
|
||||||
|
pub struct POINT {
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Clone, Copy)]
|
||||||
|
pub struct RECT {
|
||||||
|
pub left: i32,
|
||||||
|
pub top: i32,
|
||||||
|
pub right: i32,
|
||||||
|
pub bottom: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Clone, Copy)]
|
||||||
|
pub struct MSG {
|
||||||
|
pub hwnd: HWND,
|
||||||
|
pub message: u32,
|
||||||
|
pub wParam: WPARAM,
|
||||||
|
pub lParam: LPARAM,
|
||||||
|
pub time: u32,
|
||||||
|
pub pt: POINT,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct WNDCLASSEXW {
|
||||||
|
pub cbSize: UINT,
|
||||||
|
pub style: UINT,
|
||||||
|
pub lpfnWndProc: WNDPROC,
|
||||||
|
pub cbClsExtra: i32,
|
||||||
|
pub cbWndExtra: i32,
|
||||||
|
pub hInstance: HINSTANCE,
|
||||||
|
pub hIcon: HICON,
|
||||||
|
pub hCursor: HCURSOR,
|
||||||
|
pub hbrBackground: HBRUSH,
|
||||||
|
pub lpszMenuName: LPCWSTR,
|
||||||
|
pub lpszClassName: LPCWSTR,
|
||||||
|
pub hIconSm: HICON,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type WNDPROC = extern "system" fn(HWND, u32, WPARAM, LPARAM) -> LRESULT;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PAINTSTRUCT {
|
||||||
|
pub hdc: HDC,
|
||||||
|
pub fErase: BOOL,
|
||||||
|
pub rcPaint: RECT,
|
||||||
|
pub fRestore: BOOL,
|
||||||
|
pub fIncUpdate: BOOL,
|
||||||
|
pub rgbReserved: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PAINTSTRUCT {
|
||||||
|
fn default() -> Self {
|
||||||
|
unsafe { core::mem::zeroed() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Clone, Copy)]
|
||||||
|
pub struct BITMAPINFOHEADER {
|
||||||
|
pub biSize: u32,
|
||||||
|
pub biWidth: i32,
|
||||||
|
pub biHeight: i32,
|
||||||
|
pub biPlanes: u16,
|
||||||
|
pub biBitCount: u16,
|
||||||
|
pub biCompression: u32,
|
||||||
|
pub biSizeImage: u32,
|
||||||
|
pub biXPelsPerMeter: i32,
|
||||||
|
pub biYPelsPerMeter: i32,
|
||||||
|
pub biClrUsed: u32,
|
||||||
|
pub biClrImportant: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Clone, Copy)]
|
||||||
|
pub struct RGBQUAD {
|
||||||
|
pub rgbBlue: u8,
|
||||||
|
pub rgbGreen: u8,
|
||||||
|
pub rgbRed: u8,
|
||||||
|
pub rgbReserved: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Clone, Copy)]
|
||||||
|
pub struct BITMAPINFO {
|
||||||
|
pub bmiHeader: BITMAPINFOHEADER,
|
||||||
|
pub bmiColors: [RGBQUAD; 1],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link(name = "user32", kind = "raw-dylib")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn RegisterClassExW(lpwcx: *const WNDCLASSEXW) -> ATOM;
|
||||||
|
pub fn CreateWindowExW(
|
||||||
|
dwExStyle: DWORD,
|
||||||
|
lpClassName: LPCWSTR,
|
||||||
|
lpWindowName: LPCWSTR,
|
||||||
|
dwStyle: DWORD,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
nWidth: i32,
|
||||||
|
nHeight: i32,
|
||||||
|
hWndParent: HWND,
|
||||||
|
hMenu: HMENU,
|
||||||
|
hInstance: HINSTANCE,
|
||||||
|
lpParam: *mut core::ffi::c_void,
|
||||||
|
) -> HWND;
|
||||||
|
pub fn DestroyWindow(hWnd: HWND) -> BOOL;
|
||||||
|
pub fn ShowWindow(hWnd: HWND, nCmdShow: i32) -> BOOL;
|
||||||
|
pub fn UpdateWindow(hWnd: HWND) -> BOOL;
|
||||||
|
pub fn GetMessageW(lpMsg: *mut MSG, hWnd: HWND, wMsgFilterMin: u32, wMsgFilterMax: u32) -> BOOL;
|
||||||
|
pub fn PeekMessageW(
|
||||||
|
lpMsg: *mut MSG,
|
||||||
|
hWnd: HWND,
|
||||||
|
wMsgFilterMin: u32,
|
||||||
|
wMsgFilterMax: u32,
|
||||||
|
wRemoveMsg: UINT,
|
||||||
|
) -> BOOL;
|
||||||
|
pub fn TranslateMessage(lpMsg: *const MSG) -> BOOL;
|
||||||
|
pub fn DispatchMessageW(lpMsg: *const MSG) -> LRESULT;
|
||||||
|
pub fn PostQuitMessage(nExitCode: i32);
|
||||||
|
pub fn DefWindowProcW(hWnd: HWND, msg: u32, wParam: WPARAM, lParam: LPARAM) -> LRESULT;
|
||||||
|
pub fn GetClientRect(hWnd: HWND, lpRect: *mut RECT) -> BOOL;
|
||||||
|
pub fn InvalidateRect(hWnd: HWND, lpRect: *const RECT, bErase: BOOL) -> BOOL;
|
||||||
|
pub fn BeginPaint(hWnd: HWND, lpPaint: *mut PAINTSTRUCT) -> HDC;
|
||||||
|
pub fn EndPaint(hWnd: HWND, lpPaint: *const PAINTSTRUCT) -> BOOL;
|
||||||
|
pub fn GetDC(hWnd: HWND) -> HDC;
|
||||||
|
pub fn ReleaseDC(hWnd: HWND, hDC: HDC) -> i32;
|
||||||
|
pub fn LoadCursorW(hInstance: HINSTANCE, lpCursorName: usize) -> HCURSOR;
|
||||||
|
pub fn SetCursor(hCursor: HCURSOR) -> HCURSOR;
|
||||||
|
pub fn GetCursorPos(lpPoint: *mut POINT) -> BOOL;
|
||||||
|
pub fn ScreenToClient(hWnd: HWND, lpPoint: *mut POINT) -> BOOL;
|
||||||
|
pub fn GetAsyncKeyState(vKey: i32) -> i16;
|
||||||
|
pub fn SetTimer(hWnd: HWND, nIDEvent: usize, uElapse: UINT, lpTimerFunc: usize) -> usize;
|
||||||
|
pub fn KillTimer(hWnd: HWND, uIDEvent: usize) -> BOOL;
|
||||||
|
pub fn MessageBeep(uType: UINT) -> BOOL;
|
||||||
|
pub fn GetSystemMetrics(nIndex: i32) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link(name = "gdi32", kind = "raw-dylib")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn CreateCompatibleDC(hdc: HDC) -> HDC;
|
||||||
|
pub fn CreateDIBSection(
|
||||||
|
hdc: HDC,
|
||||||
|
pbmi: *const BITMAPINFO,
|
||||||
|
usage: UINT,
|
||||||
|
ppvBits: *mut *mut core::ffi::c_void,
|
||||||
|
hSection: HANDLE,
|
||||||
|
offset: DWORD,
|
||||||
|
) -> HBITMAP;
|
||||||
|
pub fn SelectObject(hdc: HDC, h: HGDIOBJ) -> HGDIOBJ;
|
||||||
|
pub fn DeleteObject(ho: HGDIOBJ) -> BOOL;
|
||||||
|
pub fn DeleteDC(hdc: HDC) -> BOOL;
|
||||||
|
pub fn BitBlt(
|
||||||
|
hdc: HDC,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
cx: i32,
|
||||||
|
cy: i32,
|
||||||
|
hdcSrc: HDC,
|
||||||
|
x1: i32,
|
||||||
|
y1: i32,
|
||||||
|
rop: DWORD,
|
||||||
|
) -> BOOL;
|
||||||
|
pub fn GetStockObject(i: i32) -> HGDIOBJ;
|
||||||
|
pub fn SetDIBitsToDevice(
|
||||||
|
hdc: HDC,
|
||||||
|
xDest: i32,
|
||||||
|
yDest: i32,
|
||||||
|
w: DWORD,
|
||||||
|
h: DWORD,
|
||||||
|
xSrc: i32,
|
||||||
|
ySrc: i32,
|
||||||
|
startScan: UINT,
|
||||||
|
numLines: UINT,
|
||||||
|
lpvBits: *const core::ffi::c_void,
|
||||||
|
lpbmi: *const BITMAPINFO,
|
||||||
|
colorUse: UINT,
|
||||||
|
) -> i32;
|
||||||
|
pub fn GdiFlush() -> BOOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link(name = "kernel32", kind = "raw-dylib")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HINSTANCE;
|
||||||
|
pub fn GetStdHandle(nStdHandle: DWORD) -> HANDLE;
|
||||||
|
pub fn WriteFile(
|
||||||
|
hFile: HANDLE,
|
||||||
|
lpBuffer: *const u8,
|
||||||
|
nNumberOfBytesToWrite: DWORD,
|
||||||
|
lpNumberOfBytesWritten: *mut DWORD,
|
||||||
|
lpOverlapped: *mut core::ffi::c_void,
|
||||||
|
) -> BOOL;
|
||||||
|
pub fn ExitProcess(uExitCode: UINT) -> !;
|
||||||
|
pub fn GetCommandLineA() -> *const u8;
|
||||||
|
pub fn GetTickCount() -> DWORD;
|
||||||
|
pub fn QueryPerformanceCounter(lpPerformanceCount: *mut i64) -> BOOL;
|
||||||
|
pub fn QueryPerformanceFrequency(lpFrequency: *mut i64) -> BOOL;
|
||||||
|
pub fn Sleep(dwMilliseconds: DWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UTF-16 helper: build a null-terminated wide string.
|
||||||
|
pub fn wide(s: &str) -> Vec<u16> {
|
||||||
|
s.encode_utf16().chain(core::iter::once(0)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Debug log through the shim's stdout (serial).
|
||||||
|
pub fn log(s: &str) {
|
||||||
|
unsafe {
|
||||||
|
let h = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
let mut written: DWORD = 0;
|
||||||
|
WriteFile(h, s.as_ptr(), s.len() as DWORD, &mut written, core::ptr::null_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+4639
@@ -0,0 +1,4639 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "Deaicup-Software"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"eframe",
|
||||||
|
"egui",
|
||||||
|
"egui_extras",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ab_glyph"
|
||||||
|
version = "0.2.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2"
|
||||||
|
dependencies = [
|
||||||
|
"ab_glyph_rasterizer",
|
||||||
|
"owned_ttf_parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ab_glyph_rasterizer"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "accesskit"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b"
|
||||||
|
dependencies = [
|
||||||
|
"enumn",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "accesskit_consumer"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c17cca53c09fbd7288667b22a201274b9becaa27f0b91bf52a526db95de45e6"
|
||||||
|
dependencies = [
|
||||||
|
"accesskit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "accesskit_macos"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd3b6ae1eabbfbced10e840fd3fce8a93ae84f174b3e4ba892ab7bcb42e477a7"
|
||||||
|
dependencies = [
|
||||||
|
"accesskit",
|
||||||
|
"accesskit_consumer",
|
||||||
|
"objc2 0.3.0-beta.3.patch-leaks.3",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "accesskit_unix"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09f46c18d99ba61ad7123dd13eeb0c104436ab6af1df6a1cd8c11054ed394a08"
|
||||||
|
dependencies = [
|
||||||
|
"accesskit",
|
||||||
|
"accesskit_consumer",
|
||||||
|
"async-channel",
|
||||||
|
"async-once-cell",
|
||||||
|
"atspi",
|
||||||
|
"futures-lite 1.13.0",
|
||||||
|
"once_cell",
|
||||||
|
"serde",
|
||||||
|
"zbus",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "accesskit_windows"
|
||||||
|
version = "0.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "afcae27ec0974fc7c3b0b318783be89fd1b2e66dd702179fe600166a38ff4a0b"
|
||||||
|
dependencies = [
|
||||||
|
"accesskit",
|
||||||
|
"accesskit_consumer",
|
||||||
|
"once_cell",
|
||||||
|
"paste",
|
||||||
|
"static_assertions",
|
||||||
|
"windows 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "accesskit_winit"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5284218aca17d9e150164428a0ebc7b955f70e3a9a78b4c20894513aabf98a67"
|
||||||
|
dependencies = [
|
||||||
|
"accesskit",
|
||||||
|
"accesskit_macos",
|
||||||
|
"accesskit_unix",
|
||||||
|
"accesskit_windows",
|
||||||
|
"winit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler2"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"getrandom 0.3.4",
|
||||||
|
"once_cell",
|
||||||
|
"serde",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android-activity"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289"
|
||||||
|
dependencies = [
|
||||||
|
"android-properties",
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"cc",
|
||||||
|
"cesu8",
|
||||||
|
"jni",
|
||||||
|
"jni-sys 0.3.1",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"ndk",
|
||||||
|
"ndk-context",
|
||||||
|
"ndk-sys",
|
||||||
|
"num_enum",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android-properties"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arboard"
|
||||||
|
version = "3.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
|
||||||
|
dependencies = [
|
||||||
|
"clipboard-win",
|
||||||
|
"log",
|
||||||
|
"objc2 0.6.4",
|
||||||
|
"objc2-app-kit",
|
||||||
|
"objc2-foundation",
|
||||||
|
"parking_lot",
|
||||||
|
"percent-encoding",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
"x11rb",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayref"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.7.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "as-raw-xcb-connection"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ash"
|
||||||
|
version = "0.37.3+1.3.251"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
|
||||||
|
dependencies = [
|
||||||
|
"libloading 0.7.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-broadcast"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b"
|
||||||
|
dependencies = [
|
||||||
|
"event-listener 2.5.3",
|
||||||
|
"futures-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-channel"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
|
||||||
|
dependencies = [
|
||||||
|
"concurrent-queue",
|
||||||
|
"event-listener-strategy",
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-executor"
|
||||||
|
version = "1.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a"
|
||||||
|
dependencies = [
|
||||||
|
"async-task",
|
||||||
|
"concurrent-queue",
|
||||||
|
"fastrand 2.5.0",
|
||||||
|
"futures-lite 2.6.1",
|
||||||
|
"pin-project-lite",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-fs"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
|
||||||
|
dependencies = [
|
||||||
|
"async-lock 2.8.0",
|
||||||
|
"autocfg",
|
||||||
|
"blocking",
|
||||||
|
"futures-lite 1.13.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-io"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
|
||||||
|
dependencies = [
|
||||||
|
"async-lock 2.8.0",
|
||||||
|
"autocfg",
|
||||||
|
"cfg-if",
|
||||||
|
"concurrent-queue",
|
||||||
|
"futures-lite 1.13.0",
|
||||||
|
"log",
|
||||||
|
"parking",
|
||||||
|
"polling 2.8.0",
|
||||||
|
"rustix 0.37.28",
|
||||||
|
"slab",
|
||||||
|
"socket2",
|
||||||
|
"waker-fn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-io"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cfg-if",
|
||||||
|
"concurrent-queue",
|
||||||
|
"futures-io",
|
||||||
|
"futures-lite 2.6.1",
|
||||||
|
"parking",
|
||||||
|
"polling 3.11.0",
|
||||||
|
"rustix 1.1.4",
|
||||||
|
"slab",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-lock"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
|
||||||
|
dependencies = [
|
||||||
|
"event-listener 2.5.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-lock"
|
||||||
|
version = "3.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
|
||||||
|
dependencies = [
|
||||||
|
"event-listener 5.4.2",
|
||||||
|
"event-listener-strategy",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-once-cell"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-process"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88"
|
||||||
|
dependencies = [
|
||||||
|
"async-io 1.13.0",
|
||||||
|
"async-lock 2.8.0",
|
||||||
|
"async-signal",
|
||||||
|
"blocking",
|
||||||
|
"cfg-if",
|
||||||
|
"event-listener 3.1.0",
|
||||||
|
"futures-lite 1.13.0",
|
||||||
|
"rustix 0.38.44",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-recursion"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-signal"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
|
||||||
|
dependencies = [
|
||||||
|
"async-io 2.6.0",
|
||||||
|
"async-lock 3.4.2",
|
||||||
|
"atomic-waker",
|
||||||
|
"cfg-if",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"rustix 1.1.4",
|
||||||
|
"signal-hook-registry",
|
||||||
|
"slab",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-task"
|
||||||
|
version = "4.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.91"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 3.0.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-waker"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atspi"
|
||||||
|
version = "0.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6059f350ab6f593ea00727b334265c4dfc7fd442ee32d264794bd9bdc68e87ca"
|
||||||
|
dependencies = [
|
||||||
|
"atspi-common",
|
||||||
|
"atspi-connection",
|
||||||
|
"atspi-proxies",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atspi-common"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92af95f966d2431f962bc632c2e68eda7777330158bf640c4af4249349b2cdf5"
|
||||||
|
dependencies = [
|
||||||
|
"enumflags2",
|
||||||
|
"serde",
|
||||||
|
"static_assertions",
|
||||||
|
"zbus",
|
||||||
|
"zbus_names",
|
||||||
|
"zvariant",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atspi-connection"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0c65e7d70f86d4c0e3b2d585d9bf3f979f0b19d635a336725a88d279f76b939"
|
||||||
|
dependencies = [
|
||||||
|
"atspi-common",
|
||||||
|
"atspi-proxies",
|
||||||
|
"futures-lite 1.13.0",
|
||||||
|
"zbus",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atspi-proxies"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52"
|
||||||
|
dependencies = [
|
||||||
|
"atspi-common",
|
||||||
|
"serde",
|
||||||
|
"zbus",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.21.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-set"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||||
|
dependencies = [
|
||||||
|
"bit-vec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-vec"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-sys"
|
||||||
|
version = "0.1.0-beta.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146"
|
||||||
|
dependencies = [
|
||||||
|
"objc-sys 0.2.0-beta.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-sys"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7"
|
||||||
|
dependencies = [
|
||||||
|
"objc-sys 0.3.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block2"
|
||||||
|
version = "0.2.0-alpha.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42"
|
||||||
|
dependencies = [
|
||||||
|
"block-sys 0.1.0-beta.1",
|
||||||
|
"objc2-encode 2.0.0-pre.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block2"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68"
|
||||||
|
dependencies = [
|
||||||
|
"block-sys 0.2.1",
|
||||||
|
"objc2 0.4.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blocking"
|
||||||
|
version = "1.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
|
||||||
|
dependencies = [
|
||||||
|
"async-channel",
|
||||||
|
"async-task",
|
||||||
|
"futures-io",
|
||||||
|
"futures-lite 2.6.1",
|
||||||
|
"piper",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.25.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck_derive"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f65693059b6b9c588b9f62fed1cedbf0a8b805631457ea162d68f0de186f3de5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "calloop"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"log",
|
||||||
|
"polling 3.11.0",
|
||||||
|
"rustix 0.38.44",
|
||||||
|
"slab",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "calloop"
|
||||||
|
version = "0.14.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"polling 3.11.0",
|
||||||
|
"rustix 1.1.4",
|
||||||
|
"slab",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "calloop-wayland-source"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02"
|
||||||
|
dependencies = [
|
||||||
|
"calloop 0.12.4",
|
||||||
|
"rustix 0.38.44",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-client",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "calloop-wayland-source"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa"
|
||||||
|
dependencies = [
|
||||||
|
"calloop 0.14.4",
|
||||||
|
"rustix 1.1.4",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-client",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
|
||||||
|
dependencies = [
|
||||||
|
"find-msvc-tools",
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cesu8"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cgl"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clipboard-win"
|
||||||
|
version = "5.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
|
||||||
|
dependencies = [
|
||||||
|
"error-code",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cocoa"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"block",
|
||||||
|
"cocoa-foundation",
|
||||||
|
"core-foundation",
|
||||||
|
"core-graphics",
|
||||||
|
"foreign-types",
|
||||||
|
"libc",
|
||||||
|
"objc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cocoa-foundation"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"block",
|
||||||
|
"core-foundation",
|
||||||
|
"core-graphics-types",
|
||||||
|
"libc",
|
||||||
|
"objc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "codespan-reporting"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||||
|
dependencies = [
|
||||||
|
"termcolor",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color_quant"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "com"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6"
|
||||||
|
dependencies = [
|
||||||
|
"com_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "com_macros"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5"
|
||||||
|
dependencies = [
|
||||||
|
"com_macros_support",
|
||||||
|
"proc-macro2",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "com_macros_support"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "combine"
|
||||||
|
version = "4.6.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "concurrent-queue"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-graphics"
|
||||||
|
version = "0.23.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"core-foundation",
|
||||||
|
"core-graphics-types",
|
||||||
|
"foreign-types",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-graphics-types"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"core-foundation",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cursor-icon"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-url"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derivative"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dispatch"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dispatch2"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"objc2 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "displaydoc"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 3.0.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dlib"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a"
|
||||||
|
dependencies = [
|
||||||
|
"libloading 0.8.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "document-features"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
|
||||||
|
dependencies = [
|
||||||
|
"litrs",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "downcast-rs"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecolor"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "eframe"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "020e2ccef6bbcec71dbc542f7eed64a5846fc3076727f5746da8fd307c91bab2"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"cocoa",
|
||||||
|
"document-features",
|
||||||
|
"egui",
|
||||||
|
"egui-wgpu",
|
||||||
|
"egui-winit",
|
||||||
|
"egui_glow",
|
||||||
|
"glow",
|
||||||
|
"glutin",
|
||||||
|
"glutin-winit",
|
||||||
|
"image",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"objc",
|
||||||
|
"parking_lot",
|
||||||
|
"percent-encoding",
|
||||||
|
"raw-window-handle 0.5.2",
|
||||||
|
"raw-window-handle 0.6.2",
|
||||||
|
"static_assertions",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
"web-time",
|
||||||
|
"winapi",
|
||||||
|
"winit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a"
|
||||||
|
dependencies = [
|
||||||
|
"accesskit",
|
||||||
|
"ahash",
|
||||||
|
"epaint",
|
||||||
|
"log",
|
||||||
|
"nohash-hasher",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui-wgpu"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "469ff65843f88a702b731a1532b7d03b0e8e96d283e70f3a22b0e06c46cb9b37"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"document-features",
|
||||||
|
"egui",
|
||||||
|
"epaint",
|
||||||
|
"log",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"type-map",
|
||||||
|
"web-time",
|
||||||
|
"wgpu",
|
||||||
|
"winit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui-winit"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e3da0cbe020f341450c599b35b92de4af7b00abde85624fd16f09c885573609"
|
||||||
|
dependencies = [
|
||||||
|
"accesskit_winit",
|
||||||
|
"arboard",
|
||||||
|
"egui",
|
||||||
|
"log",
|
||||||
|
"raw-window-handle 0.6.2",
|
||||||
|
"smithay-clipboard",
|
||||||
|
"web-time",
|
||||||
|
"webbrowser",
|
||||||
|
"winit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui_extras"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593"
|
||||||
|
dependencies = [
|
||||||
|
"egui",
|
||||||
|
"ehttp",
|
||||||
|
"enum-map",
|
||||||
|
"image",
|
||||||
|
"log",
|
||||||
|
"mime_guess2",
|
||||||
|
"resvg",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui_glow"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0e5d975f3c86edc3d35b1db88bb27c15dde7c55d3b5af164968ab5ede3f44ca"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"egui",
|
||||||
|
"glow",
|
||||||
|
"log",
|
||||||
|
"memoffset 0.9.1",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
"winit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ehttp"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59a81c221a1e4dad06cb9c9deb19aea1193a5eea084e8cd42d869068132bf876"
|
||||||
|
dependencies = [
|
||||||
|
"document-features",
|
||||||
|
"js-sys",
|
||||||
|
"ureq",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "emath"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum-map"
|
||||||
|
version = "2.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9"
|
||||||
|
dependencies = [
|
||||||
|
"enum-map-derive",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum-map-derive"
|
||||||
|
version = "0.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enumflags2"
|
||||||
|
version = "0.7.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
|
||||||
|
dependencies = [
|
||||||
|
"enumflags2_derive",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enumflags2_derive"
|
||||||
|
version = "0.7.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enumn"
|
||||||
|
version = "0.1.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "epaint"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b381f8b149657a4acf837095351839f32cd5c4aec1817fc4df84e18d76334176"
|
||||||
|
dependencies = [
|
||||||
|
"ab_glyph",
|
||||||
|
"ahash",
|
||||||
|
"bytemuck",
|
||||||
|
"ecolor",
|
||||||
|
"emath",
|
||||||
|
"log",
|
||||||
|
"nohash-hasher",
|
||||||
|
"parking_lot",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "error-code"
|
||||||
|
version = "3.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener"
|
||||||
|
version = "2.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2"
|
||||||
|
dependencies = [
|
||||||
|
"concurrent-queue",
|
||||||
|
"parking",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener"
|
||||||
|
version = "5.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2"
|
||||||
|
dependencies = [
|
||||||
|
"parking",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener-strategy"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
|
||||||
|
dependencies = [
|
||||||
|
"event-listener 5.4.2",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
|
||||||
|
dependencies = [
|
||||||
|
"instant",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fdeflate"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
||||||
|
dependencies = [
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find-msvc-tools"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float-cmp"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
|
||||||
|
dependencies = [
|
||||||
|
"foreign-types-macros",
|
||||||
|
"foreign-types-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-macros"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 3.0.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-shared"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "form_urlencoded"
|
||||||
|
version = "1.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-lite"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand 1.9.0",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"memchr",
|
||||||
|
"parking",
|
||||||
|
"pin-project-lite",
|
||||||
|
"waker-fn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-lite"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand 2.5.0",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"parking",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
|
"pin-project-lite",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gethostname"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
|
||||||
|
dependencies = [
|
||||||
|
"rustix 1.1.4",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi 5.3.0",
|
||||||
|
"wasip2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi 6.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gl_generator"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
|
||||||
|
dependencies = [
|
||||||
|
"khronos_api",
|
||||||
|
"log",
|
||||||
|
"xml-rs",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glow"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"slotmap",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glutin"
|
||||||
|
version = "0.31.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"cfg_aliases",
|
||||||
|
"cgl",
|
||||||
|
"core-foundation",
|
||||||
|
"dispatch",
|
||||||
|
"glutin_egl_sys",
|
||||||
|
"glutin_glx_sys",
|
||||||
|
"glutin_wgl_sys",
|
||||||
|
"icrate",
|
||||||
|
"libloading 0.8.9",
|
||||||
|
"objc2 0.4.1",
|
||||||
|
"once_cell",
|
||||||
|
"raw-window-handle 0.5.2",
|
||||||
|
"wayland-sys",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
"x11-dl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glutin-winit"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735"
|
||||||
|
dependencies = [
|
||||||
|
"cfg_aliases",
|
||||||
|
"glutin",
|
||||||
|
"raw-window-handle 0.5.2",
|
||||||
|
"winit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glutin_egl_sys"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd"
|
||||||
|
dependencies = [
|
||||||
|
"gl_generator",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glutin_glx_sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f"
|
||||||
|
dependencies = [
|
||||||
|
"gl_generator",
|
||||||
|
"x11-dl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glutin_wgl_sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead"
|
||||||
|
dependencies = [
|
||||||
|
"gl_generator",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gpu-alloc"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "45cf04b2726f02df5508c6de726acdc90cdf97ac771a9a0ffd8ba10a6e696bf9"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"gpu-alloc-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gpu-alloc-types"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2bbed164dd10ed526c2e4fe3e721ca4a71c61730e5aafac6844b417b3227058"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gpu-allocator"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"presser",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"winapi",
|
||||||
|
"windows 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gpu-descriptor"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"gpu-descriptor-types",
|
||||||
|
"hashbrown 0.14.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gpu-descriptor-types"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"allocator-api2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hassle-rs"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"com",
|
||||||
|
"libc",
|
||||||
|
"libloading 0.8.9",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"widestring",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hexf-parse"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "home"
|
||||||
|
version = "0.5.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icrate"
|
||||||
|
version = "0.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319"
|
||||||
|
dependencies = [
|
||||||
|
"block2 0.3.0",
|
||||||
|
"dispatch",
|
||||||
|
"objc2 0.4.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_collections"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"potential_utf",
|
||||||
|
"utf8_iter",
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_locale_core"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"litemap",
|
||||||
|
"tinystr",
|
||||||
|
"writeable",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_normalizer"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
|
||||||
|
dependencies = [
|
||||||
|
"icu_collections",
|
||||||
|
"icu_normalizer_data",
|
||||||
|
"icu_properties",
|
||||||
|
"icu_provider",
|
||||||
|
"smallvec",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_normalizer_data"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_properties"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
|
||||||
|
dependencies = [
|
||||||
|
"icu_collections",
|
||||||
|
"icu_locale_core",
|
||||||
|
"icu_properties_data",
|
||||||
|
"icu_provider",
|
||||||
|
"zerotrie",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_properties_data"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_provider"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_locale_core",
|
||||||
|
"writeable",
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
"zerotrie",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
|
||||||
|
dependencies = [
|
||||||
|
"idna_adapter",
|
||||||
|
"smallvec",
|
||||||
|
"utf8_iter",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna_adapter"
|
||||||
|
version = "1.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
|
||||||
|
dependencies = [
|
||||||
|
"icu_normalizer",
|
||||||
|
"icu_properties",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image"
|
||||||
|
version = "0.24.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"byteorder",
|
||||||
|
"color_quant",
|
||||||
|
"num-traits",
|
||||||
|
"png",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imagesize"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown 0.17.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-lifetimes"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.3.9",
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni"
|
||||||
|
version = "0.21.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
|
||||||
|
dependencies = [
|
||||||
|
"cesu8",
|
||||||
|
"cfg-if",
|
||||||
|
"combine",
|
||||||
|
"jni-sys 0.3.1",
|
||||||
|
"log",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"walkdir",
|
||||||
|
"windows-sys 0.45.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni-sys"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258"
|
||||||
|
dependencies = [
|
||||||
|
"jni-sys 0.4.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni-sys"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
|
||||||
|
dependencies = [
|
||||||
|
"jni-sys-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni-sys-macros"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.4.3",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.103"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"futures-util",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "khronos-egl"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"libloading 0.8.9",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "khronos_api"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kurbo"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.189"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libloading"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libloading"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libredox"
|
||||||
|
version = "0.1.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"libc",
|
||||||
|
"plain",
|
||||||
|
"redox_syscall 0.9.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.4.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litemap"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litrs"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "malloc_buf"
|
||||||
|
version = "0.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memmap2"
|
||||||
|
version = "0.9.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "metal"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"block",
|
||||||
|
"core-graphics-types",
|
||||||
|
"foreign-types",
|
||||||
|
"log",
|
||||||
|
"objc",
|
||||||
|
"paste",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime"
|
||||||
|
version = "0.3.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime_guess2"
|
||||||
|
version = "2.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1706dc14a2e140dec0a7a07109d9a3d5890b81e85bd6c60b906b249a77adf0ca"
|
||||||
|
dependencies = [
|
||||||
|
"mime",
|
||||||
|
"phf",
|
||||||
|
"phf_shared",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||||
|
dependencies = [
|
||||||
|
"adler2",
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "naga"
|
||||||
|
version = "0.19.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843"
|
||||||
|
dependencies = [
|
||||||
|
"bit-set",
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"codespan-reporting",
|
||||||
|
"hexf-parse",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"num-traits",
|
||||||
|
"rustc-hash 1.1.0",
|
||||||
|
"spirv",
|
||||||
|
"termcolor",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ndk"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"jni-sys 0.3.1",
|
||||||
|
"log",
|
||||||
|
"ndk-sys",
|
||||||
|
"num_enum",
|
||||||
|
"raw-window-handle 0.5.2",
|
||||||
|
"raw-window-handle 0.6.2",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ndk-context"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ndk-sys"
|
||||||
|
version = "0.5.0+25.2.9519653"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
|
||||||
|
dependencies = [
|
||||||
|
"jni-sys 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"memoffset 0.7.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nohash-hasher"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_enum"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
|
||||||
|
dependencies = [
|
||||||
|
"num_enum_derive",
|
||||||
|
"rustversion",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_enum_derive"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-crate 3.5.0",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
|
||||||
|
dependencies = [
|
||||||
|
"malloc_buf",
|
||||||
|
"objc_exception",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc-sys"
|
||||||
|
version = "0.2.0-beta.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc-sys"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2"
|
||||||
|
version = "0.3.0-beta.3.patch-leaks.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468"
|
||||||
|
dependencies = [
|
||||||
|
"block2 0.2.0-alpha.6",
|
||||||
|
"objc-sys 0.2.0-beta.2",
|
||||||
|
"objc2-encode 2.0.0-pre.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d"
|
||||||
|
dependencies = [
|
||||||
|
"objc-sys 0.3.5",
|
||||||
|
"objc2-encode 3.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
|
||||||
|
dependencies = [
|
||||||
|
"objc2-encode 4.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-app-kit"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"objc2 0.6.4",
|
||||||
|
"objc2-core-graphics",
|
||||||
|
"objc2-foundation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-core-foundation"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"dispatch2",
|
||||||
|
"objc2 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-core-graphics"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"dispatch2",
|
||||||
|
"objc2 0.6.4",
|
||||||
|
"objc2-core-foundation",
|
||||||
|
"objc2-io-surface",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-encode"
|
||||||
|
version = "2.0.0-pre.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512"
|
||||||
|
dependencies = [
|
||||||
|
"objc-sys 0.2.0-beta.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-encode"
|
||||||
|
version = "3.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-encode"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-foundation"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"objc2 0.6.4",
|
||||||
|
"objc2-core-foundation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-io-surface"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"objc2 0.6.4",
|
||||||
|
"objc2-core-foundation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc_exception"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orbclient"
|
||||||
|
version = "0.3.55"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"libredox",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordered-stream"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "owned_ttf_parser"
|
||||||
|
version = "0.25.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
|
||||||
|
dependencies = [
|
||||||
|
"ttf-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall 0.5.18",
|
||||||
|
"smallvec",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||||
|
dependencies = [
|
||||||
|
"phf_macros",
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_generator"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||||
|
dependencies = [
|
||||||
|
"phf_shared",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_macros"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
|
||||||
|
dependencies = [
|
||||||
|
"phf_generator",
|
||||||
|
"phf_shared",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_shared"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher 1.0.3",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pico-args"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "piper"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"fastrand 2.5.0",
|
||||||
|
"futures-io",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plain"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "png"
|
||||||
|
version = "0.17.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"crc32fast",
|
||||||
|
"fdeflate",
|
||||||
|
"flate2",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "polling"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"cfg-if",
|
||||||
|
"concurrent-queue",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"pin-project-lite",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "polling"
|
||||||
|
version = "3.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"concurrent-queue",
|
||||||
|
"hermit-abi 0.5.2",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rustix 1.1.4",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "potential_utf"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
|
||||||
|
dependencies = [
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "presser"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-crate"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"toml_edit 0.19.15",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-crate"
|
||||||
|
version = "3.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
|
||||||
|
dependencies = [
|
||||||
|
"toml_edit 0.25.13+spec-1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.107"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "profiling"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-xml"
|
||||||
|
version = "0.41.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.47"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.17",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "raw-window-handle"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "raw-window-handle"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rctree"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07507be7b4a5f9f26eeb41eeaebb1f5a7ff29dfb29739facc21d35bf8b11c21e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "renderdoc-sys"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "resvg"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cadccb3d99a9efb8e5e00c16fbb732cbe400db2ec7fc004697ee7d97d86cf1f4"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"pico-args",
|
||||||
|
"rgb",
|
||||||
|
"svgtypes",
|
||||||
|
"tiny-skia",
|
||||||
|
"usvg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rgb"
|
||||||
|
version = "0.8.53"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.17.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"getrandom 0.2.17",
|
||||||
|
"libc",
|
||||||
|
"untrusted",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roxmltree"
|
||||||
|
version = "0.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.37.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"errno",
|
||||||
|
"io-lifetimes",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys 0.3.8",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.38.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys 0.4.15",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys 0.12.1",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.23.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.103.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped-tls"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sctk-adwaita"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7"
|
||||||
|
dependencies = [
|
||||||
|
"ab_glyph",
|
||||||
|
"log",
|
||||||
|
"memmap2",
|
||||||
|
"smithay-client-toolkit 0.18.1",
|
||||||
|
"tiny-skia",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.229"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.229"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.229"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 3.0.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_repr"
|
||||||
|
version = "0.1.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 3.0.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||||
|
dependencies = [
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd-adler32"
|
||||||
|
version = "0.3.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simplecss"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "0.3.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slotmap"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038"
|
||||||
|
dependencies = [
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smithay-client-toolkit"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"calloop 0.12.4",
|
||||||
|
"calloop-wayland-source 0.2.0",
|
||||||
|
"cursor-icon",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"memmap2",
|
||||||
|
"rustix 0.38.44",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-csd-frame",
|
||||||
|
"wayland-cursor",
|
||||||
|
"wayland-protocols 0.31.2",
|
||||||
|
"wayland-protocols-wlr 0.2.0",
|
||||||
|
"wayland-scanner",
|
||||||
|
"xkeysym",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smithay-client-toolkit"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"calloop 0.14.4",
|
||||||
|
"calloop-wayland-source 0.4.1",
|
||||||
|
"cursor-icon",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"memmap2",
|
||||||
|
"rustix 1.1.4",
|
||||||
|
"thiserror 2.0.19",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-csd-frame",
|
||||||
|
"wayland-cursor",
|
||||||
|
"wayland-protocols 0.32.13",
|
||||||
|
"wayland-protocols-experimental",
|
||||||
|
"wayland-protocols-misc",
|
||||||
|
"wayland-protocols-wlr 0.3.12",
|
||||||
|
"wayland-scanner",
|
||||||
|
"xkeysym",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smithay-clipboard"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71704c03f739f7745053bde45fa203a46c58d25bc5c4efba1d9a60e9dba81226"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"smithay-client-toolkit 0.20.0",
|
||||||
|
"wayland-backend",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smol_str"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.4.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spirv"
|
||||||
|
version = "0.3.0+sdk-1.3.268.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strict-num"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||||
|
dependencies = [
|
||||||
|
"float-cmp",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "svgtypes"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70"
|
||||||
|
dependencies = [
|
||||||
|
"kurbo",
|
||||||
|
"siphasher 0.3.11",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.119"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "3.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand 2.5.0",
|
||||||
|
"getrandom 0.4.3",
|
||||||
|
"once_cell",
|
||||||
|
"rustix 1.1.4",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 2.0.19",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 3.0.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-skia"
|
||||||
|
version = "0.11.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"arrayvec",
|
||||||
|
"bytemuck",
|
||||||
|
"cfg-if",
|
||||||
|
"log",
|
||||||
|
"png",
|
||||||
|
"tiny-skia-path",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-skia-path"
|
||||||
|
version = "0.11.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"bytemuck",
|
||||||
|
"strict-num",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinystr"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "1.1.1+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.19.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"toml_datetime 0.6.11",
|
||||||
|
"winnow 0.5.40",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.25.13+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"toml_datetime 1.1.1+spec-1.1.0",
|
||||||
|
"toml_parser",
|
||||||
|
"winnow 1.0.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_parser"
|
||||||
|
version = "1.1.3+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
|
||||||
|
dependencies = [
|
||||||
|
"winnow 1.0.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-attributes",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-attributes"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ttf-parser"
|
||||||
|
version = "0.25.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "type-map"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90"
|
||||||
|
dependencies = [
|
||||||
|
"rustc-hash 2.1.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.20.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uds_windows"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
|
||||||
|
dependencies = [
|
||||||
|
"memoffset 0.9.1",
|
||||||
|
"tempfile",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ureq"
|
||||||
|
version = "2.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"flate2",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"url",
|
||||||
|
"webpki-roots 0.26.11",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "url"
|
||||||
|
version = "2.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
|
||||||
|
dependencies = [
|
||||||
|
"form_urlencoded",
|
||||||
|
"idna",
|
||||||
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usvg"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.7",
|
||||||
|
"log",
|
||||||
|
"pico-args",
|
||||||
|
"usvg-parser",
|
||||||
|
"usvg-tree",
|
||||||
|
"xmlwriter",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usvg-parser"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9bd4e3c291f45d152929a31f0f6c819245e2921bfd01e7bd91201a9af39a2bdc"
|
||||||
|
dependencies = [
|
||||||
|
"data-url",
|
||||||
|
"flate2",
|
||||||
|
"imagesize",
|
||||||
|
"kurbo",
|
||||||
|
"log",
|
||||||
|
"roxmltree",
|
||||||
|
"simplecss",
|
||||||
|
"siphasher 0.3.11",
|
||||||
|
"svgtypes",
|
||||||
|
"usvg-tree",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usvg-tree"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ee3d202ebdb97a6215604b8f5b4d6ef9024efd623cf2e373a6416ba976ec7d3"
|
||||||
|
dependencies = [
|
||||||
|
"rctree",
|
||||||
|
"strict-num",
|
||||||
|
"svgtypes",
|
||||||
|
"tiny-skia-path",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8_iter"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "waker-fn"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip2"
|
||||||
|
version = "1.0.4+wasi-0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-futures"
|
||||||
|
version = "0.4.76"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-backend"
|
||||||
|
version = "0.3.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "016ccf01d1c58b6f8999612813e17c9b2390f7d70671428869913310f83f54b8"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"downcast-rs",
|
||||||
|
"rustix 1.1.4",
|
||||||
|
"scoped-tls",
|
||||||
|
"smallvec",
|
||||||
|
"wayland-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-client"
|
||||||
|
version = "0.31.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"rustix 1.1.4",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-scanner",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-csd-frame"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"cursor-icon",
|
||||||
|
"wayland-backend",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-cursor"
|
||||||
|
version = "0.31.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d"
|
||||||
|
dependencies = [
|
||||||
|
"rustix 1.1.4",
|
||||||
|
"wayland-client",
|
||||||
|
"xcursor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-protocols"
|
||||||
|
version = "0.31.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-scanner",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-protocols"
|
||||||
|
version = "0.32.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-scanner",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-protocols-experimental"
|
||||||
|
version = "20250721.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-protocols 0.32.13",
|
||||||
|
"wayland-scanner",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-protocols-misc"
|
||||||
|
version = "0.3.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-protocols 0.32.13",
|
||||||
|
"wayland-scanner",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-protocols-plasma"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-protocols 0.31.2",
|
||||||
|
"wayland-scanner",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-protocols-wlr"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-protocols 0.31.2",
|
||||||
|
"wayland-scanner",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-protocols-wlr"
|
||||||
|
version = "0.3.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-protocols 0.32.13",
|
||||||
|
"wayland-scanner",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-scanner"
|
||||||
|
version = "0.31.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quick-xml",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-sys"
|
||||||
|
version = "0.31.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
|
||||||
|
dependencies = [
|
||||||
|
"dlib",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.103"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-time"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webbrowser"
|
||||||
|
version = "0.8.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation",
|
||||||
|
"home",
|
||||||
|
"jni",
|
||||||
|
"log",
|
||||||
|
"ndk-context",
|
||||||
|
"objc",
|
||||||
|
"raw-window-handle 0.5.2",
|
||||||
|
"url",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "0.26.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
||||||
|
dependencies = [
|
||||||
|
"webpki-roots 1.0.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wgpu"
|
||||||
|
version = "0.19.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"cfg-if",
|
||||||
|
"cfg_aliases",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"profiling",
|
||||||
|
"raw-window-handle 0.6.2",
|
||||||
|
"smallvec",
|
||||||
|
"static_assertions",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
"wgpu-core",
|
||||||
|
"wgpu-hal",
|
||||||
|
"wgpu-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wgpu-core"
|
||||||
|
version = "0.19.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"bit-vec",
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"cfg_aliases",
|
||||||
|
"codespan-reporting",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"naga",
|
||||||
|
"once_cell",
|
||||||
|
"parking_lot",
|
||||||
|
"profiling",
|
||||||
|
"raw-window-handle 0.6.2",
|
||||||
|
"rustc-hash 1.1.0",
|
||||||
|
"smallvec",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"web-sys",
|
||||||
|
"wgpu-hal",
|
||||||
|
"wgpu-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wgpu-hal"
|
||||||
|
version = "0.19.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfabcfc55fd86611a855816326b2d54c3b2fd7972c27ce414291562650552703"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"arrayvec",
|
||||||
|
"ash",
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"cfg_aliases",
|
||||||
|
"core-graphics-types",
|
||||||
|
"glow",
|
||||||
|
"glutin_wgl_sys",
|
||||||
|
"gpu-alloc",
|
||||||
|
"gpu-allocator",
|
||||||
|
"gpu-descriptor",
|
||||||
|
"hassle-rs",
|
||||||
|
"js-sys",
|
||||||
|
"khronos-egl",
|
||||||
|
"libc",
|
||||||
|
"libloading 0.8.9",
|
||||||
|
"log",
|
||||||
|
"metal",
|
||||||
|
"naga",
|
||||||
|
"ndk-sys",
|
||||||
|
"objc",
|
||||||
|
"once_cell",
|
||||||
|
"parking_lot",
|
||||||
|
"profiling",
|
||||||
|
"raw-window-handle 0.6.2",
|
||||||
|
"renderdoc-sys",
|
||||||
|
"rustc-hash 1.1.0",
|
||||||
|
"smallvec",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
"wgpu-types",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wgpu-types"
|
||||||
|
version = "0.19.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"js-sys",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "widestring"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-targets 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core",
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.45.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.53.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.42.2",
|
||||||
|
"windows_aarch64_msvc 0.42.2",
|
||||||
|
"windows_i686_gnu 0.42.2",
|
||||||
|
"windows_i686_msvc 0.42.2",
|
||||||
|
"windows_x86_64_gnu 0.42.2",
|
||||||
|
"windows_x86_64_gnullvm 0.42.2",
|
||||||
|
"windows_x86_64_msvc 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.48.5",
|
||||||
|
"windows_aarch64_msvc 0.48.5",
|
||||||
|
"windows_i686_gnu 0.48.5",
|
||||||
|
"windows_i686_msvc 0.48.5",
|
||||||
|
"windows_x86_64_gnu 0.48.5",
|
||||||
|
"windows_x86_64_gnullvm 0.48.5",
|
||||||
|
"windows_x86_64_msvc 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.52.6",
|
||||||
|
"windows_aarch64_msvc 0.52.6",
|
||||||
|
"windows_i686_gnu 0.52.6",
|
||||||
|
"windows_i686_gnullvm 0.52.6",
|
||||||
|
"windows_i686_msvc 0.52.6",
|
||||||
|
"windows_x86_64_gnu 0.52.6",
|
||||||
|
"windows_x86_64_gnullvm 0.52.6",
|
||||||
|
"windows_x86_64_msvc 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.53.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
"windows_aarch64_gnullvm 0.53.1",
|
||||||
|
"windows_aarch64_msvc 0.53.1",
|
||||||
|
"windows_i686_gnu 0.53.1",
|
||||||
|
"windows_i686_gnullvm 0.53.1",
|
||||||
|
"windows_i686_msvc 0.53.1",
|
||||||
|
"windows_x86_64_gnu 0.53.1",
|
||||||
|
"windows_x86_64_gnullvm 0.53.1",
|
||||||
|
"windows_x86_64_msvc 0.53.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winit"
|
||||||
|
version = "0.29.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"android-activity",
|
||||||
|
"atomic-waker",
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"bytemuck",
|
||||||
|
"calloop 0.12.4",
|
||||||
|
"cfg_aliases",
|
||||||
|
"core-foundation",
|
||||||
|
"core-graphics",
|
||||||
|
"cursor-icon",
|
||||||
|
"icrate",
|
||||||
|
"js-sys",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"memmap2",
|
||||||
|
"ndk",
|
||||||
|
"ndk-sys",
|
||||||
|
"objc2 0.4.1",
|
||||||
|
"once_cell",
|
||||||
|
"orbclient",
|
||||||
|
"percent-encoding",
|
||||||
|
"raw-window-handle 0.5.2",
|
||||||
|
"raw-window-handle 0.6.2",
|
||||||
|
"redox_syscall 0.3.5",
|
||||||
|
"rustix 0.38.44",
|
||||||
|
"sctk-adwaita",
|
||||||
|
"smithay-client-toolkit 0.18.1",
|
||||||
|
"smol_str",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-protocols 0.31.2",
|
||||||
|
"wayland-protocols-plasma",
|
||||||
|
"web-sys",
|
||||||
|
"web-time",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
"x11-dl",
|
||||||
|
"x11rb",
|
||||||
|
"xkbcommon-dl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.5.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen"
|
||||||
|
version = "0.57.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "writeable"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x11-dl"
|
||||||
|
version = "2.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x11rb"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
|
||||||
|
dependencies = [
|
||||||
|
"as-raw-xcb-connection",
|
||||||
|
"gethostname",
|
||||||
|
"libc",
|
||||||
|
"libloading 0.8.9",
|
||||||
|
"once_cell",
|
||||||
|
"rustix 1.1.4",
|
||||||
|
"x11rb-protocol",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x11rb-protocol"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xcursor"
|
||||||
|
version = "0.3.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xdg-home"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xkbcommon-dl"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"dlib",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"xkeysym",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xkeysym"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xml-rs"
|
||||||
|
version = "0.8.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xmlwriter"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yoke"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
|
||||||
|
dependencies = [
|
||||||
|
"stable_deref_trait",
|
||||||
|
"yoke-derive",
|
||||||
|
"zerofrom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yoke-derive"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zbus"
|
||||||
|
version = "3.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6"
|
||||||
|
dependencies = [
|
||||||
|
"async-broadcast",
|
||||||
|
"async-executor",
|
||||||
|
"async-fs",
|
||||||
|
"async-io 1.13.0",
|
||||||
|
"async-lock 2.8.0",
|
||||||
|
"async-process",
|
||||||
|
"async-recursion",
|
||||||
|
"async-task",
|
||||||
|
"async-trait",
|
||||||
|
"blocking",
|
||||||
|
"byteorder",
|
||||||
|
"derivative",
|
||||||
|
"enumflags2",
|
||||||
|
"event-listener 2.5.3",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-util",
|
||||||
|
"hex",
|
||||||
|
"nix",
|
||||||
|
"once_cell",
|
||||||
|
"ordered-stream",
|
||||||
|
"rand",
|
||||||
|
"serde",
|
||||||
|
"serde_repr",
|
||||||
|
"sha1",
|
||||||
|
"static_assertions",
|
||||||
|
"tracing",
|
||||||
|
"uds_windows",
|
||||||
|
"winapi",
|
||||||
|
"xdg-home",
|
||||||
|
"zbus_macros",
|
||||||
|
"zbus_names",
|
||||||
|
"zvariant",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zbus_macros"
|
||||||
|
version = "3.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-crate 1.3.1",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"zvariant_utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zbus_names"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"static_assertions",
|
||||||
|
"zvariant",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.55"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.55"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerofrom"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
|
||||||
|
dependencies = [
|
||||||
|
"zerofrom-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerofrom-derive"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerotrie"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerovec"
|
||||||
|
version = "0.11.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
|
||||||
|
dependencies = [
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
"zerovec-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerovec-derive"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zvariant"
|
||||||
|
version = "3.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"enumflags2",
|
||||||
|
"libc",
|
||||||
|
"serde",
|
||||||
|
"static_assertions",
|
||||||
|
"zvariant_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zvariant_derive"
|
||||||
|
version = "3.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-crate 1.3.1",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"zvariant_utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zvariant_utils"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "Deaicup-Software"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "Deaicup-Software"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
egui = "0.27"
|
||||||
|
eframe = "0.27"
|
||||||
|
egui_extras = { version = "0.27" , features = ["all_loaders"] }
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
#![cfg_attr(target_os = "windows", windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
use eframe::egui;
|
||||||
|
|
||||||
|
fn main() -> Result<(), eframe::Error>{
|
||||||
|
let options = eframe::NativeOptions {
|
||||||
|
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
eframe::run_native(
|
||||||
|
"Deaicup Software",
|
||||||
|
options,
|
||||||
|
Box::new(|cc| {
|
||||||
|
egui_extras::install_image_loaders(&cc.egui_ctx);
|
||||||
|
Box::new(DeaicupSoftware::default())
|
||||||
|
},)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct DeaicupSoftware {
|
||||||
|
current_page: Page,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy, Default)]
|
||||||
|
enum Page {
|
||||||
|
#[default]
|
||||||
|
Home,
|
||||||
|
Tool,
|
||||||
|
Game,
|
||||||
|
LinuxWare,
|
||||||
|
WindowsWare,
|
||||||
|
Set,
|
||||||
|
About
|
||||||
|
}
|
||||||
|
|
||||||
|
impl eframe::App for DeaicupSoftware{
|
||||||
|
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame){
|
||||||
|
egui::TopBottomPanel::top("top_image_panel").show(ctx, |ui|{
|
||||||
|
let image_url = "https://deaicup.com/DeaicupSoftware/top.png";
|
||||||
|
ui.set_min_height(75.0);
|
||||||
|
let size = ui.available_size();
|
||||||
|
ui.image(egui::Image::new(image_url).fit_to_exact_size(egui::Vec2::new(size.x, 75.0)));
|
||||||
|
});
|
||||||
|
|
||||||
|
//main
|
||||||
|
egui::SidePanel::left("navigation_panel")
|
||||||
|
.default_width(200.0)
|
||||||
|
.resizable(true)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.heading("Menu");
|
||||||
|
ui.separator();
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
if ui.selectable_label(self.current_page == Page::Home, "HOME").clicked(){
|
||||||
|
self.current_page = Page::Home;
|
||||||
|
}
|
||||||
|
if ui.selectable_label(self.current_page == Page::Tool, "TOOL").clicked(){
|
||||||
|
self.current_page = Page::Tool;
|
||||||
|
}
|
||||||
|
if ui.selectable_label(self.current_page == Page::Game, "GAME").clicked(){
|
||||||
|
self.current_page = Page::Game;
|
||||||
|
}
|
||||||
|
if ui.selectable_label(self.current_page == Page::LinuxWare, "LINUXWARE").clicked(){
|
||||||
|
self.current_page = Page::LinuxWare;
|
||||||
|
}
|
||||||
|
if ui.selectable_label(self.current_page == Page::WindowsWare, "WINDOWSWARE").clicked(){
|
||||||
|
self.current_page = Page::WindowsWare;
|
||||||
|
}
|
||||||
|
if ui.selectable_label(self.current_page == Page::Set, "SET").clicked(){
|
||||||
|
self.current_page = Page::Set;
|
||||||
|
}
|
||||||
|
if ui.selectable_label(self.current_page == Page::About, "ABOUT").clicked(){
|
||||||
|
self.current_page = Page::About;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.separator();
|
||||||
|
match self.current_page {
|
||||||
|
Page::Home => {
|
||||||
|
ui.label("Home")
|
||||||
|
}
|
||||||
|
Page::Tool => {
|
||||||
|
ui.label("Tool")
|
||||||
|
}
|
||||||
|
Page::Game => {
|
||||||
|
ui.label("Game")
|
||||||
|
}
|
||||||
|
Page::LinuxWare => {
|
||||||
|
ui.label("LinuxWare")
|
||||||
|
}
|
||||||
|
Page::WindowsWare => {
|
||||||
|
ui.label("WindowsWare")
|
||||||
|
}
|
||||||
|
Page::Set => {
|
||||||
|
ui.label("Set")
|
||||||
|
}
|
||||||
|
Page::About => {
|
||||||
|
ui.label("About")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-3
@@ -12,20 +12,28 @@ LDFLAGS := -nostdlib -static -z max-page-size=0x1000 -T linker.ld
|
|||||||
C_SOURCES := $(wildcard arch/x86_64/*.c kernel/*.c runtime/*.c mm/*.c core/*.c crypto/*.c pckc/*.c utrw/*.c drr/*.c dkm/*.c test/*.c vmm/*.c pe/*.c)
|
C_SOURCES := $(wildcard arch/x86_64/*.c kernel/*.c runtime/*.c mm/*.c core/*.c crypto/*.c pckc/*.c utrw/*.c drr/*.c dkm/*.c test/*.c vmm/*.c pe/*.c)
|
||||||
S_SOURCES := $(wildcard arch/x86_64/*.S vmm/*.S)
|
S_SOURCES := $(wildcard arch/x86_64/*.S vmm/*.S)
|
||||||
OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o)
|
OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o)
|
||||||
|
DEPS := $(C_SOURCES:.c=.d)
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|
||||||
|
# 显式指定默认目标:防止 -include 的 .d 文件(halt.d 等)抢占默认目标
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
|
||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
|
# 头文件依赖追踪:clang -MMD 生成 .d,避免 config.h 等头修改后对象文件不重建
|
||||||
|
# 注意:-include 必须放在 all: 之后,否则第一个 .d 中的目标会成为 make 默认目标
|
||||||
|
-include $(DEPS)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\boot" mkdir "$(ROOT)\SYSTEM\boot"
|
@mkdir -p $(dir $@)
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
|
||||||
|
|
||||||
%.o: %.S
|
%.o: %.S
|
||||||
$(CC) $(ASFLAGS) -c $< -o $@
|
$(CC) $(ASFLAGS) -c $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Include *.o,*.d | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
arch/x86_64/halt.o: arch\x86_64\halt.c
|
||||||
+489
-30
@@ -1,6 +1,7 @@
|
|||||||
#include <utsm/idt.h>
|
#include <utsm/idt.h>
|
||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
#include <utsm/types.h>
|
#include <utsm/types.h>
|
||||||
|
#include <utsm/instr.h>
|
||||||
|
|
||||||
/* IDT gate descriptor (64-bit) */
|
/* IDT gate descriptor (64-bit) */
|
||||||
typedef struct __attribute__((packed)) {
|
typedef struct __attribute__((packed)) {
|
||||||
@@ -29,7 +30,7 @@ static idt_entry g_idt[IDT_ENTRIES] __attribute__((aligned(16)));
|
|||||||
#define ICW1_ICW4 0x01
|
#define ICW1_ICW4 0x01
|
||||||
#define ICW1_INIT 0x10
|
#define ICW1_INIT 0x10
|
||||||
|
|
||||||
/* assembly stub declarations */
|
/* assembly stub declarations — vectors 0-255 全覆盖 */
|
||||||
extern void isr_stub_0(void);
|
extern void isr_stub_0(void);
|
||||||
extern void isr_stub_1(void);
|
extern void isr_stub_1(void);
|
||||||
extern void isr_stub_2(void);
|
extern void isr_stub_2(void);
|
||||||
@@ -78,20 +79,280 @@ extern void isr_stub_44(void);
|
|||||||
extern void isr_stub_45(void);
|
extern void isr_stub_45(void);
|
||||||
extern void isr_stub_46(void);
|
extern void isr_stub_46(void);
|
||||||
extern void isr_stub_47(void);
|
extern void isr_stub_47(void);
|
||||||
|
extern void isr_stub_48(void);
|
||||||
|
extern void isr_stub_49(void);
|
||||||
|
extern void isr_stub_50(void);
|
||||||
|
extern void isr_stub_51(void);
|
||||||
|
extern void isr_stub_52(void);
|
||||||
|
extern void isr_stub_53(void);
|
||||||
|
extern void isr_stub_54(void);
|
||||||
|
extern void isr_stub_55(void);
|
||||||
|
extern void isr_stub_56(void);
|
||||||
|
extern void isr_stub_57(void);
|
||||||
|
extern void isr_stub_58(void);
|
||||||
|
extern void isr_stub_59(void);
|
||||||
|
extern void isr_stub_60(void);
|
||||||
|
extern void isr_stub_61(void);
|
||||||
|
extern void isr_stub_62(void);
|
||||||
|
extern void isr_stub_63(void);
|
||||||
|
extern void isr_stub_64(void);
|
||||||
|
extern void isr_stub_65(void);
|
||||||
|
extern void isr_stub_66(void);
|
||||||
|
extern void isr_stub_67(void);
|
||||||
|
extern void isr_stub_68(void);
|
||||||
|
extern void isr_stub_69(void);
|
||||||
|
extern void isr_stub_70(void);
|
||||||
|
extern void isr_stub_71(void);
|
||||||
|
extern void isr_stub_72(void);
|
||||||
|
extern void isr_stub_73(void);
|
||||||
|
extern void isr_stub_74(void);
|
||||||
|
extern void isr_stub_75(void);
|
||||||
|
extern void isr_stub_76(void);
|
||||||
|
extern void isr_stub_77(void);
|
||||||
|
extern void isr_stub_78(void);
|
||||||
|
extern void isr_stub_79(void);
|
||||||
|
extern void isr_stub_80(void);
|
||||||
|
extern void isr_stub_81(void);
|
||||||
|
extern void isr_stub_82(void);
|
||||||
|
extern void isr_stub_83(void);
|
||||||
|
extern void isr_stub_84(void);
|
||||||
|
extern void isr_stub_85(void);
|
||||||
|
extern void isr_stub_86(void);
|
||||||
|
extern void isr_stub_87(void);
|
||||||
|
extern void isr_stub_88(void);
|
||||||
|
extern void isr_stub_89(void);
|
||||||
|
extern void isr_stub_90(void);
|
||||||
|
extern void isr_stub_91(void);
|
||||||
|
extern void isr_stub_92(void);
|
||||||
|
extern void isr_stub_93(void);
|
||||||
|
extern void isr_stub_94(void);
|
||||||
|
extern void isr_stub_95(void);
|
||||||
|
extern void isr_stub_96(void);
|
||||||
|
extern void isr_stub_97(void);
|
||||||
|
extern void isr_stub_98(void);
|
||||||
|
extern void isr_stub_99(void);
|
||||||
|
extern void isr_stub_100(void);
|
||||||
|
extern void isr_stub_101(void);
|
||||||
|
extern void isr_stub_102(void);
|
||||||
|
extern void isr_stub_103(void);
|
||||||
|
extern void isr_stub_104(void);
|
||||||
|
extern void isr_stub_105(void);
|
||||||
|
extern void isr_stub_106(void);
|
||||||
|
extern void isr_stub_107(void);
|
||||||
|
extern void isr_stub_108(void);
|
||||||
|
extern void isr_stub_109(void);
|
||||||
|
extern void isr_stub_110(void);
|
||||||
|
extern void isr_stub_111(void);
|
||||||
|
extern void isr_stub_112(void);
|
||||||
|
extern void isr_stub_113(void);
|
||||||
|
extern void isr_stub_114(void);
|
||||||
|
extern void isr_stub_115(void);
|
||||||
|
extern void isr_stub_116(void);
|
||||||
|
extern void isr_stub_117(void);
|
||||||
|
extern void isr_stub_118(void);
|
||||||
|
extern void isr_stub_119(void);
|
||||||
|
extern void isr_stub_120(void);
|
||||||
|
extern void isr_stub_121(void);
|
||||||
|
extern void isr_stub_122(void);
|
||||||
|
extern void isr_stub_123(void);
|
||||||
|
extern void isr_stub_124(void);
|
||||||
|
extern void isr_stub_125(void);
|
||||||
|
extern void isr_stub_126(void);
|
||||||
|
extern void isr_stub_127(void);
|
||||||
|
extern void isr_stub_128(void);
|
||||||
|
extern void isr_stub_129(void);
|
||||||
|
extern void isr_stub_130(void);
|
||||||
|
extern void isr_stub_131(void);
|
||||||
|
extern void isr_stub_132(void);
|
||||||
|
extern void isr_stub_133(void);
|
||||||
|
extern void isr_stub_134(void);
|
||||||
|
extern void isr_stub_135(void);
|
||||||
|
extern void isr_stub_136(void);
|
||||||
|
extern void isr_stub_137(void);
|
||||||
|
extern void isr_stub_138(void);
|
||||||
|
extern void isr_stub_139(void);
|
||||||
|
extern void isr_stub_140(void);
|
||||||
|
extern void isr_stub_141(void);
|
||||||
|
extern void isr_stub_142(void);
|
||||||
|
extern void isr_stub_143(void);
|
||||||
|
extern void isr_stub_144(void);
|
||||||
|
extern void isr_stub_145(void);
|
||||||
|
extern void isr_stub_146(void);
|
||||||
|
extern void isr_stub_147(void);
|
||||||
|
extern void isr_stub_148(void);
|
||||||
|
extern void isr_stub_149(void);
|
||||||
|
extern void isr_stub_150(void);
|
||||||
|
extern void isr_stub_151(void);
|
||||||
|
extern void isr_stub_152(void);
|
||||||
|
extern void isr_stub_153(void);
|
||||||
|
extern void isr_stub_154(void);
|
||||||
|
extern void isr_stub_155(void);
|
||||||
|
extern void isr_stub_156(void);
|
||||||
|
extern void isr_stub_157(void);
|
||||||
|
extern void isr_stub_158(void);
|
||||||
|
extern void isr_stub_159(void);
|
||||||
|
extern void isr_stub_160(void);
|
||||||
|
extern void isr_stub_161(void);
|
||||||
|
extern void isr_stub_162(void);
|
||||||
|
extern void isr_stub_163(void);
|
||||||
|
extern void isr_stub_164(void);
|
||||||
|
extern void isr_stub_165(void);
|
||||||
|
extern void isr_stub_166(void);
|
||||||
|
extern void isr_stub_167(void);
|
||||||
|
extern void isr_stub_168(void);
|
||||||
|
extern void isr_stub_169(void);
|
||||||
|
extern void isr_stub_170(void);
|
||||||
|
extern void isr_stub_171(void);
|
||||||
|
extern void isr_stub_172(void);
|
||||||
|
extern void isr_stub_173(void);
|
||||||
|
extern void isr_stub_174(void);
|
||||||
|
extern void isr_stub_175(void);
|
||||||
|
extern void isr_stub_176(void);
|
||||||
|
extern void isr_stub_177(void);
|
||||||
|
extern void isr_stub_178(void);
|
||||||
|
extern void isr_stub_179(void);
|
||||||
|
extern void isr_stub_180(void);
|
||||||
|
extern void isr_stub_181(void);
|
||||||
|
extern void isr_stub_182(void);
|
||||||
|
extern void isr_stub_183(void);
|
||||||
|
extern void isr_stub_184(void);
|
||||||
|
extern void isr_stub_185(void);
|
||||||
|
extern void isr_stub_186(void);
|
||||||
|
extern void isr_stub_187(void);
|
||||||
|
extern void isr_stub_188(void);
|
||||||
|
extern void isr_stub_189(void);
|
||||||
|
extern void isr_stub_190(void);
|
||||||
|
extern void isr_stub_191(void);
|
||||||
|
extern void isr_stub_192(void);
|
||||||
|
extern void isr_stub_193(void);
|
||||||
|
extern void isr_stub_194(void);
|
||||||
|
extern void isr_stub_195(void);
|
||||||
|
extern void isr_stub_196(void);
|
||||||
|
extern void isr_stub_197(void);
|
||||||
|
extern void isr_stub_198(void);
|
||||||
|
extern void isr_stub_199(void);
|
||||||
|
extern void isr_stub_200(void);
|
||||||
|
extern void isr_stub_201(void);
|
||||||
|
extern void isr_stub_202(void);
|
||||||
|
extern void isr_stub_203(void);
|
||||||
|
extern void isr_stub_204(void);
|
||||||
|
extern void isr_stub_205(void);
|
||||||
|
extern void isr_stub_206(void);
|
||||||
|
extern void isr_stub_207(void);
|
||||||
|
extern void isr_stub_208(void);
|
||||||
|
extern void isr_stub_209(void);
|
||||||
|
extern void isr_stub_210(void);
|
||||||
|
extern void isr_stub_211(void);
|
||||||
|
extern void isr_stub_212(void);
|
||||||
|
extern void isr_stub_213(void);
|
||||||
|
extern void isr_stub_214(void);
|
||||||
|
extern void isr_stub_215(void);
|
||||||
|
extern void isr_stub_216(void);
|
||||||
|
extern void isr_stub_217(void);
|
||||||
|
extern void isr_stub_218(void);
|
||||||
|
extern void isr_stub_219(void);
|
||||||
|
extern void isr_stub_220(void);
|
||||||
|
extern void isr_stub_221(void);
|
||||||
|
extern void isr_stub_222(void);
|
||||||
|
extern void isr_stub_223(void);
|
||||||
|
extern void isr_stub_224(void);
|
||||||
|
extern void isr_stub_225(void);
|
||||||
|
extern void isr_stub_226(void);
|
||||||
|
extern void isr_stub_227(void);
|
||||||
|
extern void isr_stub_228(void);
|
||||||
|
extern void isr_stub_229(void);
|
||||||
|
extern void isr_stub_230(void);
|
||||||
|
extern void isr_stub_231(void);
|
||||||
|
extern void isr_stub_232(void);
|
||||||
|
extern void isr_stub_233(void);
|
||||||
|
extern void isr_stub_234(void);
|
||||||
|
extern void isr_stub_235(void);
|
||||||
|
extern void isr_stub_236(void);
|
||||||
|
extern void isr_stub_237(void);
|
||||||
|
extern void isr_stub_238(void);
|
||||||
|
extern void isr_stub_239(void);
|
||||||
|
extern void isr_stub_240(void);
|
||||||
|
extern void isr_stub_241(void);
|
||||||
|
extern void isr_stub_242(void);
|
||||||
|
extern void isr_stub_243(void);
|
||||||
|
extern void isr_stub_244(void);
|
||||||
|
extern void isr_stub_245(void);
|
||||||
|
extern void isr_stub_246(void);
|
||||||
|
extern void isr_stub_247(void);
|
||||||
|
extern void isr_stub_248(void);
|
||||||
|
extern void isr_stub_249(void);
|
||||||
|
extern void isr_stub_250(void);
|
||||||
|
extern void isr_stub_251(void);
|
||||||
|
extern void isr_stub_252(void);
|
||||||
|
extern void isr_stub_253(void);
|
||||||
|
extern void isr_stub_254(void);
|
||||||
|
extern void isr_stub_255(void);
|
||||||
|
|
||||||
static void *g_isr_table[] = {
|
static void *g_isr_table[256] = {
|
||||||
isr_stub_0, isr_stub_1, isr_stub_2, isr_stub_3,
|
isr_stub_0, isr_stub_1, isr_stub_2, isr_stub_3,
|
||||||
isr_stub_4, isr_stub_5, isr_stub_6, isr_stub_7,
|
isr_stub_4, isr_stub_5, isr_stub_6, isr_stub_7,
|
||||||
isr_stub_8, isr_stub_9, isr_stub_10, isr_stub_11,
|
isr_stub_8, isr_stub_9, isr_stub_10, isr_stub_11,
|
||||||
isr_stub_12, isr_stub_13, isr_stub_14, isr_stub_15,
|
isr_stub_12, isr_stub_13, isr_stub_14, isr_stub_15,
|
||||||
isr_stub_16, isr_stub_17, isr_stub_18, isr_stub_19,
|
isr_stub_16, isr_stub_17, isr_stub_18, isr_stub_19,
|
||||||
isr_stub_20, isr_stub_21, isr_stub_22, isr_stub_23,
|
isr_stub_20, isr_stub_21, isr_stub_22, isr_stub_23,
|
||||||
isr_stub_24, isr_stub_25, isr_stub_26, isr_stub_27,
|
isr_stub_24, isr_stub_25, isr_stub_26, isr_stub_27,
|
||||||
isr_stub_28, isr_stub_29, isr_stub_30, isr_stub_31,
|
isr_stub_28, isr_stub_29, isr_stub_30, isr_stub_31,
|
||||||
isr_stub_32, isr_stub_33, isr_stub_34, isr_stub_35,
|
isr_stub_32, isr_stub_33, isr_stub_34, isr_stub_35,
|
||||||
isr_stub_36, isr_stub_37, isr_stub_38, isr_stub_39,
|
isr_stub_36, isr_stub_37, isr_stub_38, isr_stub_39,
|
||||||
isr_stub_40, isr_stub_41, isr_stub_42, isr_stub_43,
|
isr_stub_40, isr_stub_41, isr_stub_42, isr_stub_43,
|
||||||
isr_stub_44, isr_stub_45, isr_stub_46, isr_stub_47,
|
isr_stub_44, isr_stub_45, isr_stub_46, isr_stub_47,
|
||||||
|
isr_stub_48, isr_stub_49, isr_stub_50, isr_stub_51,
|
||||||
|
isr_stub_52, isr_stub_53, isr_stub_54, isr_stub_55,
|
||||||
|
isr_stub_56, isr_stub_57, isr_stub_58, isr_stub_59,
|
||||||
|
isr_stub_60, isr_stub_61, isr_stub_62, isr_stub_63,
|
||||||
|
isr_stub_64, isr_stub_65, isr_stub_66, isr_stub_67,
|
||||||
|
isr_stub_68, isr_stub_69, isr_stub_70, isr_stub_71,
|
||||||
|
isr_stub_72, isr_stub_73, isr_stub_74, isr_stub_75,
|
||||||
|
isr_stub_76, isr_stub_77, isr_stub_78, isr_stub_79,
|
||||||
|
isr_stub_80, isr_stub_81, isr_stub_82, isr_stub_83,
|
||||||
|
isr_stub_84, isr_stub_85, isr_stub_86, isr_stub_87,
|
||||||
|
isr_stub_88, isr_stub_89, isr_stub_90, isr_stub_91,
|
||||||
|
isr_stub_92, isr_stub_93, isr_stub_94, isr_stub_95,
|
||||||
|
isr_stub_96, isr_stub_97, isr_stub_98, isr_stub_99,
|
||||||
|
isr_stub_100, isr_stub_101, isr_stub_102, isr_stub_103,
|
||||||
|
isr_stub_104, isr_stub_105, isr_stub_106, isr_stub_107,
|
||||||
|
isr_stub_108, isr_stub_109, isr_stub_110, isr_stub_111,
|
||||||
|
isr_stub_112, isr_stub_113, isr_stub_114, isr_stub_115,
|
||||||
|
isr_stub_116, isr_stub_117, isr_stub_118, isr_stub_119,
|
||||||
|
isr_stub_120, isr_stub_121, isr_stub_122, isr_stub_123,
|
||||||
|
isr_stub_124, isr_stub_125, isr_stub_126, isr_stub_127,
|
||||||
|
isr_stub_128, isr_stub_129, isr_stub_130, isr_stub_131,
|
||||||
|
isr_stub_132, isr_stub_133, isr_stub_134, isr_stub_135,
|
||||||
|
isr_stub_136, isr_stub_137, isr_stub_138, isr_stub_139,
|
||||||
|
isr_stub_140, isr_stub_141, isr_stub_142, isr_stub_143,
|
||||||
|
isr_stub_144, isr_stub_145, isr_stub_146, isr_stub_147,
|
||||||
|
isr_stub_148, isr_stub_149, isr_stub_150, isr_stub_151,
|
||||||
|
isr_stub_152, isr_stub_153, isr_stub_154, isr_stub_155,
|
||||||
|
isr_stub_156, isr_stub_157, isr_stub_158, isr_stub_159,
|
||||||
|
isr_stub_160, isr_stub_161, isr_stub_162, isr_stub_163,
|
||||||
|
isr_stub_164, isr_stub_165, isr_stub_166, isr_stub_167,
|
||||||
|
isr_stub_168, isr_stub_169, isr_stub_170, isr_stub_171,
|
||||||
|
isr_stub_172, isr_stub_173, isr_stub_174, isr_stub_175,
|
||||||
|
isr_stub_176, isr_stub_177, isr_stub_178, isr_stub_179,
|
||||||
|
isr_stub_180, isr_stub_181, isr_stub_182, isr_stub_183,
|
||||||
|
isr_stub_184, isr_stub_185, isr_stub_186, isr_stub_187,
|
||||||
|
isr_stub_188, isr_stub_189, isr_stub_190, isr_stub_191,
|
||||||
|
isr_stub_192, isr_stub_193, isr_stub_194, isr_stub_195,
|
||||||
|
isr_stub_196, isr_stub_197, isr_stub_198, isr_stub_199,
|
||||||
|
isr_stub_200, isr_stub_201, isr_stub_202, isr_stub_203,
|
||||||
|
isr_stub_204, isr_stub_205, isr_stub_206, isr_stub_207,
|
||||||
|
isr_stub_208, isr_stub_209, isr_stub_210, isr_stub_211,
|
||||||
|
isr_stub_212, isr_stub_213, isr_stub_214, isr_stub_215,
|
||||||
|
isr_stub_216, isr_stub_217, isr_stub_218, isr_stub_219,
|
||||||
|
isr_stub_220, isr_stub_221, isr_stub_222, isr_stub_223,
|
||||||
|
isr_stub_224, isr_stub_225, isr_stub_226, isr_stub_227,
|
||||||
|
isr_stub_228, isr_stub_229, isr_stub_230, isr_stub_231,
|
||||||
|
isr_stub_232, isr_stub_233, isr_stub_234, isr_stub_235,
|
||||||
|
isr_stub_236, isr_stub_237, isr_stub_238, isr_stub_239,
|
||||||
|
isr_stub_240, isr_stub_241, isr_stub_242, isr_stub_243,
|
||||||
|
isr_stub_244, isr_stub_245, isr_stub_246, isr_stub_247,
|
||||||
|
isr_stub_248, isr_stub_249, isr_stub_250, isr_stub_251,
|
||||||
|
isr_stub_252, isr_stub_253, isr_stub_254, isr_stub_255,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void outb(u16 port, u8 value) {
|
static void outb(u16 port, u8 value) {
|
||||||
@@ -109,9 +370,11 @@ static void io_wait(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void pic_remap(void) {
|
static void pic_remap(void) {
|
||||||
/* save masks */
|
/* save masks (kept for reference; fixed masks are applied below) */
|
||||||
u8 m1 = inb(PIC1_DATA);
|
u8 m1 = inb(PIC1_DATA);
|
||||||
u8 m2 = inb(PIC2_DATA);
|
u8 m2 = inb(PIC2_DATA);
|
||||||
|
(void)m1;
|
||||||
|
(void)m2;
|
||||||
|
|
||||||
/* ICW1: start init + ICW4 */
|
/* ICW1: start init + ICW4 */
|
||||||
outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4);
|
outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4);
|
||||||
@@ -142,10 +405,21 @@ static void pic_remap(void) {
|
|||||||
outb(PIC2_DATA, 0xFF); /* mask all slave */
|
outb(PIC2_DATA, 0xFF); /* mask all slave */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 运行时读取当前内核 CS — 必须与实际生效的 GDT 一致。
|
||||||
|
* 注意:UTSM 运行在 Limine 提供的 GDT 下(CS=0x28),不能假设
|
||||||
|
* OSDev 教程布局(0x08=code)。若 IDT gate selector 指向无效/非64位
|
||||||
|
* 代码段,异常派发时加载 CS 会 #GP(selector error),异常链升级为
|
||||||
|
* #DF -> triple fault,且全程无任何日志输出。 */
|
||||||
|
static u16 idt_kernel_cs(void) {
|
||||||
|
u16 cs;
|
||||||
|
__asm__ volatile("mov %%cs, %0" : "=r"(cs));
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
static void idt_set_entry(u8 vector, void *handler, u8 ist, u8 type) {
|
static void idt_set_entry(u8 vector, void *handler, u8 ist, u8 type) {
|
||||||
u64 addr = (u64)handler;
|
u64 addr = (u64)handler;
|
||||||
g_idt[vector].offset_low = (u16)(addr & 0xffff);
|
g_idt[vector].offset_low = (u16)(addr & 0xffff);
|
||||||
g_idt[vector].selector = 0x08; /* kernel code segment (from GDT) */
|
g_idt[vector].selector = idt_kernel_cs();
|
||||||
g_idt[vector].ist = ist;
|
g_idt[vector].ist = ist;
|
||||||
g_idt[vector].type_attr = type;
|
g_idt[vector].type_attr = type;
|
||||||
g_idt[vector].offset_mid = (u16)((addr >> 16) & 0xffff);
|
g_idt[vector].offset_mid = (u16)((addr >> 16) & 0xffff);
|
||||||
@@ -156,13 +430,9 @@ static void idt_set_entry(u8 vector, void *handler, u8 ist, u8 type) {
|
|||||||
void idt_init(void) {
|
void idt_init(void) {
|
||||||
log_info("[IDT] init begin");
|
log_info("[IDT] init begin");
|
||||||
|
|
||||||
for (u16 i = 0; i < 48; i++) {
|
for (u16 i = 0; i < IDT_ENTRIES; i++) {
|
||||||
idt_set_entry((u8)i, g_isr_table[i], 0, 0x8E); /* present, ring0, 64-bit interrupt gate */
|
idt_set_entry((u8)i, g_isr_table[i], 0, 0x8E); /* present, ring0, 64-bit interrupt gate */
|
||||||
}
|
}
|
||||||
/* fill unused entries with null */
|
|
||||||
for (u16 i = 48; i < IDT_ENTRIES; i++) {
|
|
||||||
idt_set_entry((u8)i, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
idt_ptr idtr;
|
idt_ptr idtr;
|
||||||
idtr.limit = (u16)(sizeof(g_idt) - 1);
|
idtr.limit = (u16)(sizeof(g_idt) - 1);
|
||||||
@@ -178,12 +448,146 @@ void idt_init(void) {
|
|||||||
log_info("[IDT] init ok");
|
log_info("[IDT] init ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* IRQ handler table */
|
/* 安全 halt stub: cli; hlt 循环。用于 idt_halt_all()。
|
||||||
static irq_handler_t g_irq_handlers[16];
|
* 任何异常/IRQ 触发此 stub 将安全停机,不会三重故障。 */
|
||||||
|
static void isr_halt_stub(void) {
|
||||||
|
for (;;) { __asm__ volatile("cli; hlt"); }
|
||||||
|
}
|
||||||
|
|
||||||
int irq_register(u8 irq, irq_handler_t handler) {
|
void idt_halt_all(void) {
|
||||||
if (irq >= 16) return -1;
|
/* 将 IDT 所有 256 个条目替换为安全 halt stub。
|
||||||
g_irq_handlers[irq] = handler;
|
* 调用前应已 cli + mask PIC,此函数是额外防御层:
|
||||||
|
* - 实机 spurious IRQ7/15 即使 PIC 已 mask 也可能触发
|
||||||
|
* - NMI / SMI 可能导致异常
|
||||||
|
* - DSK 运行期间 CPU 异常 (PF/GP) 应安全停机而非执行过期 handler */
|
||||||
|
for (u16 i = 0; i < IDT_ENTRIES; i++) {
|
||||||
|
idt_set_entry((u8)i, (void *)isr_halt_stub, 0, 0x8E);
|
||||||
|
}
|
||||||
|
/* 重新加载 IDTR 以确保更改生效 */
|
||||||
|
idt_ptr idtr;
|
||||||
|
idtr.limit = (u16)(sizeof(g_idt) - 1);
|
||||||
|
idtr.base = (u64)&g_idt;
|
||||||
|
__asm__ volatile("lidt %0" :: "m"(idtr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void idt_install_dsk_diag(void) {
|
||||||
|
/* BUG-20260801-005: DSK 交接诊断 IDT(替代原 idt_halt_all 静默化)。
|
||||||
|
* 原 halt_all 将 256 项全部换成静默 halt stub——防御正确,但 DSK
|
||||||
|
* 阶段任何 CPU 异常(PF/GP/UD)被无声吞掉,零日志无法诊断
|
||||||
|
* (实测 pe HELLO64.EXE 早期 #PF 直接表现为"卡死/三重故障")。
|
||||||
|
* 本函数在保留 IRQ 防御的前提下恢复异常可见性:
|
||||||
|
* vector 0-31 -> 原 isr_stub(走 idt_handler 异常路径,打完整
|
||||||
|
* vector/err/rip/CR2/寄存器日志后安全停机)
|
||||||
|
* vector 32-255 -> 保持 halt stub(PIC 已全 mask 且 IF=0,IRQ 本
|
||||||
|
* 不该到达;spurious IRQ 维持原防御行为)
|
||||||
|
* 不做 pic_remap / sti——DSK 上下文 IF=0 不变式保持不变。
|
||||||
|
* isr_stub / idt_handler 是 UTSM .text 代码,SAS-R0 下常驻内存
|
||||||
|
* 有效(BUG-20260729-013 登记行为:halt_all 引入前 DSK 阶段异常
|
||||||
|
* 本就能送达 idt_handler);异常路径只用 log/serial,不依赖已过期
|
||||||
|
* 的 IRQ handler 状态。 */
|
||||||
|
for (u16 i = 0; i < 32; i++) {
|
||||||
|
idt_set_entry((u8)i, g_isr_table[i], 0, 0x8E);
|
||||||
|
}
|
||||||
|
for (u16 i = 32; i < IDT_ENTRIES; i++) {
|
||||||
|
idt_set_entry((u8)i, (void *)isr_halt_stub, 0, 0x8E);
|
||||||
|
}
|
||||||
|
idt_ptr idtr;
|
||||||
|
idtr.limit = (u16)(sizeof(g_idt) - 1);
|
||||||
|
idtr.base = (u64)&g_idt;
|
||||||
|
__asm__ volatile("lidt %0" :: "m"(idtr));
|
||||||
|
log_info("[IDT] DSK diag IDT installed (exc 0-31 logged, IRQ halted)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void idt_install_pe_diag(void) {
|
||||||
|
/* 语义与 idt_install_dsk_diag 相同:BUG-20260801-005 起 DSK 交接
|
||||||
|
* 默认装备诊断 IDT,PE 原生执行前重装一次保持幂等(防御 PE 运行
|
||||||
|
* 期间 IDT 被第三方改写的场景)。 */
|
||||||
|
idt_install_dsk_diag();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IRQ handler table — 扩展到 256 个 vector (真机 APIC/IOAPIC/MSI 需要) */
|
||||||
|
static irq_handler_t g_irq_handlers[256];
|
||||||
|
|
||||||
|
/* B7 阶段2: LAPIC EOI 钩子 (由 apic.drv 经 kernel_api.register_apic_eoi 注册)。
|
||||||
|
* 指向写 LAPIC EOI 寄存器 (0xFEE000B0) 的函数; NULL = PIC 路由模式, 不调用。
|
||||||
|
* 单核 BSP 场景: 注册发生在 apic.drv init (普通上下文), 调用发生在 IRQ
|
||||||
|
* 上下文; 函数指针赋值对齐原子, 无需锁。 */
|
||||||
|
static void (*g_apic_eoi_hook)(void) = 0;
|
||||||
|
|
||||||
|
void idt_register_apic_eoi(void (*eoi_fn)(void)) {
|
||||||
|
g_apic_eoi_hook = eoi_fn;
|
||||||
|
if (eoi_fn) {
|
||||||
|
log_info("[IDT] APIC EOI hook registered (LAPIC EOI on IRQ dispatch)");
|
||||||
|
} else {
|
||||||
|
log_info("[IDT] APIC EOI hook cleared");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 插桩: IRQ 统计计数器 ---- */
|
||||||
|
INSTR_STAT_DECL(pic_irq_total);
|
||||||
|
INSTR_STAT_DECL(apic_irq_total);
|
||||||
|
|
||||||
|
/* ---- B7 阶段3: 动态 IDT 向量分配器 (MSI/MSI-X 用) ----
|
||||||
|
* 分配范围 0x40-0xDF (160 个向量):
|
||||||
|
* 0x00-0x1F CPU 异常
|
||||||
|
* 0x20-0x2F PIC legacy IRQ (保留不动)
|
||||||
|
* 0x30-0x3F 保留给 IOAPIC GSI 16-31 未来扩展 (本阶段不动态分配)
|
||||||
|
* 0x40-0xDF 动态分配池 (MSI/MSI-X)
|
||||||
|
* 0xE0 APIC tick 自测预留 (阶段1 惯例)
|
||||||
|
* 0xFF LAPIC spurious vector
|
||||||
|
* 数据结构: 3 x u64 位图 (192 bit, 用后 160), __builtin_ctzll 找首个
|
||||||
|
* 零位, 分配/释放均 O(1)。热路径无锁: SAS-R0 单核 BSP, alloc/free 只在
|
||||||
|
* driver_init 主上下文调用 (MSI 编程期间对应设备尚未解除中断屏蔽,
|
||||||
|
* IRQ 上下文不可能并发分配), 位图字为对齐 u64 单写原子。
|
||||||
|
* 动态入口无需新增 stub: idt_stubs.S 已为 0-255 全部向量预生成入口,
|
||||||
|
* 分配后 irq_register(vector, handler) 直接挂上既有分发表。 */
|
||||||
|
#define IRQ_VEC_POOL_BASE 0x40
|
||||||
|
#define IRQ_VEC_POOL_COUNT 160
|
||||||
|
static u64 g_vec_bitmap[3];
|
||||||
|
|
||||||
|
int irq_vector_alloc(void) {
|
||||||
|
for (u32 w = 0; w < 3; w++) {
|
||||||
|
u64 free_bits = ~g_vec_bitmap[w];
|
||||||
|
if (!free_bits) continue;
|
||||||
|
u32 bit = (u32)__builtin_ctzll(free_bits);
|
||||||
|
u32 idx = w * 64u + bit;
|
||||||
|
if (idx >= IRQ_VEC_POOL_COUNT) break;
|
||||||
|
g_vec_bitmap[w] |= (1ULL << bit);
|
||||||
|
int vec = IRQ_VEC_POOL_BASE + (int)idx;
|
||||||
|
log_info("[IDT] vector alloc");
|
||||||
|
log_hex64("[IDT] vector=", (u64)vec);
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
log_warn("[IDT] vector alloc failed: pool exhausted");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void irq_vector_free(int vector) {
|
||||||
|
if (vector < IRQ_VEC_POOL_BASE ||
|
||||||
|
vector >= IRQ_VEC_POOL_BASE + (int)IRQ_VEC_POOL_COUNT) {
|
||||||
|
return; /* 越界输入幂等忽略 (静态向量不在池内) */
|
||||||
|
}
|
||||||
|
u32 idx = (u32)(vector - IRQ_VEC_POOL_BASE);
|
||||||
|
g_vec_bitmap[idx / 64u] &= ~(1ULL << (idx % 64u));
|
||||||
|
log_info("[IDT] vector free");
|
||||||
|
log_hex64("[IDT] vector=", (u64)vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
int irq_register(u8 vector, irq_handler_t handler) {
|
||||||
|
/* IRQ号/向量兼容 shim: 0-15 视为 ISA IRQ 号, 换算 vector=0x20+IRQ。
|
||||||
|
* 依据: vector 0-15 是 CPU 异常向量 (#DE/#DB/...), idt_handler 的 IRQ
|
||||||
|
* 分发只覆盖 32-255, 故 0-15 作为注册目标永不可达、无歧义。
|
||||||
|
* 现状调用方两命名空间并存: ps2kbd(1)/e1000(11)/mouseInit(12) 传 IRQ 号,
|
||||||
|
* apic(0xE0)/virtio_net(0x20+n) 传向量 — 统一在此归一到向量空间。 */
|
||||||
|
if (vector < 16) {
|
||||||
|
u8 v = (u8)(0x20u + vector);
|
||||||
|
log_info("[IDT] irq_register: ISA IRQ number translated to vector");
|
||||||
|
log_hex64("[IDT] irq=", vector);
|
||||||
|
log_hex64("[IDT] vector=", v);
|
||||||
|
vector = v;
|
||||||
|
}
|
||||||
|
g_irq_handlers[vector] = handler;
|
||||||
|
INSTR_LOG(INSTR_LOG_DEBUG, INSTR_F_UTSM_TRACE, "[IDT] IRQ handler registered");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,26 +601,81 @@ typedef struct {
|
|||||||
|
|
||||||
void idt_handler(isr_frame *frame) {
|
void idt_handler(isr_frame *frame) {
|
||||||
if (frame->vector < 32) {
|
if (frame->vector < 32) {
|
||||||
|
/* CR2: #PF 的 fault 线性地址;CR3: 当前页表基址。
|
||||||
|
* 寄存器 dump 用于定位 fault 指令的操作数来源。 */
|
||||||
|
u64 cr2, cr3;
|
||||||
|
__asm__ volatile("mov %%cr2, %0" : "=r"(cr2));
|
||||||
|
__asm__ volatile("mov %%cr3, %0" : "=r"(cr3));
|
||||||
|
INSTR_PROBE(EXCP, frame->vector, frame->error_code, frame->rip, 0);
|
||||||
log_error("[IDT] exception");
|
log_error("[IDT] exception");
|
||||||
log_hex64("[IDT] vector=", frame->vector);
|
log_hex64("[IDT] vector=", frame->vector);
|
||||||
log_hex64("[IDT] err=", frame->error_code);
|
log_hex64("[IDT] err=", frame->error_code);
|
||||||
log_hex64("[IDT] rip=", frame->rip);
|
log_hex64("[IDT] rip=", frame->rip);
|
||||||
|
log_hex64("[IDT] cr2=", cr2);
|
||||||
|
log_hex64("[IDT] cr3=", cr3);
|
||||||
|
log_hex64("[IDT] rax=", frame->rax);
|
||||||
|
log_hex64("[IDT] rcx=", frame->rcx);
|
||||||
|
log_hex64("[IDT] rdx=", frame->rdx);
|
||||||
|
log_hex64("[IDT] rsi=", frame->rsi);
|
||||||
|
log_hex64("[IDT] rdi=", frame->rdi);
|
||||||
|
log_hex64("[IDT] r8=", frame->r8);
|
||||||
|
log_hex64("[IDT] r9=", frame->r9);
|
||||||
|
log_hex64("[IDT] r10=", frame->r10);
|
||||||
|
log_hex64("[IDT] r11=", frame->r11);
|
||||||
|
log_hex64("[IDT] cs=", frame->cs);
|
||||||
|
log_hex64("[IDT] rflags=", frame->rflags);
|
||||||
for (;;) { __asm__ volatile ("cli; hlt"); }
|
for (;;) { __asm__ volatile ("cli; hlt"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PIC IRQ: vectors 0x20-0x2F (32-47)
|
||||||
|
* B7 阶段2: 本分支同时服务两种路由来源:
|
||||||
|
* - 8259 PIC 路由 (默认): handler 后只需 8259 EOI, 钩子为 NULL 跳过;
|
||||||
|
* - IOAPIC->LAPIC 路由 (apic_route=1, legacy 线重定向到 0x20-0x2F):
|
||||||
|
* 中断由 LAPIC 递交, ISR 位置位, 必须写 LAPIC EOI (钩子) 清除,
|
||||||
|
* 否则同级 (vector[7:4] 相同) 中断被永久阻塞 — 每条线只投递一次。
|
||||||
|
* LAPIC EOI 先于 8259 EOI; PIC 全屏蔽时 8259 EOI 为无害 no-op
|
||||||
|
* (8259 无 in-service 位, 非特异性 EOI 不写任何状态)。 */
|
||||||
if (frame->vector >= 32 && frame->vector <= 47) {
|
if (frame->vector >= 32 && frame->vector <= 47) {
|
||||||
|
INSTR_STAT_INC(pic_irq_total);
|
||||||
u8 irq = (u8)(frame->vector - 0x20);
|
u8 irq = (u8)(frame->vector - 0x20);
|
||||||
|
|
||||||
/* call registered handler */
|
/* call registered handler; 返回 1 = 自负 EOI 全责 (两者皆抑制) */
|
||||||
if (irq < 16 && g_irq_handlers[irq]) {
|
if (g_irq_handlers[frame->vector]) {
|
||||||
int suppress_eoi = g_irq_handlers[irq](irq);
|
int suppress_eoi = g_irq_handlers[frame->vector](irq);
|
||||||
if (suppress_eoi) return;
|
if (suppress_eoi) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send EOI */
|
/* LAPIC EOI (IOAPIC 路由时必需; PIC 路由时钩子为 NULL) */
|
||||||
|
if (g_apic_eoi_hook) {
|
||||||
|
g_apic_eoi_hook();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send PIC EOI */
|
||||||
if (frame->vector >= 0x28) {
|
if (frame->vector >= 0x28) {
|
||||||
outb(PIC2_CMD, 0x20);
|
outb(PIC2_CMD, 0x20);
|
||||||
}
|
}
|
||||||
outb(PIC1_CMD, 0x20);
|
outb(PIC1_CMD, 0x20);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* APIC/IOAPIC/MSI IRQ: vectors 0x30-0xFF (48-255)
|
||||||
|
* handler 约定: 返回 0 -> 由钩子补 LAPIC EOI (安全网);
|
||||||
|
* 返回 1 -> handler 已自行 LAPIC EOI, 跳过钩子。
|
||||||
|
* 钩子未注册 (纯 PIC 路由) 时本分支不做任何 EOI — 与旧版行为一致,
|
||||||
|
* 此时 handler 必须自行 LAPIC EOI (参见 apic.drv tick handler)。 */
|
||||||
|
if (frame->vector >= 48 && frame->vector <= 255) {
|
||||||
|
/* LAPIC spurious (SVR 低 8 位 = 0xFF): 规范上不置 ISR 位, 永不需 EOI。
|
||||||
|
* 若在此调钩子写 EOI, 会误清嵌套场景下真正 in-service 的最高位,
|
||||||
|
* 故特判直接返回 (EOI 空 ISR 虽无害, 但语义上 spurious 不该有 EOI)。 */
|
||||||
|
if (frame->vector == 0xFF) return;
|
||||||
|
INSTR_STAT_INC(apic_irq_total);
|
||||||
|
int suppress_eoi = 0;
|
||||||
|
if (g_irq_handlers[frame->vector]) {
|
||||||
|
suppress_eoi = g_irq_handlers[frame->vector]((u8)frame->vector);
|
||||||
|
}
|
||||||
|
if (!suppress_eoi && g_apic_eoi_hook) {
|
||||||
|
g_apic_eoi_hook();
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
arch/x86_64/idt.o: arch\x86_64\idt.c include\utsm\idt.h \
|
||||||
|
include\utsm\types.h include\utsm\log.h include\utsm\instr.h
|
||||||
|
include\utsm\idt.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\log.h:
|
||||||
|
include\utsm\instr.h:
|
||||||
Binary file not shown.
@@ -1,4 +1,8 @@
|
|||||||
/* IDT assembly stubs — SAS-R0 interrupt vectors */
|
/* IDT assembly stubs — SAS-R0 interrupt vectors
|
||||||
|
* Vectors 0-31: CPU exceptions (some push error code)
|
||||||
|
* Vectors 32-47: PIC IRQ remap region
|
||||||
|
* Vectors 48-255: APIC/IOAPIC/MSI dynamic vectors
|
||||||
|
*/
|
||||||
.section .text
|
.section .text
|
||||||
|
|
||||||
.macro isr_no_err n
|
.macro isr_no_err n
|
||||||
@@ -34,6 +38,62 @@ isr_no_err 36; isr_no_err 37; isr_no_err 38; isr_no_err 39
|
|||||||
isr_no_err 40; isr_no_err 41; isr_no_err 42; isr_no_err 43
|
isr_no_err 40; isr_no_err 41; isr_no_err 42; isr_no_err 43
|
||||||
isr_no_err 44; isr_no_err 45; isr_no_err 46; isr_no_err 47
|
isr_no_err 44; isr_no_err 45; isr_no_err 46; isr_no_err 47
|
||||||
|
|
||||||
|
/* Vectors 48-255: APIC/IOAPIC/MSI 真机动态向量
|
||||||
|
* 真机需要更多 vector: APIC timer (0x30+), IOAPIC GSI, MSI/MSI-X 等。
|
||||||
|
* 全部生成 stub 确保任何 vector 命中都有 handler, 不会 triple fault。 */
|
||||||
|
isr_no_err 48; isr_no_err 49; isr_no_err 50; isr_no_err 51
|
||||||
|
isr_no_err 52; isr_no_err 53; isr_no_err 54; isr_no_err 55
|
||||||
|
isr_no_err 56; isr_no_err 57; isr_no_err 58; isr_no_err 59
|
||||||
|
isr_no_err 60; isr_no_err 61; isr_no_err 62; isr_no_err 63
|
||||||
|
isr_no_err 64; isr_no_err 65; isr_no_err 66; isr_no_err 67
|
||||||
|
isr_no_err 68; isr_no_err 69; isr_no_err 70; isr_no_err 71
|
||||||
|
isr_no_err 72; isr_no_err 73; isr_no_err 74; isr_no_err 75
|
||||||
|
isr_no_err 76; isr_no_err 77; isr_no_err 78; isr_no_err 79
|
||||||
|
isr_no_err 80; isr_no_err 81; isr_no_err 82; isr_no_err 83
|
||||||
|
isr_no_err 84; isr_no_err 85; isr_no_err 86; isr_no_err 87
|
||||||
|
isr_no_err 88; isr_no_err 89; isr_no_err 90; isr_no_err 91
|
||||||
|
isr_no_err 92; isr_no_err 93; isr_no_err 94; isr_no_err 95
|
||||||
|
isr_no_err 96; isr_no_err 97; isr_no_err 98; isr_no_err 99
|
||||||
|
isr_no_err 100; isr_no_err 101; isr_no_err 102; isr_no_err 103
|
||||||
|
isr_no_err 104; isr_no_err 105; isr_no_err 106; isr_no_err 107
|
||||||
|
isr_no_err 108; isr_no_err 109; isr_no_err 110; isr_no_err 111
|
||||||
|
isr_no_err 112; isr_no_err 113; isr_no_err 114; isr_no_err 115
|
||||||
|
isr_no_err 116; isr_no_err 117; isr_no_err 118; isr_no_err 119
|
||||||
|
isr_no_err 120; isr_no_err 121; isr_no_err 122; isr_no_err 123
|
||||||
|
isr_no_err 124; isr_no_err 125; isr_no_err 126; isr_no_err 127
|
||||||
|
isr_no_err 128; isr_no_err 129; isr_no_err 130; isr_no_err 131
|
||||||
|
isr_no_err 132; isr_no_err 133; isr_no_err 134; isr_no_err 135
|
||||||
|
isr_no_err 136; isr_no_err 137; isr_no_err 138; isr_no_err 139
|
||||||
|
isr_no_err 140; isr_no_err 141; isr_no_err 142; isr_no_err 143
|
||||||
|
isr_no_err 144; isr_no_err 145; isr_no_err 146; isr_no_err 147
|
||||||
|
isr_no_err 148; isr_no_err 149; isr_no_err 150; isr_no_err 151
|
||||||
|
isr_no_err 152; isr_no_err 153; isr_no_err 154; isr_no_err 155
|
||||||
|
isr_no_err 156; isr_no_err 157; isr_no_err 158; isr_no_err 159
|
||||||
|
isr_no_err 160; isr_no_err 161; isr_no_err 162; isr_no_err 163
|
||||||
|
isr_no_err 164; isr_no_err 165; isr_no_err 166; isr_no_err 167
|
||||||
|
isr_no_err 168; isr_no_err 169; isr_no_err 170; isr_no_err 171
|
||||||
|
isr_no_err 172; isr_no_err 173; isr_no_err 174; isr_no_err 175
|
||||||
|
isr_no_err 176; isr_no_err 177; isr_no_err 178; isr_no_err 179
|
||||||
|
isr_no_err 180; isr_no_err 181; isr_no_err 182; isr_no_err 183
|
||||||
|
isr_no_err 184; isr_no_err 185; isr_no_err 186; isr_no_err 187
|
||||||
|
isr_no_err 188; isr_no_err 189; isr_no_err 190; isr_no_err 191
|
||||||
|
isr_no_err 192; isr_no_err 193; isr_no_err 194; isr_no_err 195
|
||||||
|
isr_no_err 196; isr_no_err 197; isr_no_err 198; isr_no_err 199
|
||||||
|
isr_no_err 200; isr_no_err 201; isr_no_err 202; isr_no_err 203
|
||||||
|
isr_no_err 204; isr_no_err 205; isr_no_err 206; isr_no_err 207
|
||||||
|
isr_no_err 208; isr_no_err 209; isr_no_err 210; isr_no_err 211
|
||||||
|
isr_no_err 212; isr_no_err 213; isr_no_err 214; isr_no_err 215
|
||||||
|
isr_no_err 216; isr_no_err 217; isr_no_err 218; isr_no_err 219
|
||||||
|
isr_no_err 220; isr_no_err 221; isr_no_err 222; isr_no_err 223
|
||||||
|
isr_no_err 224; isr_no_err 225; isr_no_err 226; isr_no_err 227
|
||||||
|
isr_no_err 228; isr_no_err 229; isr_no_err 230; isr_no_err 231
|
||||||
|
isr_no_err 232; isr_no_err 233; isr_no_err 234; isr_no_err 235
|
||||||
|
isr_no_err 236; isr_no_err 237; isr_no_err 238; isr_no_err 239
|
||||||
|
isr_no_err 240; isr_no_err 241; isr_no_err 242; isr_no_err 243
|
||||||
|
isr_no_err 244; isr_no_err 245; isr_no_err 246; isr_no_err 247
|
||||||
|
isr_no_err 248; isr_no_err 249; isr_no_err 250; isr_no_err 251
|
||||||
|
isr_no_err 252; isr_no_err 253; isr_no_err 254; isr_no_err 255
|
||||||
|
|
||||||
/* Common ISR */
|
/* Common ISR */
|
||||||
isr_common:
|
isr_common:
|
||||||
pushq %rdi
|
pushq %rdi
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
|||||||
|
arch/x86_64/io.o: arch\x86_64\io.c include\utsm\types.h
|
||||||
|
include\utsm\types.h:
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
arch/x86_64/serial.o: arch\x86_64\serial.c include\utsm\types.h
|
||||||
|
include\utsm\types.h:
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
core/capability.o: core\capability.c include\utsm\capability.h \
|
||||||
|
include\utsm\types.h include\utsm\status.h include\utsm\segment.h \
|
||||||
|
include\utsm\uuid.h include\utsm\dirty.h
|
||||||
|
include\utsm\capability.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\status.h:
|
||||||
|
include\utsm\segment.h:
|
||||||
|
include\utsm\uuid.h:
|
||||||
|
include\utsm\dirty.h:
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
core/dirty.o: core\dirty.c include\utsm\dirty.h include\utsm\types.h \
|
||||||
|
include\utsm\segment.h include\utsm\uuid.h include\utsm\status.h \
|
||||||
|
include\utsm\capability.h include\utsm\arena.h include\utsm\config.h
|
||||||
|
include\utsm\dirty.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\segment.h:
|
||||||
|
include\utsm\uuid.h:
|
||||||
|
include\utsm\status.h:
|
||||||
|
include\utsm\capability.h:
|
||||||
|
include\utsm\arena.h:
|
||||||
|
include\utsm\config.h:
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
core/dmp.o: core\dmp.c include\utsm\dmp.h include\utsm\types.h \
|
||||||
|
include\utsm\segment.h include\utsm\uuid.h include\utsm\status.h \
|
||||||
|
include\utsm\capability.h include\utsm\dirty.h
|
||||||
|
include\utsm\dmp.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\segment.h:
|
||||||
|
include\utsm\uuid.h:
|
||||||
|
include\utsm\status.h:
|
||||||
|
include\utsm\capability.h:
|
||||||
|
include\utsm\dirty.h:
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
core/process.o: core\process.c include\utsm\process.h \
|
||||||
|
include\utsm\types.h include\utsm\uuid.h include\utsm\capability.h \
|
||||||
|
include\utsm\status.h include\utsm\config.h include\utsm\drr.h \
|
||||||
|
include\utsm\arena.h include\utsm\log.h
|
||||||
|
include\utsm\process.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\uuid.h:
|
||||||
|
include\utsm\capability.h:
|
||||||
|
include\utsm\status.h:
|
||||||
|
include\utsm\config.h:
|
||||||
|
include\utsm\drr.h:
|
||||||
|
include\utsm\arena.h:
|
||||||
|
include\utsm\log.h:
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
core/segment.o: core\segment.c include\utsm\segment.h \
|
||||||
|
include\utsm\types.h include\utsm\uuid.h include\utsm\status.h \
|
||||||
|
include\utsm\capability.h include\utsm\dirty.h include\utsm\process.h \
|
||||||
|
include\utsm\config.h include\utsm\arena.h include\utsm\dmp.h
|
||||||
|
include\utsm\segment.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\uuid.h:
|
||||||
|
include\utsm\status.h:
|
||||||
|
include\utsm\capability.h:
|
||||||
|
include\utsm\dirty.h:
|
||||||
|
include\utsm\process.h:
|
||||||
|
include\utsm\config.h:
|
||||||
|
include\utsm\arena.h:
|
||||||
|
include\utsm\dmp.h:
|
||||||
@@ -3,13 +3,16 @@
|
|||||||
#include <utsm/pckc.h>
|
#include <utsm/pckc.h>
|
||||||
#include <utsm/process.h>
|
#include <utsm/process.h>
|
||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
|
#include <utsm/config.h>
|
||||||
|
|
||||||
static utsm_segment_desc g_segments[UTSM_MAX_SEGMENTS];
|
static utsm_segment_desc g_segments[UTSM_MAX_SEGMENTS];
|
||||||
static u64 g_generation = 1;
|
static u64 g_generation = 1;
|
||||||
|
|
||||||
void utsm_init(void) {
|
void utsm_init(void) {
|
||||||
log_info("[UTSM] segment table reset begin");
|
log_info("[UTSM] segment table reset begin");
|
||||||
for (u32 i = 0; i < UTSM_MAX_SEGMENTS; i++) {
|
u32 max_seg = g_utsm_max_segments;
|
||||||
|
if (max_seg > UTSM_MAX_SEGMENTS) max_seg = UTSM_MAX_SEGMENTS;
|
||||||
|
for (u32 i = 0; i < max_seg; i++) {
|
||||||
g_segments[i].state = UTSM_SEG_FREE;
|
g_segments[i].state = UTSM_SEG_FREE;
|
||||||
g_segments[i].generation = 1;
|
g_segments[i].generation = 1;
|
||||||
}
|
}
|
||||||
@@ -24,7 +27,7 @@ void utsm_init(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
utsm_segment_desc *utsm_get_segment(u32 slot) {
|
utsm_segment_desc *utsm_get_segment(u32 slot) {
|
||||||
if (slot >= UTSM_MAX_SEGMENTS) {
|
if (slot >= g_utsm_max_segments || slot >= UTSM_MAX_SEGMENTS) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return &g_segments[slot];
|
return &g_segments[slot];
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
core/utsm_init.o: core\utsm_init.c include\utsm\utsm.h \
|
||||||
|
include\utsm\types.h include\utsm\status.h include\utsm\config.h \
|
||||||
|
include\utsm\uuid.h include\utsm\process.h include\utsm\capability.h \
|
||||||
|
include\utsm\segment.h include\utsm\dirty.h include\utsm\utrw.h \
|
||||||
|
include\utsm\vmm.h include\utsm\linux_loader.h include\utsm\ipc_shm.h \
|
||||||
|
..\utsm-ipc\ipc_proto.h include\utsm\hypercall.h include\utsm\pckc.h \
|
||||||
|
include\utsm\crypto.h include\utsm\log.h
|
||||||
|
include\utsm\utsm.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\status.h:
|
||||||
|
include\utsm\config.h:
|
||||||
|
include\utsm\uuid.h:
|
||||||
|
include\utsm\process.h:
|
||||||
|
include\utsm\capability.h:
|
||||||
|
include\utsm\segment.h:
|
||||||
|
include\utsm\dirty.h:
|
||||||
|
include\utsm\utrw.h:
|
||||||
|
include\utsm\vmm.h:
|
||||||
|
include\utsm\linux_loader.h:
|
||||||
|
include\utsm\ipc_shm.h:
|
||||||
|
..\utsm-ipc\ipc_proto.h:
|
||||||
|
include\utsm\hypercall.h:
|
||||||
|
include\utsm\pckc.h:
|
||||||
|
include\utsm\crypto.h:
|
||||||
|
include\utsm\log.h:
|
||||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
|||||||
|
crypto/kdf.o: crypto\kdf.c include\utsm\crypto.h include\utsm\types.h \
|
||||||
|
include\utsm\uuid.h include\utsm\drr.h include\utsm\config.h
|
||||||
|
include\utsm\crypto.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\uuid.h:
|
||||||
|
include\utsm\drr.h:
|
||||||
|
include\utsm\config.h:
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
crypto/stream.o: crypto\stream.c include\utsm\crypto.h \
|
||||||
|
include\utsm\types.h include\utsm\uuid.h include\utsm\config.h
|
||||||
|
include\utsm\crypto.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\uuid.h:
|
||||||
|
include\utsm\config.h:
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
crypto/tweak.o: crypto\tweak.c include\utsm\crypto.h include\utsm\types.h \
|
||||||
|
include\utsm\uuid.h
|
||||||
|
include\utsm\crypto.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\uuid.h:
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
dkm/boot_modules.o: dkm\boot_modules.c include\utsm\dkm.h \
|
||||||
|
include\utsm\types.h include\utsm\dma.h include\utsm\block.h \
|
||||||
|
include\utsm\net.h include\utsm\paging.h include\utsm\log.h \
|
||||||
|
include\utsm\arena.h dkm\..\arch\x86_64\limine.h
|
||||||
|
include\utsm\dkm.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\dma.h:
|
||||||
|
include\utsm\block.h:
|
||||||
|
include\utsm\net.h:
|
||||||
|
include\utsm\paging.h:
|
||||||
|
include\utsm\log.h:
|
||||||
|
include\utsm\arena.h:
|
||||||
|
dkm\..\arch\x86_64\limine.h:
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
dkm/console_early.o: dkm\console_early.c include\utsm\dkm.h \
|
||||||
|
include\utsm\types.h include\utsm\dma.h include\utsm\block.h \
|
||||||
|
include\utsm\net.h include\utsm\paging.h
|
||||||
|
include\utsm\dkm.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\dma.h:
|
||||||
|
include\utsm\block.h:
|
||||||
|
include\utsm\net.h:
|
||||||
|
include\utsm\paging.h:
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
dkm/dkm_core.o: dkm\dkm_core.c include\utsm\dkm.h include\utsm\types.h \
|
||||||
|
include\utsm\dma.h include\utsm\block.h include\utsm\net.h \
|
||||||
|
include\utsm\paging.h include\utsm\log.h
|
||||||
|
include\utsm\dkm.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\dma.h:
|
||||||
|
include\utsm\block.h:
|
||||||
|
include\utsm\net.h:
|
||||||
|
include\utsm\paging.h:
|
||||||
|
include\utsm\log.h:
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
#include <utsm/idt.h>
|
#include <utsm/idt.h>
|
||||||
#include <utsm/net.h>
|
#include <utsm/net.h>
|
||||||
|
#include <utsm/mm.h>
|
||||||
|
|
||||||
static void dkm_api_info(const char *msg) {
|
static void dkm_api_info(const char *msg) {
|
||||||
log_info(msg);
|
log_info(msg);
|
||||||
@@ -52,7 +53,11 @@ static dkm_kernel_api g_kernel_api = {
|
|||||||
.boot_modules_response = 0,
|
.boot_modules_response = 0,
|
||||||
.irq_register = 0,
|
.irq_register = 0,
|
||||||
.hhdm_offset = 0,
|
.hhdm_offset = 0,
|
||||||
.block = 0
|
.block = 0,
|
||||||
|
.mmio = 0,
|
||||||
|
.mm_map_mmio = 0,
|
||||||
|
.mm_unmap_mmio = 0,
|
||||||
|
.register_apic_eoi = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
const dkm_kernel_api *dkm_get_kernel_api(void) {
|
const dkm_kernel_api *dkm_get_kernel_api(void) {
|
||||||
@@ -69,6 +74,7 @@ void dkm_fill_platform_info(void) {
|
|||||||
g_kernel_api.dma = dma_get_api();
|
g_kernel_api.dma = dma_get_api();
|
||||||
g_kernel_api.net = net_get_api();
|
g_kernel_api.net = net_get_api();
|
||||||
g_kernel_api.block = block_get_api();
|
g_kernel_api.block = block_get_api();
|
||||||
|
g_kernel_api.mmio = paging_get_api();
|
||||||
if (g_rsdp_request.response && g_rsdp_request.response->address) {
|
if (g_rsdp_request.response && g_rsdp_request.response->address) {
|
||||||
g_kernel_api.rsdp_address = g_rsdp_request.response->address;
|
g_kernel_api.rsdp_address = g_rsdp_request.response->address;
|
||||||
}
|
}
|
||||||
@@ -87,4 +93,11 @@ void dkm_fill_platform_info(void) {
|
|||||||
if (g_hhdm_request.response) {
|
if (g_hhdm_request.response) {
|
||||||
g_kernel_api.hhdm_offset = g_hhdm_request.response->offset;
|
g_kernel_api.hhdm_offset = g_hhdm_request.response->offset;
|
||||||
}
|
}
|
||||||
|
g_kernel_api.mm_map_mmio = mm_map_mmio;
|
||||||
|
g_kernel_api.mm_unmap_mmio = mm_unmap_mmio;
|
||||||
|
/* B7 阶段2: 暴露 LAPIC EOI 钩子注册入口(apic.drv LAPIC 接管时调用) */
|
||||||
|
g_kernel_api.register_apic_eoi = idt_register_apic_eoi;
|
||||||
|
/* B7 阶段3: 暴露动态向量分配器(MSI/MSI-X 设备驱动使用) */
|
||||||
|
g_kernel_api.irq_vector_alloc = irq_vector_alloc;
|
||||||
|
g_kernel_api.irq_vector_free = irq_vector_free;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
dkm/kernel_api.o: dkm\kernel_api.c include\utsm\dkm.h \
|
||||||
|
include\utsm\types.h include\utsm\dma.h include\utsm\block.h \
|
||||||
|
include\utsm\net.h include\utsm\paging.h include\utsm\log.h \
|
||||||
|
include\utsm\idt.h include\utsm\mm.h dkm\..\arch\x86_64\limine.h
|
||||||
|
include\utsm\dkm.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\dma.h:
|
||||||
|
include\utsm\block.h:
|
||||||
|
include\utsm\net.h:
|
||||||
|
include\utsm\paging.h:
|
||||||
|
include\utsm\log.h:
|
||||||
|
include\utsm\idt.h:
|
||||||
|
include\utsm\mm.h:
|
||||||
|
dkm\..\arch\x86_64\limine.h:
|
||||||
Binary file not shown.
@@ -2,6 +2,10 @@
|
|||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
#include <utsm/types.h>
|
#include <utsm/types.h>
|
||||||
#include "../arch/x86_64/limine.h"
|
#include "../arch/x86_64/limine.h"
|
||||||
|
#include "../kernel/ini_parser.h"
|
||||||
|
|
||||||
|
/* 前向声明:带 FUCK [drivers] 过滤的加载入口(定义见下文) */
|
||||||
|
void dsm_load_by_manifest_ex(const ini_config *cfg);
|
||||||
|
|
||||||
#define DSM_MAX_DRIVERS_PER_STAGE 8
|
#define DSM_MAX_DRIVERS_PER_STAGE 8
|
||||||
#define DSM_MAX_STAGES 4
|
#define DSM_MAX_STAGES 4
|
||||||
@@ -283,6 +287,10 @@ static int dsm_parse_manifest(const char *text, u64 size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dsm_load_by_manifest(void) {
|
void dsm_load_by_manifest(void) {
|
||||||
|
dsm_load_by_manifest_ex(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dsm_load_by_manifest_ex(const ini_config *cfg) {
|
||||||
struct limine_module_response *rsp = g_module_request.response;
|
struct limine_module_response *rsp = g_module_request.response;
|
||||||
if (!rsp) {
|
if (!rsp) {
|
||||||
log_warn("[DSM] no Limine module response");
|
log_warn("[DSM] no Limine module response");
|
||||||
@@ -317,6 +325,16 @@ void dsm_load_by_manifest(void) {
|
|||||||
for (u32 di = 0; di < stage->driver_count; di++) {
|
for (u32 di = 0; di < stage->driver_count; di++) {
|
||||||
dsm_driver_entry *drv = &stage->drivers[di];
|
dsm_driver_entry *drv = &stage->drivers[di];
|
||||||
|
|
||||||
|
/* === FUCK [drivers] 驱动开关检查 === */
|
||||||
|
if (cfg) {
|
||||||
|
int enabled = ini_get_bool(cfg, "drivers", drv->name, 1);
|
||||||
|
if (!enabled) {
|
||||||
|
log_info("[DSM] driver disabled by FUCK: ");
|
||||||
|
log_info(drv->name);
|
||||||
|
continue; /* 跳过此驱动 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
drv->boot_module = dsm_find_boot_module(drv->path);
|
drv->boot_module = dsm_find_boot_module(drv->path);
|
||||||
if (!drv->boot_module) {
|
if (!drv->boot_module) {
|
||||||
log_info("[DSM] module not found");
|
log_info("[DSM] module not found");
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
dkm/manifest.o: dkm\manifest.c include\utsm\dkm.h include\utsm\types.h \
|
||||||
|
include\utsm\dma.h include\utsm\block.h include\utsm\net.h \
|
||||||
|
include\utsm\paging.h include\utsm\log.h dkm\..\arch\x86_64\limine.h \
|
||||||
|
dkm\..\kernel\ini_parser.h
|
||||||
|
include\utsm\dkm.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\dma.h:
|
||||||
|
include\utsm\block.h:
|
||||||
|
include\utsm\net.h:
|
||||||
|
include\utsm\paging.h:
|
||||||
|
include\utsm\log.h:
|
||||||
|
dkm\..\arch\x86_64\limine.h:
|
||||||
|
dkm\..\kernel\ini_parser.h:
|
||||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
|||||||
|
drr/drr_stub.o: drr\drr_stub.c include\utsm\drr.h include\utsm\types.h \
|
||||||
|
include\utsm\config.h include\utsm\log.h
|
||||||
|
include\utsm\drr.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\config.h:
|
||||||
|
include\utsm\log.h:
|
||||||
@@ -1,13 +1,32 @@
|
|||||||
#ifndef UTSM_CONFIG_H
|
#ifndef UTSM_CONFIG_H
|
||||||
#define UTSM_CONFIG_H
|
#define UTSM_CONFIG_H
|
||||||
|
|
||||||
|
#include <utsm/types.h>
|
||||||
|
|
||||||
|
/* ---- 编译期固定常量(不暴露给 FUCK 配置) ---- */
|
||||||
#define UTSM_CACHE_LINE_SIZE 64ULL
|
#define UTSM_CACHE_LINE_SIZE 64ULL
|
||||||
#define UTSM_PAGE_SIZE 4096ULL
|
#define UTSM_PAGE_SIZE 4096ULL
|
||||||
#define UTSM_MAX_SEGMENTS 1024U
|
|
||||||
#define UTSM_MAX_CAPABILITIES 256U
|
|
||||||
#define UTSM_MAX_PCKC_KEYS 8U
|
|
||||||
#define UTSM_ARENA_SIZE (16ULL * 1024ULL * 1024ULL)
|
|
||||||
#define UTSM_DIRTY_SHARD_PAGES 1024ULL
|
|
||||||
#define UTSM_KERNEL_STACK_SIZE 16384ULL
|
#define UTSM_KERNEL_STACK_SIZE 16384ULL
|
||||||
|
|
||||||
|
/* ---- 编译期默认值宏(用于静态数组大小上限) ---- */
|
||||||
|
#define UTSM_MAX_SEGMENTS_DEFAULT 1024U
|
||||||
|
#define UTSM_MAX_CAPABILITIES_DEFAULT 256U
|
||||||
|
#define UTSM_MAX_PCKC_KEYS_DEFAULT 8U
|
||||||
|
#define UTSM_ARENA_SIZE_DEFAULT (64ULL * 1024ULL * 1024ULL)
|
||||||
|
#define UTSM_DIRTY_SHARD_PAGES_DEFAULT 1024ULL
|
||||||
|
|
||||||
|
/* ---- 运行期配置变量(由 kernel_main 从 FUCK 文件初始化) ---- */
|
||||||
|
extern u32 g_utsm_max_segments;
|
||||||
|
extern u32 g_utsm_max_capabilities;
|
||||||
|
extern u32 g_utsm_max_pckc_keys;
|
||||||
|
extern u64 g_utsm_arena_size;
|
||||||
|
extern u64 g_utsm_dirty_shard_pages;
|
||||||
|
|
||||||
|
/* ---- 保留旧宏名(兼容性),映射到默认值 ---- */
|
||||||
|
#define UTSM_MAX_SEGMENTS UTSM_MAX_SEGMENTS_DEFAULT
|
||||||
|
#define UTSM_MAX_CAPABILITIES UTSM_MAX_CAPABILITIES_DEFAULT
|
||||||
|
#define UTSM_MAX_PCKC_KEYS UTSM_MAX_PCKC_KEYS_DEFAULT
|
||||||
|
#define UTSM_ARENA_SIZE UTSM_ARENA_SIZE_DEFAULT
|
||||||
|
#define UTSM_DIRTY_SHARD_PAGES UTSM_DIRTY_SHARD_PAGES_DEFAULT
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <utsm/dma.h>
|
#include <utsm/dma.h>
|
||||||
#include <utsm/block.h>
|
#include <utsm/block.h>
|
||||||
#include <utsm/net.h>
|
#include <utsm/net.h>
|
||||||
|
#include <utsm/paging.h>
|
||||||
|
|
||||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
||||||
#define DKM_ABI_VERSION 1u
|
#define DKM_ABI_VERSION 1u
|
||||||
@@ -95,6 +96,21 @@ typedef struct dkm_kernel_api {
|
|||||||
int (*irq_register)(u8 irq, void *handler); /* kernel irq_register */
|
int (*irq_register)(u8 irq, void *handler); /* kernel irq_register */
|
||||||
u64 hhdm_offset; /* physical → virtual */
|
u64 hhdm_offset; /* physical → virtual */
|
||||||
const dkm_block_api *block; /* block provider registry */
|
const dkm_block_api *block; /* block provider registry */
|
||||||
|
const dkm_mmio_api *mmio; /* 页表/高位 MMIO 映射服务(尾部追加,ABI 兼容) */
|
||||||
|
/* —— 新增字段只允许追加在末尾(DSK 以 api+0xA8 硬偏移读取 block) —— */
|
||||||
|
void *(*mm_map_mmio)(u64 phys, u64 size); /* MMIO 独立窗口映射(PCD|PWT),返回虚拟地址,失败返回 0 */
|
||||||
|
void (*mm_unmap_mmio)(void *virt, u64 size); /* 解除 MMIO 独立窗口映射 */
|
||||||
|
/* B7 阶段2: 驱动向 UTSM 注册 LAPIC EOI 钩子(写 LAPIC EOI 寄存器的函数指针)。
|
||||||
|
* apic.drv 在 LAPIC 接管时注册;idt_handler 的 IRQ 分发末尾调用该钩子,
|
||||||
|
* 使 vector 0x20-0x2F 的 legacy 线在 IOAPIC 路由下可持续投递。
|
||||||
|
* 未注册(PIC 路由)时钩子为 NULL,idt_handler 跳过,行为与旧版一致。 */
|
||||||
|
void (*register_apic_eoi)(void (*eoi_fn)(void));
|
||||||
|
/* B7 阶段3: 动态 IDT 向量分配器(MSI/MSI-X 用),尾部追加保持 ABI。
|
||||||
|
* 池范围 0x40-0xDF;alloc 返回向量号、失败 -1;free 幂等越界忽略。
|
||||||
|
* 驱动拿到向量后经 irq_register(vector, handler) 直接注册(现有
|
||||||
|
* 接口已接受向量命名空间,无需新注册 ABI)。 */
|
||||||
|
int (*irq_vector_alloc)(void);
|
||||||
|
void (*irq_vector_free)(int vector);
|
||||||
} dkm_kernel_api;
|
} dkm_kernel_api;
|
||||||
|
|
||||||
typedef enum dkm_driver_state {
|
typedef enum dkm_driver_state {
|
||||||
|
|||||||
@@ -9,6 +9,23 @@
|
|||||||
#define DSK_BOOT_FLAG_FROM_UTSM (1ULL << 0)
|
#define DSK_BOOT_FLAG_FROM_UTSM (1ULL << 0)
|
||||||
#define DSK_BOOT_FLAG_DKM_READY (1ULL << 1)
|
#define DSK_BOOT_FLAG_DKM_READY (1ULL << 1)
|
||||||
#define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2)
|
#define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2)
|
||||||
|
#define DSK_BOOT_FLAG_PROBE_INFO (1ULL << 3)
|
||||||
|
|
||||||
|
/* ---- Probe 信息结构(通过 reserved[6] 传递给 shell/desktop) ---- */
|
||||||
|
#define PROBE_INFO_MAGIC 0x50524F42u /* "PRFB" — Probe inFo Buffer */
|
||||||
|
|
||||||
|
typedef struct probe_info {
|
||||||
|
u32 magic; /* PROBE_INFO_MAGIC */
|
||||||
|
u32 version; /* ABI 版本,当前 1 */
|
||||||
|
u64 probe_buf_addr; /* probe_entry 缓冲基地址(物理/HHDM 地址) */
|
||||||
|
u32 probe_cap; /* 缓冲容量(条目数) */
|
||||||
|
u32 probe_head; /* 当前写入位置(递增取模) */
|
||||||
|
u32 probe_count; /* 已写入总条目数 */
|
||||||
|
u32 probe_dropped; /* 因缓冲满而覆盖的条目数 */
|
||||||
|
u32 instr_enabled; /* g_instr_enabled 当前值 */
|
||||||
|
u32 instr_probe_enable; /* g_instr_probe_enable 当前值 */
|
||||||
|
u64 reserved[4];
|
||||||
|
} probe_info;
|
||||||
|
|
||||||
typedef struct dsk_boot_context {
|
typedef struct dsk_boot_context {
|
||||||
u64 magic;
|
u64 magic;
|
||||||
|
|||||||
@@ -30,4 +30,19 @@ int ept_identity_map(u64 gpa, u64 size, u64 flags);
|
|||||||
* Handles 4KB pages and 2MB large pages. */
|
* Handles 4KB pages and 2MB large pages. */
|
||||||
u64 ept_gpa_to_hpa(u64 gpa);
|
u64 ept_gpa_to_hpa(u64 gpa);
|
||||||
|
|
||||||
|
/* Map a 2MB large page in EPT (PD-level direct mapping).
|
||||||
|
* GPA and HPA must be 2MB-aligned. memtype = EPT_MEMORY_TYPE_*.
|
||||||
|
* Required for real hardware MTRR compliance. */
|
||||||
|
int ept_map_2m_page(u64 gpa, u64 hpa, u64 flags, u64 memtype);
|
||||||
|
|
||||||
|
/* Check INVVPID/INVEPT support from IA32_VMX_EPT_VPID_CAP MSR.
|
||||||
|
* Call once after VMX init. */
|
||||||
|
void ept_check_vpid_support(void);
|
||||||
|
|
||||||
|
/* Flush EPT TLB entries (INVEPT). Required after EPT mapping changes on real hardware. */
|
||||||
|
void ept_flush_ept(void);
|
||||||
|
|
||||||
|
/* Flush VPID TLB entries (INVVPID). Required after VPID changes on real hardware. */
|
||||||
|
void ept_flush_vpid(u16 vpid);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,8 +5,44 @@
|
|||||||
|
|
||||||
void idt_init(void);
|
void idt_init(void);
|
||||||
|
|
||||||
/* IRQ handler: return 1 to suppress default ACK, 0 for normal EOI */
|
/* 将 IDT 所有 256 个条目替换为安全 halt stub (cli; hlt)。
|
||||||
typedef int (*irq_handler_t)(u8 irq);
|
* 用于 UTSM→DSK 跳转前,防止 DSK 运行期间过期 UTSM handler
|
||||||
int irq_register(u8 irq, irq_handler_t handler);
|
* 被 spurious IRQ / 异常触发导致三重故障重启。 */
|
||||||
|
void idt_halt_all(void);
|
||||||
|
|
||||||
|
/* DSK 诊断 IDT:vector 0-31 恢复原 isr_stub(异常走 idt_handler
|
||||||
|
* 打完整日志后停机),vector 32-255 保持 halt stub。
|
||||||
|
* BUG-20260801-005:UTSM→DSK 交接处以本函数替代 idt_halt_all,
|
||||||
|
* 恢复 DSK 阶段 CPU 异常可见性(原静默 halt 零诊断)。
|
||||||
|
* 不做 pic_remap / sti,DSK 上下文 IF=0 不变式保持不变。 */
|
||||||
|
void idt_install_dsk_diag(void);
|
||||||
|
|
||||||
|
/* PE 诊断 IDT:idt_install_dsk_diag 的别名(幂等重装)。
|
||||||
|
* 用于 PE 兼容层原生执行 PE32+ 前恢复异常可见性。 */
|
||||||
|
void idt_install_pe_diag(void);
|
||||||
|
|
||||||
|
/* IRQ handler: return 1 to suppress default ACK (EOI), 0 for normal EOI.
|
||||||
|
* 注册参数兼容两种命名空间(二者不重叠,自动判别):
|
||||||
|
* - ISA IRQ 号 0-15 -> 自动换算为 vector 0x20+IRQ (兼容 ps2kbd/e1000/mouseInit)
|
||||||
|
* - IDT vector 0x20+ -> 直接按 vector 注册 (apic 0xE0 / virtio_net 0x20+n 现状)
|
||||||
|
* vector 0-15 是 CPU 异常向量,idt_handler 从不作为 IRQ 分发目标,故无歧义。
|
||||||
|
* EOI 语义:
|
||||||
|
* - PIC IRQ (0x20-0x2F): 返回 0 -> 先调 APIC EOI 钩子(若已注册)再发 8259 EOI;
|
||||||
|
* 返回 1 -> 两者都抑制, handler 自负 EOI 全责。
|
||||||
|
* - APIC/IOAPIC/MSI (0x30-0xFF): 返回 0 -> 调 APIC EOI 钩子(若已注册);
|
||||||
|
* 返回 1 -> 抑制(如 handler 内已自行 LAPIC EOI)。未注册钩子时不做任何 EOI。 */
|
||||||
|
typedef int (*irq_handler_t)(u8 vector);
|
||||||
|
int irq_register(u8 vector, irq_handler_t handler);
|
||||||
|
|
||||||
|
/* B7 阶段2: 注册 LAPIC EOI 钩子(由 apic.drv 在 LAPIC 接管后调用)。
|
||||||
|
* 钩子语义: 向 LAPIC EOI 寄存器 (MMIO base+0xB0) 写 0,清除当前 in-service
|
||||||
|
* 最高优先级 ISR 位。重复注册以最后一次为准;传 NULL 可注销。 */
|
||||||
|
void idt_register_apic_eoi(void (*eoi_fn)(void));
|
||||||
|
|
||||||
|
/* B7 阶段3: 动态 IDT 向量分配器 (MSI/MSI-X 用)。
|
||||||
|
* 池范围 0x40-0xDF (160 个), 位图 O(1); 分配返回向量号, 池耗尽返回 -1。
|
||||||
|
* free 幂等, 越界/静态向量输入忽略。主上下文调用, 无锁。 */
|
||||||
|
int irq_vector_alloc(void);
|
||||||
|
void irq_vector_free(int vector);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
/* instr.h — Deshab 统一插桩宏系统
|
||||||
|
*
|
||||||
|
* 提供四级插桩原语:
|
||||||
|
* INSTR_LOG — 带级别和模块过滤的日志输出
|
||||||
|
* INSTR_STAT — 轻量级计数器 + 周期性摘要
|
||||||
|
* INSTR_PROBE — 环形探测缓冲写入(结构化事件记录)
|
||||||
|
* INSTR_TS — 时间戳标注(阶段耗时测量)
|
||||||
|
*
|
||||||
|
* 设计原则:
|
||||||
|
* - 禁用时零开销: g_instr_enabled=0 时所有宏展开为空操作
|
||||||
|
* - 热路径安全: STAT_INC 仅一条条件自增,PROBE 仅条件写入
|
||||||
|
* - 统一配置: 通过 FUCK [debug] 段控制开关与粒度
|
||||||
|
* - 标签编码: 4 字符 ASCII 编码为 u32,避免字符串比较
|
||||||
|
*
|
||||||
|
* 使用方式:
|
||||||
|
* 1. 在 kernel_main() 中调用 instr_init(&g_boot_cfg) 初始化
|
||||||
|
* 2. 各模块 #include <utsm/instr.h> 后使用 INSTR_* 宏
|
||||||
|
* 3. 在 FUCK 中设置 [debug] enabled=1 开启插桩
|
||||||
|
*/
|
||||||
|
#ifndef UTSM_INSTR_H
|
||||||
|
#define UTSM_INSTR_H
|
||||||
|
|
||||||
|
#include <utsm/types.h>
|
||||||
|
|
||||||
|
/* ---- 日志级别 ---- */
|
||||||
|
#define INSTR_LOG_TRACE 0
|
||||||
|
#define INSTR_LOG_DEBUG 1
|
||||||
|
#define INSTR_LOG_INFO 2
|
||||||
|
#define INSTR_LOG_WARN 3
|
||||||
|
#define INSTR_LOG_ERROR 4
|
||||||
|
#define INSTR_LOG_SILENT 5
|
||||||
|
|
||||||
|
/* ---- 全局配置变量 (instr.c 定义) ---- */
|
||||||
|
extern u32 g_instr_enabled; /* 全局插桩开关,FUCK debug.enabled */
|
||||||
|
extern u32 g_instr_log_level; /* 日志级别阈值,FUCK debug.log_level */
|
||||||
|
extern u32 g_instr_probe_enable; /* 探测缓冲开关,FUCK debug.probe_enable */
|
||||||
|
extern u32 g_instr_stat_interval; /* 统计摘要间隔,FUCK debug.stat_summary_interval */
|
||||||
|
extern u32 g_instr_module_flags; /* 模块级过滤位掩码 */
|
||||||
|
|
||||||
|
/* ---- 模块标志位 ---- */
|
||||||
|
#define INSTR_F_UTSM_TRACE (1u << 0)
|
||||||
|
#define INSTR_F_VMM_TRACE (1u << 1)
|
||||||
|
#define INSTR_F_DKM_TRACE (1u << 2)
|
||||||
|
#define INSTR_F_DSK_TRACE (1u << 3)
|
||||||
|
#define INSTR_F_BLOCK_TRACE (1u << 4)
|
||||||
|
#define INSTR_F_NET_TRACE (1u << 5)
|
||||||
|
#define INSTR_F_INPUT_TRACE (1u << 6)
|
||||||
|
|
||||||
|
/* ---- INSTR_LOG: 带级别和模块过滤的日志 ----
|
||||||
|
* level: INSTR_LOG_TRACE/DEBUG/INFO/WARN/ERROR
|
||||||
|
* mod_flag: INSTR_F_xxx 或 0(不过滤)
|
||||||
|
* msg: 常量字符串
|
||||||
|
*
|
||||||
|
* 仅当 g_instr_enabled && level >= g_instr_log_level &&
|
||||||
|
* (mod_flag==0 || mod_flag & g_instr_module_flags) 时输出。
|
||||||
|
*/
|
||||||
|
#define INSTR_LOG(level, mod_flag, msg) do { \
|
||||||
|
if (g_instr_enabled && (level) >= g_instr_log_level && \
|
||||||
|
((mod_flag) == 0 || (g_instr_module_flags & (mod_flag)))) { \
|
||||||
|
if ((level) <= INSTR_LOG_ERROR) log_error(msg); \
|
||||||
|
else if ((level) <= INSTR_LOG_WARN) log_warn(msg); \
|
||||||
|
else log_info(msg); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* ---- INSTR_STAT: 轻量级计数器 (声明+增量+摘要) ----
|
||||||
|
* INSTR_STAT_DECL(name) — 在函数/文件作用域声明计数器
|
||||||
|
* INSTR_STAT_INC(name) — 原子性自增 (仅当 enabled)
|
||||||
|
* INSTR_STAT_SUMMARY(name) — 达到间隔时输出摘要并重置
|
||||||
|
*/
|
||||||
|
#define INSTR_STAT_DECL(name) \
|
||||||
|
static u64 name##_count = 0; \
|
||||||
|
static u64 name##_last_ts __attribute__((unused)) = 0
|
||||||
|
|
||||||
|
#define INSTR_STAT_INC(name) do { \
|
||||||
|
if (g_instr_enabled) { name##_count++; } \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define INSTR_STAT_SUMMARY(name) do { \
|
||||||
|
if (g_instr_enabled && name##_count >= g_instr_stat_interval) { \
|
||||||
|
u64 _now = instr_rdtsc(); \
|
||||||
|
u64 _elapsed_ms = (_now - name##_last_ts) / (instr_tsc_per_ms ? instr_tsc_per_ms : 1); \
|
||||||
|
log_info("[STAT] " #name " count="); \
|
||||||
|
log_hex64("", name##_count); \
|
||||||
|
log_info("[STAT] " #name " interval_ms="); \
|
||||||
|
log_hex64("", _elapsed_ms); \
|
||||||
|
name##_count = 0; \
|
||||||
|
name##_last_ts = _now; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* ---- INSTR_PROBE: 写入环形探测缓冲 ----
|
||||||
|
* 标签编码: 4 字符 ASCII,如 "BOOT", "VME0", "EPTV", "IDTI", "AHCI"
|
||||||
|
* a0-a3: 4 个 u64 参数,语义由标签定义
|
||||||
|
*
|
||||||
|
* 仅当 g_instr_enabled && g_instr_probe_enable 时写入。
|
||||||
|
*/
|
||||||
|
#define INSTR_PROBE(tag, a0, a1, a2, a3) do { \
|
||||||
|
if (g_instr_enabled && g_instr_probe_enable) \
|
||||||
|
probe_write(INSTR_TAG_##tag, (u64)(a0), (u64)(a1), (u64)(a2), (u64)(a3)); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* ---- INSTR_TS: 时间戳标注(阶段耗时测量) ----
|
||||||
|
* INSTR_TS_DECL(name) — 声明时间戳变量
|
||||||
|
* INSTR_TS_BEGIN(name) — 记录起始 TSC
|
||||||
|
* INSTR_TS_END(name, label) — 计算耗时并输出(仅当 enabled)
|
||||||
|
*/
|
||||||
|
#define INSTR_TS_DECL(name) static u64 name##_ts = 0
|
||||||
|
|
||||||
|
#define INSTR_TS_BEGIN(name) do { name##_ts = instr_rdtsc(); } while(0)
|
||||||
|
|
||||||
|
#define INSTR_TS_END(name, label) do { \
|
||||||
|
if (g_instr_enabled) { \
|
||||||
|
u64 _d = instr_rdtsc() - name##_ts; \
|
||||||
|
u64 _ms = _d / (instr_tsc_per_ms ? instr_tsc_per_ms : 1); \
|
||||||
|
log_info("[TS] " label " elapsed_ms="); \
|
||||||
|
log_hex64("", _ms); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* ---- 标签编码常量 ----
|
||||||
|
* 4 字符 ASCII 编码为大端序 u32:
|
||||||
|
* 'B'=0x42, 'O'=0x4F, 'O'=0x4F, 'T'=0x54 → 0x424F4F54
|
||||||
|
*/
|
||||||
|
#define INSTR_TAG_BOOT 0x424F4F54u /* "BOOT" — 通用启动事件 */
|
||||||
|
#define INSTR_TAG_IDT0 0x49445430u /* "IDT0" — IDT/中断初始化 */
|
||||||
|
#define INSTR_TAG_ARE0 0x41524530u /* "ARE0" — Arena 初始化 */
|
||||||
|
#define INSTR_TAG_DMA0 0x444D4130u /* "DMA0" — DMA 初始化 */
|
||||||
|
#define INSTR_TAG_NET0 0x4E455430u /* "NET0" — 网络注册 */
|
||||||
|
#define INSTR_TAG_DRR0 0x44525230u /* "DRR0" — DRR 初始化 */
|
||||||
|
#define INSTR_TAG_UTS0 0x55545330u /* "UTS0" — UTSM 核心初始化 */
|
||||||
|
#define INSTR_TAG_DKM0 0x444B4D30u /* "DKM0" — DKM 加载 */
|
||||||
|
#define INSTR_TAG_DSM0 0x44534D30u /* "DSM0" — 驱动 manifest 加载 */
|
||||||
|
#define INSTR_TAG_DSK0 0x44534B30u /* "DSK0" — DSK 加载跳转 */
|
||||||
|
#define INSTR_TAG_SELF 0x53454C46u /* "SELF" — 自检 */
|
||||||
|
#define INSTR_TAG_VMM0 0x564D4D30u /* "VMM0" — VMM 初始化 */
|
||||||
|
#define INSTR_TAG_LIN0 0x4C494E30u /* "LIN0" — Linux loader */
|
||||||
|
#define INSTR_TAG_VME0 0x564D4530u /* "VME0" — VM-Exit 事件 */
|
||||||
|
#define INSTR_TAG_EPTV 0x45505456u /* "EPTV" — EPT violation */
|
||||||
|
#define INSTR_TAG_EPTM 0x4550544Du /* "EPTM" — EPT misconfig */
|
||||||
|
#define INSTR_TAG_AHCI 0x41484349u /* "AHCI" — AHCI 事件 */
|
||||||
|
#define INSTR_TAG_E1K0 0x45314B30u /* "E1K0" — e1000 事件 */
|
||||||
|
#define INSTR_TAG_FAT0 0x46415430u /* "FAT0" — FAT32 事件 */
|
||||||
|
#define INSTR_TAG_NVM0 0x4E564D30u /* "NVM0" — NVMe 事件 */
|
||||||
|
#define INSTR_TAG_IRQ0 0x49525130u /* "IRQ0" — IRQ 注册/触发 */
|
||||||
|
#define INSTR_TAG_PIT0 0x50495430u /* "PIT0" — PIT 模拟 */
|
||||||
|
#define INSTR_TAG_SRL0 0x53524C30u /* "SRL0" — 串口事件 */
|
||||||
|
#define INSTR_TAG_MMIO 0x4D4D494Fu /* "MMIO" — MMIO 访问 */
|
||||||
|
#define INSTR_TAG_HCALL 0x4843414Cu /* "HCAL" — Hypercall */
|
||||||
|
#define INSTR_TAG_EXCP 0x45584350u /* "EXCP" — CPU 异常 */
|
||||||
|
|
||||||
|
/* ---- 探测条目结构(与 probe.c 共享) ---- */
|
||||||
|
typedef struct probe_entry {
|
||||||
|
u32 tag; /* 4字符ASCII标签 */
|
||||||
|
u32 tick; /* 低32位TSC(~2s溢出@2GHz,足够诊断) */
|
||||||
|
u64 args[4]; /* 4个u64参数 */
|
||||||
|
} probe_entry;
|
||||||
|
|
||||||
|
/* ---- 辅助函数声明 (instr.c / probe.c 定义) ---- */
|
||||||
|
u64 instr_rdtsc(void);
|
||||||
|
extern u64 instr_tsc_per_ms;
|
||||||
|
void instr_init(const void *ini_config);
|
||||||
|
|
||||||
|
void probe_init(const void *ini_config);
|
||||||
|
void probe_write(u32 tag, u64 a0, u64 a1, u64 a2, u64 a3);
|
||||||
|
int probe_read(u32 n, probe_entry *out);
|
||||||
|
u32 probe_count(void);
|
||||||
|
u32 probe_capacity(void);
|
||||||
|
probe_entry *probe_buf_address(void);
|
||||||
|
u32 probe_head_value(void);
|
||||||
|
u32 probe_dropped_count(void);
|
||||||
|
|
||||||
|
/* 需要前向声明 log 函数 (避免循环 include) */
|
||||||
|
#ifndef UTSM_LOG_H
|
||||||
|
void log_info(const char *msg);
|
||||||
|
void log_warn(const char *msg);
|
||||||
|
void log_error(const char *msg);
|
||||||
|
void log_hex64(const char *prefix, u64 value);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* UTSM_INSTR_H */
|
||||||
@@ -43,6 +43,20 @@ u64 ipc_shm_get_gpa(void);
|
|||||||
/* Get the size of the shared memory region. */
|
/* Get the size of the shared memory region. */
|
||||||
u64 ipc_shm_get_size(void);
|
u64 ipc_shm_get_size(void);
|
||||||
|
|
||||||
|
/* ===== Payload pool access (bulk data channel) =====
|
||||||
|
*
|
||||||
|
* The payload_pool area of the SHM region carries bulk file-transfer data
|
||||||
|
* (Phase 3 FILE_LIST/FILE_READ). Linux daemon writes into it (via
|
||||||
|
* UTSM_IOCTL_WRITE_POOL) before sending UTSM_MSG_FILE_RESPONSE; UTSM reads
|
||||||
|
* it directly through its own HHDM mapping. */
|
||||||
|
|
||||||
|
/* Host virtual pointer into payload_pool at byte offset.
|
||||||
|
* Returns NULL if not initialized or offset out of range. */
|
||||||
|
void *ipc_shm_payload_ptr(u32 offset);
|
||||||
|
|
||||||
|
/* Total capacity of the payload pool in bytes. */
|
||||||
|
u32 ipc_shm_payload_capacity(void);
|
||||||
|
|
||||||
/* ===== Message API (UTSM-side) ===== */
|
/* ===== Message API (UTSM-side) ===== */
|
||||||
|
|
||||||
/* Send a message from UTSM to Linux (enqueues into utsm_to_linux ring).
|
/* Send a message from UTSM to Linux (enqueues into utsm_to_linux ring).
|
||||||
|
|||||||
@@ -42,10 +42,68 @@ typedef struct linux_compat_service {
|
|||||||
char *stdout_buf, u64 stdout_cap, u64 *stdout_len,
|
char *stdout_buf, u64 stdout_cap, u64 *stdout_len,
|
||||||
u64 *exit_code);
|
u64 *exit_code);
|
||||||
|
|
||||||
|
/* 列出 Linux 目录内容(Phase 3 文件传输)。
|
||||||
|
* path : Linux 绝对路径(如 "/usr/bin")
|
||||||
|
* buf/cap : 接收列表文本的缓冲区(每行一项,目录名带 '/' 后缀)
|
||||||
|
* out_len : 实际写入字节数(可为 NULL)
|
||||||
|
* 返回 0 成功,负值失败(-1=服务不可用, -2=IPC 发送失败,
|
||||||
|
* -3=linux_resume 失败, -4=无响应, -5=Linux 侧错误,
|
||||||
|
* -6=缓冲区不足被截断,此时 out_len 仍给出截断长度)。 */
|
||||||
|
int (*file_list)(const char *path, char *buf, u64 cap, u64 *out_len);
|
||||||
|
|
||||||
|
/* 读取 Linux 文件内容(Phase 3 文件传输,支持大文件分块)。
|
||||||
|
* path : Linux 绝对路径(如 "/etc/hostname")
|
||||||
|
* offset : 文件内起始偏移(首次读传 0)
|
||||||
|
* buf/cap : 接收文件数据的缓冲区
|
||||||
|
* out_len : 本次实际读取字节数(可为 NULL)
|
||||||
|
* out_total : 文件总大小(可为 NULL)
|
||||||
|
* 返回 0 成功(含 EOF:out_len=0 且 offset>=total),负值失败
|
||||||
|
* (错误码同 file_list)。 */
|
||||||
|
int (*file_read)(const char *path, u64 offset, char *buf, u64 cap,
|
||||||
|
u64 *out_len, u64 *out_total);
|
||||||
|
|
||||||
/* 查询 Linux guest 状态字符串(调试用),写入 buf,返回字符串长度。 */
|
/* 查询 Linux guest 状态字符串(调试用),写入 buf,返回字符串长度。 */
|
||||||
int (*status)(char *buf, u64 cap);
|
int (*status)(char *buf, u64 cap);
|
||||||
|
|
||||||
|
/* 写入 Linux 文件(Phase 3 文件传输,支持大文件分块)。
|
||||||
|
* 调用方以递增 offset 循环调用即可上传任意大小文件。
|
||||||
|
* path : Linux 绝对路径(不存在则以 0644 创建;
|
||||||
|
* offset==0 时先截断,即 push 语义为整文件替换)
|
||||||
|
* offset : 文件内写入偏移(首次写传 0)
|
||||||
|
* buf/len : 待写数据(len 会被钳制到 payload pool 容量,
|
||||||
|
* 实际写入量以 out_written 为准;len=0 仅创建/截断)
|
||||||
|
* out_written : 本次实际写入字节数(可为 NULL)
|
||||||
|
* 返回 0 成功,负值失败(错误码同 file_list)。 */
|
||||||
|
int (*file_write)(const char *path, u64 offset, const char *buf, u64 len,
|
||||||
|
u64 *out_written);
|
||||||
|
|
||||||
|
/* 跨内核文件拉取:Linux 文件 → UTSM FAT32 系统盘根目录(≤256KB,整文件)。
|
||||||
|
* linux_path : Linux 绝对路径
|
||||||
|
* fat32_name : UTSM FAT32 根目录文件名(如 "HOSTNAME.TXT",8.3 可转换)
|
||||||
|
* out_size : 实际传输字节数(可为 NULL)
|
||||||
|
* 返回 0 成功,负值失败(-7=文件超过 256KB 暂存上限, -9=无块设备,
|
||||||
|
* -10=非法 FAT32 名, -12=FAT32 写失败,其余同 file_list)。 */
|
||||||
|
int (*file_pull)(const char *linux_path, const char *fat32_name,
|
||||||
|
u64 *out_size);
|
||||||
|
|
||||||
|
/* 跨内核文件推送:UTSM FAT32 系统盘根目录 → Linux 文件(≤256KB,整文件替换)。
|
||||||
|
* fat32_name : UTSM FAT32 根目录文件名(8.3 可转换)
|
||||||
|
* linux_path : Linux 绝对路径
|
||||||
|
* out_size : 实际传输字节数(可为 NULL)
|
||||||
|
* 返回 0 成功,负值失败(-11=文件不存在,其余同 file_pull)。 */
|
||||||
|
int (*file_push)(const char *fat32_name, const char *linux_path,
|
||||||
|
u64 *out_size);
|
||||||
} linux_compat_service;
|
} linux_compat_service;
|
||||||
|
|
||||||
|
/* ===== UTSM 内部接口(不进服务表,供 hypercall.c 等内核代码使用) =====
|
||||||
|
*
|
||||||
|
* UTSM FAT32 系统盘根目录文件访问(经 block_get_api() + fat32_io)。
|
||||||
|
* name 为普通文件名(内部做 8.3 转换)。
|
||||||
|
* 返回 0 成功;负值:-9 无块设备, -10 非法文件名, -11 文件不存在,
|
||||||
|
* -12 磁盘 I/O 错误。 */
|
||||||
|
int lxc_f32_read_file(const char *name, u8 **out_data, u32 *out_size);
|
||||||
|
int lxc_f32_write_file(const char *name, const u8 *data, u32 size);
|
||||||
|
|
||||||
/* 获取全局 Linux 兼容层服务实例指针(UTSM 内部使用,
|
/* 获取全局 Linux 兼容层服务实例指针(UTSM 内部使用,
|
||||||
* 外部通过 boot context reserved[5] 获取)。 */
|
* 外部通过 boot context reserved[5] 获取)。 */
|
||||||
const linux_compat_service *linux_compat_get_service(void);
|
const linux_compat_service *linux_compat_get_service(void);
|
||||||
|
|||||||
@@ -135,9 +135,10 @@ struct linux_setup_header {
|
|||||||
|
|
||||||
#define VIRTIO_MMIO_BLK_GPA (VIRTIO_MMIO_GPA_BASE + 0 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4000000 */
|
#define VIRTIO_MMIO_BLK_GPA (VIRTIO_MMIO_GPA_BASE + 0 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4000000 */
|
||||||
#define VIRTIO_MMIO_NET_GPA (VIRTIO_MMIO_GPA_BASE + 1 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4001000 */
|
#define VIRTIO_MMIO_NET_GPA (VIRTIO_MMIO_GPA_BASE + 1 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4001000 */
|
||||||
|
#define VIRTIO_MMIO_ROOTFS_GPA (VIRTIO_MMIO_GPA_BASE + 2 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4002000 */
|
||||||
|
|
||||||
/* 判断 GPA 是否落在 virtio-mmio 设备区(含两端) */
|
/* 判断 GPA 是否落在 virtio-mmio 设备区(含两端) */
|
||||||
#define VIRTIO_MMIO_GPA_END (VIRTIO_MMIO_GPA_BASE + 2 * VIRTIO_MMIO_GPA_STRIDE)
|
#define VIRTIO_MMIO_GPA_END (VIRTIO_MMIO_GPA_BASE + 3 * VIRTIO_MMIO_GPA_STRIDE)
|
||||||
#define IS_VIRTIO_MMIO_GPA(gpa) ((gpa) >= VIRTIO_MMIO_GPA_BASE && (gpa) < VIRTIO_MMIO_GPA_END)
|
#define IS_VIRTIO_MMIO_GPA(gpa) ((gpa) >= VIRTIO_MMIO_GPA_BASE && (gpa) < VIRTIO_MMIO_GPA_END)
|
||||||
|
|
||||||
/* ===== Linux loader API ===== */
|
/* ===== Linux loader API ===== */
|
||||||
@@ -162,6 +163,10 @@ struct linux_guest_info {
|
|||||||
* Sets *size_out to the module size. */
|
* Sets *size_out to the module size. */
|
||||||
void *linux_find_bzimage_module(u64 *size_out);
|
void *linux_find_bzimage_module(u64 *size_out);
|
||||||
|
|
||||||
|
/* Find the Arch rootfs image boot module (path contains "rootfs").
|
||||||
|
* Exposed to Linux guest via memory-backed virtio-blk as /dev/vdb. */
|
||||||
|
void *linux_find_rootfs_module(u64 *size_out);
|
||||||
|
|
||||||
/* Parse bzImage header and extract key parameters.
|
/* Parse bzImage header and extract key parameters.
|
||||||
* Returns 0 on success, negative on error. */
|
* Returns 0 on success, negative on error. */
|
||||||
int linux_parse_bzimage(const void *bzimage, u64 size,
|
int linux_parse_bzimage(const void *bzimage, u64 size,
|
||||||
@@ -194,6 +199,7 @@ int linux_launch(void);
|
|||||||
"no_timer_check loglevel=7 nohlt idle=poll " \
|
"no_timer_check loglevel=7 nohlt idle=poll " \
|
||||||
"noapic nolapic nosmp " \
|
"noapic nolapic nosmp " \
|
||||||
"virtio_mmio.device=4K@0xF4000000:5 " \
|
"virtio_mmio.device=4K@0xF4000000:5 " \
|
||||||
"virtio_mmio.device=4K@0xF4001000:6"
|
"virtio_mmio.device=4K@0xF4001000:6 " \
|
||||||
|
"virtio_mmio.device=4K@0xF4002000:7"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef UTSM_MM_H
|
||||||
|
#define UTSM_MM_H
|
||||||
|
|
||||||
|
#include <utsm/types.h>
|
||||||
|
|
||||||
|
/* MMIO 映射接口:
|
||||||
|
* mm_map_mmio 在当前页表(Limine 提供的 CR3)的 MMIO 专用窗口中
|
||||||
|
* 建立 phys → virt 的 4K 页映射,页属性带 PCD|PWT(等效 UC),
|
||||||
|
* 适用于任意物理地址(含 4G 以上的 PCI BAR)。
|
||||||
|
* 返回的虚拟地址包含 phys 的页内偏移;失败返回 0。 */
|
||||||
|
void *mm_map_mmio(u64 phys, u64 size);
|
||||||
|
|
||||||
|
/* 解除 mm_map_mmio 建立的映射(按页清除 PTE + invlpg)。
|
||||||
|
* 窗口虚拟地址与中间页表页不回收,保留复用。 */
|
||||||
|
void mm_unmap_mmio(void *virt, u64 size);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -58,6 +58,16 @@ typedef struct dkm_net_device_info {
|
|||||||
u32 flags;
|
u32 flags;
|
||||||
} dkm_net_device_info;
|
} dkm_net_device_info;
|
||||||
|
|
||||||
|
/* 网络设备统计(真机诊断用,驱动层累计)。 */
|
||||||
|
typedef struct dkm_net_stats {
|
||||||
|
u64 tx_ok; /* TX 成功帧数 */
|
||||||
|
u64 tx_err; /* TX 失败帧数 */
|
||||||
|
u64 rx_ok; /* RX 成功帧数 */
|
||||||
|
u64 rx_err; /* RX 错误帧数 */
|
||||||
|
u64 rx_overflow; /* RX buffer 溢出丢包 */
|
||||||
|
u64 link_changes; /* 链路状态变化次数 */
|
||||||
|
} dkm_net_stats;
|
||||||
|
|
||||||
typedef struct dkm_net_api {
|
typedef struct dkm_net_api {
|
||||||
int (*register_device)(const dkm_net_device_desc *desc);
|
int (*register_device)(const dkm_net_device_desc *desc);
|
||||||
u32 (*device_count)(void);
|
u32 (*device_count)(void);
|
||||||
@@ -69,6 +79,8 @@ typedef struct dkm_net_api {
|
|||||||
int (*scan_count)(u32 index);
|
int (*scan_count)(u32 index);
|
||||||
int (*scan_result)(u32 index, u32 n, dkm_net_scan_result *out);
|
int (*scan_result)(u32 index, u32 n, dkm_net_scan_result *out);
|
||||||
int (*is_wireless)(u32 index);
|
int (*is_wireless)(u32 index);
|
||||||
|
/* 网络统计查询(返回 0 成功,-1 索引越界)。 */
|
||||||
|
int (*device_stats)(u32 index, dkm_net_stats *out);
|
||||||
} dkm_net_api;
|
} dkm_net_api;
|
||||||
|
|
||||||
void net_init(void);
|
void net_init(void);
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef UTSM_PAGING_H
|
||||||
|
#define UTSM_PAGING_H
|
||||||
|
|
||||||
|
#include <utsm/types.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UTSM 页表/MMIO 映射服务
|
||||||
|
*
|
||||||
|
* 背景:内核使用 Limine 建立的页表 + HHDM 直映射访问全部 RAM,
|
||||||
|
* 但 HHDM 只保证覆盖内存映射中的 RAM 区域,不保证覆盖 4G 以上的
|
||||||
|
* PCI MMIO 空洞(QEMU/OVMF 常把 NVMe 等 64-bit BAR 分配到 48GiB
|
||||||
|
* 附近)。本模块提供:
|
||||||
|
* 1. is_mapped —— 四级页表只读 walk,运行时核实某虚拟地址是否可访问,
|
||||||
|
* 用于在触碰高位 BAR 前先确认 HHDM 覆盖情况(零 #PF 风险)。
|
||||||
|
* 2. map_mmio —— 在 Limine 页表的 HHDM 空洞处原地补充 4KiB UC 映射
|
||||||
|
* (P|RW|PCD|PWT),页表页取自 DMA 物理页分配器(低 4G、4K 对齐、清零)。
|
||||||
|
*
|
||||||
|
* SAS-R0 单地址空间模型:不新建地址空间,直接扩展当前 CR3 指向的页表,
|
||||||
|
* 所有 CPU/驱动立即可见,无切换成本。
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct dkm_mmio_api {
|
||||||
|
/* 四级页表 walk。返回 0=未映射;1=4K PTE 命中;2=2M PDE 命中;3=1G PDPTE 命中 */
|
||||||
|
int (*is_mapped)(u64 vaddr);
|
||||||
|
/* 把物理区间 [phys, phys+size) 映射到 hhdm_offset + phys,4KiB UC 页。
|
||||||
|
* 已被 Limine 覆盖(含 1G/2M 大页)的页自动跳过。返回 0 成功,负数失败。 */
|
||||||
|
int (*map_mmio)(u64 phys, u64 size);
|
||||||
|
/* 当前 CR3 物理地址(诊断用) */
|
||||||
|
u64 (*cr3)(void);
|
||||||
|
} dkm_mmio_api;
|
||||||
|
|
||||||
|
const dkm_mmio_api *paging_get_api(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -153,4 +153,13 @@ int virtio_queue_get_ptrs(u32 device_id, u32 queue_idx,
|
|||||||
* 由后端在写 used ring 元素后调用,内部做内存屏障。 */
|
* 由后端在写 used ring 元素后调用,内部做内存屏障。 */
|
||||||
void virtio_queue_bump_used(struct virtq_used *used, u16 new_idx);
|
void virtio_queue_bump_used(struct virtq_used *used, u16 new_idx);
|
||||||
|
|
||||||
|
/* 异步触发设备中断(设置 INTERRUPT_STATUS bit0 并向 guest 注入虚拟 IRQ)。
|
||||||
|
* 用于后端在 QUEUE_NOTIFY 上下文之外完成工作(如周期轮询收到 RX 包)。
|
||||||
|
* device_id: VIRTIO_ID_*;设备未注册或 irq==0 时为空操作。 */
|
||||||
|
void virtio_mmio_raise_irq(u32 device_id);
|
||||||
|
|
||||||
|
/* 周期轮询 virtio-net RX 路径(填充 guest 已投递的 RX buffer,
|
||||||
|
* 有填充时自动注入 IRQ)。由 vmexit 的 preemption timer 路径调用。 */
|
||||||
|
void virtio_net_poll(void);
|
||||||
|
|
||||||
#endif /* UTSM_VIRTIO_MMIO_H */
|
#endif /* UTSM_VIRTIO_MMIO_H */
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
kernel/block.o: kernel\block.c include\utsm\block.h include\utsm\types.h \
|
||||||
|
include\utsm\log.h
|
||||||
|
include\utsm\block.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\log.h:
|
||||||
+389
-26
@@ -5,6 +5,7 @@
|
|||||||
#include <utsm/types.h>
|
#include <utsm/types.h>
|
||||||
#include <utsm/pe.h>
|
#include <utsm/pe.h>
|
||||||
#include <utsm/linux_compat.h>
|
#include <utsm/linux_compat.h>
|
||||||
|
#include <utsm/instr.h>
|
||||||
#include "../arch/x86_64/limine.h"
|
#include "../arch/x86_64/limine.h"
|
||||||
|
|
||||||
#define DSK_PATH "/system/deshab64/deshab.elf"
|
#define DSK_PATH "/system/deshab64/deshab.elf"
|
||||||
@@ -20,6 +21,27 @@
|
|||||||
#define ET_EXEC 2
|
#define ET_EXEC 2
|
||||||
#define EM_X86_64 62
|
#define EM_X86_64 62
|
||||||
#define PT_LOAD 1
|
#define PT_LOAD 1
|
||||||
|
#define PT_DYNAMIC 2
|
||||||
|
|
||||||
|
/* ELF Dynamic section tags */
|
||||||
|
#define DT_NULL 0
|
||||||
|
#define DT_RELA 7
|
||||||
|
#define DT_RELASZ 8
|
||||||
|
#define DT_RELAENT 9
|
||||||
|
|
||||||
|
/* ELF Relocation types */
|
||||||
|
#define R_X86_64_RELATIVE 8
|
||||||
|
|
||||||
|
typedef struct elf64_dyn {
|
||||||
|
i64 tag;
|
||||||
|
u64 val;
|
||||||
|
} elf64_dyn;
|
||||||
|
|
||||||
|
typedef struct elf64_rela {
|
||||||
|
u64 offset;
|
||||||
|
u64 info;
|
||||||
|
i64 addend;
|
||||||
|
} elf64_rela;
|
||||||
|
|
||||||
void *memset(void *dst, int value, usize len);
|
void *memset(void *dst, int value, usize len);
|
||||||
void *memcpy(void *dst, const void *src, usize len);
|
void *memcpy(void *dst, const void *src, usize len);
|
||||||
@@ -152,27 +174,86 @@ static struct limine_file *dsk_find_module(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ---- FAT32 block-provider file reader ---- */
|
/* ---- FAT32 block-provider file reader ---- */
|
||||||
static u8 g_dsk_fat32_disk[65536]; /* 128-sector BPB+FAT+root dir buffer */
|
static u8 g_dsk_fat32_disk[131072]; /* 256-sector BPB+FAT+root dir buffer (与 DSK 端一致) */
|
||||||
static u8 g_dsk_fat32_cluster[4096]; /* 8-sector cluster buffer */
|
static u8 g_dsk_fat32_cluster[4096]; /* 8-sector cluster buffer */
|
||||||
/* 实机要求: 文件缓冲区足够大容纳 deshab.elf (~1.5MB) */
|
/* 实机要求: 文件缓冲区足够大容纳 deshab.elf (~1.5MB) */
|
||||||
static u8 g_dsk_fat32_filedata[2097152]; /* 2MB file data buffer */
|
static u8 g_dsk_fat32_filedata[2097152]; /* 2MB file data buffer */
|
||||||
|
/* BUG-019 修复: FAT 按需扇区读取缓冲区(仅 512 字节),
|
||||||
|
* 当 FAT 条目偏移超出 g_dsk_fat32_disk (128KB) 时使用。 */
|
||||||
|
static u8 g_dsk_fat32_fat_sec[512];
|
||||||
|
|
||||||
|
/* BUG-019 修复: 按需读取 FAT 扇区获取下一个簇号。
|
||||||
|
* 如果目标 FAT 条目在预读缓冲区内,直接读取;
|
||||||
|
* 否则只读取包含该条目的单个 FAT 扇区(512 字节)。
|
||||||
|
* fat_start: FAT 在预读缓冲区中的字节偏移(reserved_sectors * 512)。
|
||||||
|
* clus: 当前簇号。
|
||||||
|
* bpb: BPB 指针(用于计算 FAT 扇区 LBA)。
|
||||||
|
* disk: 预读缓冲区基址。
|
||||||
|
* 返回: 下一个簇号,失败返回 0x0FFFFFFF(链终止)。 */
|
||||||
|
static u32 fat32_get_next_cluster(u32 fat_start_byte, u32 clus,
|
||||||
|
const fat32_bpb *bpb, const u8 *disk) {
|
||||||
|
u32 fat_ent_off = fat_start_byte + clus * 4;
|
||||||
|
/* 情况 1: FAT 条目在预读缓冲区内(小分区) */
|
||||||
|
if (fat_ent_off + 4 <= sizeof(g_dsk_fat32_disk)) {
|
||||||
|
return fat32_read_u32(disk + fat_ent_off) & 0x0FFFFFFF;
|
||||||
|
}
|
||||||
|
/* 情况 2: FAT 条目超出预读缓冲区(实机大分区)——按需读取单个 FAT 扇区 */
|
||||||
|
const dkm_kernel_api *api = dkm_get_kernel_api();
|
||||||
|
if (!api || !api->block) return 0x0FFFFFFF;
|
||||||
|
/* FAT 条目在 FAT 表中的字节偏移(相对于 FAT 起始) */
|
||||||
|
u32 fat_byte_off_in_fat = clus * 4;
|
||||||
|
/* 该条目所在的 FAT 扇区号(相对于 FAT 起始) */
|
||||||
|
u32 fat_sec_index = fat_byte_off_in_fat / 512;
|
||||||
|
/* 条目在扇区内的字节偏移 */
|
||||||
|
u32 ent_off_in_sec = fat_byte_off_in_fat % 512;
|
||||||
|
/* FAT 扇区的绝对 LBA */
|
||||||
|
u32 fat_sec_lba = bpb->reserved_sector_count + fat_sec_index;
|
||||||
|
int st = api->block->read(0, fat_sec_lba, 1, g_dsk_fat32_fat_sec);
|
||||||
|
if (st != 0) {
|
||||||
|
log_error("[UTSM] FAT32: on-demand FAT sector read failed");
|
||||||
|
log_hex64("[UTSM] FAT32: fat_sec_lba=", fat_sec_lba);
|
||||||
|
log_hex64("[UTSM] FAT32: clus=", clus);
|
||||||
|
return 0x0FFFFFFF;
|
||||||
|
}
|
||||||
|
return fat32_read_u32(g_dsk_fat32_fat_sec + ent_off_in_sec) & 0x0FFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
static int dsk_load_from_block_fat32(const void **out_addr, u64 *out_size) {
|
static int dsk_load_from_block_fat32(const void **out_addr, u64 *out_size) {
|
||||||
const dkm_kernel_api *api = dkm_get_kernel_api();
|
const dkm_kernel_api *api = dkm_get_kernel_api();
|
||||||
if (!api || !api->block || !api->block->device_count) return -1;
|
if (!api || !api->block || !api->block->device_count) {
|
||||||
|
log_error("[UTSM] FAT32: no block api available");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
u32 count = api->block->device_count();
|
u32 count = api->block->device_count();
|
||||||
if (count == 0 || api->block->sector_size(0) != 512) return -1;
|
if (count == 0 || api->block->sector_size(0) != 512) {
|
||||||
|
log_error("[UTSM] FAT32: no block device or wrong sector size");
|
||||||
|
log_hex64("[UTSM] FAT32: device_count=", count);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Read first 128 sectors: BPB + FAT + root dir */
|
/* Read first 256 sectors: BPB + FAT + root dir */
|
||||||
u8 *disk = g_dsk_fat32_disk;
|
u8 *disk = g_dsk_fat32_disk;
|
||||||
int st = api->block->read(0, 0, 128, disk);
|
int st = api->block->read(0, 0, 256, disk);
|
||||||
if (st != 0) return -1;
|
if (st != 0) {
|
||||||
|
log_error("[UTSM] FAT32: BPB read failed");
|
||||||
|
log_hex64("[UTSM] FAT32: read status=", (u64)(i64)st);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
const fat32_bpb *bpb = (const fat32_bpb *)disk;
|
const fat32_bpb *bpb = (const fat32_bpb *)disk;
|
||||||
if (bpb->boot_sig != 0xAA55 || bpb->bytes_per_sector != 512) return -1;
|
if (bpb->boot_sig != 0xAA55 || bpb->bytes_per_sector != 512) {
|
||||||
|
log_error("[UTSM] FAT32: invalid BPB signature or sector size");
|
||||||
|
log_hex64("[UTSM] FAT32: boot_sig=", bpb->boot_sig);
|
||||||
|
log_hex64("[UTSM] FAT32: bps=", bpb->bytes_per_sector);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
u32 fat_start = bpb->reserved_sector_count * 512;
|
u32 fat_start = bpb->reserved_sector_count * 512;
|
||||||
u32 data_start_sec = bpb->reserved_sector_count + (u32)bpb->fat_count * bpb->sectors_per_fat;
|
u32 data_start_sec = bpb->reserved_sector_count + (u32)bpb->fat_count * bpb->sectors_per_fat;
|
||||||
|
log_hex64("[UTSM] FAT32: fat_start_off=", fat_start);
|
||||||
|
log_hex64("[UTSM] FAT32: data_start_sec=", data_start_sec);
|
||||||
|
log_hex64("[UTSM] FAT32: spc=", bpb->sectors_per_cluster);
|
||||||
|
log_hex64("[UTSM] FAT32: root_clus=", bpb->root_cluster);
|
||||||
|
|
||||||
u32 clus = bpb->root_cluster;
|
u32 clus = bpb->root_cluster;
|
||||||
u32 found_clus = 0;
|
u32 found_clus = 0;
|
||||||
@@ -184,7 +265,12 @@ static int dsk_load_from_block_fat32(const void **out_addr, u64 *out_size) {
|
|||||||
u32 cls_sec = bpb->sectors_per_cluster;
|
u32 cls_sec = bpb->sectors_per_cluster;
|
||||||
u8 *cb = g_dsk_fat32_cluster;
|
u8 *cb = g_dsk_fat32_cluster;
|
||||||
st = api->block->read(0, clus_lba, cls_sec, cb);
|
st = api->block->read(0, clus_lba, cls_sec, cb);
|
||||||
if (st != 0) return -1;
|
if (st != 0) {
|
||||||
|
log_error("[UTSM] FAT32: dir cluster read failed");
|
||||||
|
log_hex64("[UTSM] FAT32: clus=", clus);
|
||||||
|
log_hex64("[UTSM] FAT32: lba=", clus_lba);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
const fat32_dir_entry *dir = (const fat32_dir_entry *)cb;
|
const fat32_dir_entry *dir = (const fat32_dir_entry *)cb;
|
||||||
for (u32 e = 0; e * 32 < cls_sec * 512; e++) {
|
for (u32 e = 0; e * 32 < cls_sec * 512; e++) {
|
||||||
@@ -194,19 +280,27 @@ static int dsk_load_from_block_fat32(const void **out_addr, u64 *out_size) {
|
|||||||
if (dir[e].attr & 0x08) continue;
|
if (dir[e].attr & 0x08) continue;
|
||||||
if (fat32_name11_eq(dir[e].name, target)) {
|
if (fat32_name11_eq(dir[e].name, target)) {
|
||||||
found_size = dir[e].file_size;
|
found_size = dir[e].file_size;
|
||||||
found_clus = fat32_read_u16((const u8 *)&dir[e].cluster_low);
|
found_clus = fat32_read_u16((const u8 *)&dir[e].cluster_low) |
|
||||||
|
((u32)fat32_read_u16((const u8 *)&dir[e].cluster_high) << 16);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found_clus) break;
|
if (found_clus) break;
|
||||||
u32 fat_ent = fat_start + clus * 4;
|
/* BUG-019: 使用按需 FAT 读取,不再受 128KB 缓冲区限制 */
|
||||||
if (fat_ent + 4 > 65536) break;
|
clus = fat32_get_next_cluster(fat_start, clus, bpb, disk);
|
||||||
clus = fat32_read_u32(disk + fat_ent) & 0x0FFFFFFF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found_clus || found_size == 0) return -1;
|
if (!found_clus || found_size == 0) {
|
||||||
|
log_error("[UTSM] FAT32: DESHAB.ELF not found in root dir");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
log_hex64("[UTSM] FAT32: found_clus=", found_clus);
|
||||||
|
log_hex64("[UTSM] FAT32: found_size=", found_size);
|
||||||
|
|
||||||
if (found_size > sizeof(g_dsk_fat32_filedata)) return -1;
|
if (found_size > sizeof(g_dsk_fat32_filedata)) {
|
||||||
|
log_error("[UTSM] FAT32: file exceeds 2MB buffer");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
u8 *dst = g_dsk_fat32_filedata;
|
u8 *dst = g_dsk_fat32_filedata;
|
||||||
u32 remaining = found_size;
|
u32 remaining = found_size;
|
||||||
u32 fc = found_clus;
|
u32 fc = found_clus;
|
||||||
@@ -219,19 +313,27 @@ static int dsk_load_from_block_fat32(const void **out_addr, u64 *out_size) {
|
|||||||
|
|
||||||
u8 *chunk = g_dsk_fat32_cluster;
|
u8 *chunk = g_dsk_fat32_cluster;
|
||||||
st = api->block->read(0, fc_lba, fc_sec, chunk);
|
st = api->block->read(0, fc_lba, fc_sec, chunk);
|
||||||
if (st != 0) return -1;
|
if (st != 0) {
|
||||||
|
log_error("[UTSM] FAT32: data cluster read failed");
|
||||||
|
log_hex64("[UTSM] FAT32: fc=", fc);
|
||||||
|
log_hex64("[UTSM] FAT32: lba=", fc_lba);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
for (u32 b = 0; b < fc_bytes; b++) dst[b] = chunk[b];
|
for (u32 b = 0; b < fc_bytes; b++) dst[b] = chunk[b];
|
||||||
dst += fc_bytes;
|
dst += fc_bytes;
|
||||||
remaining -= fc_bytes;
|
remaining -= fc_bytes;
|
||||||
|
|
||||||
u32 fat_ent = fat_start + fc * 4;
|
/* BUG-019: 使用按需 FAT 读取 */
|
||||||
if (fat_ent + 4 > 65536) { fc = 0x0FFFFFFF; break; }
|
fc = fat32_get_next_cluster(fat_start, fc, bpb, disk);
|
||||||
fc = fat32_read_u32(disk + fat_ent) & 0x0FFFFFFF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy to arena for ELF loader */
|
/* Copy to arena for ELF loader */
|
||||||
u8 *file_buf = (u8 *)kmem_alloc_aligned(found_size, 16);
|
u8 *file_buf = (u8 *)kmem_alloc_aligned(found_size, 16);
|
||||||
if (!file_buf) return -1;
|
if (!file_buf) {
|
||||||
|
log_error("[UTSM] FAT32: arena alloc failed");
|
||||||
|
log_hex64("[UTSM] FAT32: size=", found_size);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
for (u32 i = 0; i < found_size; i++) file_buf[i] = g_dsk_fat32_filedata[i];
|
for (u32 i = 0; i < found_size; i++) file_buf[i] = g_dsk_fat32_filedata[i];
|
||||||
|
|
||||||
*out_addr = file_buf;
|
*out_addr = file_buf;
|
||||||
@@ -240,6 +342,139 @@ static int dsk_load_from_block_fat32(const void **out_addr, u64 *out_size) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- FAT32 subdirectory traversal ---- */
|
||||||
|
|
||||||
|
/* 在指定起始 cluster 的目录项中查找 name11。
|
||||||
|
* is_dir=1 时只匹配子目录(attr & 0x10),is_dir=0 时只匹配文件。
|
||||||
|
* 返回 0 成功,out_clus/out_size 返回匹配项的起始 cluster 和大小。 */
|
||||||
|
static int fat32_find_in_dir(u32 start_clus, const char *name11, int is_dir,
|
||||||
|
u32 *out_clus, u32 *out_size) {
|
||||||
|
const dkm_kernel_api *api = dkm_get_kernel_api();
|
||||||
|
if (!api || !api->block || !api->block->device_count) return -1;
|
||||||
|
|
||||||
|
u8 *disk = g_dsk_fat32_disk;
|
||||||
|
const fat32_bpb *bpb = (const fat32_bpb *)disk;
|
||||||
|
if (bpb->boot_sig != 0xAA55 || bpb->bytes_per_sector != 512) return -2;
|
||||||
|
|
||||||
|
u32 fat_start = bpb->reserved_sector_count * 512;
|
||||||
|
u32 data_start_sec = bpb->reserved_sector_count + (u32)bpb->fat_count * bpb->sectors_per_fat;
|
||||||
|
u32 spc = bpb->sectors_per_cluster;
|
||||||
|
|
||||||
|
u32 clus = start_clus;
|
||||||
|
while (clus >= 2 && clus < 0x0FFFFFF8) {
|
||||||
|
u32 clus_lba = data_start_sec + (clus - 2) * spc;
|
||||||
|
u8 *cb = g_dsk_fat32_cluster;
|
||||||
|
int st = api->block->read(0, clus_lba, spc, cb);
|
||||||
|
if (st != 0) return -3;
|
||||||
|
|
||||||
|
const fat32_dir_entry *dir = (const fat32_dir_entry *)cb;
|
||||||
|
for (u32 e = 0; e * 32 < spc * 512; e++) {
|
||||||
|
if (dir[e].name[0] == 0) break;
|
||||||
|
if ((u8)dir[e].name[0] == 0xE5) continue;
|
||||||
|
if (dir[e].attr == 0x0F) continue;
|
||||||
|
if (dir[e].attr & 0x08) continue;
|
||||||
|
if (!fat32_name11_eq(dir[e].name, name11)) continue;
|
||||||
|
|
||||||
|
/* 名称匹配,检查类型 */
|
||||||
|
int entry_is_dir = (dir[e].attr & 0x10) ? 1 : 0;
|
||||||
|
if (entry_is_dir != is_dir) continue;
|
||||||
|
|
||||||
|
*out_clus = fat32_read_u16((const u8 *)&dir[e].cluster_low) |
|
||||||
|
((u32)fat32_read_u16((const u8 *)&dir[e].cluster_high) << 16);
|
||||||
|
*out_size = dir[e].file_size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 跟随 FAT 链 — BUG-019: 使用按需 FAT 读取 */
|
||||||
|
clus = fat32_get_next_cluster(fat_start, clus, bpb, disk);
|
||||||
|
}
|
||||||
|
return -4; /* 未找到 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按路径查找文件并读取内容。
|
||||||
|
* path: '/' 分隔的 8.3 格式路径(如 "SYSTEM /DESHAB64 /FUCK ")。
|
||||||
|
* out_data: 返回在 g_dsk_fat32_filedata 中的文件数据指针。
|
||||||
|
* out_size: 返回文件大小。
|
||||||
|
* 返回 0 成功。 */
|
||||||
|
int fat32_read_path(const char *path, u8 **out_data, u32 *out_size) {
|
||||||
|
const dkm_kernel_api *api = dkm_get_kernel_api();
|
||||||
|
if (!api || !api->block || !api->block->device_count) return -1;
|
||||||
|
|
||||||
|
u8 *disk = g_dsk_fat32_disk;
|
||||||
|
/* 确保 BPB 缓冲区是新鲜的 */
|
||||||
|
if (api->block->read(0, 0, 256, disk) != 0) return -2;
|
||||||
|
|
||||||
|
const fat32_bpb *bpb = (const fat32_bpb *)disk;
|
||||||
|
if (bpb->boot_sig != 0xAA55 || bpb->bytes_per_sector != 512) return -3;
|
||||||
|
|
||||||
|
u32 fat_start = bpb->reserved_sector_count * 512;
|
||||||
|
u32 data_start_sec = bpb->reserved_sector_count + (u32)bpb->fat_count * bpb->sectors_per_fat;
|
||||||
|
u32 spc = bpb->sectors_per_cluster;
|
||||||
|
|
||||||
|
/* 解析路径组件 */
|
||||||
|
u32 pos = 0;
|
||||||
|
u32 path_len = 0;
|
||||||
|
while (path[path_len]) path_len++;
|
||||||
|
|
||||||
|
u32 cur_clus = bpb->root_cluster; /* 从根目录开始 */
|
||||||
|
|
||||||
|
while (pos < path_len) {
|
||||||
|
/* 提取一个路径组件(到下一个 '/' 或结尾) */
|
||||||
|
char comp[12]; /* 11 字符 8.3 名 + \0 */
|
||||||
|
u32 ci = 0;
|
||||||
|
while (pos < path_len && path[pos] != '/' && ci < 11) {
|
||||||
|
comp[ci++] = path[pos++];
|
||||||
|
}
|
||||||
|
/* 不足 11 字符则补空格 */
|
||||||
|
while (ci < 11) comp[ci++] = ' ';
|
||||||
|
comp[11] = 0;
|
||||||
|
|
||||||
|
/* 跳过 '/' */
|
||||||
|
if (pos < path_len && path[pos] == '/') pos++;
|
||||||
|
|
||||||
|
/* 判断是否为最后一个组件 */
|
||||||
|
int is_last = (pos >= path_len);
|
||||||
|
|
||||||
|
if (is_last) {
|
||||||
|
/* 最后一级:查找文件 */
|
||||||
|
u32 found_clus = 0, found_size = 0;
|
||||||
|
int rc = fat32_find_in_dir(cur_clus, comp, 0, &found_clus, &found_size);
|
||||||
|
if (rc != 0) return -4;
|
||||||
|
|
||||||
|
if (found_size > sizeof(g_dsk_fat32_filedata)) return -5;
|
||||||
|
|
||||||
|
/* 读取文件数据 */
|
||||||
|
u8 *dst = g_dsk_fat32_filedata;
|
||||||
|
u32 remaining = found_size;
|
||||||
|
u32 fc = found_clus;
|
||||||
|
while (fc >= 2 && fc < 0x0FFFFFF8 && remaining > 0) {
|
||||||
|
u32 fc_lba = data_start_sec + (fc - 2) * spc;
|
||||||
|
u32 fc_bytes = spc * 512;
|
||||||
|
if (fc_bytes > remaining) fc_bytes = remaining;
|
||||||
|
u8 *chunk = g_dsk_fat32_cluster;
|
||||||
|
if (api->block->read(0, fc_lba, spc, chunk) != 0) return -6;
|
||||||
|
for (u32 b = 0; b < fc_bytes; b++) dst[b] = chunk[b];
|
||||||
|
dst += fc_bytes;
|
||||||
|
remaining -= fc_bytes;
|
||||||
|
/* BUG-019: 使用按需 FAT 读取 */
|
||||||
|
fc = fat32_get_next_cluster(fat_start, fc, bpb, disk);
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_data = g_dsk_fat32_filedata;
|
||||||
|
*out_size = found_size;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
/* 中间级:查找子目录 */
|
||||||
|
u32 sub_clus = 0, sub_size = 0;
|
||||||
|
int rc = fat32_find_in_dir(cur_clus, comp, 1, &sub_clus, &sub_size);
|
||||||
|
if (rc != 0) return -7;
|
||||||
|
cur_clus = sub_clus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -8; /* 路径为空或无效 */
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- ELF checks ---- */
|
/* ---- ELF checks ---- */
|
||||||
static int dsk_check_elf(const void *address, u64 size) {
|
static int dsk_check_elf(const void *address, u64 size) {
|
||||||
if (!address || size < sizeof(elf64_ehdr)) return -1;
|
if (!address || size < sizeof(elf64_ehdr)) return -1;
|
||||||
@@ -263,15 +498,26 @@ static int dsk_load_elf_image(const void *address, u64 size, dsk_entry_fn *entry
|
|||||||
u64 max_align = 0x1000;
|
u64 max_align = 0x1000;
|
||||||
u32 load_count = 0;
|
u32 load_count = 0;
|
||||||
|
|
||||||
|
/* PT_DYNAMIC 的位置(PIE 重定位用) */
|
||||||
|
u64 dyn_off = 0;
|
||||||
|
u64 dyn_filesz = 0;
|
||||||
|
int has_dyn = 0;
|
||||||
|
|
||||||
for (u16 i = 0; i < eh->phnum; i++) {
|
for (u16 i = 0; i < eh->phnum; i++) {
|
||||||
const elf64_phdr *ph = (const elf64_phdr *)(base + eh->phoff + (u64)i * eh->phentsize);
|
const elf64_phdr *ph = (const elf64_phdr *)(base + eh->phoff + (u64)i * eh->phentsize);
|
||||||
if (ph->type != PT_LOAD) continue;
|
if (ph->type == PT_LOAD) {
|
||||||
if (ph->filesz > ph->memsz) return -1;
|
if (ph->filesz > ph->memsz) return -1;
|
||||||
if (!range_ok(ph->offset, ph->filesz, size)) return -2;
|
if (!range_ok(ph->offset, ph->filesz, size)) return -2;
|
||||||
if (ph->vaddr < min_vaddr) min_vaddr = ph->vaddr;
|
if (ph->vaddr < min_vaddr) min_vaddr = ph->vaddr;
|
||||||
if (ph->vaddr + ph->memsz > max_vaddr) max_vaddr = ph->vaddr + ph->memsz;
|
if (ph->vaddr + ph->memsz > max_vaddr) max_vaddr = ph->vaddr + ph->memsz;
|
||||||
if (ph->align > max_align) max_align = ph->align;
|
if (ph->align > max_align) max_align = ph->align;
|
||||||
load_count++;
|
load_count++;
|
||||||
|
} else if (ph->type == PT_DYNAMIC) {
|
||||||
|
if (!range_ok(ph->offset, ph->filesz, size)) return -2;
|
||||||
|
dyn_off = ph->offset;
|
||||||
|
dyn_filesz = ph->filesz;
|
||||||
|
has_dyn = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (load_count == 0 || min_vaddr == ~0ULL || max_vaddr <= min_vaddr) return -3;
|
if (load_count == 0 || min_vaddr == ~0ULL || max_vaddr <= min_vaddr) return -3;
|
||||||
@@ -290,6 +536,44 @@ static int dsk_load_elf_image(const void *address, u64 size, dsk_entry_fn *entry
|
|||||||
memcpy(image + dst_off, base + ph->offset, ph->filesz);
|
memcpy(image + dst_off, base + ph->offset, ph->filesz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PIE 重定位: 处理 PT_DYNAMIC → DT_RELA → R_X86_64_RELATIVE
|
||||||
|
* load_bias = image 实际加载地址 - ELF 预期最小 vaddr
|
||||||
|
* 对每个 R_X86_64_RELATIVE: slot = load_bias + addend */
|
||||||
|
if (has_dyn && eh->type == ET_DYN) {
|
||||||
|
u64 load_bias = (u64)image - min_vaddr;
|
||||||
|
const elf64_dyn *dyn = (const elf64_dyn *)(base + dyn_off);
|
||||||
|
u64 dyn_count = dyn_filesz / sizeof(elf64_dyn);
|
||||||
|
|
||||||
|
u64 rela_off = 0, rela_sz = 0, rela_ent = sizeof(elf64_rela);
|
||||||
|
|
||||||
|
for (u64 d = 0; d < dyn_count; d++) {
|
||||||
|
if (dyn[d].tag == DT_NULL) break;
|
||||||
|
if (dyn[d].tag == DT_RELA) rela_off = dyn[d].val;
|
||||||
|
if (dyn[d].tag == DT_RELASZ) rela_sz = dyn[d].val;
|
||||||
|
if (dyn[d].tag == DT_RELAENT) rela_ent = dyn[d].val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rela_off != 0 && rela_sz != 0 && rela_ent != 0) {
|
||||||
|
/* rela_off 是相对于文件开头的 vaddr,需转换为 image 内偏移 */
|
||||||
|
if (rela_off >= min_vaddr && rela_off < min_vaddr + image_size) {
|
||||||
|
u64 rela_count = rela_sz / rela_ent;
|
||||||
|
for (u64 r = 0; r < rela_count; r++) {
|
||||||
|
const elf64_rela *rel = (const elf64_rela *)(
|
||||||
|
image + (rela_off - min_vaddr) + r * rela_ent);
|
||||||
|
u32 rtype = (u32)rel->info;
|
||||||
|
if (rtype == R_X86_64_RELATIVE) {
|
||||||
|
if (rel->offset < min_vaddr || rel->offset >= min_vaddr + image_size) continue;
|
||||||
|
u64 *slot = (u64 *)(image + (rel->offset - min_vaddr));
|
||||||
|
*slot = load_bias + (u64)rel->addend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_info("[UTSM] PIE reloc applied");
|
||||||
|
log_hex64("[UTSM] load_bias=", load_bias);
|
||||||
|
log_hex64("[UTSM] rela_count=", rela_sz / rela_ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*entry_out = (dsk_entry_fn)(image + (eh->entry - min_vaddr));
|
*entry_out = (dsk_entry_fn)(image + (eh->entry - min_vaddr));
|
||||||
log_hex64("[UTSM] DSK image base=", (u64)image);
|
log_hex64("[UTSM] DSK image base=", (u64)image);
|
||||||
log_hex64("[UTSM] DSK image size=", image_size);
|
log_hex64("[UTSM] DSK image size=", image_size);
|
||||||
@@ -333,6 +617,24 @@ static void dsk_fill_boot_context(dsk_boot_context *ctx) {
|
|||||||
ctx->reserved[5] = (u64)lxc;
|
ctx->reserved[5] = (u64)lxc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Probe 探测缓冲信息(供 shell $probe 命令读取) */
|
||||||
|
if (g_instr_enabled) {
|
||||||
|
probe_info *pi = (probe_info *)kmem_alloc_aligned(sizeof(probe_info), 8);
|
||||||
|
if (pi) {
|
||||||
|
pi->magic = PROBE_INFO_MAGIC;
|
||||||
|
pi->version = 1;
|
||||||
|
pi->probe_buf_addr = (u64)probe_buf_address();
|
||||||
|
pi->probe_cap = probe_capacity();
|
||||||
|
pi->probe_head = probe_head_value();
|
||||||
|
pi->probe_count = probe_count();
|
||||||
|
pi->probe_dropped = probe_dropped_count();
|
||||||
|
pi->instr_enabled = g_instr_enabled;
|
||||||
|
pi->instr_probe_enable = g_instr_probe_enable;
|
||||||
|
ctx->reserved[6] = (u64)pi;
|
||||||
|
ctx->flags |= DSK_BOOT_FLAG_PROBE_INFO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
u64 rsp;
|
u64 rsp;
|
||||||
__asm__ volatile ("movq %%rsp, %0" : "=r"(rsp));
|
__asm__ volatile ("movq %%rsp, %0" : "=r"(rsp));
|
||||||
ctx->kernel_stack_top = rsp;
|
ctx->kernel_stack_top = rsp;
|
||||||
@@ -384,6 +686,67 @@ int dsk_load_and_jump(void) {
|
|||||||
}
|
}
|
||||||
dsk_fill_boot_context(ctx);
|
dsk_fill_boot_context(ctx);
|
||||||
|
|
||||||
|
/* 实机安全: 跳转前 cli → mask PIC → 重置 IDT → NMI off。
|
||||||
|
* DSK 加载的 ELF 映像可能覆盖 UTSM 的驱动代码段,
|
||||||
|
* 如果 UTSM 注册的 IRQ handler 所在内存被覆盖,
|
||||||
|
* 中断触发时跳转到无效地址 → 三重故障重启。
|
||||||
|
*
|
||||||
|
* P0 修复: 1) 先 cli 禁用中断,消除 PIC mask 两步操作之间的中断竞争窗口
|
||||||
|
* 2) mask PIC 确保不会有硬件中断到达
|
||||||
|
* 3) 设置 LAPIC TPR=0xFF 屏蔽所有通过 IOAPIC 路由的中断
|
||||||
|
* 4) 将 IDT 所有条目替换为安全 halt stub,防止 DSK 运行时
|
||||||
|
* 过期的 UTSM handler 被异常/spurious IRQ 触发
|
||||||
|
* 5) 禁用 NMI */
|
||||||
|
{
|
||||||
|
extern void outb(u16 port, u8 value);
|
||||||
|
extern u8 inb(u16 port);
|
||||||
|
|
||||||
|
/* 1) 先禁用中断——关键修复!原来缺少此步,
|
||||||
|
* outb(0xA1,0xFF) 和 outb(0x21,0xFF) 之间有中断窗口 */
|
||||||
|
__asm__ volatile("cli");
|
||||||
|
|
||||||
|
/* 2) mask PIC — 此时 IF=0,安全操作 */
|
||||||
|
outb(0xA1, 0xFF); /* PIC2: mask IRQ8-15 */
|
||||||
|
outb(0x21, 0xFF); /* PIC1: mask IRQ0-7 */
|
||||||
|
|
||||||
|
/* 3) BUG-020 修复: 设置 LAPIC TPR=0xFF 屏蔽所有 IOAPIC 路由中断。
|
||||||
|
* 实机多核平台中断通常经 IOAPIC 而非 PIC,PIC mask 不影响
|
||||||
|
* IOAPIC redirection entry。通过 IA32_APIC_BASE MSR 读取 LAPIC
|
||||||
|
* 基地址,写入 TPR(偏移 0x80)=0xFF 可在 LAPIC 层面屏蔽所有
|
||||||
|
* 可屏蔽中断(低于 0xFF 优先级),确保 DSK 运行期间不会有
|
||||||
|
* IOAPIC 路由的 IRQ 到达。HHDM 映射后 LAPIC MMIO 可访问。 */
|
||||||
|
{
|
||||||
|
u32 msr_lo, msr_hi;
|
||||||
|
__asm__ volatile("rdmsr" : "=a"(msr_lo), "=d"(msr_hi) : "c"(0x1B));
|
||||||
|
u64 lapic_phys = ((u64)msr_hi << 32) | (msr_lo & 0xFFFFF000ULL);
|
||||||
|
if (lapic_phys && (msr_lo & (1 << 11))) {
|
||||||
|
/* LAPIC 全局启用时(bit 11)才操作 TPR */
|
||||||
|
extern volatile struct limine_hhdm_request g_hhdm_request;
|
||||||
|
u64 hhdm_off = g_hhdm_request.response
|
||||||
|
? g_hhdm_request.response->offset : 0;
|
||||||
|
if (hhdm_off) {
|
||||||
|
u64 lapic_vaddr = lapic_phys + hhdm_off;
|
||||||
|
*(volatile u32 *)(lapic_vaddr + 0x80) = 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4) BUG-20260801-005: 换装 DSK 诊断 IDT(替代原静默 halt_all)。
|
||||||
|
* vector 0-31 → UTSM isr_stub(idt_handler 打完整异常现场后
|
||||||
|
* 安全停机),vector 32-255 保持 halt stub 防御 spurious IRQ7/15。
|
||||||
|
* SAS-R0 下 UTSM .text 常驻内存,DSK 阶段异常投递路径已验证
|
||||||
|
* 可用(BUG-20260729-013 登记行为);原 halt_all 把 #PF/#GP
|
||||||
|
* 无声吞掉,DSK 阶段零诊断。 */
|
||||||
|
extern void idt_install_dsk_diag(void);
|
||||||
|
idt_install_dsk_diag();
|
||||||
|
|
||||||
|
/* 5) NMI off */
|
||||||
|
outb(0x70, inb(0x70) | 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_hex64("[UTSM] DSK context magic=", ctx->magic);
|
||||||
|
log_hex64("[UTSM] DSK context api=", ctx->dkm_kernel_api);
|
||||||
|
log_hex64("[UTSM] DSK context fb=", ctx->framebuffer_address);
|
||||||
log_info("[UTSM] jumping to DSK");
|
log_info("[UTSM] jumping to DSK");
|
||||||
entry(ctx);
|
entry(ctx);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
kernel/dsk_loader.o: kernel\dsk_loader.c include\utsm\dsk.h \
|
||||||
|
include\utsm\types.h include\utsm\arena.h include\utsm\dkm.h \
|
||||||
|
include\utsm\dma.h include\utsm\block.h include\utsm\net.h \
|
||||||
|
include\utsm\paging.h include\utsm\log.h include\utsm\pe.h \
|
||||||
|
include\utsm\linux_compat.h include\utsm\instr.h \
|
||||||
|
kernel\..\arch\x86_64\limine.h
|
||||||
|
include\utsm\dsk.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\arena.h:
|
||||||
|
include\utsm\dkm.h:
|
||||||
|
include\utsm\dma.h:
|
||||||
|
include\utsm\block.h:
|
||||||
|
include\utsm\net.h:
|
||||||
|
include\utsm\paging.h:
|
||||||
|
include\utsm\log.h:
|
||||||
|
include\utsm\pe.h:
|
||||||
|
include\utsm\linux_compat.h:
|
||||||
|
include\utsm\instr.h:
|
||||||
|
kernel\..\arch\x86_64\limine.h:
|
||||||
Binary file not shown.
@@ -0,0 +1,166 @@
|
|||||||
|
/* INI 分区格式解析器 — freestanding, 无堆分配, 无 libc 依赖 */
|
||||||
|
|
||||||
|
#include "ini_parser.h"
|
||||||
|
|
||||||
|
/* ---- 内部辅助 ---- */
|
||||||
|
|
||||||
|
static int ini_streq(const char *a, const char *b) {
|
||||||
|
if (!a || !b) return 0;
|
||||||
|
while (*a && *b) {
|
||||||
|
if (*a != *b) return 0;
|
||||||
|
a++; b++;
|
||||||
|
}
|
||||||
|
return *a == *b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ini_isspace(char c) {
|
||||||
|
return c == ' ' || c == '\t' || c == '\r';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ini_isdigit(char c) {
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ini_strncpy(char *dst, const char *src, u32 max) {
|
||||||
|
u32 i = 0;
|
||||||
|
while (i + 1 < max && src[i]) {
|
||||||
|
dst[i] = src[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
dst[i] = 0;
|
||||||
|
return (int)i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ini_atoi(const char *s) {
|
||||||
|
int neg = 0, v = 0;
|
||||||
|
if (!s) return 0;
|
||||||
|
while (ini_isspace(*s)) s++;
|
||||||
|
if (*s == '-') { neg = 1; s++; }
|
||||||
|
while (ini_isdigit(*s)) {
|
||||||
|
v = v * 10 + (*s - '0');
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return neg ? -v : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ini_parse_bool(const char *s) {
|
||||||
|
if (!s) return -1;
|
||||||
|
if (s[0] == '1') return 1;
|
||||||
|
if (s[0] == '0') return 0;
|
||||||
|
if (ini_streq(s, "true")) return 1;
|
||||||
|
if (ini_streq(s, "false")) return 0;
|
||||||
|
if (ini_streq(s, "yes")) return 1;
|
||||||
|
if (ini_streq(s, "no")) return 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 公共接口 ---- */
|
||||||
|
|
||||||
|
int ini_parse(const char *text, u32 size, ini_config *cfg) {
|
||||||
|
if (!text || !cfg) return -1;
|
||||||
|
|
||||||
|
cfg->count = 0;
|
||||||
|
char cur_section[32] = {0}; /* 当前分区名,默认空 */
|
||||||
|
|
||||||
|
u32 pos = 0;
|
||||||
|
while (pos < size) {
|
||||||
|
/* 跳过行首空白 */
|
||||||
|
while (pos < size && (text[pos] == ' ' || text[pos] == '\t'))
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (pos >= size) break;
|
||||||
|
|
||||||
|
char c = text[pos];
|
||||||
|
|
||||||
|
/* 空行或换行 */
|
||||||
|
if (c == '\n') { pos++; continue; }
|
||||||
|
|
||||||
|
/* 注释行: # 或 ; 开头 */
|
||||||
|
if (c == '#' || c == ';') {
|
||||||
|
while (pos < size && text[pos] != '\n') pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分区头: [section] */
|
||||||
|
if (c == '[') {
|
||||||
|
pos++; /* 跳过 '[' */
|
||||||
|
u32 si = 0;
|
||||||
|
while (pos < size && text[pos] != ']' && text[pos] != '\n' && si + 1 < 32) {
|
||||||
|
cur_section[si++] = text[pos++];
|
||||||
|
}
|
||||||
|
cur_section[si] = 0;
|
||||||
|
/* 跳过到行尾 */
|
||||||
|
while (pos < size && text[pos] != '\n') pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* key=value 行 */
|
||||||
|
if (cfg->count >= 64) break; /* 达到上限 */
|
||||||
|
|
||||||
|
ini_entry *e = &cfg->entries[cfg->count];
|
||||||
|
|
||||||
|
/* 读取 key */
|
||||||
|
u32 ki = 0;
|
||||||
|
while (pos < size && text[pos] != '=' && text[pos] != '\n' &&
|
||||||
|
!ini_isspace(text[pos]) && ki + 1 < 32) {
|
||||||
|
e->key[ki++] = text[pos++];
|
||||||
|
}
|
||||||
|
e->key[ki] = 0;
|
||||||
|
|
||||||
|
/* 跳过 key 和 '=' 之间的空白 */
|
||||||
|
while (pos < size && ini_isspace(text[pos]) && text[pos] != '\n')
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
/* 期望 '=' */
|
||||||
|
if (pos >= size || text[pos] != '=') {
|
||||||
|
/* 无效行,跳到行尾 */
|
||||||
|
while (pos < size && text[pos] != '\n') pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pos++; /* 跳过 '=' */
|
||||||
|
|
||||||
|
/* 跳过 '=' 后的空白 */
|
||||||
|
while (pos < size && ini_isspace(text[pos]) && text[pos] != '\n')
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
/* 读取 value(到行尾,去除尾部空白) */
|
||||||
|
u32 vi = 0;
|
||||||
|
while (pos < size && text[pos] != '\n' && vi + 1 < 64) {
|
||||||
|
e->value[vi++] = text[pos++];
|
||||||
|
}
|
||||||
|
/* 去除尾部空白 */
|
||||||
|
while (vi > 0 && ini_isspace(e->value[vi - 1])) vi--;
|
||||||
|
e->value[vi] = 0;
|
||||||
|
|
||||||
|
/* 复制 section 名 */
|
||||||
|
ini_strncpy(e->section, cur_section, 32);
|
||||||
|
|
||||||
|
cfg->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ini_get(const ini_config *cfg, const char *section, const char *key) {
|
||||||
|
if (!cfg || !section || !key) return NULL;
|
||||||
|
for (u32 i = 0; i < cfg->count; i++) {
|
||||||
|
if (ini_streq(cfg->entries[i].section, section) &&
|
||||||
|
ini_streq(cfg->entries[i].key, key)) {
|
||||||
|
return cfg->entries[i].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ini_get_int(const ini_config *cfg, const char *section, const char *key, int defval) {
|
||||||
|
const char *v = ini_get(cfg, section, key);
|
||||||
|
if (!v || v[0] == 0) return defval;
|
||||||
|
return ini_atoi(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ini_get_bool(const ini_config *cfg, const char *section, const char *key, int defval) {
|
||||||
|
const char *v = ini_get(cfg, section, key);
|
||||||
|
if (!v || v[0] == 0) return defval;
|
||||||
|
int b = ini_parse_bool(v);
|
||||||
|
return (b >= 0) ? b : defval;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
kernel/ini_parser.o: kernel\ini_parser.c kernel\ini_parser.h \
|
||||||
|
include\utsm\types.h
|
||||||
|
kernel\ini_parser.h:
|
||||||
|
include\utsm\types.h:
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef UTSM_INI_PARSER_H
|
||||||
|
#define UTSM_INI_PARSER_H
|
||||||
|
|
||||||
|
#include <utsm/types.h>
|
||||||
|
|
||||||
|
/* INI 配置条目 */
|
||||||
|
typedef struct {
|
||||||
|
char section[32];
|
||||||
|
char key[32];
|
||||||
|
char value[64];
|
||||||
|
} ini_entry;
|
||||||
|
|
||||||
|
/* INI 配置集合(栈/静态分配,不依赖堆) */
|
||||||
|
typedef struct {
|
||||||
|
ini_entry entries[64]; /* 最多 64 条配置项 */
|
||||||
|
u32 count;
|
||||||
|
} ini_config;
|
||||||
|
|
||||||
|
/* 解析 INI 文本到 ini_config。
|
||||||
|
* text: INI 文本起始地址
|
||||||
|
* size: 文本字节长度
|
||||||
|
* cfg: 输出配置集合
|
||||||
|
* 返回 0 成功,负数失败 */
|
||||||
|
int ini_parse(const char *text, u32 size, ini_config *cfg);
|
||||||
|
|
||||||
|
/* 在 section 下查找 key,返回 value 字符串。
|
||||||
|
* 未找到返回 NULL。 */
|
||||||
|
const char *ini_get(const ini_config *cfg, const char *section, const char *key);
|
||||||
|
|
||||||
|
/* 便捷查询:返回整数值。
|
||||||
|
* 未找到或格式错误返回 defval。 */
|
||||||
|
int ini_get_int(const ini_config *cfg, const char *section, const char *key, int defval);
|
||||||
|
|
||||||
|
/* 便捷查询:返回布尔值。
|
||||||
|
* "0"/"false" = 0, "1"/"true" = 1,未找到返回 defval。 */
|
||||||
|
int ini_get_bool(const ini_config *cfg, const char *section, const char *key, int defval);
|
||||||
|
|
||||||
|
#endif /* UTSM_INI_PARSER_H */
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/* instr.c — Deshab 插桩系统初始化与全局配置管理
|
||||||
|
*
|
||||||
|
* 从 FUCK [debug] 配置段加载插桩参数,初始化全局配置变量。
|
||||||
|
* 在 kernel_main() 中调用 instr_init(&g_boot_cfg) 启用。
|
||||||
|
*/
|
||||||
|
#include <utsm/instr.h>
|
||||||
|
#include <utsm/log.h>
|
||||||
|
#include <utsm/types.h>
|
||||||
|
#include "ini_parser.h"
|
||||||
|
|
||||||
|
/* ---- 全局配置变量(供 instr.h 宏条件判断) ---- */
|
||||||
|
u32 g_instr_enabled = 0;
|
||||||
|
u32 g_instr_log_level = INSTR_LOG_INFO;
|
||||||
|
u32 g_instr_probe_enable = 0;
|
||||||
|
u32 g_instr_stat_interval = 256;
|
||||||
|
u32 g_instr_module_flags = 0;
|
||||||
|
u64 instr_tsc_per_ms = 0;
|
||||||
|
|
||||||
|
/* ---- 低级 I/O(TSC 校准用,与 arch/x86_64/io.c 独立避免链接冲突) ---- */
|
||||||
|
static void instr_outb(u16 port, u8 value) {
|
||||||
|
__asm__ volatile("outb %0, %1" :: "a"(value), "Nd"(port));
|
||||||
|
}
|
||||||
|
static u8 instr_inb(u16 port) {
|
||||||
|
u8 value;
|
||||||
|
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TSC 读取 */
|
||||||
|
u64 instr_rdtsc(void) {
|
||||||
|
u32 lo, hi;
|
||||||
|
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
|
||||||
|
return ((u64)hi << 32) | lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TSC 校准:PIT ch0 ~10ms 区间,与 serial.c / dkm_shared.h 相同逻辑 */
|
||||||
|
static void instr_tsc_calibrate(void) {
|
||||||
|
instr_outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */
|
||||||
|
instr_outb(0x40, 0x7c); /* 11932 low = ~10ms */
|
||||||
|
instr_outb(0x40, 0x2e); /* 11932 high */
|
||||||
|
u64 tsc_start = instr_rdtsc();
|
||||||
|
u16 prev = 0;
|
||||||
|
u64 loops = 0;
|
||||||
|
for (;;) {
|
||||||
|
instr_outb(0x43, 0x00);
|
||||||
|
u16 cur = (u16)instr_inb(0x40) | ((u16)instr_inb(0x40) << 8);
|
||||||
|
if (cur > prev && loops > 10) break;
|
||||||
|
prev = cur;
|
||||||
|
loops++;
|
||||||
|
}
|
||||||
|
u64 tsc_end = instr_rdtsc();
|
||||||
|
instr_tsc_per_ms = (tsc_end - tsc_start) / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
void instr_init(const void *cfg_ptr) {
|
||||||
|
const ini_config *cfg = (const ini_config *)cfg_ptr;
|
||||||
|
|
||||||
|
/* 从 FUCK [debug] 段读取配置 */
|
||||||
|
g_instr_enabled = (u32)ini_get_bool(cfg, "debug", "enabled", 0);
|
||||||
|
g_instr_log_level = (u32)ini_get_int(cfg, "debug", "log_level", INSTR_LOG_INFO);
|
||||||
|
g_instr_probe_enable = (u32)ini_get_bool(cfg, "debug", "probe_enable", 0);
|
||||||
|
g_instr_stat_interval = (u32)ini_get_int(cfg, "debug", "stat_summary_interval", 256);
|
||||||
|
|
||||||
|
/* 模块级过滤标志 */
|
||||||
|
g_instr_module_flags = 0;
|
||||||
|
if (ini_get_bool(cfg, "debug", "utsm_trace", 0))
|
||||||
|
g_instr_module_flags |= INSTR_F_UTSM_TRACE;
|
||||||
|
if (ini_get_bool(cfg, "debug", "vmm_trace", 0))
|
||||||
|
g_instr_module_flags |= INSTR_F_VMM_TRACE;
|
||||||
|
if (ini_get_bool(cfg, "debug", "dkm_trace", 0))
|
||||||
|
g_instr_module_flags |= INSTR_F_DKM_TRACE;
|
||||||
|
if (ini_get_bool(cfg, "debug", "dsk_trace", 0))
|
||||||
|
g_instr_module_flags |= INSTR_F_DSK_TRACE;
|
||||||
|
if (ini_get_bool(cfg, "debug", "block_trace", 0))
|
||||||
|
g_instr_module_flags |= INSTR_F_BLOCK_TRACE;
|
||||||
|
if (ini_get_bool(cfg, "debug", "net_trace", 0))
|
||||||
|
g_instr_module_flags |= INSTR_F_NET_TRACE;
|
||||||
|
if (ini_get_bool(cfg, "debug", "input_trace", 0))
|
||||||
|
g_instr_module_flags |= INSTR_F_INPUT_TRACE;
|
||||||
|
|
||||||
|
/* 校准 TSC(独立于 serial.c 的校准,供插桩时间戳使用) */
|
||||||
|
instr_tsc_calibrate();
|
||||||
|
|
||||||
|
/* 初始化探测缓冲 */
|
||||||
|
probe_init(cfg);
|
||||||
|
|
||||||
|
/* 输出初始化状态 */
|
||||||
|
if (g_instr_enabled) {
|
||||||
|
log_info("[INSTR] enabled");
|
||||||
|
log_hex64("[INSTR] log_level=", g_instr_log_level);
|
||||||
|
log_hex64("[INSTR] probe_enable=", g_instr_probe_enable);
|
||||||
|
log_hex64("[INSTR] module_flags=", g_instr_module_flags);
|
||||||
|
log_hex64("[INSTR] tsc_per_ms=", instr_tsc_per_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
kernel/instr.o: kernel\instr.c include\utsm\instr.h include\utsm\types.h \
|
||||||
|
include\utsm\log.h kernel\ini_parser.h
|
||||||
|
include\utsm\instr.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\log.h:
|
||||||
|
kernel\ini_parser.h:
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
kernel/log.o: kernel\log.c include\utsm\log.h include\utsm\types.h
|
||||||
|
include\utsm\log.h:
|
||||||
|
include\utsm\types.h:
|
||||||
+267
-51
@@ -9,94 +9,310 @@
|
|||||||
#include <utsm/net.h>
|
#include <utsm/net.h>
|
||||||
#include <utsm/ipc_shm.h>
|
#include <utsm/ipc_shm.h>
|
||||||
#include <utsm/linux_compat.h>
|
#include <utsm/linux_compat.h>
|
||||||
|
#include <utsm/instr.h>
|
||||||
|
#include "ini_parser.h"
|
||||||
|
#include "../arch/x86_64/limine.h"
|
||||||
|
|
||||||
void arch_halt_forever(void);
|
void arch_halt_forever(void);
|
||||||
|
|
||||||
|
extern volatile struct limine_module_request g_module_request;
|
||||||
|
|
||||||
|
/* dkm/manifest.c: 带 FUCK [drivers] 过滤的 manifest 加载入口 */
|
||||||
|
void dsm_load_by_manifest_ex(const ini_config *cfg);
|
||||||
|
|
||||||
|
/* ---- 运行期配置全局变量(从 FUCK 文件初始化) ---- */
|
||||||
|
u32 g_utsm_max_segments = UTSM_MAX_SEGMENTS_DEFAULT;
|
||||||
|
u32 g_utsm_max_capabilities = UTSM_MAX_CAPABILITIES_DEFAULT;
|
||||||
|
u32 g_utsm_max_pckc_keys = UTSM_MAX_PCKC_KEYS_DEFAULT;
|
||||||
|
u64 g_utsm_arena_size = UTSM_ARENA_SIZE_DEFAULT;
|
||||||
|
u64 g_utsm_dirty_shard_pages = UTSM_DIRTY_SHARD_PAGES_DEFAULT;
|
||||||
|
|
||||||
|
/* 将 FUCK [utsm] 区的配置应用到运行期全局变量 */
|
||||||
|
static void utsm_apply_config(const ini_config *cfg) {
|
||||||
|
if (!cfg) return;
|
||||||
|
g_utsm_max_segments = (u32)ini_get_int(cfg, "utsm", "max_segments", UTSM_MAX_SEGMENTS_DEFAULT);
|
||||||
|
g_utsm_max_capabilities = (u32)ini_get_int(cfg, "utsm", "max_capabilities", UTSM_MAX_CAPABILITIES_DEFAULT);
|
||||||
|
g_utsm_max_pckc_keys = (u32)ini_get_int(cfg, "utsm", "max_pckc_keys", UTSM_MAX_PCKC_KEYS_DEFAULT);
|
||||||
|
g_utsm_arena_size = (u64)ini_get_int(cfg, "utsm", "arena_size_mb", 16) * 1024 * 1024;
|
||||||
|
g_utsm_dirty_shard_pages = (u64)ini_get_int(cfg, "utsm", "dirty_shard_pages", UTSM_DIRTY_SHARD_PAGES_DEFAULT);
|
||||||
|
|
||||||
|
log_info("[UTSM] FUCK config applied");
|
||||||
|
log_hex64("[UTSM] max_segments=", g_utsm_max_segments);
|
||||||
|
log_hex64("[UTSM] max_pckc_keys=", g_utsm_max_pckc_keys);
|
||||||
|
log_hex64("[UTSM] dirty_shard_pages=", g_utsm_dirty_shard_pages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 全局启动配置(从 FUCK 文件读取) ---- */
|
||||||
|
static ini_config g_boot_cfg;
|
||||||
|
static int g_boot_cfg_loaded = 0;
|
||||||
|
|
||||||
|
/* 从 Limine boot module 读取 FUCK 配置。
|
||||||
|
* cmdline="fuck:config" 的模块即为 FUCK 配置文件。
|
||||||
|
* 返回 0 成功,负数失败。 */
|
||||||
|
static int utsm_load_fuck_config(ini_config *cfg) {
|
||||||
|
struct limine_module_response *rsp = g_module_request.response;
|
||||||
|
if (!rsp) return -1;
|
||||||
|
for (u64 i = 0; i < rsp->module_count; i++) {
|
||||||
|
struct limine_file *f = rsp->modules[i];
|
||||||
|
if (f && f->cmdline) {
|
||||||
|
/* 比较 cmdline 是否为 "fuck:config" */
|
||||||
|
const char *c = f->cmdline;
|
||||||
|
const char *tag = "fuck:config";
|
||||||
|
int match = 1;
|
||||||
|
for (int j = 0; tag[j]; j++) {
|
||||||
|
if (c[j] != tag[j]) { match = 0; break; }
|
||||||
|
}
|
||||||
|
if (match && c[11] == 0) {
|
||||||
|
int rc = ini_parse((const char *)f->address, (u32)f->size, cfg);
|
||||||
|
if (rc == 0) {
|
||||||
|
log_info("[UTSM] FUCK config loaded from boot module");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_warn("[UTSM] FUCK boot module not found");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
void kernel_main(void) {
|
void kernel_main(void) {
|
||||||
|
/* ---- 插桩: 启动总时间戳 ---- */
|
||||||
|
INSTR_TS_DECL(boot_total);
|
||||||
|
INSTR_TS_BEGIN(boot_total);
|
||||||
|
|
||||||
serial_init();
|
serial_init();
|
||||||
log_info("[UTSM] boot");
|
log_info("[UTSM] boot");
|
||||||
|
|
||||||
|
/* ==== 真机硬件特征早期检测 (在任何初始化之前) ====
|
||||||
|
* 检测项目:
|
||||||
|
* 1. CPU vendor (GenuineIntel / AuthenticAMD / etc.)
|
||||||
|
* 2. 最大物理地址宽度 (CPUID.80000008H:EAX[7:0])
|
||||||
|
* 3. LA57 (5-level paging) 是否激活 → 拒绝启动
|
||||||
|
* 4. CPU 核心数 (CPUID.0BH 或 CPUID.01H)
|
||||||
|
* 5. PCIe MCFG 可用性 (后续由 ACPI 驱动检测)
|
||||||
|
*
|
||||||
|
* **QEMU 无法测试但真机必需的代码路径**:
|
||||||
|
* - LA57 拒绝: QEMU 默认不启用 5 级分页
|
||||||
|
* - 物理地址宽度 > 48: 只有大内存真机才有
|
||||||
|
* - AMD CPU vendor: QEMU 默认 Intel, 实机可能是 AMD
|
||||||
|
* - 多核: QEMU -smp 可以模拟, 但真机拓扑更复杂
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
/* CPU vendor */
|
||||||
|
u32 a, b, c, d;
|
||||||
|
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(0), "c"(0));
|
||||||
|
char vendor[13];
|
||||||
|
vendor[0] = (char)(b & 0xff);
|
||||||
|
vendor[1] = (char)((b >> 8) & 0xff);
|
||||||
|
vendor[2] = (char)((b >> 16) & 0xff);
|
||||||
|
vendor[3] = (char)((b >> 24) & 0xff);
|
||||||
|
vendor[4] = (char)(d & 0xff);
|
||||||
|
vendor[5] = (char)((d >> 8) & 0xff);
|
||||||
|
vendor[6] = (char)((d >> 16) & 0xff);
|
||||||
|
vendor[7] = (char)((d >> 24) & 0xff);
|
||||||
|
vendor[8] = (char)(c & 0xff);
|
||||||
|
vendor[9] = (char)((c >> 8) & 0xff);
|
||||||
|
vendor[10] = (char)((c >> 16) & 0xff);
|
||||||
|
vendor[11] = (char)((c >> 24) & 0xff);
|
||||||
|
vendor[12] = 0;
|
||||||
|
log_info("[UTSM] CPU vendor: ");
|
||||||
|
log_info(vendor);
|
||||||
|
|
||||||
|
/* Max physical address width */
|
||||||
|
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(0x80000000), "c"(0));
|
||||||
|
if (a >= 0x80000008) {
|
||||||
|
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(0x80000008), "c"(0));
|
||||||
|
u32 phys_bits = a & 0xff;
|
||||||
|
u32 virt_bits = (a >> 8) & 0xff;
|
||||||
|
log_hex64("[UTSM] MaxPhysAddr bits=", phys_bits);
|
||||||
|
log_hex64("[UTSM] MaxVirtAddr bits=", virt_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LA57 early detection — 在 CR4 读之前先通过 CPUID 检查是否支持 */
|
||||||
|
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(7), "c"(0));
|
||||||
|
if (c & (1u << 16)) {
|
||||||
|
log_warn("[UTSM] CPU supports LA57 (5-level paging)");
|
||||||
|
/* 检查 CR4 是否实际启用了 LA57 */
|
||||||
|
u64 cr4;
|
||||||
|
__asm__ volatile("mov %%cr4, %0" : "=r"(cr4));
|
||||||
|
if (cr4 & (1ULL << 12)) {
|
||||||
|
log_error("[UTSM] LA57 is ACTIVE — this kernel requires 4-level paging");
|
||||||
|
log_error("[UTSM] Refusing to boot. Disable LA57 in firmware settings.");
|
||||||
|
arch_halt_forever();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CPU core count — CPUID leaf 0BH (Intel Extended Topology) */
|
||||||
|
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(0), "c"(0));
|
||||||
|
if (a >= 0x0B) {
|
||||||
|
/* 尝试 level 0 获取 SMT 核心数 */
|
||||||
|
u32 level_type = 0, cores = 0;
|
||||||
|
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(0x0B), "c"(0));
|
||||||
|
level_type = (c >> 8) & 0xff;
|
||||||
|
cores = b & 0xffff;
|
||||||
|
if (level_type == 1 && cores > 0) {
|
||||||
|
log_hex64("[UTSM] SMT threads=", cores);
|
||||||
|
}
|
||||||
|
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(0x0B), "c"(1));
|
||||||
|
level_type = (c >> 8) & 0xff;
|
||||||
|
cores = b & 0xffff;
|
||||||
|
if (level_type == 2 && cores > 0) {
|
||||||
|
log_hex64("[UTSM] logical processors=", cores);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* fallback: CPUID.01H EBX[23:16] */
|
||||||
|
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(1), "c"(0));
|
||||||
|
u32 lc = (b >> 16) & 0xff;
|
||||||
|
log_hex64("[UTSM] logical processors (legacy)=", lc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 插桩: CPU 检测完成,记录 vendor ---- */
|
||||||
|
INSTR_PROBE(BOOT, (u64)vendor[0] | ((u64)vendor[1] << 8), 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 插桩: IDT 初始化耗时 ---- */
|
||||||
|
INSTR_TS_DECL(ts_idt);
|
||||||
|
INSTR_TS_BEGIN(ts_idt);
|
||||||
idt_init();
|
idt_init();
|
||||||
|
INSTR_TS_END(ts_idt, "idt_init");
|
||||||
|
|
||||||
|
/* === 读取 FUCK 配置(通过 Limine boot module 预加载) === */
|
||||||
|
g_boot_cfg_loaded = (utsm_load_fuck_config(&g_boot_cfg) == 0);
|
||||||
|
ini_config *cfg = g_boot_cfg_loaded ? &g_boot_cfg : NULL;
|
||||||
|
|
||||||
|
/* ---- 插桩: FUCK 配置加载完成 ---- */
|
||||||
|
INSTR_PROBE(BOOT, (u64)g_boot_cfg_loaded, 0, 0, 0);
|
||||||
|
|
||||||
|
/* === 初始化插桩系统(在 arena_init 之前,但需要 FUCK 配置) === */
|
||||||
|
/* 注意: probe_init 需要 arena,所以 instr_init 必须在 arena_init 之后。
|
||||||
|
* 此处先设置 g_instr_enabled 等全局变量,probe_init 延后到 arena 后。
|
||||||
|
* 实际上 instr_init 内部会调用 probe_init,而 probe_init 需要 kmem_alloc。
|
||||||
|
* 所以我们将 instr_init 移到 arena_init 之后。*/
|
||||||
|
|
||||||
|
/* === 应用 UTSM 算法参数到运行期全局变量 === */
|
||||||
|
utsm_apply_config(cfg);
|
||||||
|
|
||||||
log_info("[UTSM] arena init begin");
|
log_info("[UTSM] arena init begin");
|
||||||
|
INSTR_TS_DECL(ts_arena);
|
||||||
|
INSTR_TS_BEGIN(ts_arena);
|
||||||
arena_init();
|
arena_init();
|
||||||
|
INSTR_TS_END(ts_arena, "arena_init");
|
||||||
log_info("[UTSM] arena init ok");
|
log_info("[UTSM] arena init ok");
|
||||||
|
|
||||||
|
/* ---- 插桩: arena 就绪后初始化插桩系统(含探测缓冲分配) ---- */
|
||||||
|
instr_init(cfg);
|
||||||
|
|
||||||
log_info("[UTSM] dma init begin");
|
log_info("[UTSM] dma init begin");
|
||||||
|
INSTR_TS_DECL(ts_dma);
|
||||||
|
INSTR_TS_BEGIN(ts_dma);
|
||||||
dma_init();
|
dma_init();
|
||||||
|
INSTR_TS_END(ts_dma, "dma_init");
|
||||||
log_info("[UTSM] dma init ok");
|
log_info("[UTSM] dma init ok");
|
||||||
|
|
||||||
log_info("[UTSM] net registry init begin");
|
log_info("[UTSM] net registry init begin");
|
||||||
|
INSTR_TS_DECL(ts_net);
|
||||||
|
INSTR_TS_BEGIN(ts_net);
|
||||||
net_init();
|
net_init();
|
||||||
|
INSTR_TS_END(ts_net, "net_init");
|
||||||
log_info("[UTSM] net registry init ok");
|
log_info("[UTSM] net registry init ok");
|
||||||
|
|
||||||
log_info("[UTSM] drr init begin");
|
log_info("[UTSM] drr init begin");
|
||||||
|
INSTR_TS_DECL(ts_drr);
|
||||||
|
INSTR_TS_BEGIN(ts_drr);
|
||||||
drr_init();
|
drr_init();
|
||||||
|
INSTR_TS_END(ts_drr, "drr_init");
|
||||||
log_info("[UTSM] drr init ok");
|
log_info("[UTSM] drr init ok");
|
||||||
|
|
||||||
log_info("[UTSM] core init begin");
|
log_info("[UTSM] core init begin");
|
||||||
|
INSTR_TS_DECL(ts_utsm);
|
||||||
|
INSTR_TS_BEGIN(ts_utsm);
|
||||||
utsm_init();
|
utsm_init();
|
||||||
|
INSTR_TS_END(ts_utsm, "utsm_init");
|
||||||
log_info("[UTSM] init ok");
|
log_info("[UTSM] init ok");
|
||||||
|
|
||||||
|
INSTR_TS_DECL(ts_dkm);
|
||||||
|
INSTR_TS_BEGIN(ts_dkm);
|
||||||
dkm_init();
|
dkm_init();
|
||||||
dkm_fill_platform_info();
|
dkm_fill_platform_info();
|
||||||
dsm_load_by_manifest();
|
INSTR_TS_END(ts_dkm, "dkm_init");
|
||||||
|
|
||||||
int result = utsm_selftest_run();
|
/* === 驱动加载 — 按 [drivers] 区过滤 === */
|
||||||
if (result == 0) {
|
INSTR_TS_DECL(ts_dsm);
|
||||||
log_info("[UTSM] SELFTEST PASS");
|
INSTR_TS_BEGIN(ts_dsm);
|
||||||
|
dsm_load_by_manifest_ex(cfg);
|
||||||
|
INSTR_TS_END(ts_dsm, "dsm_load_manifest");
|
||||||
|
|
||||||
/* Phase 1.1: VMM (VMX/EPT) 初始化 + 自检
|
/* === selftest — 可通过 FUCK [boot] selftest=0 跳过 === */
|
||||||
* 若 VMX 不可用(如 QEMU 未启用),仅警告,不阻断 DSK 启动 */
|
int run_selftest = ini_get_bool(cfg, "boot", "selftest", 1);
|
||||||
if (vmm_init() == 0) {
|
if (run_selftest) {
|
||||||
log_info("[UTSM] VMM init ok");
|
INSTR_TS_DECL(ts_self);
|
||||||
int vmm_st = vmm_self_test();
|
INSTR_TS_BEGIN(ts_self);
|
||||||
if (vmm_st == 0) {
|
int result = utsm_selftest_run();
|
||||||
log_info("[UTSM] VMM self-test PASS");
|
INSTR_TS_END(ts_self, "selftest");
|
||||||
|
if (result == 0) {
|
||||||
/* Phase 1.2: Linux 加载器 + 启动
|
log_info("[UTSM] SELFTEST PASS");
|
||||||
* 若 bzImage 模块存在,加载并启动 Linux guest。
|
|
||||||
* 若无 bzImage,跳过 Linux 启动,继续 DSK 流程。 */
|
|
||||||
if (linux_loader_init() == 0) {
|
|
||||||
log_info("[UTSM] Linux loader init ok");
|
|
||||||
|
|
||||||
/* Phase 1.3: Initialize IPC shared memory before
|
|
||||||
* launching Linux. Allocates 1MB, initializes ring
|
|
||||||
* buffers, and EPT-maps at GPA 0x04000000 so the
|
|
||||||
* Linux guest can access it via UTSM_HCALL_SHM_INFO. */
|
|
||||||
if (ipc_shm_init() == 0) {
|
|
||||||
log_info("[UTSM] IPC shm init ok");
|
|
||||||
} else {
|
|
||||||
log_error("[UTSM] IPC shm init failed (non-fatal)");
|
|
||||||
}
|
|
||||||
|
|
||||||
int lin_st = linux_launch();
|
|
||||||
if (lin_st == 0) {
|
|
||||||
log_info("[UTSM] Linux guest parked (daemon ready)");
|
|
||||||
/* Phase 3.2: 初始化 Linux 兼容层服务
|
|
||||||
* (需在 linux_launch 成功 + guest park 后调用) */
|
|
||||||
linux_compat_init();
|
|
||||||
} else {
|
|
||||||
log_error("[UTSM] Linux launch failed");
|
|
||||||
log_hex64("[UTSM] Linux st=", (u64)(i64)lin_st);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log_warn("[UTSM] Linux loader unavailable (no bzImage)");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log_error("[UTSM] VMM self-test FAIL");
|
|
||||||
log_hex64("[UTSM] VMM st=", (u64)(i64)vmm_st);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log_warn("[UTSM] VMM unavailable (VMX not supported or disabled)");
|
log_error("[UTSM] SELFTEST FAIL");
|
||||||
}
|
arch_halt_forever();
|
||||||
|
|
||||||
if (dsk_load_and_jump() != 0) {
|
|
||||||
log_error("[UTSM] DSK jump failed");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log_error("[UTSM] SELFTEST FAIL");
|
log_info("[UTSM] selftest skipped by FUCK config");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* === VMM — 可通过 FUCK [boot] vmm=0 跳过 === */
|
||||||
|
int run_vmm = ini_get_bool(cfg, "boot", "vmm", 1);
|
||||||
|
if (run_vmm && vmm_init() == 0) {
|
||||||
|
INSTR_TS_DECL(ts_vmm);
|
||||||
|
INSTR_TS_BEGIN(ts_vmm);
|
||||||
|
log_info("[UTSM] VMM init ok");
|
||||||
|
int vmm_st = vmm_self_test();
|
||||||
|
if (vmm_st == 0) {
|
||||||
|
INSTR_TS_END(ts_vmm, "vmm_init+selftest");
|
||||||
|
log_info("[UTSM] VMM self-test PASS");
|
||||||
|
|
||||||
|
/* === Linux guest — 可通过 FUCK [boot] linux_guest=0 跳过 === */
|
||||||
|
int run_linux = ini_get_bool(cfg, "boot", "linux_guest", 1);
|
||||||
|
if (run_linux && linux_loader_init() == 0) {
|
||||||
|
INSTR_TS_DECL(ts_linux);
|
||||||
|
INSTR_TS_BEGIN(ts_linux);
|
||||||
|
log_info("[UTSM] Linux loader init ok");
|
||||||
|
|
||||||
|
if (ipc_shm_init() == 0) {
|
||||||
|
log_info("[UTSM] IPC shm init ok");
|
||||||
|
} else {
|
||||||
|
log_error("[UTSM] IPC shm init failed (non-fatal)");
|
||||||
|
}
|
||||||
|
|
||||||
|
int lin_st = linux_launch();
|
||||||
|
INSTR_TS_END(ts_linux, "linux_launch");
|
||||||
|
if (lin_st == 0) {
|
||||||
|
log_info("[UTSM] Linux guest parked (daemon ready)");
|
||||||
|
linux_compat_init();
|
||||||
|
} else {
|
||||||
|
log_error("[UTSM] Linux launch failed");
|
||||||
|
log_hex64("[UTSM] Linux st=", (u64)(i64)lin_st);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_warn("[UTSM] Linux loader unavailable (no bzImage or disabled)");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
INSTR_TS_END(ts_vmm, "vmm_init+selftest");
|
||||||
|
log_error("[UTSM] VMM self-test FAIL");
|
||||||
|
log_hex64("[UTSM] VMM st=", (u64)(i64)vmm_st);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_warn("[UTSM] VMM unavailable (VMX not supported or disabled by FUCK)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 插桩: DSK 加载跳转前记录 ---- */
|
||||||
|
INSTR_PROBE(DSK0, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
if (dsk_load_and_jump() != 0) {
|
||||||
|
log_error("[UTSM] DSK jump failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 插桩: 启动总耗时(仅在 DSK 跳转失败时可见) ---- */
|
||||||
|
INSTR_TS_END(boot_total, "boot_total");
|
||||||
|
|
||||||
arch_halt_forever();
|
arch_halt_forever();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
kernel/main.o: kernel\main.c include\utsm\log.h include\utsm\types.h \
|
||||||
|
include\utsm\arena.h include\utsm\utsm.h include\utsm\status.h \
|
||||||
|
include\utsm\config.h include\utsm\uuid.h include\utsm\process.h \
|
||||||
|
include\utsm\capability.h include\utsm\segment.h include\utsm\dirty.h \
|
||||||
|
include\utsm\utrw.h include\utsm\vmm.h include\utsm\linux_loader.h \
|
||||||
|
include\utsm\ipc_shm.h ..\utsm-ipc\ipc_proto.h \
|
||||||
|
include\utsm\hypercall.h include\utsm\drr.h include\utsm\dkm.h \
|
||||||
|
include\utsm\dma.h include\utsm\block.h include\utsm\net.h \
|
||||||
|
include\utsm\paging.h include\utsm\idt.h include\utsm\dsk.h \
|
||||||
|
include\utsm\linux_compat.h include\utsm\instr.h kernel\ini_parser.h \
|
||||||
|
kernel\..\arch\x86_64\limine.h
|
||||||
|
include\utsm\log.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\arena.h:
|
||||||
|
include\utsm\utsm.h:
|
||||||
|
include\utsm\status.h:
|
||||||
|
include\utsm\config.h:
|
||||||
|
include\utsm\uuid.h:
|
||||||
|
include\utsm\process.h:
|
||||||
|
include\utsm\capability.h:
|
||||||
|
include\utsm\segment.h:
|
||||||
|
include\utsm\dirty.h:
|
||||||
|
include\utsm\utrw.h:
|
||||||
|
include\utsm\vmm.h:
|
||||||
|
include\utsm\linux_loader.h:
|
||||||
|
include\utsm\ipc_shm.h:
|
||||||
|
..\utsm-ipc\ipc_proto.h:
|
||||||
|
include\utsm\hypercall.h:
|
||||||
|
include\utsm\drr.h:
|
||||||
|
include\utsm\dkm.h:
|
||||||
|
include\utsm\dma.h:
|
||||||
|
include\utsm\block.h:
|
||||||
|
include\utsm\net.h:
|
||||||
|
include\utsm\paging.h:
|
||||||
|
include\utsm\idt.h:
|
||||||
|
include\utsm\dsk.h:
|
||||||
|
include\utsm\linux_compat.h:
|
||||||
|
include\utsm\instr.h:
|
||||||
|
kernel\ini_parser.h:
|
||||||
|
kernel\..\arch\x86_64\limine.h:
|
||||||
Binary file not shown.
+16
-2
@@ -52,7 +52,6 @@ static int net_tx_impl(u32 index, const void *packet, u32 length) {
|
|||||||
if (index >= g_net_device_count) return -2;
|
if (index >= g_net_device_count) return -2;
|
||||||
const dkm_net_device_desc *dev = &g_net_devices[index];
|
const dkm_net_device_desc *dev = &g_net_devices[index];
|
||||||
if (!dev->tx) return -3;
|
if (!dev->tx) return -3;
|
||||||
log_hex64("[NET] tx dispatch fn=", (u64)dev->tx);
|
|
||||||
return dev->tx(dev->ctx, packet, length);
|
return dev->tx(dev->ctx, packet, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,6 +91,20 @@ static int net_is_wireless_impl(u32 index) {
|
|||||||
return (dev->flags & DKM_NET_F_WIRELESS) ? 1 : 0;
|
return (dev->flags & DKM_NET_F_WIRELESS) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 统计查询:返回驱动层累计的收发统计。
|
||||||
|
* 当前实现为占位:返回全零统计。
|
||||||
|
* 后续由驱动在 tx/rx 回调内更新共享统计区,此处直接读取。
|
||||||
|
* 驱动层的本地统计(如 e1000 的 g_stat_tx_ok 等)与 netdev 层统计
|
||||||
|
* 通过 driver_ctx 统一后即可返回精确值。 */
|
||||||
|
static int net_device_stats_impl(u32 index, dkm_net_stats *out) {
|
||||||
|
if (!out) return -1;
|
||||||
|
if (index >= g_net_device_count) return -2;
|
||||||
|
/* 占位:全零。后续由驱动填充实际统计。 */
|
||||||
|
for (u32 i = 0; i < sizeof(dkm_net_stats); i++)
|
||||||
|
((u8 *)out)[i] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const dkm_net_api g_net_api = {
|
static const dkm_net_api g_net_api = {
|
||||||
.register_device = net_register_device_impl,
|
.register_device = net_register_device_impl,
|
||||||
.device_count = net_device_count_impl,
|
.device_count = net_device_count_impl,
|
||||||
@@ -101,7 +114,8 @@ static const dkm_net_api g_net_api = {
|
|||||||
.scan_start = net_scan_start_impl,
|
.scan_start = net_scan_start_impl,
|
||||||
.scan_count = net_scan_count_impl,
|
.scan_count = net_scan_count_impl,
|
||||||
.scan_result = net_scan_result_impl,
|
.scan_result = net_scan_result_impl,
|
||||||
.is_wireless = net_is_wireless_impl
|
.is_wireless = net_is_wireless_impl,
|
||||||
|
.device_stats = net_device_stats_impl
|
||||||
};
|
};
|
||||||
|
|
||||||
const dkm_net_api *net_get_api(void) {
|
const dkm_net_api *net_get_api(void) {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
kernel/net.o: kernel\net.c include\utsm\net.h include\utsm\types.h \
|
||||||
|
include\utsm\log.h
|
||||||
|
include\utsm\net.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\log.h:
|
||||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
|||||||
|
kernel/panic.o: kernel\panic.c include\utsm\log.h include\utsm\types.h
|
||||||
|
include\utsm\log.h:
|
||||||
|
include\utsm\types.h:
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/* probe.c — Deshab 环形探测缓冲管理
|
||||||
|
*
|
||||||
|
* 结构化事件记录系统:写入环形缓冲,支持事后回溯分析。
|
||||||
|
* 用于记录关键事件(CPU异常、EPT violation、DMA完成等),
|
||||||
|
* 通过 shell $probe 命令读取。
|
||||||
|
*
|
||||||
|
* 设计要点:
|
||||||
|
* - 环形缓冲容量由 FUCK debug.probe_capacity 控制(默认 4096 条)
|
||||||
|
* - 每条记录 40 字节 (tag:4 + tick:4 + args:4*8)
|
||||||
|
* - 写入时无内存分配,仅 head 指针推进
|
||||||
|
* - 满时覆盖最旧记录(环形覆盖)
|
||||||
|
* - 禁用时 probe_write 为空操作
|
||||||
|
*/
|
||||||
|
#include <utsm/instr.h>
|
||||||
|
#include <utsm/log.h>
|
||||||
|
#include <utsm/types.h>
|
||||||
|
#include <utsm/arena.h>
|
||||||
|
#include "ini_parser.h"
|
||||||
|
|
||||||
|
/* ---- 探测缓冲状态 ---- */
|
||||||
|
static probe_entry *g_probe_buf = 0; /* 缓冲基地址(arena 分配) */
|
||||||
|
static u32 g_probe_cap = 0; /* 缓冲容量(条目数) */
|
||||||
|
static u32 g_probe_head = 0; /* 下一条写入位置(递增取模) */
|
||||||
|
static u32 g_probe_count = 0; /* 已写入总条目数 */
|
||||||
|
static u32 g_probe_dropped = 0; /* 因缓冲满而覆盖的条目数 */
|
||||||
|
|
||||||
|
void probe_init(const void *cfg_ptr) {
|
||||||
|
const ini_config *cfg = (const ini_config *)cfg_ptr;
|
||||||
|
|
||||||
|
u32 cap = (u32)ini_get_int(cfg, "debug", "probe_capacity", 4096);
|
||||||
|
if (cap == 0) {
|
||||||
|
g_instr_probe_enable = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 从 arena 分配探测缓冲(对齐到 8 字节) */
|
||||||
|
u64 size = (u64)cap * sizeof(probe_entry);
|
||||||
|
g_probe_buf = (probe_entry *)kmem_alloc_aligned(size, 8);
|
||||||
|
if (!g_probe_buf) {
|
||||||
|
g_instr_probe_enable = 0;
|
||||||
|
log_warn("[PROBE] arena alloc failed, probe disabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_probe_cap = cap;
|
||||||
|
g_probe_head = 0;
|
||||||
|
g_probe_count = 0;
|
||||||
|
g_probe_dropped = 0;
|
||||||
|
|
||||||
|
log_info("[PROBE] init ok, capacity=");
|
||||||
|
log_hex64("", g_probe_cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void probe_write(u32 tag, u64 a0, u64 a1, u64 a2, u64 a3) {
|
||||||
|
if (!g_probe_buf || !g_probe_cap) return;
|
||||||
|
|
||||||
|
u32 idx = g_probe_head % g_probe_cap;
|
||||||
|
|
||||||
|
/* 环形覆盖:如果 head >= cap,说明有旧记录被覆盖 */
|
||||||
|
if (g_probe_head >= g_probe_cap && g_probe_dropped == 0) {
|
||||||
|
/* 仅首次覆盖时计数,避免每条都做减法 */
|
||||||
|
}
|
||||||
|
if (g_probe_count >= g_probe_cap) {
|
||||||
|
g_probe_dropped++;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_probe_buf[idx].tag = tag;
|
||||||
|
g_probe_buf[idx].tick = (u32)instr_rdtsc();
|
||||||
|
g_probe_buf[idx].args[0] = a0;
|
||||||
|
g_probe_buf[idx].args[1] = a1;
|
||||||
|
g_probe_buf[idx].args[2] = a2;
|
||||||
|
g_probe_buf[idx].args[3] = a3;
|
||||||
|
|
||||||
|
g_probe_head++;
|
||||||
|
g_probe_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int probe_read(u32 n, probe_entry *out) {
|
||||||
|
if (!g_probe_buf || n == 0) return 0;
|
||||||
|
|
||||||
|
u32 avail = (g_probe_count < g_probe_cap) ? g_probe_count : g_probe_cap;
|
||||||
|
if (n > avail) n = avail;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < n; i++) {
|
||||||
|
u32 src = (g_probe_head - n + i) % g_probe_cap;
|
||||||
|
out[i] = g_probe_buf[src];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)n;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 probe_count(void) {
|
||||||
|
return g_probe_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 probe_capacity(void) {
|
||||||
|
return g_probe_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 供 dsk_loader 读取内部状态(填充 probe_info) ---- */
|
||||||
|
probe_entry *probe_buf_address(void) {
|
||||||
|
return g_probe_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 probe_head_value(void) {
|
||||||
|
return g_probe_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 probe_dropped_count(void) {
|
||||||
|
return g_probe_dropped;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
kernel/probe.o: kernel\probe.c include\utsm\instr.h include\utsm\types.h \
|
||||||
|
include\utsm\log.h include\utsm\arena.h kernel\ini_parser.h
|
||||||
|
include\utsm\instr.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\log.h:
|
||||||
|
include\utsm\arena.h:
|
||||||
|
kernel\ini_parser.h:
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
mm/arena.o: mm\arena.c include\utsm\arena.h include\utsm\types.h \
|
||||||
|
include\utsm\config.h include\utsm\log.h
|
||||||
|
include\utsm\arena.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\config.h:
|
||||||
|
include\utsm\log.h:
|
||||||
Binary file not shown.
@@ -96,6 +96,10 @@ void dma_init(void) {
|
|||||||
int dma_alloc_pages(u64 page_count, u64 alignment, u64 max_phys, dkm_dma_buffer *out) {
|
int dma_alloc_pages(u64 page_count, u64 alignment, u64 max_phys, dkm_dma_buffer *out) {
|
||||||
if (!out || page_count == 0 || !g_dma_ready) return -1;
|
if (!out || page_count == 0 || !g_dma_ready) return -1;
|
||||||
if (page_count > DMA_BITMAP_PAGES) return -3;
|
if (page_count > DMA_BITMAP_PAGES) return -3;
|
||||||
|
/* 真机安全: max_phys 参数上限为低 4G (DMA bitmap 只覆盖低 4G)。
|
||||||
|
* 调用方若传 0 表示"无限制",按低 4G 上限处理。
|
||||||
|
* 对 NVMe/AHCI 等需要 32-bit DMA 的设备, max_phys 传 0x100000000。
|
||||||
|
* 若未来需要 >4G DMA, 需要扩展 bitmap 或使用 IOMMU/SWIOTLB。 */
|
||||||
if (max_phys == 0 || max_phys > DMA_LOW_MAX_PHYS) max_phys = DMA_LOW_MAX_PHYS;
|
if (max_phys == 0 || max_phys > DMA_LOW_MAX_PHYS) max_phys = DMA_LOW_MAX_PHYS;
|
||||||
|
|
||||||
if (alignment < PAGE_SIZE) alignment = PAGE_SIZE;
|
if (alignment < PAGE_SIZE) alignment = PAGE_SIZE;
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
mm/dma.o: mm\dma.c include\utsm\dma.h include\utsm\types.h \
|
||||||
|
include\utsm\log.h mm\..\arch\x86_64\limine.h
|
||||||
|
include\utsm\dma.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\log.h:
|
||||||
|
mm\..\arch\x86_64\limine.h:
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
#include <utsm/mm.h>
|
||||||
|
#include <utsm/dma.h>
|
||||||
|
#include <utsm/log.h>
|
||||||
|
#include <utsm/types.h>
|
||||||
|
#include "../arch/x86_64/limine.h"
|
||||||
|
|
||||||
|
#define MM_PAGE_SIZE 4096ULL
|
||||||
|
|
||||||
|
/* 页表项属性位 */
|
||||||
|
#define PTE_PRESENT (1ULL << 0)
|
||||||
|
#define PTE_RW (1ULL << 1)
|
||||||
|
#define PTE_PWT (1ULL << 3)
|
||||||
|
#define PTE_PCD (1ULL << 4)
|
||||||
|
#define PTE_PS (1ULL << 7)
|
||||||
|
#define PTE_ADDR_MASK 0x000FFFFFFFFFF000ULL
|
||||||
|
#define PTE_NX (1ULL << 63)
|
||||||
|
|
||||||
|
#define CR4_LA57 (1ULL << 12)
|
||||||
|
#define CR4_PKE (1ULL << 22)
|
||||||
|
|
||||||
|
/* MMIO 专用虚拟窗口:运行时在高半部 PML4[256..510] 中扫描空闲槽位,
|
||||||
|
* 避开 Limine HHDM 区与内核映像(PML4[511],0xFFFFFFFF80000000)。
|
||||||
|
* 单个 PML4 槽位对应 512GiB 虚拟空间,bump 分配不回收。 */
|
||||||
|
#define MM_WINDOW_SIZE (1ULL << 39)
|
||||||
|
|
||||||
|
extern volatile struct limine_hhdm_request g_hhdm_request;
|
||||||
|
|
||||||
|
static u64 g_hhdm;
|
||||||
|
static u64 g_pml4_phys;
|
||||||
|
static u64 g_window_base;
|
||||||
|
static u64 g_window_end;
|
||||||
|
static u64 g_window_next;
|
||||||
|
static int g_mm_state; /* 0=未初始化, 1=就绪, -1=不可用 */
|
||||||
|
static u32 g_max_phys_addr_bits; /* CPUID.80000008H:EAX[7:0], 真机物理地址宽度 */
|
||||||
|
|
||||||
|
static inline u64 mm_read_cr3(void) {
|
||||||
|
u64 v;
|
||||||
|
__asm__ volatile("mov %%cr3, %0" : "=r"(v));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 mm_read_cr4(void) {
|
||||||
|
u64 v;
|
||||||
|
__asm__ volatile("mov %%cr4, %0" : "=r"(v));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mm_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 inline void mm_invlpg(u64 virt) {
|
||||||
|
__asm__ volatile("invlpg (%0)" :: "r"(virt) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 在高半部 PML4[256..510] 中找一个空闲槽位作为 MMIO 窗口。
|
||||||
|
* 返回该槽位覆盖的 canonical 虚拟基址;无空闲槽返回 0。 */
|
||||||
|
static u64 mm_find_window(void) {
|
||||||
|
u64 *pml4 = (u64 *)(g_hhdm + g_pml4_phys);
|
||||||
|
for (u64 idx = 256; idx < 511; idx++) {
|
||||||
|
if (!(pml4[idx] & PTE_PRESENT)) {
|
||||||
|
/* bit39..47 为槽位号,bit48 以上符号扩展为全 1 */
|
||||||
|
return 0xFFFF000000000000ULL | (idx << 39);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 首次调用时懒初始化:取 HHDM offset 与当前 CR3。
|
||||||
|
* UTSM 复用 Limine 页表,PML4 经 HHDM 访问。
|
||||||
|
*
|
||||||
|
* 真机安全检查:
|
||||||
|
* 1. LA57 (5-level paging) 拒绝 — 页表 walk 假定 4 级
|
||||||
|
* 2. CPUID 最大物理地址宽度检测 — 确保映射的物理地址合法
|
||||||
|
* 3. PML4 槽位扫描 — 确认空闲窗口可用
|
||||||
|
*/
|
||||||
|
static void mm_lazy_init(void) {
|
||||||
|
g_mm_state = -1;
|
||||||
|
g_max_phys_addr_bits = 0;
|
||||||
|
if (!g_hhdm_request.response) {
|
||||||
|
log_warn("[MM] missing HHDM response");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_hhdm = g_hhdm_request.response->offset;
|
||||||
|
if (!g_hhdm) {
|
||||||
|
log_warn("[MM] null HHDM offset");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 真机安全: LA57 早期检测与拒绝 ---- *
|
||||||
|
* 若固件/BIOS 启用了 5 级分页 (CR4.LA57=1), 本实现的 4 级页表 walk
|
||||||
|
* 将产生错误的地址转换, 必须拒绝继续运行。
|
||||||
|
* **QEMU 默认不会启用 LA57, 此路径只在真机触发。** */
|
||||||
|
if (mm_read_cr4() & CR4_LA57) {
|
||||||
|
log_error("[MM] LA57 5-level paging ACTIVE — REFUSING to proceed");
|
||||||
|
log_error("[MM] This kernel requires 4-level paging (48-bit virtual)");
|
||||||
|
log_error("[MM] Disable LA57 in BIOS/firmware settings");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 真机安全: 物理地址宽度检测 ---- *
|
||||||
|
* CPUID leaf 0x80000008 EAX[7:0] 给出 MaxPhysAddr 位宽。
|
||||||
|
* 典型值: QEMU=40/46, 实机=46/48/52。
|
||||||
|
* 若物理地址 > 48-bit, 4 级分页的 PTE 地址位可能不够用。
|
||||||
|
* 当前 PTE_ADDR_MASK 只使用 bit12-51 (40 位物理页号 = 52 位物理地址),
|
||||||
|
* 支持到 52-bit 物理地址, 足以覆盖所有现有 x86_64 实现。 */
|
||||||
|
{
|
||||||
|
u32 a, b, c, d;
|
||||||
|
mm_cpuid(0x80000000, &a, &b, &c, &d);
|
||||||
|
if (a >= 0x80000008) {
|
||||||
|
mm_cpuid(0x80000008, &a, &b, &c, &d);
|
||||||
|
g_max_phys_addr_bits = a & 0xff;
|
||||||
|
log_hex64("[MM] CPU MaxPhysAddr bits=", g_max_phys_addr_bits);
|
||||||
|
if (g_max_phys_addr_bits > 52) {
|
||||||
|
log_error("[MM] physical address width > 52 bits — PTE cannot represent");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* 无 80000008H leaf, 假定 36 或 39 位 (旧 CPU) */
|
||||||
|
g_max_phys_addr_bits = 36;
|
||||||
|
log_warn("[MM] CPUID 80000008H not available; assuming 36-bit phys");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pml4_phys = mm_read_cr3() & PTE_ADDR_MASK;
|
||||||
|
g_window_base = mm_find_window();
|
||||||
|
if (!g_window_base) {
|
||||||
|
log_error("[MM] no free PML4 slot for mmio window");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_window_end = g_window_base + MM_WINDOW_SIZE;
|
||||||
|
g_window_next = g_window_base;
|
||||||
|
g_mm_state = 1;
|
||||||
|
log_info("[MM] mmio mapper ready");
|
||||||
|
log_hex64("[MM] PML4 phys=", g_pml4_phys);
|
||||||
|
log_hex64("[MM] mmio window=", g_window_base);
|
||||||
|
log_hex64("[MM] MaxPhysAddr bits=", g_max_phys_addr_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 逐级 walk 到 PT,缺层时从 DMA 页分配器取清零页补层。
|
||||||
|
* 返回最终 PTE 槽位的 HHDM 虚拟地址;冲突(撞上既有大页)或 OOM 返回 0。 */
|
||||||
|
static u64 *mm_walk_create(u64 virt) {
|
||||||
|
u64 *table = (u64 *)(g_hhdm + g_pml4_phys);
|
||||||
|
/* 中间三级:lvl=3 PML4(shift39) → lvl=2 PDPT(shift30) → lvl=1 PD(shift21) */
|
||||||
|
for (int lvl = 3; lvl >= 1; lvl--) {
|
||||||
|
u64 shift = 12 + (u64)lvl * 9;
|
||||||
|
u64 idx = (virt >> shift) & 0x1FFULL;
|
||||||
|
u64 entry = table[idx];
|
||||||
|
if (entry & PTE_PRESENT) {
|
||||||
|
if (entry & PTE_PS) return 0; /* 既有 1GiB/2MiB 大页占用,冲突 */
|
||||||
|
table = (u64 *)(g_hhdm + (entry & PTE_ADDR_MASK));
|
||||||
|
} else {
|
||||||
|
dkm_dma_buffer buf;
|
||||||
|
if (dma_alloc_pages(1, MM_PAGE_SIZE, 0, &buf) != 0) {
|
||||||
|
log_warn("[MM] page table alloc failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
table[idx] = buf.phys | PTE_PRESENT | PTE_RW;
|
||||||
|
table = (u64 *)buf.virt; /* dma 分配已清零 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &table[(virt >> 12) & 0x1FFULL];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 只查询不创建的 walk;任一级缺失或为大页时返回 0。 */
|
||||||
|
static u64 *mm_walk_query(u64 virt) {
|
||||||
|
u64 *table = (u64 *)(g_hhdm + g_pml4_phys);
|
||||||
|
for (int lvl = 3; lvl >= 1; lvl--) {
|
||||||
|
u64 shift = 12 + (u64)lvl * 9;
|
||||||
|
u64 entry = table[(virt >> shift) & 0x1FFULL];
|
||||||
|
if (!(entry & PTE_PRESENT) || (entry & PTE_PS)) return 0;
|
||||||
|
table = (u64 *)(g_hhdm + (entry & PTE_ADDR_MASK));
|
||||||
|
}
|
||||||
|
return &table[(virt >> 12) & 0x1FFULL];
|
||||||
|
}
|
||||||
|
|
||||||
|
void *mm_map_mmio(u64 phys, u64 size) {
|
||||||
|
if (g_mm_state == 0) mm_lazy_init();
|
||||||
|
if (g_mm_state != 1 || !size) return 0;
|
||||||
|
|
||||||
|
/* 真机安全: 验证物理地址在 CPU 支持的范围内 */
|
||||||
|
if (g_max_phys_addr_bits > 0) {
|
||||||
|
u64 max_phys = (1ULL << g_max_phys_addr_bits) - 1;
|
||||||
|
if (phys > max_phys) {
|
||||||
|
log_error("[MM] phys address exceeds CPU MaxPhysAddr");
|
||||||
|
log_hex64("[MM] phys=", phys);
|
||||||
|
log_hex64("[MM] max_phys=", max_phys);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 off = phys & (MM_PAGE_SIZE - 1ULL);
|
||||||
|
u64 base_phys = phys - off;
|
||||||
|
u64 total = (off + size + MM_PAGE_SIZE - 1ULL) & ~(MM_PAGE_SIZE - 1ULL);
|
||||||
|
u64 pages = total / MM_PAGE_SIZE;
|
||||||
|
|
||||||
|
u64 vbase = g_window_next;
|
||||||
|
if (vbase < g_window_base || vbase + total > g_window_end) {
|
||||||
|
log_error("[MM] mmio window exhausted");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 mapped = 0;
|
||||||
|
for (u64 i = 0; i < pages; i++) {
|
||||||
|
u64 virt = vbase + i * MM_PAGE_SIZE;
|
||||||
|
u64 *pte = mm_walk_create(virt);
|
||||||
|
if (!pte || (*pte & PTE_PRESENT)) {
|
||||||
|
/* 冲突/失败:回滚本次已建立的映射 */
|
||||||
|
log_error("[MM] map conflict");
|
||||||
|
for (u64 j = 0; j < mapped; j++) {
|
||||||
|
u64 v = vbase + j * MM_PAGE_SIZE;
|
||||||
|
u64 *p = mm_walk_query(v);
|
||||||
|
if (p) { *p = 0; mm_invlpg(v); }
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*pte = (base_phys + i * MM_PAGE_SIZE) | PTE_PRESENT | PTE_RW | PTE_PCD | PTE_PWT;
|
||||||
|
mm_invlpg(virt);
|
||||||
|
mapped++;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_window_next = vbase + total;
|
||||||
|
log_hex64("[MM] map phys=", base_phys);
|
||||||
|
log_hex64("[MM] map virt=", vbase);
|
||||||
|
log_hex64("[MM] map pages=", pages);
|
||||||
|
return (void *)(vbase + off);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mm_unmap_mmio(void *virt, u64 size) {
|
||||||
|
if (g_mm_state != 1 || !virt || !size) return;
|
||||||
|
u64 v = (u64)virt & ~(MM_PAGE_SIZE - 1ULL);
|
||||||
|
u64 end = ((u64)virt + size + MM_PAGE_SIZE - 1ULL) & ~(MM_PAGE_SIZE - 1ULL);
|
||||||
|
for (; v < end; v += MM_PAGE_SIZE) {
|
||||||
|
u64 *pte = mm_walk_query(v);
|
||||||
|
if (pte && (*pte & PTE_PRESENT)) {
|
||||||
|
*pte = 0;
|
||||||
|
mm_invlpg(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 窗口 bump 指针与中间页表页不回收:映射开销极小,保留复用 */
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
mm/mmio.o: mm\mmio.c include\utsm\mm.h include\utsm\types.h \
|
||||||
|
include\utsm\dma.h include\utsm\log.h mm\..\arch\x86_64\limine.h
|
||||||
|
include\utsm\mm.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\dma.h:
|
||||||
|
include\utsm\log.h:
|
||||||
|
mm\..\arch\x86_64\limine.h:
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* UTSM 页表/MMIO 映射服务实现
|
||||||
|
*
|
||||||
|
* 算法说明:
|
||||||
|
* - is_mapped: 标准 x86_64 四级页表 walk,O(1)(固定 4 级),
|
||||||
|
* 页表页均为 RAM,经 HHDM 直映射访问,全程不触发 #PF。
|
||||||
|
* - map_mmio: 对 [phys, phys+size) 逐 4K 页在 HHDM 空间 (hhdm+phys)
|
||||||
|
* 补建 P|RW|PCD|PWT 映射。若中间级已存在则复用;若整页已被
|
||||||
|
* Limine 大页覆盖则跳过(is_mapped 先行判定)。页表页由
|
||||||
|
* dma_alloc_pages 提供(低 4G、4K 对齐、返回即清零)。
|
||||||
|
* - not-present 项不会被 TLB 缓存,新映射无需 invlpg;
|
||||||
|
* 仍对每页执行 invlpg 作为防御性措施(成本可忽略)。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <utsm/paging.h>
|
||||||
|
#include <utsm/dma.h>
|
||||||
|
#include <utsm/log.h>
|
||||||
|
#include "../arch/x86_64/limine.h"
|
||||||
|
|
||||||
|
extern volatile struct limine_hhdm_request g_hhdm_request;
|
||||||
|
|
||||||
|
#define PTE_PRESENT (1ULL << 0)
|
||||||
|
#define PTE_RW (1ULL << 1)
|
||||||
|
#define PTE_PWT (1ULL << 3) /* MMIO: write-through */
|
||||||
|
#define PTE_PCD (1ULL << 4) /* MMIO: cache disable */
|
||||||
|
#define PTE_PS (1ULL << 7) /* 1G/2M 大页标志 */
|
||||||
|
#define PTE_ADDR_MASK 0x000FFFFFFFFFF000ULL
|
||||||
|
#define PAGE_4K 4096ULL
|
||||||
|
|
||||||
|
static u64 paging_hhdm(void) {
|
||||||
|
return g_hhdm_request.response ? g_hhdm_request.response->offset : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 paging_read_cr3(void) {
|
||||||
|
u64 v;
|
||||||
|
__asm__ volatile("mov %%cr3, %0" : "=r"(v));
|
||||||
|
return v & PTE_ADDR_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 返回 0=未映射;1=4K PTE;2=2M PDE;3=1G PDPTE */
|
||||||
|
static int paging_is_mapped_impl(u64 vaddr) {
|
||||||
|
u64 hhdm = paging_hhdm();
|
||||||
|
if (!hhdm) return 0;
|
||||||
|
|
||||||
|
u64 *pml4 = (u64 *)(paging_read_cr3() + hhdm);
|
||||||
|
u64 pml4e = pml4[(vaddr >> 39) & 0x1FF];
|
||||||
|
if (!(pml4e & PTE_PRESENT)) return 0;
|
||||||
|
|
||||||
|
u64 *pdpt = (u64 *)((pml4e & PTE_ADDR_MASK) + hhdm);
|
||||||
|
u64 pdpte = pdpt[(vaddr >> 30) & 0x1FF];
|
||||||
|
if (!(pdpte & PTE_PRESENT)) return 0;
|
||||||
|
if (pdpte & PTE_PS) return 3;
|
||||||
|
|
||||||
|
u64 *pd = (u64 *)((pdpte & PTE_ADDR_MASK) + hhdm);
|
||||||
|
u64 pde = pd[(vaddr >> 21) & 0x1FF];
|
||||||
|
if (!(pde & PTE_PRESENT)) return 0;
|
||||||
|
if (pde & PTE_PS) return 2;
|
||||||
|
|
||||||
|
u64 *pt = (u64 *)((pde & PTE_ADDR_MASK) + hhdm);
|
||||||
|
u64 pte = pt[(vaddr >> 12) & 0x1FF];
|
||||||
|
if (!(pte & PTE_PRESENT)) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分配一个清零的 4K 页作为页表页,返回物理地址;失败返回 0 */
|
||||||
|
static u64 paging_alloc_table(void) {
|
||||||
|
dkm_dma_buffer buf;
|
||||||
|
if (dma_alloc_pages(1, PAGE_4K, 0, &buf) != 0) return 0;
|
||||||
|
return buf.phys;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int paging_map_mmio_impl(u64 phys, u64 size) {
|
||||||
|
u64 hhdm = paging_hhdm();
|
||||||
|
if (!hhdm || !size) return -1;
|
||||||
|
|
||||||
|
u64 begin = phys & ~(PAGE_4K - 1ULL);
|
||||||
|
u64 end = (phys + size + PAGE_4K - 1ULL) & ~(PAGE_4K - 1ULL);
|
||||||
|
u64 mapped = 0;
|
||||||
|
u64 skipped = 0;
|
||||||
|
|
||||||
|
for (u64 p = begin; p < end; p += PAGE_4K) {
|
||||||
|
u64 v = hhdm + p;
|
||||||
|
|
||||||
|
if (paging_is_mapped_impl(v)) {
|
||||||
|
skipped++; /* Limine 已覆盖(含 1G/2M 大页) */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 *pml4 = (u64 *)(paging_read_cr3() + hhdm);
|
||||||
|
u64 idx4 = (v >> 39) & 0x1FF;
|
||||||
|
if (!(pml4[idx4] & PTE_PRESENT)) {
|
||||||
|
u64 t = paging_alloc_table();
|
||||||
|
if (!t) { log_error("[PAGING] alloc PDPT failed"); return -2; }
|
||||||
|
pml4[idx4] = t | PTE_PRESENT | PTE_RW;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 *pdpt = (u64 *)((pml4[idx4] & PTE_ADDR_MASK) + hhdm);
|
||||||
|
u64 idx3 = (v >> 30) & 0x1FF;
|
||||||
|
if (pdpt[idx3] & PTE_PRESENT) {
|
||||||
|
if (pdpt[idx3] & PTE_PS) { skipped++; continue; } /* 1G 大页已覆盖 */
|
||||||
|
} else {
|
||||||
|
u64 t = paging_alloc_table();
|
||||||
|
if (!t) { log_error("[PAGING] alloc PD failed"); return -3; }
|
||||||
|
pdpt[idx3] = t | PTE_PRESENT | PTE_RW;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 *pd = (u64 *)((pdpt[idx3] & PTE_ADDR_MASK) + hhdm);
|
||||||
|
u64 idx2 = (v >> 21) & 0x1FF;
|
||||||
|
if (pd[idx2] & PTE_PRESENT) {
|
||||||
|
if (pd[idx2] & PTE_PS) { skipped++; continue; } /* 2M 大页已覆盖 */
|
||||||
|
} else {
|
||||||
|
u64 t = paging_alloc_table();
|
||||||
|
if (!t) { log_error("[PAGING] alloc PT failed"); return -4; }
|
||||||
|
pd[idx2] = t | PTE_PRESENT | PTE_RW;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 *pt = (u64 *)((pd[idx2] & PTE_ADDR_MASK) + hhdm);
|
||||||
|
u64 idx1 = (v >> 12) & 0x1FF;
|
||||||
|
pt[idx1] = p | PTE_PRESENT | PTE_RW | PTE_PCD | PTE_PWT;
|
||||||
|
__asm__ volatile("invlpg (%0)" :: "r"(v) : "memory");
|
||||||
|
mapped++;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info("[PAGING] map_mmio done");
|
||||||
|
log_hex64("[PAGING] phys=", phys);
|
||||||
|
log_hex64("[PAGING] size=", size);
|
||||||
|
log_hex64("[PAGING] 4K pages mapped=", mapped);
|
||||||
|
log_hex64("[PAGING] pages already covered=", skipped);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const dkm_mmio_api g_mmio_api = {
|
||||||
|
.is_mapped = paging_is_mapped_impl,
|
||||||
|
.map_mmio = paging_map_mmio_impl,
|
||||||
|
.cr3 = paging_read_cr3
|
||||||
|
};
|
||||||
|
|
||||||
|
const dkm_mmio_api *paging_get_api(void) {
|
||||||
|
return &g_mmio_api;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
mm/paging.o: mm\paging.c include\utsm\paging.h include\utsm\types.h \
|
||||||
|
include\utsm\dma.h include\utsm\log.h mm\..\arch\x86_64\limine.h
|
||||||
|
include\utsm\paging.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\dma.h:
|
||||||
|
include\utsm\log.h:
|
||||||
|
mm\..\arch\x86_64\limine.h:
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
pckc/pckc.o: pckc\pckc.c include\utsm\pckc.h include\utsm\types.h \
|
||||||
|
include\utsm\config.h include\utsm\uuid.h include\utsm\crypto.h \
|
||||||
|
include\utsm\segment.h include\utsm\status.h include\utsm\capability.h \
|
||||||
|
include\utsm\dirty.h
|
||||||
|
include\utsm\pckc.h:
|
||||||
|
include\utsm\types.h:
|
||||||
|
include\utsm\config.h:
|
||||||
|
include\utsm\uuid.h:
|
||||||
|
include\utsm\crypto.h:
|
||||||
|
include\utsm\segment.h:
|
||||||
|
include\utsm\status.h:
|
||||||
|
include\utsm\capability.h:
|
||||||
|
include\utsm\dirty.h:
|
||||||
@@ -48,7 +48,7 @@ typedef struct {
|
|||||||
const image_file_header *file_hdr;
|
const image_file_header *file_hdr;
|
||||||
const void *optional_hdr;
|
const void *optional_hdr;
|
||||||
int is_pe32_plus;
|
int is_pe32_plus;
|
||||||
u32 image_base; /* 32位截断的加载基址(PE32+ 也截断用于 RVA 计算) */
|
u64 image_base; /* 链接时首选基址(PE32+ 为 64 位,如 0x140000000,必须全宽保留) */
|
||||||
u32 size_of_image;
|
u32 size_of_image;
|
||||||
u32 size_of_headers;
|
u32 size_of_headers;
|
||||||
u32 entry_rva;
|
u32 entry_rva;
|
||||||
@@ -75,7 +75,7 @@ static int parse_nt_headers(const void *pe_data, u64 size, pe_nt_info *out) {
|
|||||||
if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
||||||
const image_optional_header64 *oh = (const image_optional_header64 *)opt;
|
const image_optional_header64 *oh = (const image_optional_header64 *)opt;
|
||||||
out->is_pe32_plus = 1;
|
out->is_pe32_plus = 1;
|
||||||
out->image_base = (u32)oh->image_base;
|
out->image_base = oh->image_base; /* PE32+: u64 全宽,禁止截断(重定位 delta 依赖高位) */
|
||||||
out->size_of_image = oh->size_of_image;
|
out->size_of_image = oh->size_of_image;
|
||||||
out->size_of_headers = oh->size_of_headers;
|
out->size_of_headers = oh->size_of_headers;
|
||||||
out->entry_rva = oh->address_of_entry_point;
|
out->entry_rva = oh->address_of_entry_point;
|
||||||
@@ -98,7 +98,7 @@ static int parse_nt_headers(const void *pe_data, u64 size, pe_nt_info *out) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ---- 应用基址重定位 ---- */
|
/* ---- 应用基址重定位 ---- */
|
||||||
static void apply_relocations(u8 *image, u32 image_size, u64 image_base_actual, u32 image_base_preferred,
|
static void apply_relocations(u8 *image, u32 image_size, u64 image_base_actual, u64 image_base_preferred,
|
||||||
const image_data_directory *reloc_dir,
|
const image_data_directory *reloc_dir,
|
||||||
const image_section_header *sections, int nsec,
|
const image_section_header *sections, int nsec,
|
||||||
const void *pe_data, u64 pe_size, int is_pe32_plus) {
|
const void *pe_data, u64 pe_size, int is_pe32_plus) {
|
||||||
@@ -313,7 +313,7 @@ int pe_load_image(const void *pe_data, u64 size, pe_image_info *out) {
|
|||||||
|
|
||||||
/* 基址重定位 */
|
/* 基址重定位 */
|
||||||
u64 actual_base = (u64)image;
|
u64 actual_base = (u64)image;
|
||||||
u32 preferred_base = nt.image_base;
|
u64 preferred_base = nt.image_base;
|
||||||
/* image 缓冲区按 RVA 排列(image[0]=RVA 0),重定位写入 image 内 */
|
/* image 缓冲区按 RVA 排列(image[0]=RVA 0),重定位写入 image 内 */
|
||||||
/* 注意:apply_relocations 直接操作 image 缓冲区,delta = actual_base - preferred_base */
|
/* 注意:apply_relocations 直接操作 image 缓冲区,delta = actual_base - preferred_base */
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user