Skip to content

Commit ce5c674

Browse files
committed
Step 5: run a complex guest OS
1 parent a63808c commit ce5c674

13 files changed

Lines changed: 327 additions & 83 deletions

File tree

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "guest/nimbos"]
2+
path = guest/nimbos
3+
url = https://github.com/equation314/nimbos.git

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,24 @@ Install [cargo-binutils](https://github.com/rust-embedded/cargo-binutils) to use
1010
$ cargo install cargo-binutils
1111
```
1212

13+
Your also need to install [musl-gcc](http://musl.cc/x86_64-linux-musl-cross.tgz) to build guest user applications.
14+
15+
## Build Guest OS
16+
17+
```console
18+
$ git submodule init && git submodule update
19+
$ cd guest/nimbos/kernel
20+
$ make user
21+
$ make GUEST=on
22+
```
23+
24+
## Build Guest BIOS
25+
26+
```console
27+
$ cd guest/bios
28+
$ make
29+
```
30+
1331
## Build & Run Hypervisor
1432

1533
```console
@@ -33,4 +51,22 @@ arch = x86_64
3351
build_mode = release
3452
log_level = info
3553
......
54+
Running guest...
55+
56+
NN NN iii bb OOOOO SSSSS
57+
NNN NN mm mm mmmm bb OO OO SS
58+
NN N NN iii mmm mm mm bbbbbb OO OO SSSSS
59+
NN NNN iii mmm mm mm bb bb OO OO SS
60+
NN NN iii mmm mm mm bbbbbb OOOO0 SSSSS
61+
___ ____ ___ ___
62+
|__ \ / __ \ |__ \ |__ \
63+
__/ / / / / / __/ / __/ /
64+
/ __/ / /_/ / / __/ / __/
65+
/____/ \____/ /____/ /____/
66+
67+
arch = x86_64
68+
platform = rvm-guest-x86_64
69+
build_mode = release
70+
log_level = warn
71+
......
3672
```

guest/bios/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
out/

guest/bios/Makefile

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
OUT ?= out
2+
3+
SRC := boot16.S
4+
ldscript := rvm-bios.lds
5+
target := $(OUT)/rvm-bios
6+
target-obj := $(target).o
7+
target-elf := $(target).elf
8+
target-bin := $(target).bin
9+
target-disasm := $(target).asm
10+
11+
AS ?= as
12+
LD ?= ld
13+
OBJCOPY ?= objcopy
14+
OBJDUMP ?= objdump
15+
16+
all: $(OUT) $(target).bin
17+
18+
disasm:
19+
$(OBJDUMP) -d -m i8086 -M intel $(target).elf | less
20+
21+
$(OUT):
22+
mkdir -p $(OUT)
23+
24+
$(target-obj): $(SRC)
25+
$(AS) --32 -msyntax=intel -mnaked-reg $< -o $@
26+
27+
$(target-elf): $(target-obj) $(ldscript)
28+
$(LD) -T$(ldscript) $< -o $@
29+
$(OBJDUMP) -d -m i8086 -M intel $@ > $(target-disasm)
30+
31+
$(target-bin): $(target-elf)
32+
$(OBJCOPY) $< --strip-all -O binary $@
33+
34+
clean:
35+
rm -rf $(OUT)
36+
37+
.PHONY: all disasm clean

guest/bios/boot16.S

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
.section .text
2+
.code16
3+
.global entry16
4+
entry16:
5+
cli
6+
cld
7+
8+
xor ax, ax
9+
mov ds, ax
10+
mov es, ax
11+
mov ss, ax
12+
13+
lgdt [prot_gdt_desc]
14+
mov eax, cr0
15+
or eax, 0x1
16+
mov cr0, eax
17+
18+
ljmp 0x8, entry32
19+
20+
.code32
21+
.global entry32
22+
entry32:
23+
mov ax, 0x10
24+
mov ds, ax
25+
mov es, ax
26+
mov ss, ax
27+
mov fs, ax
28+
mov gs, ax
29+
30+
mov esp, 0x7000 # temporary stack
31+
mov ecx, 0x200000 # kernel entry
32+
mov eax, 0x1BADB002 # multiboot magic
33+
mov ebx, 0 # multiboot information (unsupported)
34+
jmp ecx
35+
36+
.balign 16
37+
prot_gdt:
38+
.quad 0x0000000000000000 # 0x00: null
39+
.quad 0x00cf9b000000ffff # 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k)
40+
.quad 0x00cf93000000ffff # 0x18: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k)
41+
42+
prot_gdt_desc:
43+
.short prot_gdt_desc - prot_gdt - 1 # limit
44+
.long prot_gdt # base

guest/bios/rvm-bios.lds

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
OUTPUT_ARCH(i386)
2+
3+
BASE_ADDRESS = 0x8000;
4+
5+
ENTRY(entry16)
6+
SECTIONS
7+
{
8+
. = BASE_ADDRESS;
9+
.text : {
10+
*(.text .text.*)
11+
}
12+
13+
/DISCARD/ : {
14+
*(.eh_frame) *(.eh_frame_hdr)
15+
}
16+
}

guest/nimbos

Submodule nimbos added at 545ab80

hypervisor/Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ ARCH ?= x86_64
33
MODE ?= release
44
LOG ?= warn
55

6+
BIOS_IMG ?= ../guest/bios/out/rvm-bios.bin
7+
GUEST_IMG ?= ../guest/nimbos/kernel/target/x86_64/release/nimbos.bin
8+
69
export ARCH
710
export MODE
811
export LOG
@@ -25,7 +28,9 @@ GDB := gdb-multiarch
2528
qemu := qemu-system-$(ARCH)
2629
qemu_args := -nographic -m 128M
2730

28-
qemu_args += -cpu host,+x2apic,+vmx -accel kvm
31+
qemu_args += -cpu host,+x2apic,+vmx -accel kvm \
32+
-device loader,addr=0x4000000,file=$(BIOS_IMG),force-raw=on \
33+
-device loader,addr=0x4001000,file=$(GUEST_IMG),force-raw=on
2934

3035
ifeq ($(ARCH), x86_64)
3136
qemu_args += \

hypervisor/src/hv/gconfig.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
use rvm::GuestPhysAddr;
1+
use rvm::{GuestPhysAddr, HostPhysAddr};
2+
3+
pub const BIOS_PADDR: HostPhysAddr = 0x400_0000;
4+
pub const BIOS_SIZE: usize = 0x1000;
5+
6+
pub const GUEST_IMAGE_PADDR: HostPhysAddr = 0x400_1000;
7+
pub const GUEST_IMAGE_SIZE: usize = 0x10_0000; // 1M
28

39
pub const GUEST_PHYS_MEMORY_BASE: GuestPhysAddr = 0;
10+
pub const BIOS_ENTRY: GuestPhysAddr = 0x8000;
11+
pub const GUEST_ENTRY: GuestPhysAddr = 0x20_0000;
412
pub const GUEST_PHYS_MEMORY_SIZE: usize = 0x100_0000; // 16M
5-
6-
pub const GUEST_PT1: GuestPhysAddr = 0x1000;
7-
pub const GUEST_PT2: GuestPhysAddr = 0x2000;
8-
pub const GUEST_ENTRY: GuestPhysAddr = 0x8000;
9-
pub const GUEST_STACK_TOP: GuestPhysAddr = 0x7000;

hypervisor/src/hv/mod.rs

Lines changed: 42 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ mod gpm;
33
mod hal;
44
mod vmexit;
55

6-
use rvm::{GuestPhysAddr, HostVirtAddr, MemFlags, RvmPerCpu, RvmResult};
6+
use rvm::{GuestPhysAddr, HostPhysAddr, HostVirtAddr, MemFlags, RvmPerCpu, RvmResult};
77

88
use self::gconfig::*;
99
use self::gpm::{GuestMemoryRegion, GuestPhysMemorySet};
1010
use self::hal::RvmHalImpl;
11-
use crate::mm::address::virt_to_phys;
11+
use crate::mm::address::{phys_to_virt, virt_to_phys};
1212

1313
#[repr(align(4096))]
1414
struct AlignedMemory<const LEN: usize>([u8; LEN]);
@@ -22,42 +22,51 @@ fn gpa_as_mut_ptr(guest_paddr: GuestPhysAddr) -> *mut u8 {
2222
host_vaddr as *mut u8
2323
}
2424

25-
fn setup_guest_page_table() {
26-
use x86_64::structures::paging::{PageTable, PageTableFlags as PTF};
27-
let pt1 = unsafe { &mut *(gpa_as_mut_ptr(GUEST_PT1) as *mut PageTable) };
28-
let pt2 = unsafe { &mut *(gpa_as_mut_ptr(GUEST_PT2) as *mut PageTable) };
29-
// identity mapping
30-
pt1[0].set_addr(
31-
x86_64::PhysAddr::new(GUEST_PT2 as _),
32-
PTF::PRESENT | PTF::WRITABLE,
33-
);
34-
pt2[0].set_addr(
35-
x86_64::PhysAddr::new(0),
36-
PTF::PRESENT | PTF::WRITABLE | PTF::HUGE_PAGE,
37-
);
25+
fn load_guest_image(hpa: HostPhysAddr, load_gpa: GuestPhysAddr, size: usize) {
26+
let image_ptr = phys_to_virt(hpa) as *const u8;
27+
let image = unsafe { core::slice::from_raw_parts(image_ptr, size) };
28+
unsafe {
29+
core::slice::from_raw_parts_mut(gpa_as_mut_ptr(load_gpa), size).copy_from_slice(image)
30+
}
3831
}
3932

4033
fn setup_gpm() -> RvmResult<GuestPhysMemorySet> {
41-
setup_guest_page_table();
42-
43-
// copy guest code
44-
unsafe {
45-
core::ptr::copy_nonoverlapping(
46-
test_guest as usize as *const u8,
47-
gpa_as_mut_ptr(GUEST_ENTRY),
48-
0x100,
49-
);
50-
}
34+
// copy BIOS and guest images
35+
load_guest_image(BIOS_PADDR, BIOS_ENTRY, BIOS_SIZE);
36+
load_guest_image(GUEST_IMAGE_PADDR, GUEST_ENTRY, GUEST_IMAGE_SIZE);
5137

5238
// create nested page table and add mapping
5339
let mut gpm = GuestPhysMemorySet::new()?;
54-
let guest_memory_regions = [GuestMemoryRegion {
55-
// RAM
56-
gpa: GUEST_PHYS_MEMORY_BASE,
57-
hpa: virt_to_phys(gpa_as_mut_ptr(GUEST_PHYS_MEMORY_BASE) as HostVirtAddr),
58-
size: GUEST_PHYS_MEMORY_SIZE,
59-
flags: MemFlags::READ | MemFlags::WRITE | MemFlags::EXECUTE,
60-
}];
40+
let guest_memory_regions = [
41+
GuestMemoryRegion {
42+
// RAM
43+
gpa: GUEST_PHYS_MEMORY_BASE,
44+
hpa: virt_to_phys(gpa_as_mut_ptr(GUEST_PHYS_MEMORY_BASE) as HostVirtAddr),
45+
size: GUEST_PHYS_MEMORY_SIZE,
46+
flags: MemFlags::READ | MemFlags::WRITE | MemFlags::EXECUTE,
47+
},
48+
GuestMemoryRegion {
49+
// IO APIC
50+
gpa: 0xfec0_0000,
51+
hpa: 0xfec0_0000,
52+
size: 0x1000,
53+
flags: MemFlags::READ | MemFlags::WRITE | MemFlags::DEVICE,
54+
},
55+
GuestMemoryRegion {
56+
// HPET
57+
gpa: 0xfed0_0000,
58+
hpa: 0xfed0_0000,
59+
size: 0x1000,
60+
flags: MemFlags::READ | MemFlags::WRITE | MemFlags::DEVICE,
61+
},
62+
GuestMemoryRegion {
63+
// Local APIC
64+
gpa: 0xfee0_0000,
65+
hpa: 0xfee0_0000,
66+
size: 0x1000,
67+
flags: MemFlags::READ | MemFlags::WRITE | MemFlags::DEVICE,
68+
},
69+
];
6170
for r in guest_memory_regions.into_iter() {
6271
gpm.map_region(r.into())?;
6372
}
@@ -75,27 +84,9 @@ pub fn run() -> ! {
7584
info!("{:#x?}", gpm);
7685

7786
let mut vcpu = percpu
78-
.create_vcpu(GUEST_ENTRY, gpm.nest_page_table_root())
87+
.create_vcpu(BIOS_ENTRY, gpm.nest_page_table_root())
7988
.unwrap();
80-
vcpu.set_page_table_root(GUEST_PT1);
81-
vcpu.set_stack_pointer(GUEST_STACK_TOP);
82-
info!("{:#x?}", vcpu);
8389

8490
println!("Running guest...");
8591
vcpu.run();
8692
}
87-
88-
unsafe extern "C" fn test_guest() -> ! {
89-
for i in 0..100 {
90-
core::arch::asm!(
91-
"vmcall",
92-
inout("rax") i => _,
93-
in("rdi") 2,
94-
in("rsi") 3,
95-
in("rdx") 3,
96-
in("rcx") 3,
97-
);
98-
}
99-
core::arch::asm!("mov qword ptr [$0xffff233], $2333"); // panic
100-
loop {}
101-
}

0 commit comments

Comments
 (0)