Skip to content

Commit 127ba51

Browse files
committed
examples: add an example for external kernels
Add an example for loading an external kernel, initramfs and custom kernel command line. Signed-off-by: Sergio Lopez <slp@redhat.com>
1 parent 986b9eb commit 127ba51

File tree

2 files changed

+329
-2
lines changed

2 files changed

+329
-2
lines changed

examples/Makefile

+8-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ ROOTFS_DIR = rootfs_$(ROOTFS_DISTRO)
1111

1212
.PHONY: clean rootfs
1313

14-
EXAMPLES := chroot_vm
14+
EXAMPLES := chroot_vm external_kernel
1515
ifeq ($(SEV),1)
1616
EXAMPLES := launch-tee
1717
endif
@@ -36,6 +36,12 @@ ifeq ($(OS),Darwin)
3636
codesign --entitlements chroot_vm.entitlements --force -s - $@
3737
endif
3838

39+
external_kernel: external_kernel.c
40+
gcc -o $@ $< $(CFLAGS) $(LDFLAGS_$(ARCH)_$(OS))
41+
ifeq ($(OS),Darwin)
42+
codesign --entitlements chroot_vm.entitlements --force -s - $@
43+
endif
44+
3945
# Build the rootfs to be used with chroot_vm.
4046
rootfs:
4147
mkdir -p $(ROOTFS_DIR)
@@ -44,4 +50,4 @@ rootfs:
4450
podman rm libkrun_chroot_vm
4551

4652
clean:
47-
rm -rf chroot_vm $(ROOTFS_DIR) launch-tee boot_efi
53+
rm -rf chroot_vm $(ROOTFS_DIR) launch-tee boot_efi external_kernel

examples/external_kernel.c

