FROMLIST: samples: Add sample userspace Gunyah VM Manager
Add a sample Gunyah VMM capable of launching a non-proxy scheduled VM. Change-Id: Idb561b8479fa12edc856cc600f44ebeddefd761f Signed-off-by: Elliot Berman <quic_eberman@quicinc.com> Bug: 268234781 Link: https://lore.kernel.org/all/20230304010632.2127470-15-quic_eberman@quicinc.com/
This commit is contained in:
parent
fa4e7824da
commit
8f5b89a5bc
@ -263,6 +263,16 @@ config SAMPLE_CORESIGHT_SYSCFG
|
|||||||
This demonstrates how a user may create their own CoreSight
|
This demonstrates how a user may create their own CoreSight
|
||||||
configurations and easily load them into the system at runtime.
|
configurations and easily load them into the system at runtime.
|
||||||
|
|
||||||
|
config SAMPLE_GUNYAH
|
||||||
|
bool "Build example Gunyah Virtual Machine Manager"
|
||||||
|
depends on CC_CAN_LINK && HEADERS_INSTALL
|
||||||
|
depends on GUNYAH
|
||||||
|
help
|
||||||
|
Build an example Gunyah VMM userspace program capable of launching
|
||||||
|
a basic virtual machine under the Gunyah hypervisor.
|
||||||
|
This demonstrates how to create a virtual machine under the Gunyah
|
||||||
|
hypervisor.
|
||||||
|
|
||||||
source "samples/rust/Kconfig"
|
source "samples/rust/Kconfig"
|
||||||
|
|
||||||
endif # SAMPLES
|
endif # SAMPLES
|
||||||
|
@ -36,3 +36,4 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak/
|
|||||||
obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight/
|
obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight/
|
||||||
obj-$(CONFIG_SAMPLE_FPROBE) += fprobe/
|
obj-$(CONFIG_SAMPLE_FPROBE) += fprobe/
|
||||||
obj-$(CONFIG_SAMPLES_RUST) += rust/
|
obj-$(CONFIG_SAMPLES_RUST) += rust/
|
||||||
|
obj-$(CONFIG_SAMPLE_GUNYAH) += gunyah/
|
||||||
|
2
samples/gunyah/.gitignore
vendored
Normal file
2
samples/gunyah/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
/gunyah_vmm
|
6
samples/gunyah/Makefile
Normal file
6
samples/gunyah/Makefile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
userprogs-always-y += gunyah_vmm
|
||||||
|
dtb-y += sample_vm.dtb
|
||||||
|
|
||||||
|
userccflags += -I usr/include
|
270
samples/gunyah/gunyah_vmm.c
Normal file
270
samples/gunyah/gunyah_vmm.c
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#define __USE_GNU
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include <linux/gunyah.h>
|
||||||
|
|
||||||
|
struct vm_config {
|
||||||
|
int image_fd;
|
||||||
|
int dtb_fd;
|
||||||
|
int ramdisk_fd;
|
||||||
|
|
||||||
|
uint64_t guest_base;
|
||||||
|
uint64_t guest_size;
|
||||||
|
|
||||||
|
uint64_t image_offset;
|
||||||
|
off_t image_size;
|
||||||
|
uint64_t dtb_offset;
|
||||||
|
off_t dtb_size;
|
||||||
|
uint64_t ramdisk_offset;
|
||||||
|
off_t ramdisk_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct option options[] = {
|
||||||
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{ "image", required_argument, NULL, 'i' },
|
||||||
|
{ "dtb", required_argument, NULL, 'd' },
|
||||||
|
{ "ramdisk", optional_argument, NULL, 'r' },
|
||||||
|
{ "base", optional_argument, NULL, 'B' },
|
||||||
|
{ "size", optional_argument, NULL, 'S' },
|
||||||
|
{ "image_offset", optional_argument, NULL, 'I' },
|
||||||
|
{ "dtb_offset", optional_argument, NULL, 'D' },
|
||||||
|
{ "ramdisk_offset", optional_argument, NULL, 'R' },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static void print_help(char *cmd)
|
||||||
|
{
|
||||||
|
printf("gunyah_vmm, a sample tool to launch Gunyah VMs\n"
|
||||||
|
"Usage: %s <options>\n"
|
||||||
|
" --help, -h this menu\n"
|
||||||
|
" --image, -i <image> VM image file to load (e.g. a kernel Image) [Required]\n"
|
||||||
|
" --dtb, -d <dtb> Devicetree to load [Required]\n"
|
||||||
|
" --ramdisk, -r <ramdisk> Ramdisk to load\n"
|
||||||
|
" --base, -B <address> Set the base address of guest's memory [Default: 0x80000000]\n"
|
||||||
|
" --size, -S <number> The number of bytes large to make the guest's memory [Default: 0x6400000 (100 MB)]\n"
|
||||||
|
" --image_offset, -I <number> Offset into guest memory to load the VM image file [Default: 0x10000]\n"
|
||||||
|
" --dtb_offset, -D <number> Offset into guest memory to load the DTB [Default: 0]\n"
|
||||||
|
" --ramdisk_offset, -R <number> Offset into guest memory to load a ramdisk [Default: 0x4600000]\n"
|
||||||
|
, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int gunyah_fd, vm_fd, guest_fd;
|
||||||
|
struct gh_userspace_memory_region guest_mem_desc = { 0 };
|
||||||
|
struct gh_vm_dtb_config dtb_config = { 0 };
|
||||||
|
char *guest_mem;
|
||||||
|
struct vm_config config = {
|
||||||
|
/* Defaults good enough to boot static kernel and a basic ramdisk */
|
||||||
|
.ramdisk_fd = -1,
|
||||||
|
.guest_base = 0x80000000,
|
||||||
|
.guest_size = 0x6400000, /* 100 MB */
|
||||||
|
.image_offset = 0,
|
||||||
|
.dtb_offset = 0x45f0000,
|
||||||
|
.ramdisk_offset = 0x4600000, /* put at +70MB (30MB for ramdisk) */
|
||||||
|
};
|
||||||
|
struct stat st;
|
||||||
|
int opt, optidx, ret = 0;
|
||||||
|
long l;
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, "hi:d:r:B:S:I:D:R:c:", options, &optidx)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'i':
|
||||||
|
config.image_fd = open(optarg, O_RDONLY | O_CLOEXEC);
|
||||||
|
if (config.image_fd < 0) {
|
||||||
|
perror("Failed to open image");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (stat(optarg, &st) < 0) {
|
||||||
|
perror("Failed to stat image");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
config.image_size = st.st_size;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
config.dtb_fd = open(optarg, O_RDONLY | O_CLOEXEC);
|
||||||
|
if (config.dtb_fd < 0) {
|
||||||
|
perror("Failed to open dtb");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (stat(optarg, &st) < 0) {
|
||||||
|
perror("Failed to stat dtb");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
config.dtb_size = st.st_size;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
config.ramdisk_fd = open(optarg, O_RDONLY | O_CLOEXEC);
|
||||||
|
if (config.ramdisk_fd < 0) {
|
||||||
|
perror("Failed to open ramdisk");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (stat(optarg, &st) < 0) {
|
||||||
|
perror("Failed to stat ramdisk");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
config.ramdisk_size = st.st_size;
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
l = strtol(optarg, NULL, 0);
|
||||||
|
if (l == LONG_MIN) {
|
||||||
|
perror("Failed to parse base address");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
config.guest_base = l;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
l = strtol(optarg, NULL, 0);
|
||||||
|
if (l == LONG_MIN) {
|
||||||
|
perror("Failed to parse memory size");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
config.guest_size = l;
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
l = strtol(optarg, NULL, 0);
|
||||||
|
if (l == LONG_MIN) {
|
||||||
|
perror("Failed to parse image offset");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
config.image_offset = l;
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
l = strtol(optarg, NULL, 0);
|
||||||
|
if (l == LONG_MIN) {
|
||||||
|
perror("Failed to parse dtb offset");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
config.dtb_offset = l;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
l = strtol(optarg, NULL, 0);
|
||||||
|
if (l == LONG_MIN) {
|
||||||
|
perror("Failed to parse ramdisk offset");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
config.ramdisk_offset = l;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
print_help(argv[0]);
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
print_help(argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.image_fd || !config.dtb_fd) {
|
||||||
|
print_help(argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.image_offset + config.image_size > config.guest_size) {
|
||||||
|
fprintf(stderr, "Image offset and size puts it outside guest memory. Make image smaller or increase guest memory size.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.dtb_offset + config.dtb_size > config.guest_size) {
|
||||||
|
fprintf(stderr, "DTB offset and size puts it outside guest memory. Make dtb smaller or increase guest memory size.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.ramdisk_fd == -1 &&
|
||||||
|
config.ramdisk_offset + config.ramdisk_size > config.guest_size) {
|
||||||
|
fprintf(stderr, "Ramdisk offset and size puts it outside guest memory. Make ramdisk smaller or increase guest memory size.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gunyah_fd = open("/dev/gunyah", O_RDWR | O_CLOEXEC);
|
||||||
|
if (gunyah_fd < 0) {
|
||||||
|
perror("Failed to open /dev/gunyah");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_fd = ioctl(gunyah_fd, GH_CREATE_VM, 0);
|
||||||
|
if (vm_fd < 0) {
|
||||||
|
perror("Failed to create vm");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
guest_fd = memfd_create("guest_memory", MFD_CLOEXEC);
|
||||||
|
if (guest_fd < 0) {
|
||||||
|
perror("Failed to create guest memfd");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftruncate(guest_fd, config.guest_size) < 0) {
|
||||||
|
perror("Failed to grow guest memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
guest_mem = mmap(NULL, config.guest_size, PROT_READ | PROT_WRITE, MAP_SHARED, guest_fd, 0);
|
||||||
|
if (guest_mem == MAP_FAILED) {
|
||||||
|
perror("Not enough memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read(config.image_fd, guest_mem + config.image_offset, config.image_size) < 0) {
|
||||||
|
perror("Failed to read image into guest memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read(config.dtb_fd, guest_mem + config.dtb_offset, config.dtb_size) < 0) {
|
||||||
|
perror("Failed to read dtb into guest memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.ramdisk_fd > 0 &&
|
||||||
|
read(config.ramdisk_fd, guest_mem + config.ramdisk_offset,
|
||||||
|
config.ramdisk_size) < 0) {
|
||||||
|
perror("Failed to read ramdisk into guest memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
guest_mem_desc.label = 0;
|
||||||
|
guest_mem_desc.flags = GH_MEM_ALLOW_READ | GH_MEM_ALLOW_WRITE | GH_MEM_ALLOW_EXEC;
|
||||||
|
guest_mem_desc.guest_phys_addr = config.guest_base;
|
||||||
|
guest_mem_desc.memory_size = config.guest_size;
|
||||||
|
guest_mem_desc.userspace_addr = (__u64)guest_mem;
|
||||||
|
|
||||||
|
if (ioctl(vm_fd, GH_VM_SET_USER_MEM_REGION, &guest_mem_desc) < 0) {
|
||||||
|
perror("Failed to register guest memory with VM");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dtb_config.guest_phys_addr = config.guest_base + config.dtb_offset;
|
||||||
|
dtb_config.size = config.dtb_size;
|
||||||
|
if (ioctl(vm_fd, GH_VM_SET_DTB_CONFIG, &dtb_config) < 0) {
|
||||||
|
perror("Failed to set DTB configuration for VM");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ioctl(vm_fd, GH_VM_START);
|
||||||
|
if (ret) {
|
||||||
|
perror("GH_VM_START failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
sleep(10);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
68
samples/gunyah/sample_vm.dts
Normal file
68
samples/gunyah/sample_vm.dts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/dts-v1/;
|
||||||
|
|
||||||
|
/ {
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
interrupt-parent = <&intc>;
|
||||||
|
|
||||||
|
chosen {
|
||||||
|
bootargs = "nokaslr";
|
||||||
|
};
|
||||||
|
|
||||||
|
cpus {
|
||||||
|
#address-cells = <0x2>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
cpu@0 {
|
||||||
|
device_type = "cpu";
|
||||||
|
compatible = "arm,armv8";
|
||||||
|
reg = <0 0>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
intc: interrupt-controller@3FFF0000 {
|
||||||
|
compatible = "arm,gic-v3";
|
||||||
|
#interrupt-cells = <3>;
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
interrupt-controller;
|
||||||
|
reg = <0 0x3FFF0000 0 0x10000>,
|
||||||
|
<0 0x3FFD0000 0 0x20000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
timer {
|
||||||
|
compatible = "arm,armv8-timer";
|
||||||
|
always-on;
|
||||||
|
interrupts = <1 13 0x108>,
|
||||||
|
<1 14 0x108>,
|
||||||
|
<1 11 0x108>,
|
||||||
|
<1 10 0x108>;
|
||||||
|
clock-frequency = <19200000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
gunyah-vm-config {
|
||||||
|
image-name = "linux_vm_0";
|
||||||
|
|
||||||
|
memory {
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
|
||||||
|
base-address = <0 0x80000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
interrupts {
|
||||||
|
config = <&intc>;
|
||||||
|
};
|
||||||
|
|
||||||
|
vcpus {
|
||||||
|
affinity-map = < 0 >;
|
||||||
|
sched-priority = < (-1) >;
|
||||||
|
sched-timeslice = < 2000 >;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user