vbus机制分析

2019-07-14 02:46发布

 

1.概述

moe通过解析uvmm.tmgr来启动io进程和vm虚拟机进程。在uvmm.tmgr脚本中会将io.cfg和vm_pass.vbus文件(双虚拟机会有两个vbus文件)作为参数传递给io进程解析;另外,会将与vm虚拟机对应的vbus的能力权限传递给vm虚拟机。Uvmm在初始化时,会获取对应的vbus能力权限,通过IPC与io server服务端通信来获取整个vbus总线设备,并保存在本地变量_devices中。在uvmm解析dtb文件,并尝试与vbus总线设备进行匹配时,还会通过IPC与io server进行交互,来获取设备资源、兼容性等信息。下面分三个小节分别讲述io.cfg文件、vm_pass.vbus文件的解析过程以及uvmm中vbus总线初始化。

2.构建物理设备树(io.cfg文件解析)

Io进程中构建的物理设备树,是一个两层结构,如下图: 根节点是一个名字为“System Bus”的Hw::Root_bus类型的设备,其子节点为Hw::Device类型的各个设备,如VGIC、VSPI等。每个子节点又包含一系列的属性和资源。相关类图如下: 根节点类Hw::Root_bus继承自Hw::Device,其在构造函数里会初始化根节点的irq、mmio及io资源,同时注册各种资源类型的request和alloc方法。 子节点是Hw::Device类型,它从Device_tree_mixin类继承了设备树相关成员及方法(包括增加子节点、获取next节点、遍历设备树等),从Generic_device类继承了属性(Property)、资源(Resource)的相关方法及成员。 Device_factory是一个工厂类,用于收集各种设备创建的方法,保存在静态变量_factories中,其定义如下: typedef cxx::Avl_map Name_map; static Name_map _factories; Device_factory_t类继承自Device_factory,用于注册工厂方法,并提供了create接口用于创建设备。目前,zeos中注册了如下工厂方法: static Device_factory_t __hw_pf_factory("Device"); static Hw::Device_factory_t __hw_pf_factory("Gpio_bcm2835_chip"); static Hw::Device_factory_t __hw_scm_factory("Scm_omap"); static Hw::Device_factory_t               __hw_pci_root_bridge_factory("Pci_iomem_root_bridge");   static Hw::Device_factory_t>        __hw_gpio_omap35x_factory("Gpio_omap35x_chip"); static Hw::Device_factory_t>        __hw_gpio_omap44x_factory("Gpio_omap44x_chip"); static Hw::Device_factory_t>        __hw_gpio_omap54x_factory("Gpio_omap54x_chip");   物理设备树的构造过程主要分为以下几步:
  1. 构建根节点“System Bus”
Io进程会解析io.cfg文件,通过解释执行其中的lua代码来构建一颗完整的物理设备树。其中根节点的构建是通过Io.system_bus()函数实现的。其调用关系如下: Io.system_bus()——》_wrap_system_bus——》(Hw::Device *)system_bus()——》Hw::Root_bus *hw_system_bus()——》Hw::Root_bus _sb("System Bus")
  1. 依次创建各子节点,如VGIC、VSPI等
