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:
commit
8539e1fbf0
4 changed files with 194 additions and 163 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
64
tools/uhyve-syscalls.h
Normal 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
|
268
tools/uhyve.c
268
tools/uhyve.c
|
@ -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, ®s);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue