Skip to content

Commit

Permalink
Merge pull request #17 from yanjiew1/arm64
Browse files Browse the repository at this point in the history
Separate x86-64 specific code out of generic implementation
  • Loading branch information
jserv authored Jun 17, 2023
2 parents 29afb07 + 5b23e25 commit ce6ac24
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 180 deletions.
13 changes: 12 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
include mk/common.mk

ARCH ?= $(shell uname -m)
PWD ?= $(shell pwd)

CC ?= gcc
CFLAGS = -O2
CFLAGS += -Wall -std=gnu99
CFLAGS += -I$(PWD)/src
CFLAGS += -g
LDFLAGS = -lpthread

Expand All @@ -21,6 +25,13 @@ OBJS := \
virtio-blk.o \
diskimg.o \
main.o

ifeq ($(ARCH), x86_64)
CFLAGS += -I$(PWD)/src/arch/x86
CFLAGS += -include src/arch/x86/desc.h
OBJS += arch/x86/vm.o
endif

OBJS := $(addprefix $(OUT)/,$(OBJS))
deps := $(OBJS:%.o=%.o.d)

Expand All @@ -29,7 +40,7 @@ $(BIN): $(OBJS)
$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LDFLAGS)

$(OUT)/%.o: src/%.c
$(Q)mkdir -p $(OUT)
$(Q)mkdir -p $(shell dirname $@)
$(VECHO) " CC\t$@\n"
$(Q)$(CC) -o $@ $(CFLAGS) -c -MMD -MF $@.d $<

Expand Down
3 changes: 3 additions & 0 deletions src/arch/x86/desc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

#define RAM_BASE 0
208 changes: 208 additions & 0 deletions src/arch/x86/vm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#if !defined(__x86_64__) || !defined(__linux__)
#error "This implementation is dedicated to Linux/x86-64."
#endif

#include <asm/bootparam.h>
#include <asm/e820.h>
#include <linux/kvm.h>
#include <linux/kvm_para.h>
#include <string.h>
#include <sys/ioctl.h>

#include "err.h"
#include "vm.h"

static int vm_init_regs(vm_t *v)
{
struct kvm_sregs sregs;
if (ioctl(v->vcpu_fd, KVM_GET_SREGS, &sregs) < 0)
return throw_err("Failed to get registers");

#define X(R) sregs.R.base = 0, sregs.R.limit = ~0, sregs.R.g = 1
X(cs), X(ds), X(fs), X(gs), X(es), X(ss);
#undef X

sregs.cs.db = 1;
sregs.ss.db = 1;
sregs.cr0 |= 1; /* enable protected mode */

if (ioctl(v->vcpu_fd, KVM_SET_SREGS, &sregs) < 0)
return throw_err("Failed to set special registers");

struct kvm_regs regs;
if (ioctl(v->vcpu_fd, KVM_GET_REGS, &regs) < 0)
return throw_err("Failed to get registers");

regs.rflags = 2;
regs.rip = 0x100000, regs.rsi = 0x10000;
if (ioctl(v->vcpu_fd, KVM_SET_REGS, &regs) < 0)
return throw_err("Failed to set registers");

return 0;
}

#define N_ENTRIES 100
static void vm_init_cpu_id(vm_t *v)
{
struct {
uint32_t nent;
uint32_t padding;
struct kvm_cpuid_entry2 entries[N_ENTRIES];
} kvm_cpuid = {.nent = N_ENTRIES};
ioctl(v->kvm_fd, KVM_GET_SUPPORTED_CPUID, &kvm_cpuid);

for (unsigned int i = 0; i < N_ENTRIES; i++) {
struct kvm_cpuid_entry2 *entry = &kvm_cpuid.entries[i];
if (entry->function == KVM_CPUID_SIGNATURE) {
entry->eax = KVM_CPUID_FEATURES;
entry->ebx = 0x4b4d564b; /* KVMK */
entry->ecx = 0x564b4d56; /* VMKV */
entry->edx = 0x4d; /* M */
}
}
ioctl(v->vcpu_fd, KVM_SET_CPUID2, &kvm_cpuid);
}

#define MSR_IA32_MISC_ENABLE 0x000001a0
#define MSR_IA32_MISC_ENABLE_FAST_STRING_BIT 0
#define MSR_IA32_MISC_ENABLE_FAST_STRING \
(1ULL << MSR_IA32_MISC_ENABLE_FAST_STRING_BIT)

#define KVM_MSR_ENTRY(_index, _data) \
(struct kvm_msr_entry) \
{ \
.index = _index, .data = _data \
}
static void vm_init_msrs(vm_t *v)
{
int ndx = 0;
struct kvm_msrs *msrs =
calloc(1, sizeof(struct kvm_msrs) + (sizeof(struct kvm_msr_entry) * 1));

msrs->entries[ndx++] =
KVM_MSR_ENTRY(MSR_IA32_MISC_ENABLE, MSR_IA32_MISC_ENABLE_FAST_STRING);
msrs->nmsrs = ndx;

ioctl(v->vcpu_fd, KVM_SET_MSRS, msrs);

free(msrs);
}