子节点的构建则通过Io.Hw.Device函数。系统中并没有直接定义Io.Hw.Device函数,而是定义了table——Io.Hw = {},并把table(Io.Hw)的元表的__index属性设置为function,如下表: Io.Hw = {} setmetatable(Io.Hw, { __index = function (self, t)   return function (data)     local b = check_device(Io.Hw_dev_factory_create(t), 3, "could not create device: " .. t)  //创建设备     if type(data) == "function" then       add_children(b, data)     //添加叶节点     end     return b                  //返回带资源信息的Device节点   end end}) 这样,Device会作为实参传给t;更进一步使用“Io.Hw.Device(function()”调用时,Device的参数function会作为其内部闭包“function (data)”的data参数传入。 设备创建 调用关系为:Io.Hw_dev_factory_create——》_wrap_Hw_dev_factory_create——》 _wrap_Hw_dev_factory_create__SWIG_1——》Hw::Device_factory::create(“Device”),最终会调用静态注册好的工厂——“__hw_pf_factory”创建好设备并赋值给本地变量b。 添加叶节点 通过调用add_children函数(同Io.Dt.add_children函数),将设备的Property和Resource 填充到本地变量d。然后,调用Io.Dt.add_device_data函数将d添加到上一步创建的设备的叶节点中。
  1. 将各子节点加入根节点“System Bus”
通过调用Io.Dt.add_children函数,将各子节点(VGIC、VSPI等)加入根节点“System Bus”。这一步的lua脚本中还涉及到一个lua的关键语法“upvalue”的概念,详请参考我的另一个文档——“vbus机制之lua代码注释.docx”。 至此,物理设备树已经构建完成。

3.Vbus虚拟设备树构建(vm_pass.vbus文件解析)

Vbus虚拟设备树可以有1条或者多条,其子节点通过字符串(可以提供多个)与物理设备树的hid属性进行匹配,返回所有匹配的设备,加入根节点。一个字符串同样可以匹配多个设备。其结构图大致如下: 注意:vm_pass.vbus中定义的子节点名(如VGIC、VSPI等)同io.cfg中定义的子节点名(如VGIC、VSPI等)一样,都只是别名,并不必须存在一一对应关系。而真正的对应关系是通过hid属性匹配的结果。 Vbus虚拟设备树创建过程涉及的类图如下: 根节点类Virtual_sbus继承自Vi::System_bus,其主要作用是通过物理设备树构建一个静态的Platform_control类实例pfc,在将pfc传给Vi::System_bus的构造函数,完成自身的构造。Platform_control类是一个电源管理类,这里不讨论。 Vbus的核心类是Vi::System_bus,它继承了众多类的属性,如Device(虚拟设备类,包含了设备和树节点的相关属性和方法)、Dev_feature(提供设备feature的虚拟接口)、L4::Epiface_t(IPC相关接口类,这里不讨论)、Inhibitor_provider(为vbus之间通信提供接口)和Vbus_event_source(vbus事件机制)。另外,它还包含几个内部类:Root_resource_factory(提供创建root资源对象的工厂)、Root_resource_factory_t(继承自Root_resource_factory,实现create方法)和Res_cmp(资源比较)。 Dev_factory类为一个工厂类,其Name_map类型的静态变量_name_map用于搜集创建Vbus的根节点的工厂方法。其两个子类,则用于注册工厂方法,并分别实现其do_match和vcreate方法。目前系统中静态变量 _name_map中定义的工厂有: static Vi::Dev_factory_t __sb_root_factory("System_bus"); static Dev_factory_t __gpio_factory; static Dev_factory_t __pci_dummy_factory("PCI_dummy_device"); static Dev_factory_t __pci_to_pci_factory("PCI_PCI_bridge"); static Dev_factory_t __pci_root_factory("PCI_bus"); static Dev_factory_t __ghwdf;   Vbus虚拟设备树创建步骤大致如下:
  1. 创建根节点“System_bus
在vm_pass.vbus脚本文件中,通过Io.Vi.System_bus(function()…end)函数创建根节点。Io.Vi.System_bus的定义同样使用了元表,参见io.cfg解析过程: Io.Vi = {} setmetatable(Io.Vi, { __index = function (self, t)  //System_bus作为t传入,其后“function()…end”作为data传入   return function (data)     local b = Io.Vi_dev_factory_create(t)  //创建虚拟设备(System_bus)     if type(data) == "function" then       add_children(b, data)             //添加子节点     elseif type(data) == "table" then       set_dev_data(b, data)     end     return b   end end}) 其调用关系为:Io.Vi_dev_factory_create(System_bus)——》_wrap_Vi_dev_factory_create——》_wrap_Vi_dev_factory_create__SWIG_0——》Vi::Dev_factory::create(System_bus)。此处create函数会调用系统中静态注册的工厂—— __sb_root_factory”,创建Virtual_sbus类型的根节点。
  1. 创建子节点如VGIC、VSPI等
这一步会遍历io.cfg解析过程中生成的硬件设备树,调用其match_cid方法,找到与提供的字符串(如"arm-gicc")匹配的所有设备(如果有多个匹配,则将其命名为类似VGIC[1]、VGIC[2]),并生成一个table返回。详细lua流程可参见“vbus机制之lua代码注释.docx”。
  1. 将各子节点加入根节点“System_bus
调用add_children将各子节点依次加入到根节点。
  1. 将vbus注册到server列表中
Vbus注册到server列表后就可以为uvmm提供服务了。

4.uvmm中vbus总线初始化:

Uvmm中在解析dtb文件之前会调用create_default_devices函数,创建一些默认设备,其中就包括vbus的初始化,主要步骤如下:
  1. 获取vbus能力权限
  2. 创建vbus对象,构造函数中会通过IPC获取vbus所有设备,并保存在本地变量_devices中。
在uvmm中根据dtb创建设备流程中,查找匹配设备会调用Virt_bus::find_unassigned_dev方法,还会涉及与Io Server的IPC交互过程。