+321
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
/*
2+
* This is an example implementing chroot-like functionality with libkrun.
3+
*
4+
* It executes the requested command (relative to NEWROOT) inside a fresh
5+
* Virtual Machine created and managed by libkrun.
6+
*/
7+
8+
#include <errno.h>
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
#include <string.h>
12+
#include <sys/socket.h>
13+
#include <sys/un.h>
14+
#include <unistd.h>
15+
#include <libkrun.h>
16+
#include <getopt.h>
17+
#include <stdbool.h>
18+
#include <assert.h>
19+
#include <pthread.h>
20+
21+
#define MAX_ARGS_LEN 4096
22+
#ifndef MAX_PATH
23+
#define MAX_PATH 4096
24+
#endif
25+
26+
enum net_mode
27+
{
28+
NET_MODE_PASST = 0,
29+
NET_MODE_TSI,
30+
};
31+
32+
#if defined(__x86_64__)
33+
#define KERNEL_FORMAT KRUN_KERNEL_FORMAT_ELF
34+
#else
35+
#define KERNEL_FORMAT KRUN_KERNEL_FORMAT_RAW
36+
#endif
37+
38+
static void print_help(char *const name)
39+
{
40+
fprintf(stderr,
41+
"Usage: %s [OPTIONS] KERNEL\n"
42+
"OPTIONS: \n"
43+
" -b --boot-disk Path to a boot disk in raw format\n"
44+
" -c --kernel-cmdline Kernel command line\n"
45+
" -d --data-disk Path to a data disk in raw format\n"
46+
" -h --help Show help\n"
47+
" -i --initrd Path to initramfs\n"
48+
" --net=NET_MODE Set network mode\n"
49+
" --passt-socket=PATH Connect to passt socket at PATH"
50+
"\n"
51+
"NET_MODE can be either TSI (default) or PASST\n"
52+
"\n"
53+
#if defined(__x86_64__)
54+
"KERNEL: path to the kernel image in ELF format\n",
55+
#else
56+
"KERNEL: path to the kernel image in RAW format\n",
57+
#endif
58+
name);
59+
}
60+
61+
static const struct option long_options[] = {
62+
{"boot-disk", required_argument, NULL, 'b'},
63+
{"kernel-cmdline", required_argument, NULL, 'c'},
64+
{"data-disk", required_argument, NULL, 'd'},
65+
{"initrd-path", required_argument, NULL, 'i'},
66+
{"help", no_argument, NULL, 'h'},
67+
{"passt-socket", required_argument, NULL, 'P'},
68+
{NULL, 0, NULL, 0}};
69+
70+
struct cmdline
71+
{
72+
bool show_help;
73+
enum net_mode net_mode;
74+
char const *boot_disk;
75+
char const *data_disk;
76+
char const *passt_socket_path;
77+
char const *kernel_path;
78+
char const *kernel_cmdline;
79+
char const *initrd_path;
80+
};
81+
82+
bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline)
83+
{
84+
assert(cmdline != NULL);
85+
86+
// set the defaults
87+
*cmdline = (struct cmdline){
88+
.show_help = false,
89+
.net_mode = NET_MODE_TSI,
90+
.passt_socket_path = "/tmp/network.sock",
91+
.boot_disk = NULL,
92+
.data_disk = NULL,
93+
.kernel_path = NULL,
94+
.kernel_cmdline = NULL,
95+
.initrd_path = NULL,
96+
};
97+
98+
int option_index = 0;
99+
int c;
100+
// the '+' in optstring is a GNU extension that disables permutating argv
101+
while ((c = getopt_long(argc, argv, "+hb:c:d:i:", long_options, &option_index)) != -1)
102+
{
103+
switch (c)
104+
{
105+
case 'b':
106+
cmdline->boot_disk = optarg;
107+
break;
108+
case 'c':
109+
cmdline->kernel_cmdline = optarg;
110+
break;
111+
case 'd':
112+
cmdline->data_disk = optarg;
113+
break;
114+
case 'h':
115+
cmdline->show_help = true;
116+
return true;
117+
case 'i':
118+
cmdline->initrd_path = optarg;
119+
break;
120+
case 'P':
121+
cmdline->passt_socket_path = optarg;
122+
break;
123+
case '?':
124+
return false;
125+
default:
126+
fprintf(stderr, "internal argument parsing error (returned character code 0x%x)\n", c);
127+
return false;
128+
}
129+
}
130+
131+
if (optind <= argc - 1)
132+
{
133+
cmdline->kernel_path = argv[optind];
134+
return true;
135+
}
136+
137+
if (optind == argc)
138+
{
139+
fprintf(stderr, "Missing KERNEL argument\n");
140+
}
141+
142+
return false;
143+
}
144+
145+
int connect_to_passt(char *socket_path)
146+
{
147+
struct sockaddr_un addr;
148+
int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
149+
if (socket_fd < 0)
150+
{
151+
perror("Failed to create passt socket fd");
152+
return -1;
153+
}
154+
155+
memset(&addr, 0, sizeof(addr));
156+
addr.sun_family = AF_UNIX;
157+
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
158+
159+
if (connect(socket_fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0)
160+
{
161+
perror("Failed to bind passt socket");
162+
return -1;
163+
}
164+
165+
return socket_fd;
166+
}
167+
168+
int start_passt()
169+
{
170+
int socket_fds[2];
171+
const int PARENT = 0;
172+
const int CHILD = 1;
173+
174+
if (socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds) < 0)
175+
{
176+
perror("Failed to create passt socket fd");
177+
return -1;
178+
}
179+
180+
int pid = fork();
181+
if (pid < 0)
182+
{
183+
perror("fork");
184+
return -1;
185+
}
186+
187+
if (pid == 0)
188+
{ // child
189+
if (close(socket_fds[PARENT]) < 0)
190+
{
191+
perror("close PARENT");
192+
}
193+
194+
char fd_as_str[16];
195+
snprintf(fd_as_str, sizeof(fd_as_str), "%d", socket_fds[CHILD]);
196+
197+
printf("passing fd %s to passt", fd_as_str);
198+
199+
if (execlp("passt", "passt", "-f", "--fd", fd_as_str, NULL) < 0)
200+
{
201+
perror("execlp");
202+
return -1;
203+
}
204+
}
205+
else
206+
{ // parent
207+
if (close(socket_fds[CHILD]) < 0)
208+
{
209+
perror("close CHILD");
210+
}
211+
212+
return socket_fds[PARENT];
213+
}
214+
}
215+
216+
int main(int argc, char *const argv[])
217+
{
218+
int ctx_id;
219+
int err;
220+
pthread_t thread;
221+
struct cmdline cmdline;
222+
223+
if (!parse_cmdline(argc, argv, &cmdline))
224+
{
225+
putchar('\n');
226+
print_help(argv[0]);
227+
return -1;
228+
}
229+
230+
if (cmdline.show_help)
231+
{
232+
print_help(argv[0]);
233+
return 0;
234+
}
235+
236+
// Set the log level to "off".
237+
err = krun_set_log_level(0);
238+
if (err)
239+
{
240+
errno = -err;
241+
perror("Error configuring log level");
242+
return -1;
243+
}
244+
245+
// Create the configuration context.
246+
ctx_id = krun_create_ctx();
247+
if (ctx_id < 0)
248+
{
249+
errno = -ctx_id;
250+
perror("Error creating configuration context");
251+
return -1;
252+
}
253+
254+
// Configure the number of vCPUs (2) and the amount of RAM (1024 MiB).
255+
if (err = krun_set_vm_config(ctx_id, 2, 2048))
256+
{
257+
errno = -err;
258+
perror("Error configuring the number of vCPUs and/or the amount of RAM");
259+
return -1;
260+
}
261+
262+
if (cmdline.boot_disk)
263+
{
264+
if (err = krun_add_disk(ctx_id, "boot", cmdline.boot_disk, 0))
265+
{
266+
errno = -err,
267+
perror("Error configuring boot disk");
268+
return -1;
269+
}
270+
}
271+
if (cmdline.data_disk)
272+
{
273+
if (err = krun_add_disk(ctx_id, "data", cmdline.data_disk, 0))
274+
{
275+
errno = -err,
276+
perror("Error configuring data disk");
277+
return -1;
278+
}
279+
}
280+
281+
if (cmdline.net_mode == NET_MODE_PASST)
282+
{
283+
int passt_fd = cmdline.passt_socket_path ? connect_to_passt(cmdline.passt_socket_path) : start_passt();
284+
285+
if (passt_fd < 0)
286+
{
287+
return -1;
288+
}
289+
290+
if (err = krun_set_passt_fd(ctx_id, passt_fd))
291+
{
292+
errno = -err;
293+
perror("Error configuring net mode");
294+
return -1;
295+
}
296+
}
297+
298+
fprintf(stderr, "kernel_path: %s\n", cmdline.kernel_path);
299+
fprintf(stderr, "kernel_cmdline: %s\n", cmdline.kernel_cmdline);
300+
fflush(stderr);
301+
302+
if (err = krun_set_kernel(ctx_id, cmdline.kernel_path, KERNEL_FORMAT,
303+
cmdline.initrd_path, cmdline.kernel_cmdline))
304+
{
305+
errno = -err;
306+
perror("Error configuring kernel");
307+
return -1;
308+
}
309+
310+
// Start and enter the microVM. Unless there is some error while creating the microVM
311+
// this function never returns.
312+
if (err = krun_start_enter(ctx_id))
313+
{
314+
errno = -err;
315+
perror("Error creating the microVM");
316+
return -1;
317+
}
318+
319+
// Not reached.
320+
return 0;
321+
}

0 commit comments

Comments
 (0)