Uvmm中DTB文件解析

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

Uvmm中对guest的管理实例——Vmm::Vm vm_instance;

首先找到main函数(pkg\uvmm\server\src\main.cc)----->run函数。Run函数中关于dtb解析的程序段如下:

if (device_tree)

    {

      info.printf("Loading device tree...\n");

      dt_addr = next_free_addr;

      dt = load_device_tree_at(ram, device_tree, dt_addr, dtb_padding);  //加载dtb文件

      // assume /choosen and /memory is present at this point

 

      if (cmd_line)  //如果有cmd_line参数,则添加到dtb的node(/chosen)中

        {

          auto node = dt.path_offset("/chosen");

          node.setprop_string("bootargs", cmd_line);

        }

 

      ram->setup_device_tree(dt);   //在“/memory”节点中添加信息

      vmm->setup_device_tree(dt);  //在“psci”节点中添加信息

 

      dt.scan([] (Vdev::Dt_node const &node, unsigned /* depth */)

                { return node_cb(node); },

              [] (Vdev::Dt_node const &, unsigned)

                {});  //逐个node进行处理

 

      vm_instance.init_devices(dt);  //初始化设备

 

      // Round to the next page to load anything else to a new page.

      next_free_addr =

        l4_round_size(l4virtio::Ptr<void>(dt_addr.get() + dt.size()),

                                            l4_PAGESHIFT);

    }

 

下面分别就~中的步骤进行分析

加载dtb文件——load_device_tree_at

static Vdev::Device_tree

load_device_tree_at(Vmm::Ram_ds *ram, char const *name, l4virtio::Ptr<void> addr,

                    l4_size_t padding)

{

  ram->load_file(name, addr);

 

  auto dt = Vdev::Device_tree(ram->access(addr));

  dt.check_tree();

  // use 1.25 * size + padding for the time being

  dt.add_to_size(dt.size() / 4 + padding);

  info.printf("Loaded device tree to %llx:%llx\n", addr.get(),

                addr.get() + dt.size());

 

  return dt;

}

“ram->load_file”函数(Ram_ds::load_file)——>ZCore::Vfs::vfs_ops->get_file得到ZCore::Vfs::File类型的指针file,并最终通过_ram->copy_in函数将dtb文件拷贝到内存指定地点。

copy_in函数定义在pkg\zcore-core\zcore\include\dataspace文件中,通过l4::Ipc::Msg::Rpc_call进行调用,这里不做深究。

 /**

   * Copy contents from another dataspace.

   *

   * \param dst_offs    Offset in destination dataspace.

   * \param src         Source dataspace to copy from.

   * \param src_offs    Offset in the source dataspace.

   * \param size        Size to copy (in bytes).

   *

   * \retval l4_EOK       Success

   * \retval -l4_EACCESS  Destination dataspace not writable.

   * \retval -l4_EINVAL   Invalid parameter supplied.

   * \retval <0           IPC errors

   *

   * The copy operation may use copy-on-write mechanisms. The operation may

   * also fail if both dataspaces are not from the same dataspace manager

   * or the dataspace managers do not cooperate.

   */

  l4_RPC(long, copy_in, (l4_addr_t dst_offs, l4::Ipc::Cap<Dataspace> src,

                         l4_addr_t src_offs, unsigned long size));

 

l4_RPC_DEF(ZCore::Dataspace::copy_in);

 

/**

 * Generate the definition of an RPC stub.

 *

 * \param name  The fully qualified method name to be implemented, this means

 *              `class::method`.

 *

 * This macro generates the definition (implementation) for the given RPC

 * interface method.

 */

#define l4_RPC_DEF(name) \

  template struct  l4::Ipc::Msg::Rpc_call \

    <name##_t, name##_t::class_type, name##_t::ipc_type, name##_t::flags_type>

 

 

cmd_line参数处理

在“/chosen”节点下添加bootargs属性。

 

“/memory”节点中添加信息

void setup_device_tree(Vdev::Device_tree dt)

  {

    auto mem_node = dt.path_offset("/memory");

    mem_node.set_reg_val(vm_start(), size());  //设置“reg”属性的address/size值

 

    /*将ram的物理地址(_phys_ram)、大小(_phys_size)和虚拟机开始地址(vm_start())设置到dma-ranges属性*/

    int addr_cells = mem_node.get_address_cells();

    mem_node.setprop("dma-ranges", _phys_ram, addr_cells);

    mem_node.appendprop("dma-ranges", vm_start(), addr_cells);

    mem_node.appendprop("dma-ranges", _phys_size, mem_node.get_size_cells());

  }

 

