0%

背 景

  • Read the fucking source code! –By 鲁迅
  • A picture is worth a thousand words. –By 高尔基

说明:

  1. Kernel 版本:4.14
  2. ARM64 处理器
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

先回顾一下 PCIe 的架构图:

image-20240416111001038

  • 本文将讲 PCIe Host 的驱动,对应为Root Complex部分,相当于 PCI 的Host Bridge部分;
  • 本文会选择 Xilinx 的nwl-pcie来进行分析;
  • 驱动的编写整体偏简单,往现有的框架上套就可以了,因此不会花太多笔墨,点到为止;

2. 流程分析

  • 但凡涉及到驱动的分析,都离不开驱动模型的介绍,驱动模型的实现让具体的驱动开发变得更容易;
  • 所以,还是回顾一下上篇文章提到的驱动模型:Linux 内核建立了一个统一的设备模型,分别采用总线、设备、驱动三者进行抽象,其中设备与驱动都挂在总线上,当有新的设备注册或者新的驱动注册时,总线会去进行匹配操作(match函数),当发现驱动与设备能进行匹配时,就会执行 probe 函数的操作;
阅读全文 »

背景

  • Read the fucking source code! –By 鲁迅
  • A picture is worth a thousand words. –By 高尔基

说明:

  1. KVM 版本:5.9.1
  2. QEMU 版本:5.0.0
  3. 工具:Source Insight 3.5, Visio
  4. 文章同步在博客园:https://www.cnblogs.com/LoyenWang/

1. 概述

先从操作系统的角度来看一下 timer 的作用吧:

image-20240416110453083

通过 timer 的中断,OS 实现的功能包括但不局限于上图:

  • 定时器的维护,包括用户态和内核态,当指定时间段过去后触发事件操作,比如 IO 操作注册的超时定时器等;
  • 更新系统的运行时间、wall time 等,此外还保存当前的时间和日期,以便能通过time()等接口返回给用户程序,内核中也可以利用其作为文件和网络包的时间戳;
  • 调度器在调度任务分配给 CPU 时,也会去对 task 的运行时间进行统计计算,比如 CFS 调度,Round-Robin 调度等;
  • 资源使用统计,比如系统负载的记录等,此外用户使用 top 命令也能进行查看;

timer 就像是系统的脉搏,重要性不言而喻。ARMv8 架构处理器提供了一个 Generic Timer,与 GIC 类似,Generic Timer 在硬件上也支持了虚拟化,减少了软件模拟带来的 overhead。

阅读全文 »

背景

  • Read the fucking source code! –By 鲁迅
  • A picture is worth a thousand words. –By 高尔基

说明:

  1. KVM 版本:5.9.1
  2. QEMU 版本:5.0.0
  3. 工具:Source Insight 3.5, Visio
  4. 文章同步在博客园:https://www.cnblogs.com/LoyenWang/

1. 概述

  • 本文围绕 ARMv8 CPU 的虚拟化展开;
  • 本文会结合 Qemu + KVM 的代码分析,捋清楚上层到底层的脉络;
  • 本文会提供一个 Sample Code,用于类比 Qemu 和 KVM 的关系,总而言之,大同小异,大题小做,大道至简,大功告成,大恩不言谢;

先来两段前戏。

1.1 CPU 工作原理

AI 的世界,程序的执行不再冰冷,CPU 对a.out说,hello啊,world已经ok啦,下来return吧!

既然要说 CPU 的虚拟化,那就先简要介绍一下 CPU 的工作原理:

阅读全文 »

背景

  • Read the fucking source code! –By 鲁迅
  • A picture is worth a thousand words. –By 高尔基

说明:

  1. KVM 版本:5.9.1
  2. QEMU 版本:5.0.0
  3. 工具:Source Insight 3.5, Visio
  4. 文章同步在博客园:https://www.cnblogs.com/LoyenWang/

1. 概述

  • 从本文开始将开始source code的系列分析了;
  • KVM作为内核模块,可以认为是一个中间层,向上对接用户的控制,向下对接不同架构的硬件虚拟化支持;
  • 本文主要介绍体系架构初始化部分,以及向上的框架;

image-20240416110125240

2. KVM 初始化

  • 贝多芬曾经说过,一旦你找到了代码的入口,你就扼住了软件的咽喉;
  • 我们的故事,从module_init(arm_init)开始,代码路径:arch/arm64/kvm/arm.c

老规矩,先来一张图(图片中涉及到的红色框函数,都是会展开描述的):

阅读全文 »

本文主要分析 linux kernel 中 SMMUv3 的代码 (drivers/iommu/arm-smmu-v3.c)
linux kernel 版本是 linux 5.7, 体系结构是 aarch64

smmu 的位置

