1
0
Fork 0
mirror of https://github.com/hermitcore/libhermit.git synced 2025-03-09 00:00:03 +01:00

Merge pull request #65 from daniel-k/pr/uhyve_refactor

tools/uhyve: refactoring and minor fixes
This commit is contained in:
Stefan Lankes 2017-03-01 21:05:51 +01:00 committed by GitHub
commit 8539e1fbf0
4 changed files with 194 additions and 163 deletions

View file

@ -1043,7 +1043,18 @@ int main(int argc, char **argv)
if (ret)
return ret;
if (monitor != UHYVE)
switch(monitor) {
case UHYVE:
return uhyve_loop();
case BAREMETAL:
case QEMU:
return socket_loop(argc, argv);
return uhyve_loop();
default:
perror("Unknown monitor");
}
return 1;
}

View file

@ -70,16 +70,6 @@
(((base) & _AC(0x00ffffff, ULL)) << 16) | \
(((limit) & _AC(0x0000ffff, ULL))))
struct _kvm_segment {
__u64 base;
__u32 limit;
__u16 selector;
__u8 type;
__u8 present, dpl, db, s, l, g, avl;
__u8 unusable;
__u8 padding;
};
#define GDT_GET_G(x) (__u8)(((x) & 0x0080000000000000) >> 55)
#define GDT_GET_DB(x) (__u8)(((x) & 0x0040000000000000) >> 54)
#define GDT_GET_L(x) (__u8)(((x) & 0x0020000000000000) >> 53)

64
tools/uhyve-syscalls.h Normal file
View file

