1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * A sample program to run a User VM on the ACRN hypervisor |
4 | * |
5 | * This sample runs in a Service VM, which is a privileged VM of ACRN. |
6 | * CONFIG_ACRN_HSM need to be enabled in the Service VM. |
7 | * |
8 | * Guest VM code in guest16.s will be executed after the VM launched. |
9 | * |
10 | * Copyright (C) 2020 Intel Corporation. All rights reserved. |
11 | */ |
12 | #include <stdio.h> |
13 | #include <stdint.h> |
14 | #include <stdlib.h> |
15 | #include <string.h> |
16 | #include <fcntl.h> |
17 | #include <unistd.h> |
18 | #include <signal.h> |
19 | #include <sys/ioctl.h> |
20 | #include <linux/acrn.h> |
21 | |
22 | #define GUEST_MEMORY_SIZE (1024*1024) |
23 | void *guest_memory; |
24 | |
25 | extern const unsigned char guest16[], guest16_end[]; |
26 | static char io_request_page[4096] __attribute__((aligned(4096))); |
27 | static struct acrn_io_request *io_req_buf = (struct acrn_io_request *)io_request_page; |
28 | |
29 | __u16 vcpu_num; |
30 | __u16 vmid; |
31 | |
32 | int hsm_fd; |
33 | int is_running = 1; |
34 | |
35 | void vm_exit(int sig) |
36 | { |
37 | sig = sig; |
38 | |
39 | is_running = 0; |
40 | ioctl(fd: hsm_fd, ACRN_IOCTL_PAUSE_VM, vmid); |
41 | ioctl(fd: hsm_fd, ACRN_IOCTL_DESTROY_IOREQ_CLIENT, 0); |
42 | } |
43 | |
44 | int main(int argc, char **argv) |
45 | { |
46 | int vcpu_id, ret; |
47 | struct acrn_vm_creation create_vm = {0}; |
48 | struct acrn_vm_memmap ram_map = {0}; |
49 | struct acrn_vcpu_regs regs; |
50 | struct acrn_io_request *io_req; |
51 | struct acrn_ioreq_notify __attribute__((aligned(8))) notify; |
52 | |
53 | argc = argc; |
54 | argv = argv; |
55 | |
56 | ret = posix_memalign(memptr: &guest_memory, alignment: 4096, GUEST_MEMORY_SIZE); |
57 | if (ret < 0) { |
58 | printf(format: "No enough memory!\n" ); |
59 | return -1; |
60 | } |
61 | hsm_fd = open(file: "/dev/acrn_hsm" , O_RDWR|O_CLOEXEC); |
62 | |
63 | create_vm.ioreq_buf = (__u64)io_req_buf; |
64 | ret = ioctl(fd: hsm_fd, ACRN_IOCTL_CREATE_VM, &create_vm); |
65 | printf(format: "Created VM! [%d]\n" , ret); |
66 | vcpu_num = create_vm.vcpu_num; |
67 | vmid = create_vm.vmid; |
68 | |
69 | /* setup guest memory */ |
70 | ram_map.type = ACRN_MEMMAP_RAM; |
71 | ram_map.vma_base = (__u64)guest_memory; |
72 | ram_map.len = GUEST_MEMORY_SIZE; |
73 | ram_map.user_vm_pa = 0; |
74 | ram_map.attr = ACRN_MEM_ACCESS_RWX; |
75 | ret = ioctl(fd: hsm_fd, ACRN_IOCTL_SET_MEMSEG, &ram_map); |
76 | printf(format: "Set up VM memory! [%d]\n" , ret); |
77 | |
78 | memcpy(dest: guest_memory, src: guest16, n: guest16_end-guest16); |
79 | |
80 | /* setup vcpu registers */ |
81 | memset(s: ®s, c: 0, n: sizeof(regs)); |
82 | regs.vcpu_id = 0; |
83 | regs.vcpu_regs.rip = 0; |
84 | |
85 | /* CR0_ET | CR0_NE */ |
86 | regs.vcpu_regs.cr0 = 0x30U; |
87 | regs.vcpu_regs.cs_ar = 0x009FU; |
88 | regs.vcpu_regs.cs_sel = 0xF000U; |
89 | regs.vcpu_regs.cs_limit = 0xFFFFU; |
90 | regs.vcpu_regs.cs_base = 0 & 0xFFFF0000UL; |
91 | regs.vcpu_regs.rip = 0 & 0xFFFFUL; |
92 | |
93 | ret = ioctl(fd: hsm_fd, ACRN_IOCTL_SET_VCPU_REGS, ®s); |
94 | printf(format: "Set up VM BSP registers! [%d]\n" , ret); |
95 | |
96 | /* create an ioreq client for this VM */ |
97 | ret = ioctl(fd: hsm_fd, ACRN_IOCTL_CREATE_IOREQ_CLIENT, 0); |
98 | printf(format: "Created IO request client! [%d]\n" , ret); |
99 | |
100 | /* run vm */ |
101 | ret = ioctl(fd: hsm_fd, ACRN_IOCTL_START_VM, vmid); |
102 | printf(format: "Start VM! [%d]\n" , ret); |
103 | |
104 | signal(SIGINT, handler: vm_exit); |
105 | while (is_running) { |
106 | ret = ioctl(fd: hsm_fd, ACRN_IOCTL_ATTACH_IOREQ_CLIENT, 0); |
107 | |
108 | for (vcpu_id = 0; vcpu_id < vcpu_num; vcpu_id++) { |
109 | io_req = &io_req_buf[vcpu_id]; |
110 | if ((__sync_add_and_fetch(&io_req->processed, 0) == ACRN_IOREQ_STATE_PROCESSING) |
111 | && (!io_req->kernel_handled)) |
112 | if (io_req->type == ACRN_IOREQ_TYPE_PORTIO) { |
113 | int bytes, port, in; |
114 | |
115 | port = io_req->reqs.pio_request.address; |
116 | bytes = io_req->reqs.pio_request.size; |
117 | in = (io_req->reqs.pio_request.direction == ACRN_IOREQ_DIR_READ); |
118 | printf(format: "Guest VM %s PIO[%x] with size[%x]\n" , in ? "read" : "write" , port, bytes); |
119 | |
120 | notify.vmid = vmid; |
121 | notify.vcpu = vcpu_id; |
122 | ioctl(fd: hsm_fd, ACRN_IOCTL_NOTIFY_REQUEST_FINISH, ¬ify); |
123 | } |
124 | } |
125 | } |
126 | |
127 | ret = ioctl(fd: hsm_fd, ACRN_IOCTL_DESTROY_VM, NULL); |
128 | printf(format: "Destroy VM! [%d]\n" , ret); |
129 | close(fd: hsm_fd); |
130 | free(ptr: guest_memory); |
131 | return 0; |
132 | } |
133 | |