您的位置:首页 > 科技 > 能源 > 中国500强企业排名一览表_惠州seo推广外包_aso优化软件_搜索引擎优化是什么

中国500强企业排名一览表_惠州seo推广外包_aso优化软件_搜索引擎优化是什么

2025/1/18 3:57:40 来源:https://blog.csdn.net/cxjczy1990/article/details/144271680  浏览:    关键词:中国500强企业排名一览表_惠州seo推广外包_aso优化软件_搜索引擎优化是什么
中国500强企业排名一览表_惠州seo推广外包_aso优化软件_搜索引擎优化是什么

本篇幅,我将带领大家去用基于上一篇幅的代码基础来 bring-up 一个 Linux 操作系统:

我们要做的第一件事是编译一个 Linux,获得其内核镜像:

wget https://github.com/torvalds/linux/archive/refs/tags/v6.12.tar.gz
tar -xvzf v6.12.tar.gz
cd linux-6.12
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
cp arch/arm64/config/defconfig .config
make menuconfig
make -j16

为了经可能缩小 Image 的大小,我们在make menuconfig 时尽可能将一些外设驱动全部删除。

获取内核镜像后,我们还需要创建对应的设备树 dtb:

qemu-system-aarch64 -cpu cortex-a72 -machine virt,gic-version=3,dumpdtb=virt.dtb -smp 2 -m 128M -nographic

然后我们修改设备树如下,我们只保留关键的分支,之后用到再扩展:

/dts-v1/;/ {interrupt-parent = <0x8003>;model = "linux,dummy-virt";#size-cells = <0x2>;#address-cells = <0x2>;compatible = "linux,dummy-virt";psci {migrate = <0xc4000005>;cpu_on = <0xc4000003>;cpu_off = <0x84000002>;cpu_suspend = <0xc4000001>;method = "hvc";compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci";};memory@40000000 {reg = <0x0 0x80000000 0x0 0x4000000>;device_type = "memory";};platform-bus@c000000 {interrupt-parent = <0x8003>;ranges = <0x0 0x0 0xc000000 0x2000000>;#address-cells = <0x1>;#size-cells = <0x1>;compatible = "qemu,platform", "simple-bus";};pl011@9000000 {clock-names = "uartclk", "apb_pclk";clocks = <0x8000 0x8000>;interrupts = <0x0 0x1 0x4>;reg = <0x0 0x9000000 0x0 0x1000>;compatible = "arm,pl011", "arm,primecell";};intc@8000000 {phandle = <0x8003>;reg = <0x0 0x8000000 0x0 0x10000 0x0 0x80a0000 0x0 0xf60000>;#redistributor-regions = <0x1>;compatible = "arm,gic-v3";ranges;#size-cells = <0x2>;#address-cells = <0x2>;interrupt-controller;#interrupt-cells = <0x3>;its@8080000 {phandle = <0x8004>;reg = <0x0 0x8080000 0x0 0x20000>;#msi-cells = <0x1>;msi-controller;compatible = "arm,gic-v3-its";};};cpus {#size-cells = <0x0>;#address-cells = <0x1>;cpu-map {socket0 {cluster0 {core0 {cpu = <0x8002>;};core1 {cpu = <0x8001>;};};};};cpu@0 {phandle = <0x8002>;reg = <0x0>;enable-method = "psci";compatible = "arm,cortex-a72";device_type = "cpu";};cpu@1 {phandle = <0x8001>;reg = <0x1>;enable-method = "psci";compatible = "arm,cortex-a72";device_type = "cpu";};};timer {interrupts = <0x1 0xd 0x4 0x1 0xe 0x4 0x1 0xb 0x4 0x1 0xa 0x4>;always-on;compatible = "arm,armv8-timer", "arm,armv7-timer";};apb-pclk {phandle = <0x8000>;clock-output-names = "clk24mhz";clock-frequency = <0x16e3600>;#clock-cells = <0x0>;compatible = "fixed-clock";};chosen {stdout-path = "/pl011@9000000";rng-seed = <0x1a8d6e59 0x3644466e 0x24b258f8 0x1ae5e04d 0x96e22649 0x142857c2 0xdb1aa43d 0xa75edb65>;kaslr-seed = <0x5addae6b 0x7d35b193>;};
};

