TinyEMU源码分析之虚拟机初始化

static VirtMachine \*riscv\_machine\_init(const VirtMachineParams \*p)
{
    RISCVMachine \*s;
    VIRTIODevice \*blk_dev;
    VIRTIOBusDef vbus_s, \*vbus = &vbus_s;
    
	// 初始化结构参数
    s->common.vmc = p->vmc;
    s->ram_size = p->ram_size;
    s->max_xlen = max_xlen;
    s->mem_map = phys\_mem\_map\_init();
    s->mem_map->opaque = s;
    s->mem_map->flush_tlb_write_range = riscv_flush_tlb_write_range;
    s->cpu_state = riscv\_cpu\_init(s->mem_map, max_xlen);
	
	// 配置RAM地址空间
    /\* RAM \*/
    ram_flags = 0;
    cpu\_register\_ram(s->mem_map, RAM_BASE_ADDR, p->ram_size, ram_flags);
    cpu\_register\_ram(s->mem_map, 0x00000000, LOW_RAM_SIZE, 0);
    cpu\_register\_device(s->mem_map, CLINT_BASE_ADDR, CLINT_SIZE, s,
                        clint_read, clint_write, DEVIO_SIZE32);
    cpu\_register\_device(s->mem_map, PLIC_BASE_ADDR, PLIC_SIZE, s,
                        plic_read, plic_write, DEVIO_SIZE32);
    cpu\_register\_device(s->mem_map, HTIF_BASE_ADDR, 16,
                        s, htif_read, htif_write, DEVIO_SIZE32);
    vbus->addr = VIRTIO_BASE_ADDR;
	
	// 初始化设备
    /\* virtio console \*/
    if (p->console) {
        s->common.console_dev = virtio\_console\_init(vbus, p->console);
        vbus->addr += VIRTIO_SIZE;
    }
    
	...

    if (p->input_device) {
    	// 键盘
		s->keyboard_dev = virtio\_input\_init(vbus,
											VIRTIO_INPUT_TYPE_KEYBOARD);
		vbus->addr += VIRTIO_SIZE;

		// 鼠标
		s->mouse_dev = virtio\_input\_init(vbus,
										 VIRTIO_INPUT_TYPE_TABLET);
		vbus->addr += VIRTIO_SIZE;
    }
	
	// 拷贝BIOS和Kernel;手动写入5条指令
    copy\_bios(s, p->files[VM_FILE_BIOS].buf, p->files[VM_FILE_BIOS].len,
              p->files[VM_FILE_KERNEL].buf, p->files[VM_FILE_KERNEL].len,
              p->files[VM_FILE_INITRD].buf, p->files[VM_FILE_INITRD].len,
              p->cmdline);
    
    return (VirtMachine \*)s;
}

首先,初始化VirtMachineClass、ram大小、max_xlen,以及内存映射初始化等。
然后,在riscv_cpu_init函数中,会完成pc赋初值和TLB初始化(赋值为-1)。

s->pc = 0x1000; 
s->cpu_state = riscv\_cpu\_init(s->mem_map, max_xlen);

cpu_state的类型为RISCVCPUState结构,该结构中,包含mstatus、mtvec、mscratch等CSR寄存器定义。

我们猜测,第一条指令地址,就是0x1000

初始化结构参数,其实就是把一些参数,保存到RISCVMachine对象中。

2 配置RAM地址空间

我们对本部分代码,进行分析,并结合以下常量定义。

#define LOW\_RAM\_SIZE 0x00010000 /\* 64KB \*/
#define RAM\_BASE\_ADDR 0x80000000
#define CLINT\_BASE\_ADDR 0x02000000
#define CLINT\_SIZE 0x000c0000
#define HTIF\_BASE\_ADDR 0x40008000
#define IDE\_BASE\_ADDR 0x40009000
#define VIRTIO\_BASE\_ADDR 0x40010000
#define VIRTIO\_SIZE 0x1000
#define VIRTIO\_IRQ 1
#define PLIC\_BASE\_ADDR 0x40100000
#define PLIC\_SIZE 0x00400000
#define FRAMEBUFFER\_BASE\_ADDR 0x41000000

发现代码,构成了,如下的内存地址空间:
在这里插入图片描述
这里,主要是,确定Low Dram、CLINT、HTIF、VBUS、PLIC、High Dram的地址空间范围(申请内存),可以结合上面代码,好好看看,比较简单。

因为,在执行指令时,必须要知道具体的内存空间,是如何分布的,以便正确访问内存。

3 初始化设备

初始化console、net device、block device、filesystem、display device、input device。
不详述,自己看源码。

4 拷贝BIOS和Kernel

在copy_bios函数中,完成拷贝BIOS和Kernel,其代码如下:

static void copy\_bios(RISCVMachine \*s, const uint8\_t \*buf, int buf_len,
                      const uint8\_t \*kernel_buf, int kernel_buf_len,
                      const uint8\_t \*initrd_buf, int initrd_buf_len,
                      const char \*cmd_line)
{
	// 拷贝BIOS到0x80000000
    ram_ptr = get\_ram\_ptr(s, RAM_BASE_ADDR, TRUE);
    memcpy(ram_ptr, buf, buf_len);

	// 拷贝Kernel到0x80200000
    kernel_base = 0;
    if (kernel_buf_len > 0) {
        /\* copy the kernel if present \*/
        if (s->max_xlen == 32)
            align = 4 << 20; /\* 4 MB page align \*/
        else
            align = 2 << 20; /\* 2 MB page align \*/
        kernel_base = (buf_len + align - 1) & ~(align - 1);
        memcpy(ram_ptr + kernel_base, kernel_buf, kernel_buf_len);
    }
    
	// 创建设备树,并写入内存地址(0x1000+8\*8)处
    ram_ptr = get\_ram\_ptr(s, 0, TRUE);
    fdt_addr = 0x1000 + 8 \* 8;
    riscv\_build\_fdt(s, ram_ptr + fdt_addr,


# 面试准备+复习分享:

> 为了应付面试也刷了很多的面试题与资料,现在就分享给有需要的读者朋友,资料我只截取出来一部分哦

![秋招|美团java一面二面HR面面经,分享攒攒人品](https://img-blog.csdnimg.cn/img_convert/ddfef4bbea24ad53d64707f1ef6f1551.webp?x-oss-process=image/format,png)



 8;
    riscv\_build\_fdt(s, ram_ptr + fdt_addr,


# 面试准备+复习分享:

> 为了应付面试也刷了很多的面试题与资料,现在就分享给有需要的读者朋友,资料我只截取出来一部分哦

[外链图片转存中...(img-eE4YfzzS-1714479026900)]



> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/topics/618154847)收录**
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值