Browse Source

Merge pull request #37 from rcore-os/crosvm

Add support for running aarch64 example under crosvm
Andrew Walbran 2 years ago
parent
commit
66aa265669

+ 0 - 5
examples/aarch64/.cargo/config

@@ -1,7 +1,2 @@
 [build]
 target = "aarch64-unknown-none"
-
-[target.aarch64-unknown-none]
-rustflags = [
-    "-C", "link-arg=-Timage.ld",
-]

+ 24 - 9
examples/aarch64/Makefile

@@ -1,7 +1,8 @@
 target := aarch64-unknown-none
 mode := release
 kernel := target/$(target)/$(mode)/aarch64
-kernel_bin := target/$(target)/$(mode)/aarch64.bin
+kernel_qemu_bin := target/$(target)/$(mode)/aarch64_qemu.bin
+kernel_crosvm_bin := target/$(target)/$(mode)/aarch64_crosvm.bin
 img := target/$(target)/$(mode)/img
 
 sysroot := $(shell rustc --print sysroot)
@@ -19,11 +20,19 @@ env:
 	rustup component add llvm-tools-preview rustfmt
 	rustup target add $(target)
 
-kernel:
-	cargo build $(BUILD_ARGS)
+kernel_qemu:
+	cargo clean
+	cargo build $(BUILD_ARGS) --config 'build.rustflags="--cfg platform=\"qemu\""'
+
+kernel_crosvm:
+	cargo clean
+	cargo build $(BUILD_ARGS) --config 'build.rustflags="--cfg platform=\"crosvm\""'
 
-$(kernel_bin): kernel
-	aarch64-linux-gnu-objcopy -O binary $(kernel) $(kernel_bin)
+$(kernel_qemu_bin): kernel_qemu
+	aarch64-linux-gnu-objcopy -O binary $(kernel) $(kernel_qemu_bin)
+
+$(kernel_crosvm_bin): kernel_crosvm
+	aarch64-linux-gnu-objcopy -O binary $(kernel) $(kernel_crosvm_bin)
 
 asm: kernel
 	$(objdump) -d $(kernel) | less
@@ -37,13 +46,13 @@ header: kernel
 clean:
 	cargo clean
 
-qemu: $(kernel_bin) $(img)
+qemu: $(kernel_qemu_bin) $(img)
 	qemu-system-aarch64 \
 	  $(QEMU_ARGS) \
 		-machine virt \
 		-cpu max \
 		-serial chardev:char0 \
-		-kernel $(kernel_bin) \
+		-kernel $(kernel_qemu_bin) \
 		-global virtio-mmio.force-legacy=false \
 		-nic none \
 		-drive file=$(img),if=none,format=raw,id=x0 \
@@ -53,12 +62,12 @@ qemu: $(kernel_bin) $(img)
 		-chardev stdio,id=char0,mux=on \
 		-device virtconsole,chardev=char0
 
-qemu-pci: $(kernel_bin) $(img)
+qemu-pci: $(kernel_qemu_bin) $(img)
 	qemu-system-aarch64 \
 		-machine virt \
 		-cpu max \
 		-serial chardev:char0 \
-		-kernel $(kernel_bin) \
+		-kernel $(kernel_qemu_bin) \
 		-nic none \
 		-drive file=$(img),if=none,format=raw,id=x0 \
 		-device virtio-blk-pci,drive=x0 \
@@ -67,6 +76,12 @@ qemu-pci: $(kernel_bin) $(img)
 		-chardev stdio,id=char0,mux=on \
 		-device virtconsole,chardev=char0
 
+crosvm: $(kernel_crosvm_bin) $(img)
+	adb shell 'mkdir -p /data/local/tmp/virt_raw'
+	adb push $(kernel_crosvm_bin) /data/local/tmp/virt_raw/aarch64_example
+	adb push $(img) /data/local/tmp/virt_raw/disk_img
+	adb shell "/data/local/tmp/crosvm --log-level=trace --extended-status run --disable-sandbox --serial=stdout,hardware=serial,num=1 --rwdisk=/data/local/tmp/virt_raw/disk_img --bios=/data/local/tmp/virt_raw/aarch64_example"
+
 $(img):
 	dd if=/dev/zero of=$@ bs=512 count=32
 

+ 23 - 5
examples/aarch64/build.rs

@@ -3,9 +3,27 @@ use std::env;
 
 fn main() {
     env::set_var("CROSS_COMPILE", "aarch64-linux-gnu");
-    Build::new()
-        .file("entry.S")
-        .file("exceptions.S")
-        .file("idmap.S")
-        .compile("empty")
+    let platform = env::var("CARGO_CFG_PLATFORM").expect("Missing platform name");
+    match platform.as_ref() {
+        "qemu" => {
+            Build::new()
+                .file("entry.S")
+                .file("exceptions.S")
+                .file("idmap.S")
+                .compile("empty");
+            println!("cargo:rustc-link-arg=-Tqemu.ld");
+        }
+        "crosvm" => {
+            Build::new()
+                .file("entry.S")
+                .file("exceptions.S")
+                .file("idmap_crosvm.S")
+                .compile("empty");
+            println!("cargo:rustc-link-arg=-Tcrosvm.ld");
+        }
+        _ => {
+            panic!("Unexpected platform name \"{}\"", platform);
+        }
+    }
+    println!("cargo:rustc-link-arg=-Timage.ld");
 }

+ 4 - 0
examples/aarch64/crosvm.ld

@@ -0,0 +1,4 @@
+MEMORY
+{
+	image : ORIGIN = 0x80200000, LENGTH = 2M
+}

+ 25 - 0
examples/aarch64/idmap_crosvm.S

@@ -0,0 +1,25 @@
+.set .L_TT_TYPE_BLOCK, 0x1
+.set .L_TT_TYPE_PAGE,  0x3
+.set .L_TT_TYPE_TABLE, 0x3
+
+/* Access flag. */
+.set .L_TT_AF, 0x1 << 10
+/* Not global. */
+.set .L_TT_NG, 0x1 << 11
+.set .L_TT_XN, 0x3 << 53
+
+.set .L_TT_MT_DEV, 0x0 << 2			// MAIR #0 (DEV_nGnRE)
+.set .L_TT_MT_MEM, (0x1 << 2) | (0x3 << 8)	// MAIR #1 (MEM_WBWA), inner shareable
+
+.set .L_BLOCK_DEV, .L_TT_TYPE_BLOCK | .L_TT_MT_DEV | .L_TT_AF | .L_TT_XN
+.set .L_BLOCK_MEM, .L_TT_TYPE_BLOCK | .L_TT_MT_MEM | .L_TT_AF | .L_TT_NG
+
+.section ".rodata.idmap", "a", %progbits
+.global idmap
+.align 12
+idmap:
+	/* level 1 */
+	.quad		.L_BLOCK_DEV | 0x0		    // 1 GiB of device mappings
+	.quad		.L_BLOCK_DEV | 0x40000000	// Another 1 GiB of device mappings
+	.quad		.L_BLOCK_MEM | 0x80000000 // 1 GiB of DRAM
+	.fill		509, 8, 0x0			// 509 GiB of unmapped VA space

+ 0 - 5
examples/aarch64/image.ld

@@ -4,11 +4,6 @@
  */
 ENTRY(entry)
 
