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函数 } } |