在“psci”节点中添加信息

void

Guest::setup_device_tree(Vdev::Device_tree dt)

{

  Dt_node parent;

  Dt_node node = get_psci_node(dt);  //找到psci节点

 

  if (node.is_valid())  //获取psci节点的父节点

    {

      parent = node.parent_node();

      if (node.del_node() < 0)

        {

          Err().printf("Failed to delete %s, unable to set psci methods\n",

                       node.get_name());

          return;

        }

    }

  else

    parent = dt.first_node();

 

  node = parent.add_subnode("psci");  //增加一个psci节点

  if (!node.is_valid())

    return;

  //添加属性compatible、method

  node.setprop_string("compatible", "arm,psci-0.2");

  node.setprop_string("method", "hvc");

 

  // 16 * 32 spis

  max_cpus = get_max_cpus(dt);

  if(max_cpus < 1){

    Err().printf("Failed get cpu num %d\n", max_cpus);

    max_cpus = 2;

  }

  //初始化GIC的Distributer对象

  _gic = Vdev::make_device<Gic::Dist>(16, max_cpus);

}

 

逐个node进行处理

dt.scan([] (Vdev::Dt_node const &node, unsigned /* depth */)

                { return node_cb(node); },

              [] (Vdev::Dt_node const &, unsigned)

                {});

scan函数的两个参数都是lambda函数,一个作为pre(前)处理,一个作为post(后)处理。其原型(pkg\uvmm\server\include\device_tree.h)为:

template <typename ERR>

template <typename PRE, typename POST>

void

Node<ERR>::scan_recursive(int depth,

                          PRE &&pre_order_cb, POST &&post_order_cb,

                          bool skip_disabled) const

{

  assert(is_valid());

 

  if (skip_disabled && !is_enabled())

    return;

 

  if (!pre_order_cb(*this, depth))  //当前node的pre(前)处理

    return;

 

  // scan child nodes

  for (auto child_node = first_child_node();  //子节点遍历

       child_node.is_valid();

       child_node = child_node.sibling_node())

    child_node.scan_recursive(depth + 1, std::forward<PRE>(pre_order_cb),

                              std::forward<POST>(post_order_cb),

                              skip_disabled);

 

  post_order_cb(*this, depth);  //当前node的post(后)处理

}

 

/**

 * Traverse the device tree and invoke callbacks on all nodes

 *

 * This function invokes scan_node on the root node of the tree.

 */

template <typename ERR>

template <typename PRE, typename POST>

inline void

Tree<ERR>::scan(PRE &&pre_order_cb, POST &&post_order_cb,

                bool skip_disabled) const

{

  auto first = first_node();

  int depth = 0;

  first.scan_recursive(depth, std::forward<PRE>(pre_order_cb),

                       std::forward<POST>(post_order_cb), skip_disabled);

}

 

由于两个lambda函数中post处理为空函数——不做任何操作。所以我们只看pre处理的关键函数——node_cb。下面详细分析下node_cb函数。

static bool

node_cb(Vdev::Dt_node const &node)

{

  // device_type is a deprecated option and should be set for "cpu"

  // and "memory" devices only. Currently there are some more uses

  // like "pci", "network", "phy", "soc2, "mdio", but we ignore these

  // here, since they do not need special treatment.

  //获取device_type属性值

  char const *devtype = node.get_prop<char>("device_type", nullptr);

 

  // Ignore memory nodes

  if (devtype && strcmp("memory", devtype) == 0)  //忽略memory节点

    {

      // there should be no subnode to memory devices so it should be

      // safe to return false to stop traversal of subnodes

      return false;

    }

 

  cxx::Ref_ptr<Vdev::Device> dev;

  bool is_cpu_dev = devtype && strcmp("cpu", devtype) == 0;  

 

  // Cpu devices need to be treated specially because they use a

  // different factory (there are too many compatible attributes to

  // use the normal factory mechanism).

  if (is_cpu_dev)  //cpu节点

    dev = vm_instance.cpus()->create_vcpu(&node);  //创建vcpu

  else

    dev = Vdev::Factory::create_dev(&vm_instance, node);  //创建其他设备

 

  if (dev)

    {

      vm_instance.add_device(node, dev);  //添加设备,并返回

      return true;

    }

 

  //创建失败,错误处理

  if (!is_cpu_dev && !node.needs_vbus_resources())

    return true; // no error, just continue parsing the tree

 

  if (node.has_prop("l4vmm,force-enable"))

    {

      warn.printf("Device creation for %s failed, 'l4vmm,force-enable' set\n",

                  node.get_name());

      return true;

    }

 

  warn.printf("Device creation for %s failed. Disabling device \n",

              node.get_name());

 

  node.setprop_string("status", "disabled");  //失败后,将状态设为disabled

  return false;

}

 