-MEMORY
-{
-	image : ORIGIN = 0x40080000, LENGTH = 2M
-}
-
 SECTIONS
 {
 	/*

+ 4 - 0
examples/aarch64/qemu.ld

@@ -0,0 +1,4 @@
+MEMORY
+{
+	image : ORIGIN = 0x40080000, LENGTH = 2M
+}

+ 2 - 5
examples/aarch64/src/logger.rs

@@ -1,13 +1,10 @@
 //! Log implementation using the UART.
 
-use crate::pl011::Uart;
+use crate::{uart::Uart, UART_BASE_ADDRESS};
 use core::fmt::Write;
 use log::{LevelFilter, Log, Metadata, Record, SetLoggerError};
 use spin::mutex::SpinMutex;
 
-/// Base memory-mapped address of the primary PL011 UART device.
-pub const BASE_ADDRESS: usize = 0x900_0000;
-
 static LOGGER: Logger = Logger {
     uart: SpinMutex::new(None),
 };
@@ -20,7 +17,7 @@ struct Logger {
 pub fn init(max_level: LevelFilter) -> Result<(), SetLoggerError> {
     // Safe because BASE_ADDRESS is the base of the MMIO region for a UART and is mapped as device
     // memory.
-    let uart = unsafe { Uart::new(BASE_ADDRESS) };
+    let uart = unsafe { Uart::new(UART_BASE_ADDRESS) };
     LOGGER.uart.lock().replace(uart);
 
     log::set_logger(&LOGGER)?;

+ 23 - 1
examples/aarch64/src/main.rs

@@ -4,7 +4,14 @@
 mod exceptions;
 mod hal;
 mod logger;
+#[cfg(platform = "qemu")]
 mod pl011;
+#[cfg(platform = "qemu")]
+use pl011 as uart;
+#[cfg(platform = "crosvm")]
+mod uart8250;
+#[cfg(platform = "crosvm")]
+use uart8250 as uart;
 
 use core::{
     mem::size_of,
@@ -27,6 +34,14 @@ use virtio_drivers::{
     },
 };
 
+/// Base memory-mapped address of the primary PL011 UART device.
+#[cfg(platform = "qemu")]
+pub const UART_BASE_ADDRESS: usize = 0x900_0000;
+
+/// The base address of the first 8250 UART.
+#[cfg(platform = "crosvm")]
+pub const UART_BASE_ADDRESS: usize = 0x3f8;
+
 #[no_mangle]
 extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
     logger::init(LevelFilter::Debug).unwrap();
@@ -247,7 +262,14 @@ impl PciMemory32Allocator {
                 cpu_physical,
                 size,
             );
-            if range_type == PciRangeType::Memory32 && size > memory_32_size.into() {
+            // Use the largest range within the 32-bit address space for 32-bit memory, even if it
+            // is marked as a 64-bit range. This is necessary because crosvm doesn't currently
+            // provide any 32-bit ranges.
+            if !prefetchable
+                && matches!(range_type, PciRangeType::Memory32 | PciRangeType::Memory64)
+                && size > memory_32_size.into()
+                && bus_address + size < u32::MAX.into()
+            {
                 assert_eq!(bus_address, cpu_physical);
                 memory_32_address = u32::try_from(cpu_physical).unwrap();
                 memory_32_size = u32::try_from(size).unwrap();

+ 47 - 0
examples/aarch64/src/uart8250.rs

@@ -0,0 +1,47 @@
+//! Minimal driver for an 8250 UART. This only implements enough to work with the emulated 8250
+//! provided by crosvm, and won't work with real hardware.
+
+use core::fmt::{self, Write};
+use core::ptr::write_volatile;
+
+/// Minimal driver for an 8250 UART. This only implements enough to work with the emulated 8250
+/// provided by crosvm, and won't work with real hardware.
+pub struct Uart {
+    base_address: *mut u8,
+}
+
+impl Uart {
+    /// Constructs a new instance of the UART driver for a device at the given base address.
+    ///
+    /// # Safety
+    ///
+    /// The given base address must point to the 8 MMIO control registers of an appropriate UART
+    /// device, which must be mapped into the address space of the process as device memory and not
+    /// have any other aliases.
+    pub unsafe fn new(base_address: usize) -> Self {
+        Self {
+            base_address: base_address as *mut u8,
+        }
+    }
+
+    /// Writes a single byte to the UART.
+    pub fn write_byte(&self, byte: u8) {
+        // Safe because we know that the base address points to the control registers of an UART
+        // device which is appropriately mapped.
+        unsafe {
+            write_volatile(self.base_address, byte);
+        }
+    }
+}
+
+impl Write for Uart {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        for c in s.as_bytes() {
+            self.write_byte(*c);
+        }
+        Ok(())
+    }
+}
+
+// Safe because it just contains a pointer to device memory, which can be accessed from any context.
+unsafe impl Send for Uart {}