vbus机制之lua代码注释

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gaojy19881225/article/details/82053800

在modules.list中带uvmm的entry会在moe中启动uvmm.tmgr。

entry uvmm-zcu102

kernel fiasco-serial_esc

roottask moe rom/uvmm.tmgr

module uvmm

module l4re

module tmgr

module hello

module cons

module io

module virt-zcu102.dtb

module[shell] echo $SRC_BASE_ABS/pkg/uvmm/configs/vmm.lua

module[shell] echo $SRC_BASE_ABS/pkg/uvmm/configs/bsp/plat-zcu102/io.cfg

module[shell] echo $SRC_BASE_ABS/pkg/uvmm/configs/bsp/plat-zcu102/vm_pass.vbus

module[shell] echo $SRC_BASE_ABS/pkg/uvmm/configs/bsp/plat-zcu102/uvmm.tmgr

module Image_zc102_ubuntu

 

而这个uvmm.tmgr就是io进程(@1)和uvmm进程(@2)的入口。如下:

……

-- virtual IO busses

local io_busses = {

  vm_pass = 1,

}

……

vmm.start_io(io_busses, "rom/io.cfg");  //@1

vmm.start_vm{                      //@2

  id=1,

  mem=1024,

  kernel="rom/bzImage",

  vbus=io_busses.vm_pass,

  fdt="rom/virt-pc.dtb",

……

};

 

 

vmm.start_iovmm.start_vm定义在vmm.lua中

function start_io(busses, opts)

……

  for k, v in pairs(busses) do

    local c = l:new_channel();

    busses[k] = c              //创建一个ipc通道赋值给vm_pass

    caps[k] = c:svr();

    files = files .. " rom/" .. k .. ".vbus";   //files赋值为“rom/vm_pass.vbus”

  end

 

  return l:start({

    log = { "io", "red" },

    caps = caps

  }, "rom/io " .. opts .. files)  //启动进程“rom/io”,并将opts("rom/io.cfg")和files(“rom/vm_pass.vbus”)作为其参数

End

function start_vm(options)

……

  local vbus    = options.vbus;    //本地变量vbus赋……值为“vm_pass”

 

  local caps = {

    net  = vnet;

    vbus = vbus;               //本地变量caps(其中包含vbus)

……

  };

……

  local opts = {

    --log  = l.log_fab:create(ZRE.Proto.Log, "vm" .. nr, "w", keyb_shortcut);

    log = ZRE.Env.log:m("rws");

    caps = caps;               //本地变量opts(包含caps)

  };

 

  set_sched(opts, prio, cpus);

  return l:startv(opts, "rom/uvmm", table.unpack(cmdline));  //将opts作为能力权限,启动 "rom/uvmm"进程,并将处理过后的cmdline作为参数传给uvmm

end

 

综上,在io进程中会对“io.cfg”和“vm_pass.vbus”进行解析;在uvmm进程中会通过获取vm_pass的能力权限,进行vbus资源申请。

 

下面分别从io进程中vbus总线的构建和uvmm中对vbus总线资源的申请两方面进行vbus机制的分析。

 

io进程中vbus总线的构建

Io进程在进行了lua运行环境初始化及其他一些初始化工作后,就会依次读入传入的两个参数(lua文件——io.cfg和vm_pass.vbus),并进行解析。

 

io.cfg文件解析

io.cfg文件结构如下:

Io.Dt.add_children(Io.system_bus(), function()

  VGIC = Io.Hw.Device(function()

    Property.hid = "arm-gicc"

    Resource.reg0 = Io.Res.mmio(0xf9060000, 0xf907ffff);

    Property.flags = Io.Hw_device_DF_multi_vbus;

  end)

……

end)

Lua脚本中Io.Dt.add_children定义如下:

function Io.Dt.add_children(parent, bus_func)

  check_device(parent, 2);

  if type(bus_func) == "function" then

    local d = {

      Property = {},

      Resource = {},

      Child = {}

    }