下面分别就创建vcpu、创建其他设备和添加设备进行分析。

 

创建vcpu

cxx::Ref_ptr<Vdev::Device>

Cpu_dev_array::create_vcpu(Vdev::Dt_node const *node)

{

  l4_int32_t prop_val = 0;

  if (node)

    {

      auto *prop = node->get_prop<fdt32_t>("reg", nullptr);  //获取reg属性值

      if (!prop)

        {

          Err().printf("Cpu node has missing reg property. Ignored.\n");

          return nullptr;

        }

      prop_val = fdt32_to_cpu(*prop);

    }

 

  unsigned id = Cpu_dev::dtid_to_cpuid(prop_val);  //转化为cpuid

  if (id >= Max_cpus)

    return nullptr;

 

  if (_cpus[id])  //如果此id的vcpu已经创建,则直接返回其引用

    {

      Dbg(Dbg::Cpu, Dbg::Warn).printf("Duplicate definitions for Cpu%d (%x)\n",

                                      id, prop_val);

      return _cpus[id];

    }

 

  unsigned cpu_mask = _placement.next_free();  //获取下一个未分配的cpuid对应的mask

  if (cpu_mask == Vcpu_placement::Invalid_id)

    return nullptr;

 

  _cpus[id] = Vdev::make_device<Cpu_dev>(id, cpu_mask, node);  //创建vcpu对象

 

  return _cpus[id];

}

 

创建其他设备

dev = Vdev::Factory::create_dev(&vm_instance, node);

static cxx::Ref_ptr<Device> create_dev(Device_lookup const *devs,

                                         Dt_node const &node)

  {

    if (auto r = create_vdev(devs, node))  //vdev设备创建

      return r;

 

    if (pass_thru)  //透传设备创建

      return pass_thru->create(devs, node);

 

    return nullptr;

  }

 

vdev设备创建

static cxx::Ref_ptr<Device> create_vdev(Device_lookup const *devs,

                                          Dt_node const &node)

  {

    char const *const comp = "compatible";

    int count = node.stringlist_count(comp);  //获取兼容属性值字符串数

    if (count <= 0)

      return nullptr;

 

    int l4type_len;

    char const *l4type = node.get_prop<char>("l4vmm,vdev", &l4type_len);  //获取节点“l4vmm,vdev”属性值

 

    for (int i = 0; i < count; ++i)

      {  //遍历查找设备类型

        int cid_len;

        char const *cid = node.stringlist_get(comp, i, &cid_len);

        auto const *t = Device_type::find(cid, cid_len, l4type, l4type_len);  

        if (t)  //根据设备类型,调用其create函数,创建设备

          return t->f->create(devs, node);

      }

 

    return nullptr;

  }

 

添加设备,并返回

vm_instance.add_device(node, dev);

void add_device(Vdev::Dt_node const &node,

                  cxx::Ref_ptr<Vdev::Device> dev)

  { _devices.add(node, dev); }

void add(Dt_node const &node, cxx::Ref_ptr<Device> dev)

  {

    char buf[1024];

    l4_uint32_t phandle = node.get_phandle();  //获取phandle

    node.get_path(buf, sizeof(buf));  //获取path,存在buf中

 

    _devices.push_back({buf, phandle, dev});  //加入Dt_device类型的vector中

  }

 

初始化设备

vm_instance.init_devices(dt);

void init_devices(Device_lookup const *lookup, Device_tree dt)

  {

    for (auto &d : _devices)

      {

        Dbg(Dbg::Dev, Dbg::Trace).printf("Init device '%s'.\n", d.path.c_str());

 

        auto node = Dt_node();

        if (d.phandle != 0 && d.phandle != -1U)

          node = dt.phandle_offset(d.phandle);  //根据phandle获取节点

 

        if (!node.is_valid())  //如果失败,则根据路径获取节点

          node = dt.path_offset(d.path.c_str());

 

        d.dev->init_device(lookup, node);  //根据具体设备,调用其init_device函数

      }

  }

猜你喜欢

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