SMMU 的作用是把 CPU 提交给设备的 VA 地址,直接作为设备发出的地址,变成正确的物理地址,访问到物理内存上。
和 mmu 不同的是,一个 smmu 可以有多个设备连着,他们的页表不可能复用,SMMU 用 stream id 作区分。
一个设备有多个进程,所以 smmu 单元也要支持多页表,smmu 使用 substream id 区分多进程的页表。

smmu 的设备节点定义

在讨论 smmu 的代码前,先看下 smmu 的设备节点是怎么定义的:
Example:

1
2
3
4
5
6
7
8
9
10
11
12
smmu@2b400000 {
compatible = "arm,smmu-v3";
reg = <0x0 0x2b400000 0x0 0x20000>;
interrupts = <GIC_SPI 74 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 75 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 77 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 79 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "eventq", "priq", "cmdq-sync", "gerror";
dma-coherent;
#iommu-cells = <1>;
msi-parent = <&its 0xff0000>;
};

compatible: 用于匹配 smmu 驱动。
reg:smmu 设备的物理基地址。
interrupts: 描述与中断名称对应的 smmu 中断源,上述分别对应中断类型,中断号以及中断触发方式。
interrupt-names: 中断名称。
eventq,当 event queue 从空变为非空状态时上报中断。
priq, 当 pri queue 从空变为非空状态时上报中断。
cmdq-sync, command queue 中 CMDQ_SYNC 命令完成时产生中断。
gerror,event 记录到 event queue 过程中产生的错误会记录在 SMMU_GERROR 寄存器中,并产生中断。
combined,组合中断,需要硬件支持,如果提供了组合中断,则将优先使用组合中断。
dma-coherent:表示设备通过 smmu 进行的 DMA 访问是否 cache coherent 的,假设 DMA 把外设的数据搬运到内存的某个位置,cpu 去读那段地址,因为 cache 命中了,读到的还是旧的值,这就是 cache 的不 coherent。
#iommu-cells: 一个 cell 代表一个 streamid, smmu-v3 必须定义为 1。
msi-parent:指定 msi 中断控制器。

SMMU 结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct arm_smmu_domain {
struct arm_smmu_device *smmu;
struct mutex init_mutex; /* Protects smmu pointer */

struct io_pgtable_ops *pgtbl_ops;
bool non_strict;
atomic_t nr_ats_masters;

enum arm_smmu_domain_stage stage;
union {
struct arm_smmu_s1_cfg s1_cfg;
struct arm_smmu_s2_cfg s2_cfg;
};

struct iommu_domain domain;

struct list_head devices;
spinlock_t devices_lock;
};
阅读全文 »

1. RISC-V: Add initial skeletal KVM support

This patch adds initial skeletal KVM RISC-V support which has:
1. A simple implementation of arch specific VM functions
   except kvm_vm_ioctl_get_dirty_log() which will implemeted
   in-future as part of stage2 page loging.
2. Stubs of required arch specific VCPU functions except
   kvm_arch_vcpu_ioctl_run() which is semi-complete and
   extended by subsequent patches.
3. Stubs for required arch specific stage2 MMU functions.

这个补丁增加了初始的KVM RISC-V 框架
它具有。

  1. 除了kvm_vm_ioctl_get_dirty_log()之外,一个简单的arch特定虚拟机函数的实现,它将作为第二阶段页面记录的一部分在未来实施。
  2. 除了kvm_arch_vcpu_ioctl_run()是半完全的并由后续补丁扩展外,所需的特定arch VCPU函数的stub。
  3. 所需的arch 特定第二阶段MMU功能的 stub 。

2. RISC-V: KVM: Implement VCPU create, init and destroy functions

This patch implements VCPU create, init and destroy functions
required by generic KVM module. We don't have much dynamic
resources in struct kvm_vcpu_arch so these functions are quite
simple for KVM RISC-V.

这个补丁实现了通用KVM模块所需的VCPU创建、启动和销毁功能。我们在结构kvm_vcpu_arch中没有很多动态资源,所以这些函数对于KVM RISC-V来说非常简单。

3. RISC-V: KVM: Implement VCPU interrupts and requests handling

This patch implements VCPU interrupts and requests which are both
asynchronous events.

The VCPU interrupts can be set/unset using KVM_INTERRUPT ioctl from
user-space. In future, the in-kernel IRQCHIP emulation will use
kvm_riscv_vcpu_set_interrupt() and kvm_riscv_vcpu_unset_interrupt()
functions to set/unset VCPU interrupts.

Important VCPU requests implemented by this patch are:
KVM_REQ_SLEEP       - set whenever VCPU itself goes to sleep state
KVM_REQ_VCPU_RESET  - set whenever VCPU reset is requested

The WFI trap-n-emulate (added later) will use KVM_REQ_SLEEP request
and kvm_riscv_vcpu_has_interrupt() function.

