-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Andy Lutomirski <[email protected]>
- Loading branch information
0 parents
commit 6fa0e03
Showing
7 changed files
with
612 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*~ | ||
__pycache__ |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
What is virtme? | ||
=============== | ||
|
||
Virtme is a set of simple tools to run a virtualized Linux kernel that | ||
uses the host Linux distribution instead of a separate image. | ||
|
||
Virtme is tiny, easy to use, and makes testing kernel changes quite simple. | ||
|
||
Some day this might be useful as a sort of sandbox. Right now it's not | ||
really configurable enough for that. | ||
|
||
How to use virtme | ||
================= | ||
|
||
You'll need a Linux kernel compiled with these options: | ||
|
||
CONFIG_VIRTIO=y | ||
CONFIG_VIRTIO_PCI=y | ||
CONFIG_NET_9P=y | ||
CONFIG_NET_9P_VIRTIO=y | ||
CONFIG_9P_FS=y | ||
|
||
|
||
Virtme does not (yet) support modular virtio or 9p, so you can't use your | ||
distro kernel. Fixing this is a high priority. | ||
|
||
Once you have such a kernel, run: | ||
|
||
virtme-runkernel PATH_TO_BZIMAGE | ||
|
||
On x86, you can usually find a bzImage in `arch/x86/boot/bzImage` once you've | ||
compiled your kernel. To save time (since modules aren't supported yet), | ||
build your kernel with `make bzImage`. | ||
|
||
You can then do things like `cd /home/username` and you will have readonly | ||
access to all your files. | ||
|
||
Upcoming features | ||
================= | ||
|
||
In the near term, the high-priority features are: | ||
|
||
* Support for modular virtfs and 9p. | ||
* Some way to configure writable mounts. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
#!/usr/bin/python3 | ||
# -*- mode: python -*- | ||
import sys | ||
|
||
class CpioWriter(object): | ||
TYPE_DIR = 0o0040000 | ||
TYPE_REG = 0o0100000 | ||
TYPE_SYMLINK = 0o0120000 | ||
TYPE_MASK = 0o0170000 | ||
|
||
def __init__(self, f): | ||
self.__f = f | ||
self.__totalsize = 0 | ||
self.__next_ino = 0 | ||
|
||
def __write(self, data): | ||
self.__f.write(data) | ||
self.__totalsize += len(data) | ||
|
||
def write_object(self, name, body, mode, ino=None, nlink=None, | ||
uid=0, gid=0, mtime=0, devmajor=0, devminor=0, | ||
rdevmajor=0, rdevminor=0): | ||
if nlink is None: | ||
nlink = (2 if (mode & CpioWriter.TYPE_MASK) == CpioWriter.TYPE_DIR | ||
else 1) | ||
|
||
if b'\0' in name: | ||
raise ValueError('Filename cannot contain a NUL') | ||
|
||
namesize = len(name) + 1 | ||
|
||
if isinstance(body, bytes): | ||
filesize = len(body) | ||
else: | ||
filesize = body.seek(0, 2) | ||
body.seek(0) | ||
|
||
if ino is None: | ||
ino = self.__next_ino | ||
self.__next_ino += 1 | ||
|
||
fields = [ino, mode, uid, gid, nlink, mtime, filesize, | ||
devmajor, devminor, rdevmajor, rdevminor, namesize, 0] | ||
hdr = ('070701' + ''.join('%08X' % f for f in fields)).encode('ascii') | ||
|
||
self.__write(hdr) | ||
self.__write(name) | ||
self.__write(b'\0') | ||
self.__write(((2-namesize) % 4) * b'\0') | ||
|
||
if isinstance(body, bytes): | ||
self.__write(body) | ||
else: | ||
while True: | ||
buf = body.read(65536) | ||
if buf == b'': | ||
break | ||
self.__write(buf) | ||
|
||
self.__write(((-filesize) % 4) * b'\0') | ||
|
||
def write_trailer(self): | ||
self.write_object(name=b'TRAILER!!!', body=b'', mode=0, ino=0, nlink=1) | ||
self.__write(((-self.__totalsize) % 512) * b'\0') | ||
|
||
def mkdir(self, name, mode): | ||
self.write_object(name=name, mode=CpioWriter.TYPE_DIR | mode, body=b'') | ||
|
||
def symlink(self, src, dst): | ||
self.write_object(name=dst, mode=CpioWriter.TYPE_SYMLINK | 0o777, | ||
body=src) | ||
|
||
def write_file(self, name, body, mode): | ||
self.write_object(name=name, body=body, mode=CpioWriter.TYPE_REG | mode) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
#!/bin/sh | ||
# virtme-init: virtme's basic init (PID 1) process | ||
# Copyright © 2013 Andy Lutomirski | ||
# Licensed under the GPLv2, which is available in the virtme distribution | ||
# as a file called LICENSE with SHA-256 hash: | ||
# 8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643 | ||
|
||
export PATH=/bin:/sbin:/usr/bin:/usr/sbin | ||
export TERM=linux | ||
|
||
# Set up a minimally functional system | ||
mount -t proc -o nosuid,noexec,nodev /proc/ | ||
mount -t sysfs -o nosuid,noexec,nodev /sys/ | ||
|
||
mount -t tmpfs tmpfs /tmp/ | ||
mount -t tmpfs tmpfs /run/ | ||
|
||
# Find udevd | ||
if [[ -x /usr/lib/systemd/systemd-udevd ]]; then | ||
udevd=/usr/lib/systemd/systemd-udevd | ||
else | ||
udevd=`which udevd` | ||
fi | ||
|
||
# Ideally we'll use devtmpfs | ||
if mount -t devtmpfs -o mode=0755,nosuid,noexec none /dev &>/dev/null; then | ||
"$udevd" --daemon --resolve-names=never | ||
else | ||
# The running kernel doesn't have devtmpfs. Use regular tmpfs. | ||
mount -t tmpfs -o mode=0755,nosuid,noexec none /dev | ||
|
||
# Make some basic devices first, let udev handle the rest | ||
mknod -m 0666 /dev/null c 1 3 | ||
mknod -m 0660 /dev/kmsg c 1 11 | ||
mknod -m 0600 /dev/console c 5 1 | ||
|
||
# Try to get udevd to coldplug everything | ||
"$udevd" --daemon --resolve-names=never | ||
udevadm trigger --action=add >/dev/null 2>&1 | ||
udevadm settle | ||
fi | ||
|
||
mkdir -p -m 0755 /dev/shm /dev/pts | ||
mount -t devpts -o gid=tty,mode=620,noexec,nosuid devpts /dev/pts | ||
mount -t tmpfs -o mode=1777,nosuid,nodev tmpfs /dev/shm | ||
|
||
# Bring up networking | ||
ip link set dev lo up | ||
|
||
# Create some VTs | ||
deallocvt | ||
openvt -c 2 -- /bin/bash | ||
openvt -c 3 -- /bin/bash | ||
openvt -c 4 -- /bin/bash | ||
|
||
# Figure out what the main console is | ||
consdev="`grep ' ... (.C' /proc/consoles |cut -d' ' -f1`" | ||
if [[ -z "$consdev" ]]; then | ||
echo "Can't deduce console device" >&2 | ||
exec bash --login # At least try to be helpful | ||
fi | ||
if [[ "$consdev" == "tty0" ]]; then | ||
consdev=tty1 # sigh | ||
fi | ||
if [[ ! -e "/dev/$consdev" ]]; then | ||
echo "/dev/$consdev doesn't exist." | ||
exec bash --login | ||
fi | ||
|
||
echo "virtme-init: console is $consdev" | ||
|
||
# Bring up a functioning shell on the console. This is a bit magical: | ||
# We have no controlling terminal because we're attached to a fake | ||
# console device (probably something like /dev/console), which can't | ||
# be a controlling terminal. We are also not a member of a session. | ||
# Init apparently can't setsid (whether that's a limitation of the | ||
# setsid binary or the system call, I don't know). | ||
while true; do | ||
setsid bash 0<>"/dev/$consdev" 1>&0 2>&0 | ||
echo "Shell died. Will respawn." | ||
sleep 0.5 | ||
done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#!/usr/bin/python3 | ||
# -*- mode: python -*- | ||
# virtme-mkinitramfs: Generate an initramfs image for virtme | ||
# Copyright © 2013 Andy Lutomirski | ||
# Licensed under the GPLv2, which is available in the virtme distribution | ||
# as a file called LICENSE with SHA-256 hash: | ||
# 8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643 | ||
|
||
import argparse | ||
import shutil | ||
import cpiowriter | ||
import inspect | ||
import io | ||
import os.path | ||
import shlex | ||
|
||
def make_base_layout(cw): | ||
for dir in (b'lib', b'bin', b'var', b'etc', b'newroot'): | ||
cw.mkdir(dir, 0o755) | ||
|
||
cw.symlink(b'bin', b'sbin') | ||
cw.symlink(b'lib', b'lib64') | ||
|
||
def install_busybox(cw): | ||
bbpath = shutil.which('busybox') | ||
with open(bbpath, 'rb') as busybox: | ||
cw.write_file(name=b'bin/busybox', body=busybox, mode=0o755) | ||
|
||
for tool in ('sh', 'mount', 'umount', 'modprobe', 'switch_root'): | ||
cw.symlink(b'busybox', ('bin/%s' % tool).encode('ascii')) | ||
|
||
def generate_init(): | ||
mypath = os.path.dirname(os.path.abspath( | ||
inspect.getfile(inspect.currentframe()))) | ||
|
||
out = io.StringIO() | ||
out.write('#!/bin/sh\n') | ||
out.write('echo \'Mounting hostfs...\'\n') | ||
out.write('/bin/mount -t 9p -o ro,version=9p2000.L,trans=virtio,access=any hostroot /newroot/\n') | ||
|
||
out.write('exec /bin/switch_root /newroot %s\n' % shlex.quote(os.path.join(mypath, 'virtme-init'))) | ||
return out.getvalue().encode('utf-8') | ||
|
||
def main(): | ||
import sys | ||
cw = cpiowriter.CpioWriter(sys.stdout.buffer) | ||
make_base_layout(cw) | ||
install_busybox(cw) | ||
cw.write_file(b'init', body=generate_init(), mode=0o755) | ||
cw.write_trailer() | ||
return 0 | ||
|
||
if __name__ == '__main__': | ||
exit(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#!/bin/bash | ||
# virtme-runkernel: A silly script to run a kernel image | ||
# Copyright © 2013 Andy Lutomirski | ||
# Licensed under the GPLv2, which is available in the virtme distribution | ||
# as a file called LICENSE with SHA-256 hash: | ||
# 8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643 | ||
|
||
# This is a giant hack. I'll make it better at some point. | ||
|
||
tmpfile="`mktemp --tmpdir initramfs.XXXXXXXXXX`" | ||
exec 3<>"$tmpfile" | ||
rm "$tmpfile" | ||
|
||
v_mki="$(dirname $0)/virtme-mkinitramfs" | ||
"$v_mki" >&3 | ||
|
||
exec qemu-kvm -virtfs local,id=rootfs,path=/,security_model=passthrough,mount_tag=hostroot,readonly -initrd /proc/self/fd/3 -kernel "$@" |