这里最关键的一个修改是 memory 的范围,这个范围和我们对 guest 的配置要一致,这块可以看之前的关于 stage2 转换的文章。

然后我们将上述 dts 转换为 dtb:

 dtc -I dts -O dtb -o virt.dtb virt.dts

最后我们将内核镜像和 dtb 通过 ld 打包到 X-Hyper 镜像中:

set(Guest_VM_Image "${PROJECT_BINARY_DIR}/../linux/image.o \ ${PROJECT_BINARY_DIR}/../linux/virt.dtb.o")
set(X_HYPER_LINK "${CMAKE_LINKER} -pie -Map X_Hyper.map -T${LSCRIPT} -L${LINK_PATH} \-lx_hyper_libs -o X-Hyper.elf ${Guest_VM_Image}")

在 IPA mapping 中,我们需要将 dtb 进行映射:

    LOG_INFO("-->Create dtb range mapping for guest\n");for(p = 0; p < vm_config->guest_dtb->image_size; p += PAGESIZE) {char *page = alloc_one_page();if(page == NULL) {abort("Unable to alloc a page");}if(vm_config->guest_dtb->image_size - p > PAGESIZE) {copy_size = PAGESIZE;} else {copy_size = vm_config->guest_dtb->image_size - p;}/* copy the guest image content from X-Hyper image to pages */memcpy(page, (char *)vm_config->guest_dtb->start_addr + p, copy_size);create_guest_mapping(pgt, vm_config->dtb_addr + p, (u64)page, PAGESIZE, S2PTE_NORMAL | S2PTE_RW);}

最后我们需要通过 x0 寄存器告诉内核这个 dtb 在物理内存(IPA) 的位置:

    if(vcpuid == 0) {  /* If it is the primary virtual cpu, set the dtb address (ipa) */vcpu->regs.x[0] = vm->dtb;}

完成上述操作后,我们就可以重新编译 X-Hyper,然后运行 qemu 了,这个过程会出现各种问题,我们带着问题来一个个解决:

debug 过程:

问题 1:

Linux 调用 hvc 下发了一个 0x80000000 的 function ID,但是这个 psci 的 function id 为0x80000000 并没有在 psci 的协议中找到对应的值?这块后面可以在深入研究一下。

u64 vpsci_trap_smc(vcpu_t *vcpu, u64 funid, u64 target_cpu, u64 entry_addr)
{if(vcpu == NULL) {abort("vpsci_trap_smc with NULL vcpu");}switch(funid) {case PSCI_VERSION:return vpsci_version();case PSCI_MIGRATE_INFO_TYPE:return (s64)vpsci_migrate_info_type();case PSCI_SYSTEM_OFF:LOG_WARN("Unsupported PSCI CPU OFF\n");break;case PSCI_SYSTEM_RESET:LOG_WARN("Unsupported PSCI CPU RESET\n");break;case PSCI_SYSTEM_CPUON:return (s64)vpsci_cpu_on(vcpu, funid, target_cpu, entry_addr);case PSCI_FEATURE:    /* Linux will use this funid to get the PSCI FEATURE *//* fake it */return 0;case 0x80000000:    /* TODO:要弄清楚这个function id的作用 */return 0;default:abort("Unknown function id : %p from hvc/smc call", funid);return -1;}return -1;
}

问题 2:

Linux 访问 GIC Distributor 的 0x800ffe8 地址:

翻阅 GICv3 的数据手册,我们知道 offset = 0xffe8 是 GICD_PIDR2

        case GICD_PIDR2:    /* Linux gic driver will read gicd_pidr2 */*val = GICD_READ32(GICD_PIDR2);goto finished;

问题 3:

Linux 启动过程中卡死,通过 LOG 发现是在配置完成 GICR 之后,但是通过跟踪 GICR 在 Hypervisor 中的读写发现没有什么问题,继续跟踪 Linux 发现是由于 Linux 读到的cntfrq_el0 寄存器值为 0,导致 Linux 时钟配置发生问题,原因是我们在 Hypervisor 中没有初始化cntfrq_el0,这样的话在restore_sysreg 中会把 0 值赋值给cntfrq_el0,所以我们需要对cntfrq_el0 进行初始化工作:

vcpu_t *create_vcpu(vm_t *vm, int vcpuid, u64 entry)
{u64 cnt;vcpu_t *vcpu = vcpu_alloc();if(vcpu == NULL) {abort("Unable to alloc a vcpu");}vcpu->core_name = "Cortex-A72";vcpu->vm        = vm;vcpu->cpuid     = vcpuid;vcpu->regs.spsr = SPSR_M(5) | SPSR_DAIF;    /* used to set the spsr_el2 */vcpu->regs.elr  = entry;                    /* used to set the elr_el2 */vcpu->sys_regs.mpidr_el1 = vcpuid;          /* used to fake the mpidr_el1 */vcpu->sys_regs.midr_el1  = 0x410FD081;      /* used to fake the core to cortex-a72 */read_sysreg(cnt, cntfrq_el0);vcpu->sys_regs.cntfrq_el0 = cnt;  /* 初始化cntfrq_el0 */

问题 4:

EL1 发生同步异常,异常编号 0x18:

通过 ESR_EL2 的描述,当异常编号为 0x18 时,说明在 Linux 中执行了敏感指令的 MSR 或 MRS,并且在当前系统状态下会被 trap 到 EL2 处理,然后我们读取的 ISS 为 0x3a3276:

通过 ISS 我们可以得到系统寄存器编码:

原来这是一个 ICC_SGI1R_EL1 的写请求,这个寄存器干嘛用的呢?其实就是 Linux 用来发送核间中断 IPI 的,写入这个寄存器就可以给指定的 Core 发送中断请求,所以我们接下来要做两件事:

  • 在 EL1 的同步异常处理中处理 0x18 的异常编号,并通过 ISS 得到访问的寄存器的寄存器编码;
  • 我们先只处理ICC_SGI1R_EL1,得到其写入的值后通过虚拟中断注入到指定的 core;
        case 0x18:/* trapped by read/write system register */if(vsysreg_handler(vcpu, esr_iss) < 0) {abort("Unknow system register trap");}vcpu->regs.elr += 4;break;
int vsysreg_handler(vcpu_t *vcpu, u64 iss)
{int write_not_read = !(iss & 1);/* The Rt value from the issued instruction, the general-purpose register used for the transfer. */int rt = (iss >> 5) & 0x1F;iss = iss & ~(0x1F << 5);switch(iss) {case VSYSREG_ICC_SGI1R_EL1:vgicv3_generate_sgi(vcpu, rt, write_not_read);return 0;}LOG_WARN("Unable to handler a system register\n");return -1;
}
int vgicv3_generate_sgi(struct vcpu *vcpu, int rt, int wr)
{u64  regs_sgi = vcpu->regs.x[rt];u16  target   = regs_sgi & 0xFFFF;u8   intid    = (regs_sgi >> 24) & 0xF;bool irm      = (regs_sgi >> 40) & 0x1;write_sysreg(ICC_SGI1R_EL1, regs_sgi);return 1;
}

这里vgicv3_generate_sgi 先简化了直接把写的值写入ICC_SGI1R_EL1,但是这样其实是有问题,大家可以自己思考一下为什么?等后面支持多个 vm 后我们再修改。

上述问题都解决后,我们再次运行:

Linux 可以起来了☺☺:

最后在加载 rootfs 的时候失败,因为我们现在压根没有根文件系统,这块后面篇幅再继续了,今天到此为止。

项目构建:

  1. clone 源代码到本地:git clone GitCode - 全球开发者的开源社区,开源代码托管平台;
  2. 编译生成 u-boot 的 bin 文件:sh build_uboot.sh;
  3. 编译虚拟机 Guest OS 镜像:cd ./guest; sh build_vm.sh;
  4. 编译虚拟机管理器代码,生成虚拟机管理器镜像:sh run_build.sh;
  5. 运行 qemu 并加载镜像:sh run_qemu.sh (直接运行);

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com