arceos-hypervisor / arceos-umhv

Unified modular arceos-hypervisor
3 stars 7 forks source link

Hardware Support #30

Closed luodeb closed 1 week ago

luodeb commented 3 weeks ago

Device Tree Support

Currently, there is a need to boot arceos-hypervisor on hardware. However, adding a new configuration file for each new piece of hardware (in TOML format) is not ideal. Therefore, it is hoped to port over https://github.com/Starry-OS/of, which may require some effort.

Loading the Operating System

Due to the lack of a disk driver, the OS bin file can only be loaded from memory at present. The current solution involves specifying the file to be written into boot.img in arceos-vmm/build.rs, and loading it into memory when the Hypervisor starts. This approach lacks flexibility. Is there a better solution?

/// Creates an assembly file to embed the kernel and device tree binary files.
fn new_guest_img() -> io::Result<()> {
    let mut f = File::create("./guest.S").unwrap();
    // let guest = std::env::var("GUEST").unwrap();
    let mut img_path = String::new();
    let mut dtb_path = String::new();

    img_path = "../linux-aarch64.bin".to_string();
    dtb_path = "../rk3588.dtb".to_string();

    writeln!(
        f,
        r#"
    .section .data
    .global guestkernel_start
    .global guestkernel_end
    .align 16
guestkernel_start:
    .incbin "{}"
guestkernel_end:

  .section .data
    .global guestdtb_start
    .global guestdtb_end
    .align 16
guestdtb_start:
    .incbin "{}"
guestdtb_end:"#,
        img_path, dtb_path
    )?;
    Ok(())
}
pub fn load_vm_images_buffer(
    buffer: *mut u8,
    load_addr: usize,
    image_size: usize,
    vm: VMRef,
) -> AxResult {
    // let image_size = buffer.len();
    let mut buffer_pos = 0;
    let image_load_gpa = VirtAddr::from(load_addr);

    let image_load_regions = vm.get_image_load_region(image_load_gpa, image_size)?;

    for region in image_load_regions {
        let region_len = region.len();
        let bytes_to_write = region_len.min(image_size - buffer_pos);

        // copy data from .tbdata section
        unsafe {
            core::ptr::copy_nonoverlapping(
                buffer.offset(buffer_pos as isize),
                (&mut region[0]) as *mut u8,
                bytes_to_write,
            );
        }

        // Update the position of the buffer.
        buffer_pos += bytes_to_write;

        // If the buffer is fully written, exit the loop.
        if buffer_pos >= image_size {
            debug!("copy size: {}", bytes_to_write);
            break;
        }
    }

    Ok(())
}
hky1999 commented 3 weeks ago

Device Tree Support Currently, there is a need to boot arceos-hypervisor on hardware. However, adding a new configuration file for each new piece of hardware (in TOML format) is not ideal. Therefore, it is hoped to port over https://github.com/Starry-OS/of, which may require some effort.

It's the problem brought by ArceOS, of couse it's a important capability.

hky1999 commented 3 weeks ago

Due to the lack of a disk driver, the OS bin file can only be loaded from memory at present. The current solution involves specifying the file to be written into boot.img in arceos-vmm/build.rs, and loading it into memory when the Hypervisor starts. This approach lacks flexibility. Is there a better solution?

We can use TFTP to load image file into different memory locations seperately

But I do not think it's a problem, a good design is to pass-through mmc to the first Linux, we just need to prepare kernel image file for this Linux

luodeb commented 3 weeks ago

