Compare commits
32 Commits
Author | SHA1 | Date |
---|---|---|
|
7d3ff6c39c | |
|
f901021254 | |
|
7e2239ae07 | |
|
0be2d439a9 | |
|
78d13aafff | |
|
5c90daa2f7 | |
|
0258af1295 | |
|
885d642c43 | |
|
e81f9b73a0 | |
|
13d0351806 | |
|
01a748bbe6 | |
|
e5434228c9 | |
|
fd839ead34 | |
|
73227fcca5 | |
|
0ba624a03b | |
|
6f8bc92b04 | |
|
11479de00f | |
|
fb5c6e2fbf | |
|
b80a18ac0f | |
|
05bbf88e72 | |
|
052bd8ec1d | |
|
6a3b27833c | |
|
136b6c49c6 | |
|
c8c5185dab | |
|
22d7b61b2b | |
|
3d08c75533 | |
|
826ffe0c94 | |
|
c226f2286a | |
|
03924f4d6c | |
|
2176075a54 | |
|
1d54577077 | |
|
b805f3a1a0 |
File diff suppressed because it is too large
Load Diff
22
Makefile
22
Makefile
|
@ -1,7 +1,7 @@
|
|||
LIBRARY_HEADER = include/libkrun.h
|
||||
|
||||
ABI_VERSION=1
|
||||
FULL_VERSION=1.12.2
|
||||
FULL_VERSION=1.13.0
|
||||
|
||||
INIT_SRC = init/init.c
|
||||
KBS_INIT_SRC = init/tee/kbs/kbs.h \
|
||||
|
@ -27,9 +27,6 @@ ifeq ($(SEV),1)
|
|||
INIT_SRC += $(SNP_INIT_SRC)
|
||||
BUILD_INIT = 0
|
||||
endif
|
||||
ifeq ($(GPU),1)
|
||||
FEATURE_FLAGS += --features gpu
|
||||
endif
|
||||
ifeq ($(VIRGL_RESOURCE_MAP2),1)
|
||||
FEATURE_FLAGS += --features virgl_resource_map2
|
||||
endif
|
||||
|
@ -39,12 +36,20 @@ endif
|
|||
ifeq ($(NET),1)
|
||||
FEATURE_FLAGS += --features net
|
||||
endif
|
||||
ifeq ($(EFI),1)
|
||||
VARIANT = -efi
|
||||
FEATURE_FLAGS := --features efi # EFI Implies blk and net
|
||||
BUILD_INIT = 0
|
||||
endif
|
||||
ifeq ($(GPU),1)
|
||||
FEATURE_FLAGS += --features gpu
|
||||
endif
|
||||
ifeq ($(SND),1)
|
||||
FEATURE_FLAGS += --features snd
|
||||
endif
|
||||
ifeq ($(EFI),1)
|
||||
VARIANT = -efi
|
||||
FEATURE_FLAGS := --features efi,gpu
|
||||
ifeq ($(NITRO),1)
|
||||
VARIANT = -nitro
|
||||
FEATURE_FLAGS := --features nitro
|
||||
BUILD_INIT = 0
|
||||
endif
|
||||
|
||||
|
@ -91,6 +96,9 @@ $(LIBRARY_RELEASE_$(OS)): $(INIT_BINARY)
|
|||
ifeq ($(SEV),1)
|
||||
mv target/release/libkrun.so target/release/$(KRUN_BASE_$(OS))
|
||||
endif
|
||||
ifeq ($(NITRO),1)
|
||||
mv target/release/libkrun.so target/release/$(KRUN_BASE_$(OS))
|
||||
endif
|
||||
ifeq ($(OS),Darwin)
|
||||
ifeq ($(EFI),1)
|
||||
install_name_tool -id libkrun-efi.dylib target/release/libkrun.dylib
|
||||
|
|
|
@ -5,6 +5,7 @@ LDFLAGS_aarch64_Linux = -lkrun
|
|||
LDFLAGS_arm64_Darwin = -L/opt/homebrew/lib -lkrun
|
||||
LDFLAGS_sev = -lkrun-sev
|
||||
LDFLAGS_efi = -L/opt/homebrew/lib -lkrun-efi
|
||||
LDFLAGS_nitro = -lkrun-nitro
|
||||
CFLAGS = -O2 -g -I../include
|
||||
ROOTFS_DISTRO := fedora
|
||||
ROOTFS_DIR = rootfs_$(ROOTFS_DISTRO)
|
||||
|
@ -42,6 +43,9 @@ ifeq ($(OS),Darwin)
|
|||
codesign --entitlements chroot_vm.entitlements --force -s - $@
|
||||
endif
|
||||
|
||||
nitro: nitro.c
|
||||
gcc -o $@ $< $(CFLAGS) $(LDFLAGS_nitro)
|
||||
|
||||
# Build the rootfs to be used with chroot_vm.
|
||||
rootfs:
|
||||
mkdir -p $(ROOTFS_DIR)
|
||||
|
@ -50,4 +54,4 @@ rootfs:
|
|||
podman rm libkrun_chroot_vm
|
||||
|
||||
clean:
|
||||
rm -rf chroot_vm $(ROOTFS_DIR) launch-tee boot_efi external_kernel
|
||||
rm -rf chroot_vm $(ROOTFS_DIR) launch-tee boot_efi external_kernel nitro
|
||||
|
|
|
@ -90,7 +90,7 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline)
|
|||
return false;
|
||||
}
|
||||
|
||||
int connect_to_passt(char *socket_path)
|
||||
int connect_to_passt(char const *socket_path)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -34,6 +35,8 @@ static void print_help(char *const name)
|
|||
"Usage: %s [OPTIONS] NEWROOT COMMAND [COMMAND_ARGS...]\n"
|
||||
"OPTIONS: \n"
|
||||
" -h --help Show help\n"
|
||||
" --log=PATH Write libkrun log to file or named pipe at PATH\n"
|
||||
" --color-log=PATH Write libkrun log to file or named pipe at PATH, use color\n"
|
||||
" --net=NET_MODE Set network mode\n"
|
||||
" --passt-socket=PATH Instead of starting passt, connect to passt socket at PATH"
|
||||
"NET_MODE can be either TSI (default) or PASST\n"
|
||||
|
@ -47,6 +50,8 @@ static void print_help(char *const name)
|
|||
|
||||
static const struct option long_options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "log", required_argument, NULL, 'L' },
|
||||
{ "color-log", required_argument, NULL, 'C' },
|
||||
{ "net_mode", required_argument, NULL, 'N' },
|
||||
{ "passt-socket", required_argument, NULL, 'P' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
|
@ -54,12 +59,27 @@ static const struct option long_options[] = {
|
|||
|
||||
struct cmdline {
|
||||
bool show_help;
|
||||
int log_target;
|
||||
uint32_t log_style;
|
||||
enum net_mode net_mode;
|
||||
char const *passt_socket_path;
|
||||
char const *new_root;
|
||||
char *const *guest_argv;
|
||||
};
|
||||
|
||||
bool cmdline_set_log_target(struct cmdline *cmdline, const char *arg) {
|
||||
int fd = open(arg, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
perror(arg);
|
||||
return false;
|
||||
}
|
||||
if (cmdline->log_target > 0) {
|
||||
close(cmdline->log_target);
|
||||
}
|
||||
cmdline->log_target = fd;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline)
|
||||
{
|
||||
assert(cmdline != NULL);
|
||||
|
@ -71,6 +91,8 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline)
|
|||
.passt_socket_path = NULL,
|
||||
.new_root = NULL,
|
||||
.guest_argv = NULL,
|
||||
.log_target = KRUN_LOG_TARGET_DEFAULT,
|
||||
.log_style = KRUN_LOG_STYLE_AUTO
|
||||
};
|
||||
|
||||
int option_index = 0;
|
||||
|
@ -81,6 +103,14 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline)
|
|||
case 'h':
|
||||
cmdline->show_help = true;
|
||||
return true;
|
||||
case 'C':
|
||||
cmdline->log_style = KRUN_LOG_STYLE_ALWAYS;
|
||||
/* fall through */
|
||||
case 'L':
|
||||
if (!cmdline_set_log_target(cmdline, optarg)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'N':
|
||||
if (strcasecmp("TSI", optarg) == 0) {
|
||||
cmdline->net_mode = NET_MODE_TSI;
|
||||
|
@ -119,7 +149,7 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline)
|
|||
return false;
|
||||
}
|
||||
|
||||
int connect_to_passt()
|
||||
int connect_to_passt(char const *socket_path)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
@ -130,7 +160,7 @@ int connect_to_passt()
|
|||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, "/tmp/passt_1.socket", sizeof(addr.sun_path) - 1);
|
||||
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
||||
|
||||
if (connect(socket_fd, (const struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
perror("Failed to bind passt socket");
|
||||
|
@ -217,8 +247,8 @@ int main(int argc, char *const argv[])
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Set the log level to "off".
|
||||
err = krun_set_log_level(0);
|
||||
// Set the log level to "warn".
|
||||
err = krun_init_log(cmdline.log_target, KRUN_LOG_LEVEL_WARN, cmdline.log_style, 0);
|
||||
if (err) {
|
||||
errno = -err;
|
||||
perror("Error configuring log level");
|
||||
|
|
|
@ -149,7 +149,7 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline)
|
|||
return false;
|
||||
}
|
||||
|
||||
int connect_to_passt(char *socket_path)
|
||||
int connect_to_passt(char const *socket_path)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* This is an example implementing running an example AWS nitro enclave with
|
||||
* libkrun.
|
||||
*
|
||||
* Given a nitro enclave image, run the image in a nitro enclave with 1 vCPU and
|
||||
* 256 MiB of memory allocated.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <libkrun.h>
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define MAX_ARGS_LEN 4096
|
||||
#ifndef MAX_PATH
|
||||
#define MAX_PATH 4096
|
||||
#endif
|
||||
|
||||
#define IPC_SOCK_PATH "/tmp/krun_nitro_example_ipc.sock"
|
||||
|
||||
static void print_help(char *const name)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: %s EIF_FILE [COMMAND_ARGS...]\n"
|
||||
"OPTIONS: \n"
|
||||
" -h --help Show help\n"
|
||||
"\n"
|
||||
"ENCLAVE_IMAGE: The enclave image to run\n",
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
static const struct option long_options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
struct cmdline {
|
||||
bool show_help;
|
||||
const char *eif_path;
|
||||
};
|
||||
|
||||
bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline)
|
||||
{
|
||||
int c, option_index = 0;
|
||||
|
||||
assert(cmdline != NULL);
|
||||
|
||||
// set the defaults
|
||||
*cmdline = (struct cmdline){
|
||||
.show_help = false,
|
||||
.eif_path = NULL,
|
||||
};
|
||||
|
||||
// the '+' in optstring is a GNU extension that disables permutating argv
|
||||
while ((c = getopt_long(argc, argv, "+h", long_options, &option_index)) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
cmdline->show_help = true;
|
||||
return true;
|
||||
case '?':
|
||||
return false;
|
||||
default:
|
||||
fprintf(stderr, "internal argument parsing error (returned character code 0x%x)\n", c);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
cmdline->eif_path = argv[optind];
|
||||
return true;
|
||||
} else
|
||||
fprintf(stderr, "Missing EIF_FILE argument");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void *listen_enclave_output(void *opaque)
|
||||
{
|
||||
int ret, fd = (int) opaque, sock, len;
|
||||
char buf[512];
|
||||
struct sockaddr_un client_sockaddr;
|
||||
|
||||
sock = accept(fd, (struct sockaddr *) &client_sockaddr, &len);
|
||||
if (sock < 1)
|
||||
return (void *) -1;
|
||||
|
||||
for (;;) {
|
||||
ret = read(sock, &buf, 512);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
else if (ret < 512) {
|
||||
buf[ret] = '\0';
|
||||
}
|
||||
|
||||
printf("%s", buf);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *const argv[])
|
||||
{
|
||||
int ret, ctx_id, err, i, sock_fd, enable = 1;
|
||||
struct cmdline cmdline;
|
||||
struct sockaddr_un addr;
|
||||
pthread_t thread;
|
||||
|
||||
if (!parse_cmdline(argc, argv, &cmdline)) {
|
||||
putchar('\n');
|
||||
print_help(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cmdline.show_help){
|
||||
print_help(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set the log level to "off".
|
||||
err = krun_set_log_level(0);
|
||||
if (err) {
|
||||
errno = -err;
|
||||
perror("Error configuring log level");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create the configuration context.
|
||||
ctx_id = krun_create_ctx();
|
||||
if (ctx_id < 0) {
|
||||
errno = -ctx_id;
|
||||
perror("Error creating configuration context");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Configure the number of vCPUs (1) and the amount of RAM (512 MiB).
|
||||
if (err = krun_set_vm_config(ctx_id, 1, 512)) {
|
||||
errno = -err;
|
||||
perror("Error configuring the number of vCPUs and/or the amount of RAM");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set the nitro enclave image specified on the command line.
|
||||
if (err = krun_nitro_set_image(ctx_id, cmdline.eif_path,
|
||||
KRUN_NITRO_IMG_TYPE_EIF)) {
|
||||
errno = -err;
|
||||
perror("Error configuring nitro enclave image");
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
// Configure the nitro enclave to run in debug mode.
|
||||
if (err = krun_nitro_set_start_flags(ctx_id, KRUN_NITRO_START_FLAG_DEBUG)) {
|
||||
errno = -err;
|
||||
perror("Error configuring nitro enclave start flags");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create and initialize UNIX IPC socket for reading enclave output.
|
||||
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("Error creating UNIX IPC socket for enclave communication");
|
||||
return -1;
|
||||
}
|
||||
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, IPC_SOCK_PATH);
|
||||
|
||||
// Listen on the socket for enclave output.
|
||||
unlink(IPC_SOCK_PATH);
|
||||
ret = bind(sock_fd, (struct sockaddr *) &addr, sizeof(addr));
|
||||
if (ret < 0) {
|
||||
perror("Error binding socket");
|
||||
close(sock_fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = listen(sock_fd, 1);
|
||||
if (ret < 0) {
|
||||
perror("Error listening on socket");
|
||||
close(sock_fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Configure the IPC socket to read output from the enclave. The "port"
|
||||
// argument is ignored.
|
||||
if (err = krun_add_vsock_port(ctx_id, 0, IPC_SOCK_PATH)) {
|
||||
close(sock_fd);
|
||||
errno = -err;
|
||||
perror("Error configuring enclave vsock");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = pthread_create(&thread, NULL, listen_enclave_output,
|
||||
(void *) sock_fd);
|
||||
if (ret < 0) {
|
||||
perror("unable to create new listener thread");
|
||||
close(sock_fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Start and enter the microVM. Unless there is some error while creating the microVM
|
||||
// this function never returns.
|
||||
if (err = krun_start_enter(ctx_id)) {
|
||||
close(sock_fd);
|
||||
errno = -err;
|
||||
perror("Error creating the microVM");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = pthread_join(thread, NULL);
|
||||
if (ret < 0) {
|
||||
perror("unable to join listener thread");
|
||||
close(sock_fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -26,6 +26,44 @@ extern "C" {
|
|||
*/
|
||||
int32_t krun_set_log_level(uint32_t level);
|
||||
|
||||
|
||||
#define KRUN_LOG_TARGET_DEFAULT -1
|
||||
|
||||
#define KRUN_LOG_LEVEL_OFF 0
|
||||
#define KRUN_LOG_LEVEL_ERROR 1
|
||||
#define KRUN_LOG_LEVEL_WARN 2
|
||||
#define KRUN_LOG_LEVEL_INFO 3
|
||||
#define KRUN_LOG_LEVEL_DEBUG 4
|
||||
#define KRUN_LOG_LEVEL_TRACE 5
|
||||
|
||||
#define KRUN_LOG_STYLE_AUTO 0
|
||||
#define KRUN_LOG_STYLE_ALWAYS 1
|
||||
#define KRUN_LOG_STYLE_NEVER 2
|
||||
|
||||
#define KRUN_LOG_OPTION_NO_ENV 1
|
||||
|
||||
/**
|
||||
* Initializes logging for the library.
|
||||
*
|
||||
* Arguments:
|
||||
* "target_fd" - File descriptor to write log to. Note that using a file descriptor pointing to a regular file on
|
||||
* filesystem might slow down the VM.
|
||||
* Use KRUN_LOG_TARGET_DEFAULT to use the default target for log output (stderr).
|
||||
*
|
||||
* "level" - Level is an integer specifying the level of verbosity, higher number means more verbose log.
|
||||
* The log levels are described by the constants: KRUN_LOG_LEVEL_{OFF, ERROR, WARN, INFO, DEBUG, TRACE}
|
||||
*
|
||||
* "style" - Enable/disable usage of terminal escape sequences (to display colors)
|
||||
* One of: KRUN_LOG_STYLE_{AUTO, ALWAYS, NEVER}.
|
||||
*
|
||||
* "options" - Bitmask of logging options, use 0 for default options.
|
||||
* KRUN_LOG_OPTION_NO_ENV to disallow environment variables to override these settings.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success or a negative error number on failure.
|
||||
*/
|
||||
int32_t krun_init_log(int target_fd, uint32_t level, uint32_t style, uint32_t options);
|
||||
|
||||
/**
|
||||
* Creates a configuration context.
|
||||
*
|
||||
|
@ -585,6 +623,29 @@ int32_t krun_check_nested_virt(void);
|
|||
*/
|
||||
int32_t krun_split_irqchip(uint32_t ctx_id, bool enable);
|
||||
|
||||
#define KRUN_NITRO_IMG_TYPE_EIF 1
|
||||
/**
|
||||
* Configure a Nitro Enclaves image.
|
||||
*
|
||||
* Arguments:
|
||||
* "ctx_id" - the configuration context ID.
|
||||
* "image_path" - a null-terminated string representing the path of the image
|
||||
* in the host.
|
||||
* "image_type" - the type of enclave image being provided.
|
||||
*/
|
||||
int32_t krun_nitro_set_image(uint32_t ctx_id, const char *image_path,
|
||||
uint32_t image_type);
|
||||
|
||||
#define KRUN_NITRO_START_FLAG_DEBUG (1 << 0)
|
||||
/**
|
||||
* Configure a Nitro Enclave's start flags.
|
||||
*
|
||||
* Arguments:
|
||||
* "ctx_id" - the configuration context ID.
|
||||
* "start_flags" - Start flags.
|
||||
*/
|
||||
int32_t krun_nitro_set_start_flags(uint32_t ctx_id, uint64_t start_flags);
|
||||
|
||||
/**
|
||||
* Starts and enters the microVM with the configured parameters. The VMM will attempt to take over
|
||||
* stdin/stdout to manage them on behalf of the process running inside the isolated environment,
|
||||
|
|
|
@ -12,6 +12,7 @@ efi = []
|
|||
[dependencies]
|
||||
libc = ">=0.2.39"
|
||||
vm-memory = { version = ">=0.13", features = ["backend-mmap"] }
|
||||
vmm-sys-util = ">= 0.14"
|
||||
|
||||
arch_gen = { path = "../arch_gen" }
|
||||
smbios = { path = "../smbios" }
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
use std::{mem, num::TryFromIntError, result};
|
||||
use std::{mem, mem::offset_of, num::TryFromIntError, result};
|
||||
|
||||
use super::super::get_fdt_addr;
|
||||
use kvm_bindings::{
|
||||
user_pt_regs, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG_CRM_MASK,
|
||||
kvm_regs, user_pt_regs, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG_CRM_MASK,
|
||||
KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_SHIFT,
|
||||
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP1_MASK,
|
||||
KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_SHIFT,
|
||||
|
@ -42,24 +42,9 @@ const PSR_D_BIT: u64 = 0x0000_0200;
|
|||
// Taken from arch/arm64/kvm/inject_fault.c.
|
||||
const PSTATE_FAULT_BITS_64: u64 = PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT;
|
||||
|
||||
// Following are macros that help with getting the ID of a aarch64 core register.
|
||||
// This is a macro that helps with getting the ID of a aarch64 core register.
|
||||
// The core register are represented by the user_pt_regs structure. Look for it in
|
||||
// arch/arm64/include/uapi/asm/ptrace.h.
|
||||
|
||||
// This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
|
||||
// an instance of that structure.
|
||||
// It uses a null pointer to retrieve the offset to the field.
|
||||
// Inspired by C solution: `#define offsetof(str, f) ((size_t)(&((str *)0)->f))`.
|
||||
// Doing `offset__of!(user_pt_regs, pstate)` in our rust code will trigger the following:
|
||||
// unsafe { &(*(0 as *const user_pt_regs)).pstate as *const _ as usize }
|
||||
// The dereference expression produces an lvalue, but that lvalue is not actually read from,
|
||||
// we're just doing pointer math on it, so in theory, it should safe.
|
||||
macro_rules! offset__of {
|
||||
($str:ty, $field:ident) => {
|
||||
unsafe { &(*(std::ptr::null::<user_pt_regs>())).$field as *const _ as usize }
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! arm64_core_reg {
|
||||
($reg: tt) => {
|
||||
// As per `kvm_arm_copy_reg_indices`, the id of a core register can be obtained like this:
|
||||
|
@ -87,7 +72,7 @@ macro_rules! arm64_core_reg {
|
|||
KVM_REG_ARM64 as u64
|
||||
| KVM_REG_SIZE_U64 as u64
|
||||
| u64::from(KVM_REG_ARM_CORE)
|
||||
| ((offset__of!(user_pt_regs, $reg) / mem::size_of::<u32>()) as u64)
|
||||
| (((offset_of!(kvm_regs, regs) + offset_of!(user_pt_regs, $reg)) / mem::size_of::<u32>()) as u64)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -126,14 +111,12 @@ arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
|
|||
/// * `mem` - Reserved DRAM for current VM.
|
||||
pub fn setup_regs(vcpu: &VcpuFd, cpu_id: u8, boot_ip: u64, mem: &GuestMemoryMmap) -> Result<()> {
|
||||
// Get the register index of the PSTATE (Processor State) register.
|
||||
#[allow(deref_nullptr)]
|
||||
vcpu.set_one_reg(arm64_core_reg!(pstate), &PSTATE_FAULT_BITS_64.to_le_bytes())
|
||||
.map_err(Error::SetCoreRegister)?;
|
||||
|
||||
// Other vCPUs are powered off initially awaiting PSCI wakeup.
|
||||
if cpu_id == 0 {
|
||||
// Setting the PC (Processor Counter) to the current program address (kernel address).
|
||||
#[allow(deref_nullptr)]
|
||||
vcpu.set_one_reg(arm64_core_reg!(pc), &boot_ip.to_le_bytes())
|
||||
.map_err(Error::SetCoreRegister)?;
|
||||
|
||||
|
@ -141,7 +124,6 @@ pub fn setup_regs(vcpu: &VcpuFd, cpu_id: u8, boot_ip: u64, mem: &GuestMemoryMmap
|
|||
// "The device tree blob (dtb) must be placed on an 8-byte boundary and must
|
||||
// not exceed 2 megabytes in size." -> https://www.kernel.org/doc/Documentation/arm64/booting.txt.
|
||||
// We are choosing to place it the end of DRAM. See `get_fdt_addr`.
|
||||
#[allow(deref_nullptr)]
|
||||
vcpu.set_one_reg(arm64_core_reg!(regs), &get_fdt_addr(mem).to_le_bytes())
|
||||
.map_err(Error::SetCoreRegister)?;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@ pub use self::macos::*;
|
|||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::{round_up, ArchMemoryInfo};
|
||||
use crate::ArchMemoryInfo;
|
||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap};
|
||||
use vmm_sys_util::align_upwards;
|
||||
|
||||
#[cfg(feature = "efi")]
|
||||
use smbios;
|
||||
|
@ -44,7 +45,7 @@ pub fn arch_memory_regions(
|
|||
initrd_size: u64,
|
||||
) -> (ArchMemoryInfo, Vec<(GuestAddress, usize)>) {
|
||||
let page_size: usize = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() };
|
||||
let dram_size = round_up(size, page_size);
|
||||
let dram_size = align_upwards!(size, page_size);
|
||||
let ram_last_addr = layout::DRAM_MEM_START + (dram_size as u64);
|
||||
let shm_start_addr = ((ram_last_addr / 0x4000_0000) + 1) * 0x4000_0000;
|
||||
|
||||
|
@ -97,8 +98,9 @@ pub fn get_kernel_start() -> u64 {
|
|||
|
||||
/// Returns the memory address where the initrd could be loaded.
|
||||
pub fn initrd_load_addr(guest_mem: &GuestMemoryMmap, initrd_size: usize) -> super::Result<u64> {
|
||||
let round_to_pagesize = |size| (size + (super::PAGE_SIZE - 1)) & !(super::PAGE_SIZE - 1);
|
||||
match GuestAddress(get_fdt_addr(guest_mem)).checked_sub(round_to_pagesize(initrd_size) as u64) {
|
||||
match GuestAddress(get_fdt_addr(guest_mem))
|
||||
.checked_sub(align_upwards!(initrd_size, super::PAGE_SIZE) as u64)
|
||||
{
|
||||
Some(offset) => {
|
||||
if guest_mem.address_in_range(offset) {
|
||||
Ok(offset.raw_value())
|
||||
|
|
|
@ -48,12 +48,3 @@ pub struct InitrdConfig {
|
|||
|
||||
/// Default (smallest) memory page size for the supported architectures.
|
||||
pub const PAGE_SIZE: usize = 4096;
|
||||
|
||||
pub fn round_up(size: usize, align: usize) -> usize {
|
||||
let page_mask = align - 1;
|
||||
(size + page_mask) & !page_mask
|
||||
}
|
||||
pub fn round_down(size: usize, align: usize) -> usize {
|
||||
let page_mask = !(align - 1);
|
||||
size & page_mask
|
||||
}
|
||||
|
|
|
@ -17,12 +17,13 @@ pub mod msr;
|
|||
/// Logic for configuring x86_64 registers.
|
||||
pub mod regs;
|
||||
|
||||
use crate::{round_up, ArchMemoryInfo, InitrdConfig};
|
||||
use crate::{ArchMemoryInfo, InitrdConfig};
|
||||
use arch_gen::x86::bootparam::{boot_params, E820_RAM};
|
||||
use vm_memory::Bytes;
|
||||
use vm_memory::{
|
||||
Address, ByteValued, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion,
|
||||
};
|
||||
use vmm_sys_util::align_upwards;
|
||||
|
||||
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
|
||||
// trait (in this case `ByteValued`) where:
|
||||
|
@ -73,7 +74,7 @@ pub fn arch_memory_regions(
|
|||
) -> (ArchMemoryInfo, Vec<(GuestAddress, usize)>) {
|
||||
let page_size: usize = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() };
|
||||
|
||||
let size = round_up(size, page_size);
|
||||
let size = align_upwards!(size, page_size);
|
||||
|
||||
// It's safe to cast MMIO_MEM_START to usize because it fits in a u32 variable
|
||||
// (It points to an address in the 32 bit space).
|
||||
|
@ -155,7 +156,7 @@ pub fn arch_memory_regions(
|
|||
) -> (ArchMemoryInfo, Vec<(GuestAddress, usize)>) {
|
||||
let page_size: usize = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() };
|
||||
|
||||
let size = round_up(size, page_size);
|
||||
let size = align_upwards!(size, page_size);
|
||||
if let Some(kernel_load_addr) = kernel_load_addr {
|
||||
if size < (kernel_load_addr + kernel_size as u64) as usize {
|
||||
panic!("Kernel doesn't fit in RAM");
|
||||
|
|
|
@ -5,7 +5,7 @@ authors = ["Amazon Firecracker team <firecracker-devel@amazon.com>"]
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vmm-sys-util = "0.12.1"
|
||||
vmm-sys-util = ">= 0.14"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
kvm-bindings = { version = ">=0.11", features = ["fam-wrappers"] }
|
||||
|
|
|
@ -13,11 +13,11 @@ efi = ["blk", "net"]
|
|||
gpu = ["rutabaga_gfx", "thiserror", "zerocopy", "zerocopy-derive"]
|
||||
snd = ["pw", "thiserror"]
|
||||
virgl_resource_map2 = []
|
||||
nitro = []
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.2.0"
|
||||
crossbeam-channel = ">=0.5.15"
|
||||
env_logger = "0.9.0"
|
||||
libc = ">=0.2.39"
|
||||
libloading = "0.8"
|
||||
log = "0.4.0"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crossbeam_channel::unbounded;
|
||||
use kvm_bindings::{
|
||||
kvm_enable_cap, kvm_irq_routing, kvm_irq_routing_entry, kvm_irq_routing_entry__bindgen_ty_1,
|
||||
kvm_irq_routing_msi, KVM_CAP_SPLIT_IRQCHIP, KVM_IRQ_ROUTING_MSI,
|
||||
kvm_enable_cap, kvm_irq_routing_entry, kvm_irq_routing_entry__bindgen_ty_1,
|
||||
kvm_irq_routing_msi, KvmIrqRouting, KVM_CAP_SPLIT_IRQCHIP, KVM_IRQ_ROUTING_MSI,
|
||||
};
|
||||
use kvm_ioctls::{Error, VmFd};
|
||||
|
||||
|
@ -125,20 +125,10 @@ impl IoApic {
|
|||
|
||||
(0..IOAPIC_NUM_PINS).for_each(|i| ioapic.add_msi_route(i));
|
||||
|
||||
let mut irq_routing = utils::sized_vec::vec_with_array_field::<
|
||||
kvm_irq_routing,
|
||||
kvm_irq_routing_entry,
|
||||
>(ioapic.irq_routes.len());
|
||||
irq_routing[0].nr = ioapic.irq_routes.len() as u32;
|
||||
irq_routing[0].flags = 0;
|
||||
|
||||
unsafe {
|
||||
let entries_slice: &mut [kvm_irq_routing_entry] =
|
||||
irq_routing[0].entries.as_mut_slice(ioapic.irq_routes.len());
|
||||
entries_slice.copy_from_slice(ioapic.irq_routes.as_slice());
|
||||
}
|
||||
|
||||
vm.set_gsi_routing(&irq_routing[0])?;
|
||||
let mut routing = KvmIrqRouting::new(ioapic.irq_routes.len()).unwrap();
|
||||
let routing_entires = routing.as_mut_slice();
|
||||
routing_entires.copy_from_slice(ioapic.irq_routes.as_slice());
|
||||
vm.set_gsi_routing(&routing)?;
|
||||
|
||||
Ok(ioapic)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ pub mod console;
|
|||
pub mod descriptor_utils;
|
||||
pub mod device;
|
||||
pub mod file_traits;
|
||||
#[cfg(not(feature = "tee"))]
|
||||
#[cfg(not(any(feature = "tee", feature = "nitro")))]
|
||||
pub mod fs;
|
||||
#[cfg(feature = "gpu")]
|
||||
pub mod gpu;
|
||||
|
@ -42,7 +42,7 @@ pub use self::balloon::*;
|
|||
pub use self::block::{Block, CacheType};
|
||||
pub use self::console::*;
|
||||
pub use self::device::*;
|
||||
#[cfg(not(feature = "tee"))]
|
||||
#[cfg(not(any(feature = "tee", feature = "nitro")))]
|
||||
pub use self::fs::*;
|
||||
#[cfg(feature = "gpu")]
|
||||
pub use self::gpu::*;
|
||||
|
|
|
@ -8,6 +8,5 @@ edition = "2021"
|
|||
crossbeam-channel = ">=0.5.15"
|
||||
libloading = "0.8"
|
||||
log = "0.4.0"
|
||||
env_logger = "0.9.0"
|
||||
|
||||
arch = { path = "../arch" }
|
||||
|
|
|
@ -88,6 +88,10 @@ const CNTHCTL_EL0PCTEN: u64 = 1 << 0;
|
|||
// Trap accesses to both virtual and physical counter registers.
|
||||
const CNTHCTL_EL2_BITS: u64 = CNTHCTL_EL0VCTEN | CNTHCTL_EL0PCTEN;
|
||||
|
||||
const AA64PFR0_EL1_EL2EN: u64 = 1 << 8;
|
||||
const AA64PFR0_EL1_GIC3EN: u64 = 1 << 24;
|
||||
const AA64PFR1_EL1_SMEMASK: u64 = 3 << 24;
|
||||
|
||||
const EC_WFX_TRAP: u64 = 0x1;
|
||||
const EC_AA64_HVC: u64 = 0x16;
|
||||
const EC_AA64_SMC: u64 = 0x17;
|
||||
|
@ -399,6 +403,53 @@ impl HvfVcpu<'_> {
|
|||
if ret != HV_SUCCESS {
|
||||
return Err(Error::VcpuInitialRegisters);
|
||||
}
|
||||
|
||||
// Enable EL2 and GICv3 in ID_AA64PFR0_EL1
|
||||
let val: u64 = 0;
|
||||
let ret = unsafe {
|
||||
hv_vcpu_get_sys_reg(
|
||||
self.vcpuid,
|
||||
hv_sys_reg_t_HV_SYS_REG_ID_AA64PFR0_EL1,
|
||||
&val as *const _ as *mut _,
|
||||
)
|
||||
};
|
||||
if ret != HV_SUCCESS {
|
||||
return Err(Error::VcpuInitialRegisters);
|
||||
}
|
||||
let ret = unsafe {
|
||||
hv_vcpu_set_sys_reg(
|
||||
self.vcpuid,
|
||||
hv_sys_reg_t_HV_SYS_REG_ID_AA64PFR0_EL1,
|
||||
val | AA64PFR0_EL1_EL2EN | AA64PFR0_EL1_GIC3EN,
|
||||
)
|
||||
};
|
||||
if ret != HV_SUCCESS {
|
||||
return Err(Error::VcpuInitialRegisters);
|
||||
}
|
||||
|
||||
// If SME is enabled in ID_AA64PFR1_EL1 in the VM, the guest will
|
||||
// break after enabling the MMU. Mask it out.
|
||||
let val: u64 = 0;
|
||||
let ret = unsafe {
|
||||
hv_vcpu_get_sys_reg(
|
||||
self.vcpuid,
|
||||
hv_sys_reg_t_HV_SYS_REG_ID_AA64PFR1_EL1,
|
||||
&val as *const _ as *mut _,
|
||||
)
|
||||
};
|
||||
if ret != HV_SUCCESS {
|
||||
return Err(Error::VcpuInitialRegisters);
|
||||
}
|
||||
let ret = unsafe {
|
||||
hv_vcpu_set_sys_reg(
|
||||
self.vcpuid,
|
||||
hv_sys_reg_t_HV_SYS_REG_ID_AA64PFR1_EL1,
|
||||
val & !AA64PFR1_EL1_SMEMASK,
|
||||
)
|
||||
};
|
||||
if ret != HV_SUCCESS {
|
||||
return Err(Error::VcpuInitialRegisters);
|
||||
}
|
||||
} else {
|
||||
let ret = unsafe {
|
||||
hv_vcpu_set_reg(self.vcpuid, hv_reg_t_HV_REG_CPSR, PSTATE_EL1_FAULT_BITS_64)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "libkrun"
|
||||
version = "1.12.2"
|
||||
version = "1.13.0"
|
||||
authors = ["Sergio Lopez <slp@redhat.com>"]
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
|
@ -14,10 +14,11 @@ efi = [ "blk", "net" ]
|
|||
gpu = []
|
||||
snd = []
|
||||
virgl_resource_map2 = []
|
||||
nitro = [ "dep:nitro", "dep:nitro-enclaves" ]
|
||||
|
||||
[dependencies]
|
||||
crossbeam-channel = ">=0.5.15"
|
||||
env_logger = "0.9.0"
|
||||
env_logger = "0.11"
|
||||
libc = ">=0.2.39"
|
||||
libloading = "0.8"
|
||||
log = "0.4.0"
|
||||
|
@ -34,6 +35,8 @@ hvf = { path = "../hvf" }
|
|||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
kvm-bindings = { version = ">=0.11", features = ["fam-wrappers"] }
|
||||
kvm-ioctls = ">=0.21"
|
||||
nitro = { path = "../nitro", optional = true }
|
||||
nitro-enclaves = { version = "0.2.0", optional = true }
|
||||
vm-memory = ">=0.13"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -8,11 +8,12 @@ use std::env;
|
|||
use std::ffi::CStr;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::ffi::CString;
|
||||
#[cfg(all(target_arch = "x86_64", not(feature = "tee")))]
|
||||
use std::fs::File;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::fd::RawFd;
|
||||
use std::os::fd::{FromRawFd, RawFd};
|
||||
#[cfg(feature = "nitro")]
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::path::PathBuf;
|
||||
use std::slice;
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
|
@ -27,7 +28,7 @@ use devices::virtio::block::ImageType;
|
|||
use devices::virtio::net::device::VirtioNetBackend;
|
||||
#[cfg(feature = "blk")]
|
||||
use devices::virtio::CacheType;
|
||||
use env_logger::Env;
|
||||
use env_logger::{Env, Target};
|
||||
#[cfg(not(feature = "efi"))]
|
||||
use libc::size_t;
|
||||
use libc::{c_char, c_int};
|
||||
|
@ -51,6 +52,12 @@ use vmm::vmm_config::machine_config::VmConfig;
|
|||
use vmm::vmm_config::net::NetworkInterfaceConfig;
|
||||
use vmm::vmm_config::vsock::VsockDeviceConfig;
|
||||
|
||||
#[cfg(feature = "nitro")]
|
||||
use nitro::enclaves::NitroEnclave;
|
||||
|
||||
#[cfg(feature = "nitro")]
|
||||
use nitro_enclaves::launch::StartFlags;
|
||||
|
||||
// Value returned on success. We use libc's errors otherwise.
|
||||
const KRUN_SUCCESS: i32 = 0;
|
||||
// Maximum number of arguments/environment variables we allow
|
||||
|
@ -152,6 +159,10 @@ struct ContextConfig {
|
|||
console_output: Option<PathBuf>,
|
||||
vmm_uid: Option<libc::uid_t>,
|
||||
vmm_gid: Option<libc::gid_t>,
|
||||
#[cfg(feature = "nitro")]
|
||||
nitro_image_path: Option<PathBuf>,
|
||||
#[cfg(feature = "nitro")]
|
||||
nitro_start_flags: StartFlags,
|
||||
}
|
||||
|
||||
impl ContextConfig {
|
||||
|
@ -296,22 +307,143 @@ impl ContextConfig {
|
|||
fn set_vmm_gid(&mut self, vmm_gid: libc::gid_t) {
|
||||
self.vmm_gid = Some(vmm_gid);
|
||||
}
|
||||
|
||||
#[cfg(feature = "nitro")]
|
||||
fn set_nitro_image(&mut self, image_path: PathBuf) {
|
||||
self.nitro_image_path = Some(image_path);
|
||||
}
|
||||
|
||||
#[cfg(feature = "nitro")]
|
||||
fn set_nitro_start_flags(&mut self, start_flags: StartFlags) {
|
||||
self.nitro_start_flags = start_flags;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nitro")]
|
||||
impl TryFrom<ContextConfig> for NitroEnclave {
|
||||
type Error = i32;
|
||||
|
||||
fn try_from(ctx: ContextConfig) -> Result<Self, Self::Error> {
|
||||
let vm_config = ctx.vmr.vm_config();
|
||||
|
||||
let Some(mem_size_mib) = vm_config.mem_size_mib else {
|
||||
error!("memory size not configured");
|
||||
return Err(-libc::EINVAL);
|
||||
};
|
||||
|
||||
let Some(vcpus) = vm_config.vcpu_count else {
|
||||
error!("vCPU count not configured");
|
||||
return Err(-libc::EINVAL);
|
||||
};
|
||||
|
||||
let Some(image_path) = ctx.nitro_image_path else {
|
||||
error!("nitro image not configured");
|
||||
return Err(-libc::EINVAL);
|
||||
};
|
||||
|
||||
let Ok(image) = File::open(&image_path) else {
|
||||
error!("unable to open {}", image_path.display());
|
||||
return Err(-libc::EINVAL);
|
||||
};
|
||||
|
||||
let Some(port_map) = ctx.unix_ipc_port_map else {
|
||||
error!("enclave vsock not configured");
|
||||
return Err(-libc::EINVAL);
|
||||
};
|
||||
|
||||
if port_map.len() > 1 {
|
||||
error!("too many nitro vsocks detected (max 1)");
|
||||
return Err(-libc::EINVAL);
|
||||
}
|
||||
|
||||
let ipc_stream = {
|
||||
let mut vec = Vec::from_iter(port_map.values());
|
||||
let Some((path, _)) = vec.pop() else {
|
||||
error!("enclave vsock path not found");
|
||||
return Err(-libc::EINVAL);
|
||||
};
|
||||
|
||||
UnixStream::connect(path).unwrap()
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
image,
|
||||
mem_size_mib,
|
||||
vcpus,
|
||||
ipc_stream,
|
||||
start_flags: ctx.nitro_start_flags,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static CTX_MAP: Lazy<Mutex<HashMap<u32, ContextConfig>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static CTX_IDS: AtomicI32 = AtomicI32::new(0);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn krun_set_log_level(level: u32) -> i32 {
|
||||
let log_level = match level {
|
||||
fn log_level_to_filter_str(level: u32) -> &'static str {
|
||||
match level {
|
||||
0 => "off",
|
||||
1 => "error",
|
||||
2 => "warn",
|
||||
3 => "info",
|
||||
4 => "debug",
|
||||
_ => "trace",
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn krun_set_log_level(level: u32) -> i32 {
|
||||
let filter = log_level_to_filter_str(level);
|
||||
env_logger::Builder::from_env(Env::default().default_filter_or(filter)).init();
|
||||
KRUN_SUCCESS
|
||||
}
|
||||
|
||||
mod log_defs {
|
||||
pub const KRUN_LOG_STYLE_AUTO: u32 = 0;
|
||||
pub const KRUN_LOG_STYLE_ALWAYS: u32 = 1;
|
||||
pub const KRUN_LOG_STYLE_NEVER: u32 = 2;
|
||||
pub const KRUN_LOG_OPTION_NO_ENV: u32 = 1;
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn krun_init_log(target: RawFd, level: u32, style: u32, options: u32) -> i32 {
|
||||
let target = match target {
|
||||
..-1 => return -libc::EINVAL,
|
||||
-1 => Target::default(),
|
||||
0 /* stdin */ => return -libc::EINVAL,
|
||||
1 /* stdout */ => Target::Stdout,
|
||||
2 /* stderr */ => Target::Stderr,
|
||||
fd => Target::Pipe(Box::new(File::from_raw_fd(fd))),
|
||||
};
|
||||
env_logger::Builder::from_env(Env::default().default_filter_or(log_level)).init();
|
||||
|
||||
let filter = log_level_to_filter_str(level);
|
||||
|
||||
let write_style = match style {
|
||||
log_defs::KRUN_LOG_STYLE_AUTO => "auto",
|
||||
log_defs::KRUN_LOG_STYLE_ALWAYS => "always",
|
||||
log_defs::KRUN_LOG_STYLE_NEVER => "never",
|
||||
_ => return -libc::EINVAL,
|
||||
};
|
||||
|
||||
let use_env = match options {
|
||||
0 => true,
|
||||
log_defs::KRUN_LOG_OPTION_NO_ENV => false,
|
||||
_ => return -libc::EINVAL,
|
||||
};
|
||||
|
||||
let mut builder = if use_env {
|
||||
env_logger::Builder::from_env(
|
||||
Env::new()
|
||||
.default_filter_or(filter)
|
||||
.default_write_style_or(write_style),
|
||||
)
|
||||
} else {
|
||||
let mut builder = env_logger::Builder::new();
|
||||
builder.parse_filters(filter).parse_write_style(write_style);
|
||||
builder
|
||||
};
|
||||
builder.target(target).init();
|
||||
|
||||
KRUN_SUCCESS
|
||||
}
|
||||
|
||||
|
@ -930,6 +1062,11 @@ pub unsafe extern "C" fn krun_add_vsock_port2(
|
|||
c_filepath: *const c_char,
|
||||
listen: bool,
|
||||
) -> i32 {
|
||||
#[cfg(feature = "nitro")]
|
||||
if listen {
|
||||
return -libc::EINVAL;
|
||||
}
|
||||
|
||||
let filepath = match CStr::from_ptr(c_filepath).to_str() {
|
||||
Ok(f) => PathBuf::from(f.to_string()),
|
||||
Err(_) => return -libc::EINVAL,
|
||||
|
@ -1346,7 +1483,52 @@ pub extern "C" fn krun_setgid(ctx_id: u32, gid: libc::gid_t) -> i32 {
|
|||
KRUN_SUCCESS
|
||||
}
|
||||
|
||||
#[cfg(feature = "nitro")]
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn krun_nitro_set_image(ctx_id: u32, c_image_filepath: *const c_char) -> i32 {
|
||||
let filepath = match CStr::from_ptr(c_image_filepath).to_str() {
|
||||
Ok(f) => PathBuf::from(f.to_string()),
|
||||
Err(_) => return -libc::EINVAL,
|
||||
};
|
||||
|
||||
match CTX_MAP.lock().unwrap().entry(ctx_id) {
|
||||
Entry::Occupied(mut ctx_cfg) => {
|
||||
let cfg = ctx_cfg.get_mut();
|
||||
cfg.set_nitro_image(filepath);
|
||||
}
|
||||
Entry::Vacant(_) => return -libc::ENOENT,
|
||||
}
|
||||
|
||||
KRUN_SUCCESS
|
||||
}
|
||||
|
||||
#[cfg(feature = "nitro")]
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn krun_nitro_set_start_flags(ctx_id: u32, start_flags: u64) -> i32 {
|
||||
let mut flags = StartFlags::empty();
|
||||
|
||||
// Only debug mode is supported at the moment. To avoid doing conversion and
|
||||
// checking if the "start_flags" argument is valid, set the flags to debug mode
|
||||
// if the "start_flags" argument is greater than zero.
|
||||
if start_flags > 0 {
|
||||
flags |= StartFlags::DEBUG;
|
||||
}
|
||||
|
||||
match CTX_MAP.lock().unwrap().entry(ctx_id) {
|
||||
Entry::Occupied(mut ctx_cfg) => {
|
||||
let cfg = ctx_cfg.get_mut();
|
||||
cfg.set_nitro_start_flags(flags);
|
||||
}
|
||||
Entry::Vacant(_) => return -libc::ENOENT,
|
||||
}
|
||||
|
||||
KRUN_SUCCESS
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(unreachable_code)]
|
||||
pub extern "C" fn krun_start_enter(ctx_id: u32) -> i32 {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
|
@ -1357,6 +1539,9 @@ pub extern "C" fn krun_start_enter(ctx_id: u32) -> i32 {
|
|||
unsafe { libc::prctl(libc::PR_SET_NAME, prname.as_ptr()) };
|
||||
}
|
||||
|
||||
#[cfg(feature = "nitro")]
|
||||
return krun_start_enter_nitro(ctx_id);
|
||||
|
||||
let mut event_manager = match EventManager::new() {
|
||||
Ok(em) => em,
|
||||
Err(e) => {
|
||||
|
@ -1529,3 +1714,23 @@ pub extern "C" fn krun_start_enter(ctx_id: u32) -> i32 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nitro")]
|
||||
#[no_mangle]
|
||||
fn krun_start_enter_nitro(ctx_id: u32) -> i32 {
|
||||
let ctx_cfg = match CTX_MAP.lock().unwrap().remove(&ctx_id) {
|
||||
Some(ctx_cfg) => ctx_cfg,
|
||||
None => return -libc::ENOENT,
|
||||
};
|
||||
|
||||
let Ok(mut enclave) = NitroEnclave::try_from(ctx_cfg) else {
|
||||
return -libc::EINVAL;
|
||||
};
|
||||
|
||||
if let Err(e) = enclave.run() {
|
||||
error!("Error running nitro enclave: {e}");
|
||||
return -libc::EINVAL;
|
||||
}
|
||||
|
||||
KRUN_SUCCESS
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "nitro"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
nitro = []
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.171"
|
||||
nix = { version = "0.26.0", features = ["ioctl", "poll"] }
|
||||
vsock = "0.5.1"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
nitro-enclaves = "0.2.0"
|
|
@ -0,0 +1,171 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::error::NitroError;
|
||||
use nitro_enclaves::{
|
||||
launch::{ImageType, Launcher, MemoryInfo, PollTimeout, StartFlags},
|
||||
Device,
|
||||
};
|
||||
use nix::{
|
||||
poll::{poll, PollFd, PollFlags},
|
||||
sys::{
|
||||
socket::{connect, socket, AddressFamily, SockFlag, SockType, VsockAddr as NixVsockAddr},
|
||||
time::{TimeVal, TimeValLike},
|
||||
},
|
||||
unistd::read,
|
||||
};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
os::{
|
||||
fd::{AsRawFd, RawFd},
|
||||
unix::net::UnixStream,
|
||||
},
|
||||
};
|
||||
use vsock::{VsockAddr, VsockListener};
|
||||
|
||||
type Result<T> = std::result::Result<T, NitroError>;
|
||||
|
||||
const ENCLAVE_READY_VSOCK_PORT: u32 = 9000;
|
||||
const CID_TO_CONSOLE_PORT_OFFSET: u32 = 10000;
|
||||
|
||||
const VMADDR_CID_PARENT: u32 = 3;
|
||||
const VMADDR_CID_HYPERVISOR: u32 = 0;
|
||||
|
||||
const SO_VM_SOCKETS_CONNECT_TIMEOUT: i32 = 6;
|
||||
|
||||
const HEART_BEAT: u8 = 0xb7;
|
||||
|
||||
/// Nitro Enclave data.
|
||||
pub struct NitroEnclave {
|
||||
/// Enclave image.
|
||||
pub image: File,
|
||||
/// Amount of RAM (in MiB).
|
||||
pub mem_size_mib: usize,
|
||||
/// Number of vCPUs.
|
||||
pub vcpus: u8,
|
||||
/// Path of vsock for initial enclave communication.
|
||||
pub ipc_stream: UnixStream,
|
||||
/// Enclave start flags.
|
||||
pub start_flags: StartFlags,
|
||||
}
|
||||
|
||||
impl NitroEnclave {
|
||||
/// Run the enclave.
|
||||
pub fn run(&mut self) -> Result<()> {
|
||||
let device = Device::open().map_err(NitroError::DeviceOpen)?;
|
||||
|
||||
let mut launcher = Launcher::new(&device).map_err(NitroError::VmCreate)?;
|
||||
|
||||
let mem = MemoryInfo::new(ImageType::Eif(&mut self.image), self.mem_size_mib);
|
||||
launcher.set_memory(mem).map_err(NitroError::VmMemorySet)?;
|
||||
|
||||
for _ in 0..self.vcpus {
|
||||
launcher.add_vcpu(None).map_err(NitroError::VcpuAdd)?;
|
||||
}
|
||||
|
||||
let sockaddr = VsockAddr::new(VMADDR_CID_PARENT, ENCLAVE_READY_VSOCK_PORT);
|
||||
let listener = VsockListener::bind(&sockaddr).map_err(NitroError::HeartbeatBind)?;
|
||||
|
||||
let cid = launcher
|
||||
.start(self.start_flags, None)
|
||||
.map_err(NitroError::VmStart)?;
|
||||
|
||||
// Safe to unwrap.
|
||||
let cid: u32 = cid.try_into().unwrap();
|
||||
|
||||
let poll_timeout = PollTimeout::try_from((&self.image, self.mem_size_mib << 20))
|
||||
.map_err(NitroError::PollTimeoutCalculate)?;
|
||||
|
||||
enclave_check(listener, poll_timeout.into(), cid)?;
|
||||
|
||||
self.listen(VMADDR_CID_HYPERVISOR, cid + CID_TO_CONSOLE_PORT_OFFSET)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn listen(&mut self, cid: u32, port: u32) -> Result<()> {
|
||||
let socket_fd = socket(
|
||||
AddressFamily::Vsock,
|
||||
SockType::Stream,
|
||||
SockFlag::empty(),
|
||||
None,
|
||||
)
|
||||
.map_err(|_| NitroError::VsockCreate)?;
|
||||
|
||||
let sockaddr = NixVsockAddr::new(cid, port);
|
||||
|
||||
vsock_timeout(socket_fd)?;
|
||||
|
||||
connect(socket_fd, &sockaddr).map_err(|_| NitroError::VsockConnect)?;
|
||||
|
||||
let mut buf = [0u8; 512];
|
||||
loop {
|
||||
// Read debug output from vsock.
|
||||
if let Ok(sz) = read(socket_fd, &mut buf) {
|
||||
// If there is enclave debug output read, write it to the IPC socket.
|
||||
if sz > 0 {
|
||||
self.ipc_stream
|
||||
.write_all(&buf[..sz])
|
||||
.map_err(NitroError::IpcWrite)?;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn enclave_check(listener: VsockListener, poll_timeout_ms: libc::c_int, cid: u32) -> Result<()> {
|
||||
let mut poll_fds = [PollFd::new(listener.as_raw_fd(), PollFlags::POLLIN)];
|
||||
let result = poll(&mut poll_fds, poll_timeout_ms);
|
||||
if result == Ok(0) {
|
||||
return Err(NitroError::PollNoSelectedEvents);
|
||||
} else if result != Ok(1) {
|
||||
return Err(NitroError::PollMoreThanOneSelectedEvent);
|
||||
}
|
||||
|
||||
let mut stream = listener.accept().map_err(NitroError::HeartbeatAccept)?;
|
||||
|
||||
let mut buf = [0u8];
|
||||
let bytes = stream.0.read(&mut buf).map_err(NitroError::HeartbeatRead)?;
|
||||
|
||||
if bytes != 1 || buf[0] != HEART_BEAT {
|
||||
return Err(NitroError::EnclaveHeartbeatNotDetected);
|
||||
}
|
||||
|
||||
stream
|
||||
.0
|
||||
.write_all(&buf)
|
||||
.map_err(NitroError::HeartbeatWrite)?;
|
||||
|
||||
if stream.1.cid() != cid {
|
||||
return Err(NitroError::HeartbeatCidMismatch);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn vsock_timeout(socket_fd: RawFd) -> Result<()> {
|
||||
// Set the timeout to 20 seconds.
|
||||
let timeval = TimeVal::milliseconds(20000);
|
||||
|
||||
let ret = unsafe {
|
||||
libc::setsockopt(
|
||||
socket_fd,
|
||||
libc::AF_VSOCK,
|
||||
SO_VM_SOCKETS_CONNECT_TIMEOUT,
|
||||
&timeval as *const _ as *const libc::c_void,
|
||||
size_of::<TimeVal>() as u32,
|
||||
)
|
||||
};
|
||||
|
||||
if ret != 0 {
|
||||
return Err(NitroError::VsockSetTimeout);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nitro_enclaves::launch::LaunchError;
|
||||
use std::{fmt, io};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum NitroError {
|
||||
DeviceOpen(io::Error),
|
||||
VmCreate(LaunchError),
|
||||
VmMemorySet(LaunchError),
|
||||
VcpuAdd(LaunchError),
|
||||
HeartbeatAccept(io::Error),
|
||||
HeartbeatBind(io::Error),
|
||||
HeartbeatRead(io::Error),
|
||||
HeartbeatWrite(io::Error),
|
||||
VmStart(LaunchError),
|
||||
PollTimeoutCalculate(LaunchError),
|
||||
PollNoSelectedEvents,
|
||||
PollMoreThanOneSelectedEvent,
|
||||
EnclaveHeartbeatNotDetected,
|
||||
HeartbeatCidMismatch,
|
||||
VsockCreate,
|
||||
VsockSetTimeout,
|
||||
VsockConnect,
|
||||
IpcWrite(io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for NitroError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let msg = match self {
|
||||
NitroError::DeviceOpen(e) => format!("unable to open nitro enclaves device: {e}"),
|
||||
NitroError::VmCreate(e) => format!("unable to create enclave VM: {e}"),
|
||||
NitroError::VmMemorySet(e) => format!("unable to set enclave memory regions: {e}"),
|
||||
NitroError::VcpuAdd(e) => format!("unable to add vCPU to enclave: {e}"),
|
||||
NitroError::HeartbeatAccept(e) => {
|
||||
format!("unable to accept enclave heartbeat vsock: {e}")
|
||||
}
|
||||
NitroError::HeartbeatBind(e) => {
|
||||
format!("unable to bind to enclave hearbeat vsock: {e}")
|
||||
}
|
||||
NitroError::HeartbeatRead(e) => format!("unable to read enclave hearbeat vsock: {e}"),
|
||||
NitroError::HeartbeatWrite(e) => {
|
||||
format!("unable to write to enclave heartbeat vsock: {e}")
|
||||
}
|
||||
NitroError::VmStart(e) => format!("unable to start enclave: {e}"),
|
||||
NitroError::PollTimeoutCalculate(e) => {
|
||||
format!("unable to calculate vsock poll timeout: {e}")
|
||||
}
|
||||
NitroError::PollNoSelectedEvents => {
|
||||
"no selected poll fds for heartbeat vsock found".to_string()
|
||||
}
|
||||
NitroError::PollMoreThanOneSelectedEvent => {
|
||||
"more than one selected pollfd for heartbeat vsock found".to_string()
|
||||
}
|
||||
NitroError::EnclaveHeartbeatNotDetected => {
|
||||
"enclave heartbeat message not detected".to_string()
|
||||
}
|
||||
NitroError::HeartbeatCidMismatch => "enclave heartbeat vsock CID mismatch".to_string(),
|
||||
NitroError::VsockCreate => "unable to create enclave vsock".to_string(),
|
||||
NitroError::VsockSetTimeout => {
|
||||
"unable to set poll timeout for enclave vsock".to_string()
|
||||
}
|
||||
NitroError::VsockConnect => "unable to connect to enclave vsock".to_string(),
|
||||
NitroError::IpcWrite(e) => {
|
||||
format!("unable to write enclave vsock data to UNIX IPC socket: {e}")
|
||||
}
|
||||
};
|
||||
|
||||
write!(f, "{}", msg)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#[cfg(feature = "nitro")]
|
||||
pub mod enclaves;
|
||||
|
||||
#[cfg(feature = "nitro")]
|
||||
mod error;
|
|
@ -26,6 +26,7 @@ remain = "0.2"
|
|||
thiserror = "1.0.23"
|
||||
zerocopy = "0.6"
|
||||
log = "0.4"
|
||||
vmm-sys-util = ">=0.14"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = "0.26.1"
|
||||
|
|
|
@ -14,6 +14,7 @@ use nix::sys::memfd::MemFdCreateFlag;
|
|||
use nix::unistd::ftruncate;
|
||||
use nix::unistd::sysconf;
|
||||
use nix::unistd::SysconfVar;
|
||||
use vmm_sys_util::align_upwards;
|
||||
|
||||
use crate::rutabaga_os::descriptor::AsRawDescriptor;
|
||||
use crate::rutabaga_os::descriptor::IntoRawDescriptor;
|
||||
|
@ -72,8 +73,7 @@ impl IntoRawDescriptor for SharedMemory {
|
|||
pub fn round_up_to_page_size(v: u64) -> RutabagaResult<u64> {
|
||||
let page_size_opt = sysconf(SysconfVar::PAGE_SIZE)?;
|
||||
if let Some(page_size) = page_size_opt {
|
||||
let page_mask = (page_size - 1) as u64;
|
||||
let aligned_size = (v + page_mask) & !page_mask;
|
||||
let aligned_size = align_upwards!(v, page_size as u64);
|
||||
Ok(aligned_size)
|
||||
} else {
|
||||
Err(RutabagaError::SpecViolation("no page size"))
|
||||
|
|
|
@ -6,10 +6,9 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
bitflags = "1.2.0"
|
||||
env_logger = "0.9.0"
|
||||
libc = ">=0.2.85"
|
||||
log = "0.4.0"
|
||||
vmm-sys-util = "0.12.1"
|
||||
vmm-sys-util = ">= 0.14"
|
||||
crossbeam-channel = ">=0.5.15"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
|
|
|
@ -12,15 +12,16 @@ blk = []
|
|||
efi = [ "blk", "net" ]
|
||||
gpu = []
|
||||
snd = []
|
||||
nitro = []
|
||||
|
||||
[dependencies]
|
||||
crossbeam-channel = ">=0.5.15"
|
||||
env_logger = "0.9.0"
|
||||
flate2 = "1.0.35"
|
||||
libc = ">=0.2.39"
|
||||
linux-loader = { version = "0.13.0", features = ["bzimage", "elf", "pe"] }
|
||||
log = "0.4.0"
|
||||
vm-memory = { version = ">=0.13", features = ["backend-mmap"] }
|
||||
vmm-sys-util = ">=0.14"
|
||||
|
||||
arch = { path = "../arch" }
|
||||
devices = { path = "../devices" }
|
||||
|
@ -30,7 +31,7 @@ polly = { path = "../polly" }
|
|||
|
||||
# Dependencies for amd-sev
|
||||
codicon = { version = "3.0.0", optional = true }
|
||||
kbs-types = { version = "0.8.0", features = ["tee-sev", "tee-snp"], optional = true }
|
||||
kbs-types = { version = "0.11.0", features = ["tee-snp"], optional = true }
|
||||
procfs = { version = "0.12", optional = true }
|
||||
rdrand = { version = "^0.8", optional = true }
|
||||
serde = { version = "1.0.125", optional = true }
|
||||
|
@ -50,6 +51,3 @@ kvm-ioctls = ">=0.21"
|
|||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
hvf = { path = "../hvf" }
|
||||
|
||||
[dev-dependencies]
|
||||
vmm-sys-util = "0.12.1"
|
||||
|
|
|
@ -55,7 +55,7 @@ use crate::terminal::term_set_raw_mode;
|
|||
#[cfg(feature = "blk")]
|
||||
use crate::vmm_config::block::BlockBuilder;
|
||||
use crate::vmm_config::boot_source::DEFAULT_KERNEL_CMDLINE;
|
||||
#[cfg(not(feature = "tee"))]
|
||||
#[cfg(not(any(feature = "tee", feature = "nitro")))]
|
||||
use crate::vmm_config::fs::FsDeviceConfig;
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::vstate::KvmContext;
|
||||
|
@ -64,7 +64,7 @@ use crate::vstate::MeasuredRegion;
|
|||
use crate::vstate::{Error as VstateError, Vcpu, VcpuConfig, Vm};
|
||||
use arch::{ArchMemoryInfo, InitrdConfig};
|
||||
use device_manager::shm::ShmManager;
|
||||
#[cfg(not(feature = "tee"))]
|
||||
#[cfg(not(any(feature = "tee", feature = "nitro")))]
|
||||
use devices::virtio::{fs::ExportTable, VirtioShmRegion};
|
||||
use flate2::read::GzDecoder;
|
||||
#[cfg(feature = "tee")]
|
||||
|
@ -78,12 +78,14 @@ use utils::eventfd::EventFd;
|
|||
use utils::worker_message::WorkerMessage;
|
||||
#[cfg(all(target_arch = "x86_64", not(feature = "efi"), not(feature = "tee")))]
|
||||
use vm_memory::mmap::MmapRegion;
|
||||
#[cfg(not(feature = "tee"))]
|
||||
#[cfg(not(any(feature = "tee", feature = "nitro")))]
|
||||
use vm_memory::Address;
|
||||
use vm_memory::Bytes;
|
||||
#[cfg(not(feature = "nitro"))]
|
||||
use vm_memory::GuestMemory;
|
||||
#[cfg(all(target_arch = "x86_64", not(feature = "tee")))]
|
||||
use vm_memory::GuestRegionMmap;
|
||||
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap};
|
||||
use vm_memory::{GuestAddress, GuestMemoryMmap};
|
||||
|
||||
#[cfg(feature = "efi")]
|
||||
static EDK2_BINARY: &[u8] = include_bytes!("../../../edk2/KRUN_EFI.silent.fd");
|
||||
|
@ -784,7 +786,7 @@ pub fn build_microvm(
|
|||
vm_resources.console_output.clone(),
|
||||
)?;
|
||||
|
||||
#[cfg(not(feature = "tee"))]
|
||||
#[cfg(not(any(feature = "tee", feature = "nitro")))]
|
||||
let export_table: Option<ExportTable> = if cfg!(feature = "gpu") {
|
||||
Some(Default::default())
|
||||
} else {
|
||||
|
@ -805,8 +807,7 @@ pub fn build_microvm(
|
|||
_sender.clone(),
|
||||
)?;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tee"))]
|
||||
#[cfg(not(any(feature = "tee", feature = "nitro")))]
|
||||
attach_fs_devices(
|
||||
&mut vmm,
|
||||
&vm_resources.fs,
|
||||
|
@ -1579,7 +1580,7 @@ fn attach_mmio_device(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tee"))]
|
||||
#[cfg(not(any(feature = "tee", feature = "nitro")))]
|
||||
fn attach_fs_devices(
|
||||
vmm: &mut Vmm,
|
||||
fs_devs: &[FsDeviceConfig],
|
||||
|
|
|
@ -1,553 +0,0 @@
|
|||
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, io};
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use arch::aarch64::DeviceInfoForFDT;
|
||||
use arch::DeviceType;
|
||||
use devices;
|
||||
|
||||
use devices::BusDevice;
|
||||
use kernel::cmdline as kernel_cmdline;
|
||||
use kvm_ioctls::{IoEventAddress, VmFd};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use utils::eventfd::EventFd;
|
||||
|
||||
/// Errors for MMIO device manager.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Failed to perform an operation on the bus.
|
||||
BusError(devices::BusError),
|
||||
/// Appending to kernel command line failed.
|
||||
Cmdline(kernel_cmdline::Error),
|
||||
/// Failure in creating or cloning an event fd.
|
||||
EventFd(io::Error),
|
||||
/// No more IRQs are available.
|
||||
IrqsExhausted,
|
||||
/// Registering an IO Event failed.
|
||||
RegisterIoEvent(kvm_ioctls::Error),
|
||||
/// Registering an IRQ FD failed.
|
||||
RegisterIrqFd(kvm_ioctls::Error),
|
||||
/// The device couldn't be found
|
||||
DeviceNotFound,
|
||||
/// Failed to update the mmio device.
|
||||
UpdateFailed,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::BusError(ref e) => write!(f, "failed to perform bus operation: {}", e),
|
||||
Error::Cmdline(ref e) => {
|
||||
write!(f, "unable to add device to kernel command line: {}", e)
|
||||
}
|
||||
Error::EventFd(ref e) => write!(f, "failed to create or clone event descriptor: {}", e),
|
||||
Error::IrqsExhausted => write!(f, "no more IRQs are available"),
|
||||
Error::RegisterIoEvent(ref e) => write!(f, "failed to register IO event: {}", e),
|
||||
Error::RegisterIrqFd(ref e) => write!(f, "failed to register irqfd: {}", e),
|
||||
Error::DeviceNotFound => write!(f, "the device couldn't be found"),
|
||||
Error::UpdateFailed => write!(f, "failed to update the mmio device"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
/// This represents the size of the mmio device specified to the kernel as a cmdline option
|
||||
/// It has to be larger than 0x100 (the offset where the configuration space starts from
|
||||
/// the beginning of the memory mapped device registers) + the size of the configuration space
|
||||
/// Currently hardcoded to 4K.
|
||||
const MMIO_LEN: u64 = 0x1000;
|
||||
|
||||
/// Manages the complexities of registering a MMIO device.
|
||||
pub struct MMIODeviceManager {
|
||||
pub bus: devices::Bus,
|
||||
mmio_base: u64,
|
||||
irq: u32,
|
||||
last_irq: u32,
|
||||
id_to_dev_info: HashMap<(DeviceType, String), MMIODeviceInfo>,
|
||||
}
|
||||
|
||||
impl MMIODeviceManager {
|
||||
/// Create a new DeviceManager handling mmio devices (virtio net, block).
|
||||
pub fn new(mmio_base: &mut u64, irq_interval: (u32, u32)) -> MMIODeviceManager {
|
||||
if cfg!(target_arch = "aarch64") {
|
||||
*mmio_base += MMIO_LEN;
|
||||
}
|
||||
MMIODeviceManager {
|
||||
mmio_base: *mmio_base,
|
||||
irq: irq_interval.0,
|
||||
last_irq: irq_interval.1,
|
||||
bus: devices::Bus::new(),
|
||||
id_to_dev_info: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Register an already created MMIO device to be used via MMIO transport.
|
||||
pub fn register_mmio_device(
|
||||
&mut self,
|
||||
vm: &VmFd,
|
||||
mmio_device: devices::virtio::MmioTransport,
|
||||
type_id: u32,
|
||||
device_id: String,
|
||||
) -> Result<(u64, u32)> {
|
||||
if self.irq > self.last_irq {
|
||||
return Err(Error::IrqsExhausted);
|
||||
}
|
||||
|
||||
for (i, queue_evt) in mmio_device
|
||||
.locked_device()
|
||||
.queue_events()
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let io_addr = IoEventAddress::Mmio(
|
||||
self.mmio_base + u64::from(devices::virtio::NOTIFY_REG_OFFSET),
|
||||
);
|
||||
|
||||
vm.register_ioevent(queue_evt, &io_addr, i as u32)
|
||||
.map_err(Error::RegisterIoEvent)?;
|
||||
}
|
||||
|
||||
vm.register_irqfd(mmio_device.locked_device().interrupt_evt(), self.irq)
|
||||
.map_err(Error::RegisterIrqFd)?;
|
||||
|
||||
self.bus
|
||||
.insert(Arc::new(Mutex::new(mmio_device)), self.mmio_base, MMIO_LEN)
|
||||
.map_err(Error::BusError)?;
|
||||
let ret = (self.mmio_base, self.irq);
|
||||
self.id_to_dev_info.insert(
|
||||
(DeviceType::Virtio(type_id), device_id),
|
||||
MMIODeviceInfo {
|
||||
addr: self.mmio_base,
|
||||
len: MMIO_LEN,
|
||||
irq: self.irq,
|
||||
},
|
||||
);
|
||||
self.mmio_base += MMIO_LEN;
|
||||
self.irq += 1;
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Append a registered MMIO device to the kernel cmdline.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub fn add_device_to_cmdline(
|
||||
&mut self,
|
||||
cmdline: &mut kernel_cmdline::Cmdline,
|
||||
mmio_base: u64,
|
||||
irq: u32,
|
||||
) -> Result<()> {
|
||||
// as per doc, [virtio_mmio.]device=<size>@<baseaddr>:<irq> needs to be appended
|
||||
// to kernel commandline for virtio mmio devices to get recognized
|
||||
// the size parameter has to be transformed to KiB, so dividing hexadecimal value in
|
||||
// bytes to 1024; further, the '{}' formatting rust construct will automatically
|
||||
// transform it to decimal
|
||||
cmdline
|
||||
.insert(
|
||||
"virtio_mmio.device",
|
||||
&format!("{}K@0x{:08x}:{}", MMIO_LEN / 1024, mmio_base, irq),
|
||||
)
|
||||
.map_err(Error::Cmdline)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
/// Register an early console at some MMIO address.
|
||||
pub fn register_mmio_serial(
|
||||
&mut self,
|
||||
vm: &VmFd,
|
||||
cmdline: &mut kernel_cmdline::Cmdline,
|
||||
serial: Arc<Mutex<devices::legacy::Serial>>,
|
||||
) -> Result<()> {
|
||||
if self.irq > self.last_irq {
|
||||
return Err(Error::IrqsExhausted);
|
||||
}
|
||||
|
||||
vm.register_irqfd(&serial.lock().unwrap().interrupt_evt(), self.irq)
|
||||
.map_err(Error::RegisterIrqFd)?;
|
||||
|
||||
self.bus
|
||||
.insert(serial, self.mmio_base, MMIO_LEN)
|
||||
.map_err(|err| Error::BusError(err))?;
|
||||
|
||||
cmdline
|
||||
.insert("earlycon", &format!("uart,mmio,0x{:08x}", self.mmio_base))
|
||||
.map_err(Error::Cmdline)?;
|
||||
|
||||
let ret = self.mmio_base;
|
||||
self.id_to_dev_info.insert(
|
||||
(DeviceType::Serial, DeviceType::Serial.to_string()),
|
||||
MMIODeviceInfo {
|
||||
addr: ret,
|
||||
len: MMIO_LEN,
|
||||
irq: self.irq,
|
||||
},
|
||||
);
|
||||
|
||||
self.mmio_base += MMIO_LEN;
|
||||
self.irq += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
/// Register a MMIO RTC device.
|
||||
pub fn register_mmio_rtc(&mut self, vm: &VmFd) -> Result<()> {
|
||||
if self.irq > self.last_irq {
|
||||
return Err(Error::IrqsExhausted);
|
||||
}
|
||||
|
||||
// Attaching the RTC device.
|
||||
let rtc_evt = EventFd::new(utils::eventfd::EFD_NONBLOCK).map_err(Error::EventFd)?;
|
||||
let device = devices::legacy::RTC::new(rtc_evt.try_clone().map_err(Error::EventFd)?);
|
||||
vm.register_irqfd(&rtc_evt, self.irq)
|
||||
.map_err(Error::RegisterIrqFd)?;
|
||||
|
||||
self.bus
|
||||
.insert(Arc::new(Mutex::new(device)), self.mmio_base, MMIO_LEN)
|
||||
.map_err(|err| Error::BusError(err))?;
|
||||
|
||||
let ret = self.mmio_base;
|
||||
self.id_to_dev_info.insert(
|
||||
(DeviceType::RTC, "rtc".to_string()),
|
||||
MMIODeviceInfo {
|
||||
addr: ret,
|
||||
len: MMIO_LEN,
|
||||
irq: self.irq,
|
||||
},
|
||||
);
|
||||
|
||||
self.mmio_base += MMIO_LEN;
|
||||
self.irq += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
/// Gets the information of the devices registered up to some point in time.
|
||||
pub fn get_device_info(&self) -> &HashMap<(DeviceType, String), MMIODeviceInfo> {
|
||||
&self.id_to_dev_info
|
||||
}
|
||||
|
||||
/// Gets the the specified device.
|
||||
pub fn get_device(
|
||||
&self,
|
||||
device_type: DeviceType,
|
||||
device_id: &str,
|
||||
) -> Option<&Mutex<dyn BusDevice>> {
|
||||
if let Some(dev_info) = self
|
||||
.id_to_dev_info
|
||||
.get(&(device_type, device_id.to_string()))
|
||||
{
|
||||
if let Some((_, device)) = self.bus.get_device(dev_info.addr) {
|
||||
return Some(device);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Private structure for storing information about the MMIO device registered at some address on the bus.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MMIODeviceInfo {
|
||||
addr: u64,
|
||||
irq: u32,
|
||||
len: u64,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
impl DeviceInfoForFDT for MMIODeviceInfo {
|
||||
fn addr(&self) -> u64 {
|
||||
self.addr
|
||||
}
|
||||
fn irq(&self) -> u32 {
|
||||
self.irq
|
||||
}
|
||||
fn length(&self) -> u64 {
|
||||
self.len
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::super::builder;
|
||||
use super::*;
|
||||
use arch;
|
||||
use devices::virtio::{ActivateResult, Queue, VirtioDevice};
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::Arc;
|
||||
use utils::errno;
|
||||
use utils::eventfd::EventFd;
|
||||
use vm_memory::{GuestAddress, GuestMemoryMmap};
|
||||
|
||||
const QUEUE_SIZES: &[u16] = &[64];
|
||||
|
||||
impl MMIODeviceManager {
|
||||
fn register_virtio_device(
|
||||
&mut self,
|
||||
vm: &VmFd,
|
||||
guest_mem: GuestMemoryMmap,
|
||||
device: Arc<Mutex<dyn devices::virtio::VirtioDevice>>,
|
||||
cmdline: &mut kernel_cmdline::Cmdline,
|
||||
type_id: u32,
|
||||
device_id: &str,
|
||||
) -> Result<u64> {
|
||||
let mmio_device = devices::virtio::MmioTransport::new(guest_mem, device);
|
||||
let (mmio_base, _irq) =
|
||||
self.register_mmio_device(vm, mmio_device, type_id, device_id.to_string())?;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
self.add_device_to_cmdline(cmdline, mmio_base, _irq)?;
|
||||
Ok(mmio_base)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct DummyDevice {
|
||||
dummy: u32,
|
||||
queues: Vec<Queue>,
|
||||
queue_evts: [EventFd; 1],
|
||||
interrupt_evt: EventFd,
|
||||
}
|
||||
|
||||
impl DummyDevice {
|
||||
pub fn new() -> Self {
|
||||
DummyDevice {
|
||||
dummy: 0,
|
||||
queues: QUEUE_SIZES.iter().map(|&s| Queue::new(s)).collect(),
|
||||
queue_evts: [EventFd::new(utils::eventfd::EFD_NONBLOCK).expect("cannot create eventFD")],
|
||||
interrupt_evt: EventFd::new(utils::eventfd::EFD_NONBLOCK).expect("cannot create eventFD"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl devices::virtio::VirtioDevice for DummyDevice {
|
||||
fn avail_features(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
fn acked_features(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
fn set_acked_features(&mut self, _: u64) {}
|
||||
|
||||
fn device_type(&self) -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
fn queues(&self) -> &[Queue] {
|
||||
&self.queues
|
||||
}
|
||||
|
||||
fn queues_mut(&mut self) -> &mut [Queue] {
|
||||
&mut self.queues
|
||||
}
|
||||
|
||||
fn queue_events(&self) -> &[EventFd] {
|
||||
&self.queue_evts
|
||||
}
|
||||
|
||||
fn interrupt_evt(&self) -> &EventFd {
|
||||
&self.interrupt_evt
|
||||
}
|
||||
|
||||
fn interrupt_status(&self) -> Arc<AtomicUsize> {
|
||||
Arc::new(AtomicUsize::new(0))
|
||||
}
|
||||
|
||||
fn ack_features_by_page(&mut self, page: u32, value: u32) {
|
||||
let _ = page;
|
||||
let _ = value;
|
||||
}
|
||||
|
||||
fn read_config(&self, offset: u64, data: &mut [u8]) {
|
||||
let _ = offset;
|
||||
let _ = data;
|
||||
}
|
||||
|
||||
fn write_config(&mut self, offset: u64, data: &[u8]) {
|
||||
let _ = offset;
|
||||
let _ = data;
|
||||
}
|
||||
|
||||
fn activate(&mut self, _: GuestMemoryMmap) -> ActivateResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_activated(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_virtio_device() {
|
||||
let start_addr1 = GuestAddress(0x0);
|
||||
let start_addr2 = GuestAddress(0x1000);
|
||||
let guest_mem =
|
||||
GuestMemoryMmap::from_ranges(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap();
|
||||
let mut vm = builder::setup_kvm_vm(&guest_mem).unwrap();
|
||||
let mut device_manager =
|
||||
MMIODeviceManager::new(&mut 0xd000_0000, (arch::IRQ_BASE, arch::IRQ_MAX));
|
||||
|
||||
let mut cmdline = kernel_cmdline::Cmdline::new(4096);
|
||||
let dummy = Arc::new(Mutex::new(DummyDevice::new()));
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
assert!(builder::setup_interrupt_controller(&mut vm).is_ok());
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
assert!(builder::setup_interrupt_controller(&mut vm, 1).is_ok());
|
||||
|
||||
assert!(device_manager
|
||||
.register_virtio_device(vm.fd(), guest_mem, dummy, &mut cmdline, 0, "dummy")
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_too_many_devices() {
|
||||
let start_addr1 = GuestAddress(0x0);
|
||||
let start_addr2 = GuestAddress(0x1000);
|
||||
let guest_mem =
|
||||
GuestMemoryMmap::from_ranges(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap();
|
||||
let mut vm = builder::setup_kvm_vm(&guest_mem).unwrap();
|
||||
let mut device_manager =
|
||||
MMIODeviceManager::new(&mut 0xd000_0000, (arch::IRQ_BASE, arch::IRQ_MAX));
|
||||
|
||||
let mut cmdline = kernel_cmdline::Cmdline::new(4096);
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
assert!(builder::setup_interrupt_controller(&mut vm).is_ok());
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
assert!(builder::setup_interrupt_controller(&mut vm, 1).is_ok());
|
||||
|
||||
for _i in arch::IRQ_BASE..=arch::IRQ_MAX {
|
||||
device_manager
|
||||
.register_virtio_device(
|
||||
vm.fd(),
|
||||
guest_mem.clone(),
|
||||
Arc::new(Mutex::new(DummyDevice::new())),
|
||||
&mut cmdline,
|
||||
0,
|
||||
"dummy1",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{}",
|
||||
device_manager
|
||||
.register_virtio_device(
|
||||
vm.fd(),
|
||||
guest_mem,
|
||||
Arc::new(Mutex::new(DummyDevice::new())),
|
||||
&mut cmdline,
|
||||
0,
|
||||
"dummy2"
|
||||
)
|
||||
.unwrap_err()
|
||||
),
|
||||
"no more IRQs are available".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dummy_device() {
|
||||
let dummy = DummyDevice::new();
|
||||
assert_eq!(dummy.device_type(), 0);
|
||||
assert_eq!(dummy.queues().len(), QUEUE_SIZES.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_messages() {
|
||||
let device_manager =
|
||||
MMIODeviceManager::new(&mut 0xd000_0000, (arch::IRQ_BASE, arch::IRQ_MAX));
|
||||
let mut cmdline = kernel_cmdline::Cmdline::new(4096);
|
||||
let e = Error::Cmdline(
|
||||
cmdline
|
||||
.insert(
|
||||
"virtio_mmio=device",
|
||||
&format!(
|
||||
"{}K@0x{:08x}:{}",
|
||||
MMIO_LEN / 1024,
|
||||
device_manager.mmio_base,
|
||||
device_manager.irq
|
||||
),
|
||||
)
|
||||
.unwrap_err(),
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", e),
|
||||
format!(
|
||||
"unable to add device to kernel command line: {}",
|
||||
kernel_cmdline::Error::HasEquals
|
||||
),
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", Error::UpdateFailed),
|
||||
"failed to update the mmio device"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", Error::BusError(devices::BusError::Overlap)),
|
||||
format!(
|
||||
"failed to perform bus operation: {}",
|
||||
devices::BusError::Overlap
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", Error::IrqsExhausted),
|
||||
"no more IRQs are available"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", Error::RegisterIoEvent(errno::Error::new(0))),
|
||||
format!("failed to register IO event: {}", errno::Error::new(0))
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", Error::RegisterIrqFd(errno::Error::new(0))),
|
||||
format!("failed to register irqfd: {}", errno::Error::new(0))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_device_info() {
|
||||
let start_addr1 = GuestAddress(0x0);
|
||||
let start_addr2 = GuestAddress(0x1000);
|
||||
let guest_mem =
|
||||
GuestMemoryMmap::from_ranges(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap();
|
||||
let vm = builder::setup_kvm_vm(&guest_mem).unwrap();
|
||||
let mut device_manager =
|
||||
MMIODeviceManager::new(&mut 0xd000_0000, (arch::IRQ_BASE, arch::IRQ_MAX));
|
||||
let mut cmdline = kernel_cmdline::Cmdline::new(4096);
|
||||
let dummy = Arc::new(Mutex::new(DummyDevice::new()));
|
||||
|
||||
let type_id = 0;
|
||||
let id = String::from("foo");
|
||||
if let Ok(addr) = device_manager.register_virtio_device(
|
||||
vm.fd(),
|
||||
guest_mem,
|
||||
dummy,
|
||||
&mut cmdline,
|
||||
type_id,
|
||||
&id,
|
||||
) {
|
||||
assert!(device_manager
|
||||
.get_device(DeviceType::Virtio(type_id), &id)
|
||||
.is_some());
|
||||
assert_eq!(
|
||||
addr,
|
||||
device_manager.id_to_dev_info[&(DeviceType::Virtio(type_id), id.clone())].addr
|
||||
);
|
||||
assert_eq!(
|
||||
arch::IRQ_BASE,
|
||||
device_manager.id_to_dev_info[&(DeviceType::Virtio(type_id), id.clone())].irq
|
||||
);
|
||||
}
|
||||
let id = "bar";
|
||||
assert!(device_manager
|
||||
.get_device(DeviceType::Virtio(type_id), &id)
|
||||
.is_none());
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use arch::{round_up, ArchMemoryInfo};
|
||||
use arch::ArchMemoryInfo;
|
||||
use vm_memory::GuestAddress;
|
||||
use vmm_sys_util::align_upwards;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
|
@ -46,7 +47,7 @@ impl ShmManager {
|
|||
regions
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tee"))]
|
||||
#[cfg(not(any(feature = "tee", feature = "nitro")))]
|
||||
pub fn fs_region(&self, index: usize) -> Option<&ShmRegion> {
|
||||
self.fs_regions.get(&index)
|
||||
}
|
||||
|
@ -57,7 +58,7 @@ impl ShmManager {
|
|||
}
|
||||
|
||||
fn create_region(&mut self, size: usize) -> Result<ShmRegion, Error> {
|
||||
let size = round_up(size, self.page_size);
|
||||
let size = align_upwards!(size, self.page_size);
|
||||
|
||||
let region = ShmRegion {
|
||||
guest_addr: GuestAddress(self.next_guest_addr),
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use devices::virtio::{Console, ConsoleError};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ConsoleConfigError {
|
||||
/// Failed to create the console device.
|
||||
CreateConsoleDevice(ConsoleError),
|
||||
}
|
||||
|
||||
impl fmt::Display for ConsoleConfigError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::ConsoleConfigError::*;
|
||||
match *self {
|
||||
CreateConsoleDevice(ref e) => write!(f, "Cannot create console device: {:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, ConsoleConfigError>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ConsoleDeviceConfig {
|
||||
pub fs_id: String,
|
||||
pub shared_dir: String,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FsBuilder {
|
||||
pub list: VecDeque<Arc<Mutex<Fs>>>,
|
||||
}
|
||||
|
||||
impl FsBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
list: VecDeque::<Arc<Mutex<Fs>>>::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, config: FsDeviceConfig) -> Result<()> {
|
||||
let fs_dev = Arc::new(Mutex::new(Self::create_fs(config)?));
|
||||
self.list.push_back(fs_dev);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_fs(config: FsDeviceConfig) -> Result<Fs> {
|
||||
Ok(devices::virtio::Fs::new(config.fs_id, config.shared_dir)
|
||||
.map_err(FsConfigError::CreateFsDevice)?)
|
||||
}
|
||||
}
|
|
@ -15,7 +15,9 @@ use libc::{fallocate, madvise, FALLOC_FL_KEEP_SIZE, FALLOC_FL_PUNCH_HOLE, MADV_D
|
|||
#[cfg(feature = "tee")]
|
||||
use std::ffi::c_void;
|
||||
#[cfg(feature = "tee")]
|
||||
use vm_memory::{guest_memory::GuestMemory, GuestAddress, GuestMemoryRegion, MemoryRegionAddress};
|
||||
use vm_memory::{
|
||||
guest_memory::GuestMemory, Address, GuestAddress, GuestMemoryRegion, MemoryRegionAddress,
|
||||
};
|
||||
|
||||
pub fn start_worker_thread(
|
||||
vmm: Arc<Mutex<super::Vmm>>,
|
||||
|
@ -44,21 +46,11 @@ impl super::Vmm {
|
|||
WorkerMessage::GpuRemoveMapping(s, g, l) => self.remove_mapping(s, g, l),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
WorkerMessage::GsiRoute(sender, entries) => {
|
||||
let mut irq_routing = utils::sized_vec::vec_with_array_field::<
|
||||
kvm_bindings::kvm_irq_routing,
|
||||
kvm_bindings::kvm_irq_routing_entry,
|
||||
>(entries.len());
|
||||
irq_routing[0].nr = entries.len() as u32;
|
||||
irq_routing[0].flags = 0;
|
||||
|
||||
unsafe {
|
||||
let entries_slice: &mut [kvm_bindings::kvm_irq_routing_entry] =
|
||||
irq_routing[0].entries.as_mut_slice(entries.len());
|
||||
entries_slice.copy_from_slice(&entries);
|
||||
}
|
||||
|
||||
let mut routing = kvm_bindings::KvmIrqRouting::new(entries.len()).unwrap();
|
||||
let routing_entries = routing.as_mut_slice();
|
||||
routing_entries.copy_from_slice(&entries);
|
||||
sender
|
||||
.send(self.vm.fd().set_gsi_routing(&irq_routing[0]).is_ok())
|
||||
.send(self.vm.fd().set_gsi_routing(&routing).is_ok())
|
||||
.unwrap();
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
|
@ -77,15 +69,15 @@ impl super::Vmm {
|
|||
|
||||
#[cfg(feature = "tee")]
|
||||
fn convert_memory(&self, sender: Sender<bool>, properties: MemoryProperties) {
|
||||
let (guest_memfd, region_start) = self
|
||||
.kvm_vm()
|
||||
.guest_memfd_get(properties.gpa)
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"unable to find KVM guest_memfd for memory region corresponding to GPA 0x{:x}",
|
||||
properties.gpa
|
||||
)
|
||||
});
|
||||
let Some((guest_memfd, region_start)) = self.kvm_vm().guest_memfd_get(properties.gpa)
|
||||
else {
|
||||
error!(
|
||||
"unable to find KVM guest_memfd for memory region corresponding to GPA 0x{:x}",
|
||||
properties.gpa
|
||||
);
|
||||
sender.send(false).unwrap();
|
||||
return;
|
||||
};
|
||||
|
||||
let attributes: u64 = if properties.private {
|
||||
KVM_MEMORY_ATTRIBUTE_PRIVATE as u64
|
||||
|
@ -100,10 +92,11 @@ impl super::Vmm {
|
|||
flags: 0,
|
||||
};
|
||||
|
||||
self.kvm_vm()
|
||||
.fd()
|
||||
.set_memory_attributes(attr)
|
||||
.unwrap_or_else(|_| panic!("unable to set memory attributes for memory region corresponding to guest address 0x{:x}", properties.gpa));
|
||||
if self.kvm_vm().fd().set_memory_attributes(attr).is_err() {
|
||||
error!("unable to set memory attributes for memory region corresponding to guest address 0x{:x}", properties.gpa);
|
||||
sender.send(false).unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
let region = self
|
||||
.guest_memory()
|
||||
|
@ -122,10 +115,14 @@ impl super::Vmm {
|
|||
if properties.private {
|
||||
let region_addr = MemoryRegionAddress(offset);
|
||||
|
||||
let host_startaddr = region
|
||||
.unwrap()
|
||||
.get_host_address(region_addr)
|
||||
.expect("host address corresponding to memory region address 0x{:x} not found");
|
||||
let Ok(host_startaddr) = region.unwrap().get_host_address(region_addr) else {
|
||||
error!(
|
||||
"host address corresponding to memory region address 0x{:x} not found",
|
||||
region_addr.raw_value()
|
||||
);
|
||||
sender.send(false).unwrap();
|
||||
return;
|
||||
};
|
||||
|
||||
let ret = unsafe {
|
||||
madvise(
|
||||
|
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
test_cases = { path = "../test_cases", features = ["host"] }
|
||||
anyhow = "1.0.95"
|
||||
nix = { version = "0.29.0", features = ["resource"] }
|
||||
nix = { version = "0.29.0", features = ["resource", "fs"] }
|
||||
macros = { path = "../macros" }
|
||||
clap = { version = "4.5.27", features = ["derive"] }
|
||||
tempdir = "0.3.7"
|
||||
|
|
Loading…
Reference in New Issue