……

debug.upvaluejoin(bus_func, env_upval, function() return d end, 1) // 后面详解

    bus_func();    //此处会逐个执行上表中“Io.Hw.Device(function()”程序段

    Io.Dt.add_device_data(parent, d)  //添加到父节点

  end

  return parent;

end

 

debug.upvaluejoin(bus_func, env_upval, function() return d end, 1):函数解释如下

debug.upvaluejoin (f1, n1, f2, n2)

让 Lua 闭包 f1 的第 n1 个上值 引用 Lua 闭包 f2 的第 n2 个上值。

这里bus_func的env_upval指的是“_ENV”,而后面那个function的第一个upvalue指的是前面定义的local d;而lua中变量的定义如a=1,会被编译为_ENV.a=1,这里讲“_ENV”引用d,所以编译会变为d.a=1;所以bus_func中所有的变量定义都会放入d这个table里。

 

Io.Hw定义如下表,当使用“Io.Hw.Device”调用时,由于Io.Hw元表的__index设置为function属性,所以Device会作为t的实参传入;更进一步使用“Io.Hw.Device(function()”调用时,Device的参数function会作为其内部闭包“function (data)”的data参数传入。

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})

 

创建设备

函数Hw_dev_factory_create的定义可以从由SWIG工具生成的代码文件——lua_glue.swg.cc中找到:

static swig_lua_method swig_SwigModule_methods[]= {

    { "swig_class",swig_class},

    { "swig_instance_of",swig_instance_of},

    { "Resource_str_to_id", _wrap_Resource_str_to_id},

    { "Vi_dev_factory_create", _wrap_Vi_dev_factory_create},

    { "Hw_dev_factory_create", _wrap_Hw_dev_factory_create},

    { "system_bus", _wrap_system_bus},

    { "dump_devs", _wrap_dump_devs},

    { "add_vbus", _wrap_add_vbus},

    {0,0}

};

所以调用关系为Hw_dev_factory_create——》_wrap_Hw_dev_factory_create——》_wrap_Hw_dev_factory_create__SWIG_1——》Hw::Device_factory::create,创建好设备(Hw::Device *)赋值给本地变量b。

 

Device *

Device_factory::create(cxx::String const &name)

{

  Name_map::const_iterator i = nm().find(std::string(name.start(), name.end()));

  if (i != nm().end())

    return i->second->create();  //调用注册的子类创建

 

  return 0;

}

 

在hw_device.cc中静态定义了Device的工厂类

static Device_factory_t<Device> __hw_pf_factory("Device")

 

 

添加子节点

local add_children = Io.Dt.add_children

所以,又递归调用Io.Dt.add_children函数。本次会将设备的Property和Resource 填充到本地变量d。然后,调用Io.Dt.add_device_data函数将d添加到上一步创建的设备的子节点中。

function Io.Dt.add_device_data(dev, data)

  local maxi = 0

  for i, v in ipairs(data) do

    handle_device_member(dev, v, i)

    maxi = i

  end

  for k, v in pairs(data) do

    if (type(k) ~= "number") or (k > maxi) then

      handle_device_member(dev, v, k)

    end

  end

end

local function handle_device_member(dev, val, name)

  local vtype = type(val)

  if name == "compatible" then

    ……

      dev:add_cid(val)          //compatible属性,调用“Hw::Device::add_cid”加入设备cid列表

    ……

  elseif name == "Property" then  //Property属性添加,调用Generic_device::property

    ……

  elseif name == "Resource" then  //Resource属性添加

    ……

        v:set_id(k)          //设置ResourceID(regX、irqX)

        dev:add_resource(v)  //调用Generic_device::add_resource

      ……

  elseif name == "Child" then  //如果有Child属性,则处理

    ……

/*下面是一些特殊处理*/

  elseif Io.swig_instance_of(val, "Resource *") then

    ……

  elseif Io.swig_instance_of(val, "Generic_device *") then

    add_child(dev, name, val)   //如果是设备类型,则加入父节点

    return

  elseif vtype == "table" then

    ……

end

 

返回带资源信息的Device节点

这一步将创建好的,带资源信息的Device节点返回,如io.cfg文件中:

VGIC = Io.Hw.Device(function()

    ……

  end)

Io.Hw.Device调用后会将Device节点信息赋值给VGIC 对象。

 

这里有几点需要说明下:

Io.system_bus()——》_wrap_system_bus——》(Hw::Device *)system_bus()——》Hw::Root_bus *hw_system_bus()——》Hw::Root_bus _sb("System Bus")

Io.Res.mmio——》_proxy__wrap_new_Resource——》_wrap_new_Resource——》_wrap_new_Resource__SWIG_0——》(Resource *)new Resource(Resource::Mmio_res)

Io.Res.irq,同上,最后创建资源传入“Resource::Irq_res”参数

Io.Hw_device_DF_multi_vbus:设置设备可供多vbus总线共用,目前只是在没设此flag又多总线引用情况下输出一条warning,没做进一步处理。

 

Io.cfg解析的最后一步是将创建的所有Device加入到root device中:

Io.Dt.add_device_data(parent, d)

此时的d是个什么状态呢?如下:

local d = {

      Property = {},

      Resource = {},

      Child = {},

      VGIC = ……,

      VSPI = ……,

      ……

    }

所以会程序会走到handle_device_member函数特殊处理部分的val为"Generic_device *"场景,然后调用add_child(即Io.Dt.add_child),将各子节点加入vbus根节点:

function Io.Dt.add_child(parent, name, dev, idx)

  parent:add_child(dev)  //调用Hw::Device::add_child

  if dev.plugin and (parent:parent() or swig_equals(parent, Io.system_bus())) then

    dev:plugin()        //调用设备init函数初始化

  end

  if type(name) == "string" then

    if idx ~= nil then

      name = name .. '[' .. idx ..']'

    end

    dev:set_name(name)  //设置设备名字,如VGIC、VSPI

  end

end

 

Io.cfg的解析完成。

 

vm_pass.vbus文件解析

文件结构如下:

Io.add_vbusses

{

  vm_pass = Io.Vi.System_bus(function()

VGIC = wrap(Io.system_bus():match("arm-gicc"))

……

end);

}

 

 

Io.add_vbusses

函数实现如下:

function Io.add_vbusses(busses)

  for name, bus in pairs(busses) do

    Io.add_vbus(name, bus)

  end

  return busses

end

function Io.add_vbus(name, bus)

  bus:set_name(name)

  add_vbus(bus)

end

 

上表中name就是“vm_pass”,bus就是“Io.Vi.System_bus(function()……”的返回值。同上面io.cfg解析一样,Io.Vi设置了元表:

Io.Vi = {}

setmetatable(Io.Vi, { __index = function (self, t)  //System_bus作为t传入,其后“function()……”作为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})

创建虚拟设备(System_bus)

调用关系为Io.Vi_dev_factory_create(t)——》_wrap_Vi_dev_factory_create——》_wrap_Vi_dev_factory_create__SWIG_0——》Vi::Dev_factory::create;create函数如下:

namespace Vi {

Device *

Dev_factory::create(std::string const &_class)     //_class值为“System_bus”

{

  Name_map &m = name_map();

  Name_map::iterator i = m.find(_class);  //在静态变量static Name_map _name_map中查找

  if (i == m.end())

    {

      d_printf(DBG_WARN, "WARNING: cannot create virtual device: '%s'\n",

               _class.c_str());

      return 0;

    }

 

  return i->second->vcreate();   //找到则调用其vcreate函数创建设备

}

}

由于类Dev_factory_t<V_DEV, void>继承自Dev_factory,且其在构造函数中将自己添加到_name_map中,所以系统中定义的Dev_factory_t<V_DEV, void>类型的工厂类都会添加到_name_map中,如下:

template< typename V_DEV >

class Dev_factory_t<V_DEV, void> :  public Dev_factory

{

……

  explicit Dev_factory_t(std::string const &_class) : Dev_factory(0)

  { name_map()[_class] = this; }

……

  Device *vcreate()

  { return new V_dev; }

 

};

在目前版本的zeos中静态变量 _name_map中定义的工厂有:

static Vi::Dev_factory_t<Virtual_sbus> __sb_root_factory("System_bus");

static Dev_factory_t<Gpio, Hw::Gpio_device> __gpio_factory;

static Dev_factory_t<Pci_dummy> __pci_dummy_factory("PCI_dummy_device");

static Dev_factory_t<Pci_to_pci_bridge> __pci_to_pci_factory("PCI_PCI_bridge");

static Dev_factory_t<Pci_vroot> __pci_root_factory("PCI_bus");

static Dev_factory_t<Proxy_dev, Hw::Device> __ghwdf;

所以,会创建一个名字为“System_bus”的Virtual_sbus类型的根设备__sb_root_factory

 

添加子节点

调用Io.Dt.add_children进行子节点添加,Io.Dt.add_children函数分析见io.cfg解析部分。

其中“bus_func”子节点的创建如下:

VGIC = wrap(Io.system_bus():match("arm-gicc"))

function wrap(devs_, filter)

  local devs = devs_

  if type(devs_) ~= "table" then

    devs = { devs_ }

  end

  local v = {}

  for _, d in ipairs(devs) do

    local vd = Io.Vi_dev_factory_create(d)  //创建设备

    if vd then

      if type(filter) == "table" then

        for tag, val in pairs(filter) do

          local res = vd:add_filter_val(tag, val)

          if res < 0 then

            print("ERROR: applying filter expression: "..tag.."=...", debug.traceback(2))

          end

        end

      end

      v[#v + 1] = vd   //将创建的设备加入table——v

    end

  end

  if #v == 1 then  //返回设备或设备table

    return v[1]

  else

    return v

  end

end

wrap函数的参数,调用Io.system_bus()的match方法,在lua脚本里:

match = Io.Dt.match,

function Io.Dt.match(self, ...)  //self就是指调用者——Io.system_bus()

  local cids = {...}  //变参列表,此处(例如第一项)指"arm-gicc"

  for t,v in pairs(Io.Dt.PCI_cc) do  //字符串处理,arm架构字符串没有改变

    for i, cid in ipairs(cids) do

      cids[i] = cid:gsub("(PCI/"..t..")", "PCI/" .. v)  //lua库函数——string.gsub应用;将cid中格式为(PCI/storage)(storage为举例)的字符串替换为PCI/CC_01(CC_01为与storage对应的举例)

    end

  end

 

  local devs = {}

  for d in self:devices(Io.Dt.MAX_DEPTH) do   //遍历设备

    if d:match_cids(table.unpack(cids)) then     //调用match_cids进行匹配

      devs[#devs+1] = d

    end

  end

  return devs   //将所有匹配的设备返回

end

 

遍历设备

self:devices方法在lua脚本中定义为Io.Dt.iterator

function Io.Dt.iterator(dev, max_depth)  //dev为调用者self传入即——Io.system_bus()

  ……     //函数体略过。遍历返回子节点

end

 

调用match_cids进行匹配

d:match_cids方法在lua脚本中定义为Io.Dt.match_cids

function Io.Dt.match_cids(self, ...)

  local r = {}

  for _, v in ipairs{...} do

    if self:match_cid(v) then  //调用设备的match_cid函数

      return true

    end

  end

  return false

end

 self:match_cid——》_wrap_Hw_device_match_cid——》Hw::Device::match_cid

 

将所有匹配的设备返回

这里有点需要注意,就是返回的是所有匹配的设备,即一个compatible字符串可以匹配多个设备。

 

上面就是所有设备节点的创建,最后调用Io.add_vbus

function Io.add_vbus(name, bus)

  bus:set_name(name)   //调用Vi::Device::set_name设置名字为vm_pass

  add_vbus(bus)       //调用add_vbus函数(main.cc中定义)

end

int add_vbus(Vi::Device *dev)

{

  Vi::System_bus *b = dynamic_cast<Vi::System_bus*>(dev);

  if (!b)

    {

      d_printf(DBG_ERR, "ERROR: found non system-bus device as root device, ignored\n");

      return -1;

    }

 

  b->request_child_resources();

  b->allocate_pending_child_resources();

  b->setup_resources();

  if (!registry->register_obj(b, b->name()).is_valid())  //注册服务

    {

      d_printf(DBG_WARN, "WARNING: Service registration failed: '%s'\n", b->name());

      return -1;

    }

  if (dlevel(DBG_DEBUG2))

    dump(b);

  return 0;

}

 

vm_pass.vbus解析完成。

 

uvmm中对vbus总线资源的申请

 

在uvmm中通过main()——》run()——》create_default_devices()进行vbus总线资源申请:

void create_default_devices(zre_addr_t rambase)

  {

……

    auto vbus_cap = e->get_cap<ZREvbus::Vbus>("vbus");  //获取vbus能力权限

    if (!vbus_cap)

      vbus_cap = e->get_cap<ZREvbus::Vbus>("vm_bus");

    _vbus = cxx::make_ref_obj<Vmm::Virt_bus>(vbus_cap); //创建vbus对象

……

  }

 

获取vbus能力权限

vbus能力权限在前文讲vmm.luastart_vm函数时,传递到虚拟机uvmm。其最终关联的是Io Server的vm_pass虚拟总线。

 

创建vbus对象

cxx::make_ref_obj<Vmm::Virt_bus>(vbus_cap)会调用Virt_bus的构造函数:

explicit Virt_bus(ZRE::Cap<ZREvbus::Vbus> bus)

  : _bus(bus)

  {

  ……

    scan_bus();

  }

void

Virt_bus::scan_bus()

{

  ZREvbus::Device root = _bus->root();  //创建根设备

  Devinfo info;

 

  while (root.next_device(&info.io_dev, ZREVBUS_MAX_DEPTH, &info.dev_info) == 0)

    _devices.push_back(info);  //获取vbus所有子节点,并保存在_devices中

}

int next_device(Device *child, int depth = ZREVBUS_MAX_DEPTH,

                  zrevbus_device_t *devinfo = 0) const

  {

child->_bus = _bus;

//_dev初始化时赋值为ZREVBUS_ROOT_BUS=0

    return zrevbus_get_next_device(_bus.cap(), _dev, &child->_dev, depth,

                                  devinfo);  

  }

int

zrevbus_get_next_device(zre_cap_idx_t vbus, zrevbus_device_handle_t parent,

                       zrevbus_device_handle_t *child, int depth,

                       zrevbus_device_t *devinfo)

{

  ZRE::Ipc::Iostream s(zre_utcb());

  s << parent << zre_uint32_t(ZREvbus_vdevice_get_next) << *child << depth;

  int err = zre_error(s.call(vbus, ZREvbus::Vbus::Protocol));

  if (err < 0)

    return err;

  s >> *child;

  if (devinfo)

    s.get(*devinfo);

  return err;

}

可以看出,获取vbus子节点过程是通过IPC,获取后全部保存在本地变量_devices中。

在uvmm中根据dtb创建设备流程中,查找匹配设备会调用Virt_bus::find_unassigned_dev方法,还会涉及与Io Server的IPC交互过程。

 

猜你喜欢

转载自blog.csdn.net/gaojy19881225/article/details/82053800