Loading the Operating System

  1. Add configuration items in configs/*.toml.
    
    ...

load from memory

image_location = "memory" kernel_path = "linux-rk3588-aarch64.bin" kernel_size = 0x400_0000 dtb_path = "linux-rk3588.dtb" dtb_size = 0x8_0000

load from file system

image_location = "fs"

kernel_path = "arceos-aarch64.bin"

kernel_load_addr = 0x4008_0000

platform = "aarch64-rk3588j" arch = "aarch64" ...


``` rust
// axvm
pub struct AxVMCrateConfig {
    /// The location of the image, default is 'fs'.
    pub image_location: Option<String>, 
    /// The size of kernel.
    pub kernel_size: Option<usize>,
    /// The size of dtb.
    pub dtb_size: Option<usize>,
}

// image.rs
match vm_create_config.image_location.as_deref() {
    Some("memory") => {
        load_vm_memory(vm_create_config, vm.clone())
        .expect("Failed to load VM images");
    }
    _ => {
        load_vm_images(vm_create_config, vm.clone())
            .expect("Failed to load VM images");
    }
}

Since load_vm_memory will be compiled, it is also necessary to explicitly provide guestkernel_start and guestdtb_start.

// image.rs
extern "C" {
    fn guestdtb_start();
    // fn guestdtb_end();
    fn guestkernel_start();
    // fn guestkernel_end();
} 

// build.rs
fn new_guest_img() -> io::Result<()> {
    let mut f = File::create("./guest.S").unwrap();
    // let guest = std::env::var("GUEST").unwrap();
    let mut img_path = String::new();
    let mut dtb_path = String::new();

    img_path = "../linux-aarch64.bin".to_string();  // how can i read this from configs/*.toml?
    dtb_path = "../rk3588.dtb".to_string();

    writeln!(
        f,
        r#"
    .section .data
    .global guestkernel_start
    .global guestkernel_end
    .align 16
guestkernel_start:
    .incbin "{}"
guestkernel_end:

  .section .data
    .global guestdtb_start
    .global guestdtb_end
    .align 16
guestdtb_start:
    .incbin "{}"
guestdtb_end:"#,
        img_path, dtb_path
    )?;
    Ok(())
}
  1. use #[cfg(any(platform_family = "aarch64-rk3588j"))]
// build.rs
    let platform = env::var("AX_PLATFORM").unwrap_or("".to_string());
    let platform_family = env::var("AX_PLATFORM").unwrap_or("".to_string());
    println!("cargo:rustc-cfg=platform=\"{}\"", platform);
    println!("cargo:rustc-cfg=platform_family=\"{}\"", platform_family);

in this way,we should add #[cfg(any(platform_family = "aarch64-rk3588j"))] or #[cfg(not(any(platform_family = "aarch64-rk3588j")))] in every function of image.rs. And not easy to expand.

eg:

// image.rs
/// Loads the VM image files from the filesystem
/// into the guest VM's memory space based on the VM configuration.
pub fn load_vm_images(config: AxVMCrateConfig, vm: VMRef) -> AxResult {
    #[cfg(any(platform_family = "aarch64-rk3588j"))]
    {
        let dtb_start_addr = guestdtb_start as usize;
        let kernel_start_addr = guestkernel_start as usize;

        // Load kernel image.
        load_vm_image_memory(
            kernel_start_addr as *mut u8,
            config.kernel_load_addr,
            0x400_0000,
            vm.clone(),
        )
        ...
    }

    #[cfg(not(any(platform_family = "aarch64-rk3588j")))]
    {
        // Load kernel image.
        load_vm_image(
            config.kernel_path,
            VirtAddr::from(config.kernel_load_addr),
            vm.clone(),
        )?;
        ....
    }

    Ok(())
}
  1. create platform directory arceos-umhv/arceos-vmm/src/vmm
    • platform ├── aarch64_bsta1000b ├── aarch64_common ├── aarch64_qemu_virt ├── aarch64_raspi ├── aarch64_rk3588j ├── dummy ├── mod.rs ├── riscv64_qemu_virt └── x86_pc

It's also bad.

hky1999 commented 3 weeks ago

Since load_vm_memory will be compiled, it is also necessary to explicitly provide guestkernel_start and guestdtb_start.

Try include_bytes!

hky1999 commented 3 weeks ago

create platform directory arceos-umhv/arceos-vmm/src/vmm platform ├── aarch64_bsta1000b ├── aarch64_common ├── aarch64_qemu_virt ├── aarch64_raspi ├── aarch64_rk3588j ├── dummy ├── mod.rs ├── riscv64_qemu_virt └── x86_pc

I do not get it, since we use image_location = "memory" in toml config, why do we still need these platform config files?

luodeb commented 3 weeks ago

Since load_vm_memory will be compiled, it is also necessary to explicitly provide guestkernel_start and guestdtb_start.

Try include_bytes!

include_bytes! must be provide a string literal. Do we add a env in Makefile?