The KVM_REQ_VCPU_RESET request will be used by SBI emulation (added
later) to power-up a VCPU in power-off state. The user-space can use
the GET_MPSTATE/SET_MPSTATE ioctls to get/set power state of a VCPU.

这个补丁实现了VCPU中断和请求,它们都是异步事件。
VCPU中断可以使用用户空间的KVM_INTERRUPT ioctl进行设置/取消。
在未来,内核IRQCHIP仿真将使用kvm_riscv_vcpu_set_interrupt()和kvm_riscv_vcpu_unset_interrupt()函数来设置/取消VCPU中断。
这个补丁实现的重要VCPU请求是。
KVM_REQ_SLEEP -每当VCPU本身进入睡眠状态时设置
KVM_REQ_VCPU_RESET -每当VCPU复位时设置
WFI trap-n-emulate(稍后添加)将使用KVM_REQ_SLEEP请求和kvm_riscv_vcpu_has_interrupt()函数。
KVM_REQ_VCPU_RESET请求将被SBI仿真使用(稍后添加),以使VCPU处于断电状态。
用户空间可以使用GET_MPSTATE/SET_MPSTATE ioctls来获取/设置VCPU的电源状态。

阅读全文 »

虚拟化技术概述

虚拟机特性:

  • 指令模拟 (guest 和 host 可以是不同的isa)
  • 本地指令直接执行( guest 和 host isa是相同的)

系统资源虚拟化:

  • cpu虚拟化
  • 内存虚拟化
  • io 虚拟化

常见的虚拟机软件:

  • vmware
  • virtual box
  • xen
  • linux-kvm qemu

image-20240416111641703

Hypervisor Virtual Machine Manager(VMM) 的功能

  • 控制所有的系统资源(CPU 内存 网络 存储等)
  • 创建虚拟机并分配响应的资源
  • 管理虚拟机的生命周期
阅读全文 »

zicbom 提供的 cache 指令

Zicbom 指令 作用
CBO.FLUSH l1 & l2 cache 内容刷新到主存, 并将 cache line 状态置为无效, 下次访问该地址时, 会从主存 load 回来
CBO.CLEAN cache line 处于修改状态, 会将 cache line 的内容刷新回主存, 不改变该 cache line 的状态
CBO.INVAL 将 cache line 置为无效, 再次访问地址时, 会从主存上 load 回来

mcache

缓存操作用于控制操作,如初始化、失效、驱逐等。以下是缓存操作的简要描述:

  1. 索引写回失效(Index Writeback Invalidate):如果指定索引处的缓存行状态为有效且脏,该行将被写回到由缓存标记指定的内存地址。完成该操作后,缓存行的状态将设置为无效。如果行有效但不脏,则将行的状态设置为无效。
  2. 命中失效(Hit Invalidate):如果缓存包含指定地址,该缓存行的状态将设置为无效。
  3. 命中写回失效(Hit Writeback Inv):如果缓存包含指定地址且该行有效且脏,该行的内容将写回到主存。完成该操作后,缓存行的状态将设置为无效。如果行有效但不脏,则将行的状态设置为无效。
  4. 命中写回(Hit Writeback):如果缓存包含指定地址且该行有效且脏,该行的内容将写回到主存。完成操作后,行的状态保持有效,但脏状态被清除。
  5. 获取并锁定(Fetch And Lock):如果缓存包含指定地址,锁定该行。如果缓存不包含指定地址,从主存重新填充该行,然后锁定该行。 (只对 l2 cache 有效)

对比

对应于 cbo 指令:

  • mcache Hit Invalidate 与 CBO.INVAL 等效
  • mcache Hit Writeback 与 CBO.CLEAN 等效
  • mcache Hit WriteBack Inv 与 CBO.FLUSH 等效

不同:

  1. cbo 的三条指令针对的是所有 cache (l1 l2)等,
    mcache 的这三条指令粒度更细, 需要分别指定 l1 和 l2 进行操作
    如:
    Hit Writeback 有 Hit_Writeback_D 和 Hit_Writeback_S 两个操作 type, 分别用来 writeback l1 cache 和 l2 cache

  2. CBO 的三条指令是可以在 S-mode 及 M-mode 下执行的
    而 mcache 指令只能在 M-mode 下执行, S-mode 需要 cache 相关操作只能通过陷入到 M-mode 的 opensbi 来完成.

  3. mcache 另外提供了 index 操作, 主要方便用来回写并清空所有 l1 l2 cache line(不需要知道具体地址, 只需要按 index 覆盖 cacheline 内存范围即可)
    另外还提供了 Index invalidate icache Hit invalidate icache 和 fill icache 以及 fetch & lock l2 cache 的行为.

阅读全文 »