int vm_arch_init(vm_t *v)
{
if (ioctl(v->vm_fd, KVM_SET_TSS_ADDR, 0xffffd000) < 0)
return throw_err("Failed to set TSS addr");

__u64 map_addr = 0xffffc000;
if (ioctl(v->vm_fd, KVM_SET_IDENTITY_MAP_ADDR, &map_addr) < 0)
return throw_err("Failed to set identity map address");

if (ioctl(v->vm_fd, KVM_CREATE_IRQCHIP, 0) < 0)
return throw_err("Failed to create IRQ chip");

struct kvm_pit_config pit = {.flags = 0};
if (ioctl(v->vm_fd, KVM_CREATE_PIT2, &pit) < 0)
return throw_err("Failed to create i8254 interval timer");

return 0;
}

int vm_arch_cpu_init(vm_t *v)
{
vm_init_regs(v);
vm_init_cpu_id(v);
vm_init_msrs(v);
return 0;
}

int vm_arch_init_platform_device(vm_t *v)
{
pci_init(&v->pci);
bus_register_dev(&v->io_bus, &v->pci.pci_addr_dev);
bus_register_dev(&v->io_bus, &v->pci.pci_bus_dev);
if (serial_init(&v->serial, &v->io_bus))
return throw_err("Failed to init UART device");
virtio_blk_init(&v->virtio_blk_dev);
return 0;
}

int vm_arch_load_image(vm_t *v, void *data, size_t datasz)
{
struct boot_params *boot =
(struct boot_params *) ((uint8_t *) v->mem + 0x10000);
void *cmdline = ((uint8_t *) v->mem) + 0x20000;
void *kernel = ((uint8_t *) v->mem) + 0x100000;

memset(boot, 0, sizeof(struct boot_params));
memmove(boot, data, sizeof(struct boot_params));

size_t setup_sectors = boot->hdr.setup_sects;
size_t setupsz = (setup_sectors + 1) * 512;
boot->hdr.vid_mode = 0xFFFF; // VGA
boot->hdr.type_of_loader = 0xFF;
boot->hdr.loadflags |= CAN_USE_HEAP | 0x01 | KEEP_SEGMENTS;
boot->hdr.heap_end_ptr = 0xFE00;
boot->hdr.ext_loader_ver = 0x0;
boot->hdr.cmd_line_ptr = 0x20000;
memset(cmdline, 0, boot->hdr.cmdline_size);
memcpy(cmdline, KERNEL_OPTS, sizeof(KERNEL_OPTS));
memmove(kernel, (char *) data + setupsz, datasz - setupsz);

/* setup E820 memory map to report usable address ranges for initrd */
unsigned int idx = 0;
boot->e820_table[idx++] = (struct boot_e820_entry){
.addr = 0x0,
.size = ISA_START_ADDRESS - 1,
.type = E820_RAM,
};
boot->e820_table[idx++] = (struct boot_e820_entry){
.addr = ISA_END_ADDRESS,
.size = RAM_SIZE - ISA_END_ADDRESS,
.type = E820_RAM,
};
boot->e820_entries = idx;

return 0;
}

int vm_arch_load_initrd(vm_t *v, void *data, size_t datasz)
{
struct boot_params *boot =
(struct boot_params *) ((uint8_t *) v->mem + 0x10000);
unsigned long addr = boot->hdr.initrd_addr_max & ~0xfffff;

for (;;) {
if (addr < 0x100000)
return throw_err("Not enough memory for initrd");
if (addr < (RAM_SIZE - datasz))
break;
addr -= 0x100000;
}

void *initrd = ((uint8_t *) v->mem) + addr;

memset(initrd, 0, datasz);
memmove(initrd, data, datasz);

boot->hdr.ramdisk_image = addr;
boot->hdr.ramdisk_size = datasz;
return 0;
}

int vm_late_init(vm_t *v)
{
return 0;
}

int vm_irq_line(vm_t *v, int irq, int level)
{
struct kvm_irq_level irq_level = {
{.irq = irq},
.level = level,
};

if (ioctl(v->vm_fd, KVM_IRQ_LINE, &irq_level) < 0)
return throw_err("Failed to set the status of an IRQ line");

return 0;
}
3 changes: 3 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ int main(int argc, char *argv[])
if (diskimg_file && vm_load_diskimg(&vm, diskimg_file) < 0)
return throw_err("Failed to load disk image");

if (vm_late_init(&vm) < 0)
return -1;

vm_run(&vm);
vm_exit(&vm);

Expand Down
4 changes: 1 addition & 3 deletions src/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,11 @@ void pci_dev_register(struct pci_dev *dev)
#define PCI_CONFIG_ADDR 0xCF8
#define PCI_CONFIG_DATA 0xCFC

void pci_init(struct pci *pci, struct bus *io_bus)
void pci_init(struct pci *pci)
{
dev_init(&pci->pci_addr_dev, PCI_CONFIG_ADDR, sizeof(uint32_t), pci,
pci_address_io);
dev_init(&pci->pci_bus_dev, PCI_CONFIG_DATA, sizeof(uint32_t), pci,
pci_data_io);
bus_init(&pci->pci_bus);
bus_register_dev(io_bus, &pci->pci_addr_dev);
bus_register_dev(io_bus, &pci->pci_bus_dev);
}
2 changes: 1 addition & 1 deletion src/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ void pci_dev_init(struct pci_dev *dev,
struct pci *pci,
struct bus *io_bus,
struct bus *mmio_bus);
void pci_init();
void pci_init(struct pci *pci);
Loading

0 comments on commit ce6ac24

Please sign in to comment.