@ -0,0 +1,64 @@
/* Copyright (c) 2017, RWTH Aachen University
* Author(s): Daniel Krebs <github@daniel-krebs.net>
*
* Permission to use, copy, modify, and/or distribute this software
* for any purpose with or without fee is hereby granted, provided
* that the above copyright notice and this permission notice appear
* in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef UHYVE_SYSCALLS_H
#define UHYVE_SYSCALLS_H
#include <unistd.h>
#include <stddef.h>
typedef enum {
UHYVE_PORT_WRITE = 0x499,
UHYVE_PORT_OPEN = 0x500,
UHYVE_PORT_CLOSE = 0x501,
UHYVE_PORT_READ = 0x502,
UHYVE_PORT_EXIT = 0x503,
UHYVE_PORT_LSEEK = 0x504
} uhyve_syscall_t;
typedef struct {
int fd;
const char* buf;
size_t len;
} __attribute__((packed)) uhyve_write_t;
typedef struct {
const char* name;
int flags;
int mode;
int ret;
} __attribute__((packed)) uhyve_open_t;
typedef struct {
int fd;
int ret;
} __attribute__((packed)) uhyve_close_t;
typedef struct {
int fd;
char* buf;
size_t len;
ssize_t ret;
} __attribute__((packed)) uhyve_read_t;
typedef struct {
int fd;
off_t offset;
int whence;
} __attribute__((packed)) uhyve_lseek_t;
#endif // UHYVE_SYSCALLS_H

View file

@ -58,6 +58,7 @@
#include <asm/msr-index.h>
#include "uhyve-cpu.h"
#include "uhyve-syscalls.h"
#include "proxy.h"
#define GUEST_OFFSET 0x0
@ -79,15 +80,8 @@
#define KVM_32BIT_GAP_SIZE (768 << 20)
#define KVM_32BIT_GAP_START (KVM_32BIT_MAX_MEM_SIZE - KVM_32BIT_GAP_SIZE)
#define UHYVE_PORT_WRITE 0x499
#define UHYVE_PORT_OPEN 0x500
#define UHYVE_PORT_CLOSE 0x501
#define UHYVE_PORT_READ 0x502
#define UHYVE_PORT_EXIT 0x503
#define UHYVE_PORT_LSEEK 0x504
#define kvm_ioctl(fd, cmd, arg) ({ \
int ret = ioctl(fd, cmd, arg); \
const int ret = ioctl(fd, cmd, arg); \
if(ret == -1) \
err(1, "KVM: ioctl " #cmd " failed"); \
ret; \
@ -104,147 +98,119 @@ static int kvm = -1, vmfd = -1;
static __thread struct kvm_run *run = NULL;
static __thread int vcpufd = 1;
typedef struct {
int fd;
const char* buf;
size_t len;
} __attribute__((packed)) uhyve_write_t;
typedef struct {
const char* name;
int flags;
int mode;
int ret;
} __attribute__((packed)) uhyve_open_t;
typedef struct {
int fd;
int ret;
} __attribute__((packed)) uhyve_close_t;
typedef struct {
int fd;
char* buf;
size_t len;
ssize_t ret;
} __attribute__((packed)) uhyve_read_t;
typedef struct {
int fd;
off_t offset;
int whence;
} __attribute__((packed)) uhyve_lseek_t;
static size_t memparse(const char *ptr)
static uint64_t memparse(const char *ptr)
{
char *endptr; /* local pointer to end of parsed string */
size_t ret = strtoull(ptr, &endptr, 0);
// local pointer to end of parsed string
char *endptr;
// parse number
uint64_t size = strtoull(ptr, &endptr, 0);
// parse size extension, intentional fall-through
switch (*endptr) {
case 'E':
case 'e':
ret <<= 10;
case 'P':
case 'p':
ret <<= 10;
case 'T':
case 't':
ret <<= 10;
case 'G':
case 'g':
ret <<= 10;
case 'M':
case 'm':
ret <<= 10;
case 'K':
case 'k':
ret <<= 10;
endptr++;
default:
break;
}
case 'E':
case 'e':
size <<= 10;
case 'P':
case 'p':
size <<= 10;
case 'T':
case 't':
size <<= 10;
case 'G':
case 'g':
size <<= 10;
case 'M':
case 'm':
size <<= 10;
case 'K':
case 'k':
size <<= 10;
endptr++;
default:
break;
}
return ret;
return size;
}
/// Just close file descriptor if not already done
static inline void close_fd(int* fd)
{
if(*fd != -1) {
close(*fd);
*fd = -1;
}
}
static void sig_func(int sig)
{
if (vcpufd != -1)
close(vcpufd);
vcpufd = -1;
(void) sig;
pthread_exit(0);
close_fd(&vcpufd);
pthread_exit(NULL);
}
static void uhyve_exit(void)
{
char* str = getenv("HERMIT_VERBOSE");
// only the main thread will execute this
if (vcpu_threads) {
for(uint32_t i=0; i<ncores; i++) {
if (vcpu_threads[i] != pthread_self()) {
pthread_kill(vcpu_threads[i], SIGTERM);
pthread_join(vcpu_threads[i], NULL);
}
for(uint32_t i = 1; i < ncores; i++) {
pthread_kill(vcpu_threads[i], SIGTERM);
pthread_join(vcpu_threads[i], NULL);
}
free(vcpu_threads);
}
if (klog && str && (strcmp(str, "0") != 0))
char* verbose = getenv("HERMIT_VERBOSE");
if (klog && verbose && (strcmp(verbose, "0") != 0))
{
puts("\nDump kernel log:");
puts("================\n");
printf("%s\n", klog);
}
if (vcpufd != -1)
close(vcpufd);
vcpufd = -1;
if (vmfd != -1)
close(vmfd);
vmfd = -1;
if (kvm != -1)
close(kvm);
kvm = -1;
// clean up and close KVM
close_fd(&vcpufd);
close_fd(&vmfd);
close_fd(&kvm);
}
static uint32_t get_cpufreq(void)
{
char line[2048];
char line[128];
uint32_t freq = 0;
char* match;
char* point;
FILE* fp = fopen("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "r");
if (fp) {
if (fgets(line, 2048, fp))
freq = atoi(line) / 1000;
if (fp != NULL) {
if (fgets(line, sizeof(line), fp) != NULL) {
// cpuinfo_max_freq is in kHz
freq = (uint32_t) atoi(line) / 1000;
}
fclose(fp);
} else if( (fp = fopen("/proc/cpuinfo", "r")) ) {
// Resorting to /proc/cpuinfo, however on most systems this will only
// return the current frequency that might change over time.
// Currently only needed when running inside a VM
return freq;
}
// read until we find the line indicating cpu frequency
while(fgets(line, sizeof(line), fp) != NULL) {
match = strstr(line, "cpu MHz");
fp = fopen("/proc/cpuinfo", "r");
if (!fp)
return freq;
if(match != NULL) {
// advance pointer to beginning of number
while( ((*match < '0') || (*match > '9')) && (*match != '\0') )
match++;
while(fgets(line, 2048, fp)) {
if ((match = strstr(line, "cpu MHz")) == NULL)
continue;
freq = (uint32_t) atoi(match);
break;
}
}
// scan strinf for the next number
for(; (*match < 0x30) || (*match > 0x39); match++)
;
for(point = match; ((*point != '.') && (*point != '\0')); point++)
;
*point = '\0';
freq = atoi(match);
fclose(fp);
return freq;
}
return freq;
@ -373,25 +339,27 @@ out:
return 0;
}
/// Filter CPUID functions that are not supported by the hypervisor and enable
/// features according to our needs.
static void filter_cpuid(struct kvm_cpuid2 *kvm_cpuid)
{
/*
* Filter CPUID functions that are not supported by the hypervisor.
*/
for (uint32_t i = 0; i < kvm_cpuid->nent; i++) {
struct kvm_cpuid_entry2 *entry = &kvm_cpuid->entries[i];
switch (entry->function) {
case 1: // CPUID to define basic cpu features
entry->ecx = entry->ecx | (1 << 31); // propagate that we are running on a hypervisor
//entry->ecx = entry->ecx & ~(1 << 21); // disable X2APIC support
entry->edx = entry->edx | (1 << 5); // enable msr support
case 1:
// CPUID to define basic cpu features
entry->ecx |= (1U << 31); // propagate that we are running on a hypervisor
entry->edx |= (1U << 5); // enable msr support
break;
case CPUID_FUNC_PERFMON:
entry->eax = 0x00; /* disable it */
// disable it
entry->eax = 0x00;
break;
default:
/* Keep the CPUID function as -is */
// Keep the CPUID function as-is
break;
};
}
@ -466,16 +434,13 @@ static void setup_system(int vcpufd, uint8_t *mem, uint32_t id)
// all cores use the same startup code
// => all cores use the same sregs
// => only the boot processor has to initialize sregs
if (id == 0)
{
if (id == 0) {
kvm_ioctl(vcpufd, KVM_GET_SREGS, &sregs);
/* Set all cpu/mem system structures */
setup_system_gdt(&sregs, mem, BOOT_GDT);
setup_system_page_tables(&sregs, mem);
setup_system_64bit(&sregs);
//printf("APIC is located at 0x%zx\n", (size_t)sregs.apic_base);
}
kvm_ioctl(vcpufd, KVM_SET_SREGS, &sregs);
@ -485,15 +450,20 @@ static void setup_system(int vcpufd, uint8_t *mem, uint32_t id)
static void setup_cpuid(int kvm, int vcpufd)
{
struct kvm_cpuid2 *kvm_cpuid;
int max_entries = 100;
unsigned int max_entries = 100;
kvm_cpuid = calloc(1, sizeof(*kvm_cpuid) + max_entries * sizeof(*kvm_cpuid->entries));
// allocate space for cpuid we get from KVM
kvm_cpuid = calloc(1, sizeof(*kvm_cpuid) +
(max_entries * sizeof(kvm_cpuid->entries[0])) );
kvm_cpuid->nent = max_entries;
kvm_ioctl(kvm, KVM_GET_SUPPORTED_CPUID, kvm_cpuid);
// set features
filter_cpuid(kvm_cpuid);
kvm_ioctl(vcpufd, KVM_SET_CPUID2, kvm_cpuid);
free(kvm_cpuid);
}
static int vcpu_loop(void)
@ -624,18 +594,9 @@ static int vcpu_init(uint32_t id)
/* Setup registers and memory. */
setup_system(vcpufd, guest_mem, id);
/*
* Initialize registers: instruction pointer for our code, addends,
* and initial flags required by x86 architecture.
* Arguments to the kernel main are passed using the x86_64 calling
* convention: RDI, RSI, RDX, RCX, R8, and R9
*/
struct kvm_regs regs = {
.rip = elf_entry,
.rax = 2,
.rbx = 2,
.rdx = 0,
.rflags = 0x2,
.rip = elf_entry, // entry point to HermitCore
.rflags = 0x2, // POR value required by x86 architecture
};
kvm_ioctl(vcpufd, KVM_SET_REGS, &regs);
@ -662,11 +623,13 @@ static int vcpu_init(uint32_t id)
static void* uhyve_thread(void* arg)
{
size_t id = (size_t) arg;
size_t ret;
const size_t id = (size_t) arg;
// create new cpu
vcpu_init(id);
ret = vcpu_loop();
// run cpu loop until thread gets killed
const size_t ret = vcpu_loop();
return (void*) ret;
}
@ -679,9 +642,9 @@ int uhyve_init(char *path)
// register routine to close the VM
atexit(uhyve_exit);
char* str = getenv("HERMIT_MEM");
if (str)
guest_size = memparse(str);
const char* hermit_memory = getenv("HERMIT_MEM");
if (hermit_memory)
guest_size = memparse(hermit_memory);
kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC);
if (kvm < 0)
@ -716,28 +679,31 @@ int uhyve_init(char *path)
kvm_ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &kvm_region);
kvm_ioctl(vmfd, KVM_CREATE_IRQCHIP, NULL);
//kvm_ioctl(vmfd, KVM_SET_BOOT_CPU_ID, 0);
// create first CPU, it will be the boot processor by default
return vcpu_init(0);
}
int uhyve_loop(void)
{
char* str = getenv("HERMIT_CPUS");
const char* hermit_cpus = getenv("HERMIT_CPUS");
if (hermit_cpus)
ncores = (uint32_t) atoi(hermit_cpus);
if (str)
ncores = atoi(str);
*((uint32_t*) (mboot+0x24)) = ncores;
vcpu_threads = (pthread_t*) calloc(ncores, sizeof(pthread_t));
if (!vcpu_threads)
err(1, "Not enough memoyr");
err(1, "Not enough memory");
// First CPU is special because it will boot the system. Other CPUs will
// be booted linearily after the first one.
vcpu_threads[0] = pthread_self();
// start threads to create VCPU
for(size_t i=1; i<ncores; i++)
pthread_create(vcpu_threads+i, NULL, uhyve_thread, (void*) i);
// start threads to create VCPUs
for(size_t i = 1; i < ncores; i++)
pthread_create(&vcpu_threads[i], NULL, uhyve_thread, (void*) i);
// Run first CPU
return vcpu_loop();
}