Skip to content

Commit

Permalink
Merge pull request #1 from ItzPires/Implement-support-for-VMWare
Browse files Browse the repository at this point in the history
Implement support for VMware and VMware ESXi
  • Loading branch information
ItzPires authored Nov 16, 2024
2 parents bd040ac + f32685b commit 2493fb3
Show file tree
Hide file tree
Showing 9 changed files with 311 additions and 20 deletions.
1 change: 1 addition & 0 deletions Exemples/HelloWorld/.gitignore → Examples/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
hello
networkTest

# Extra
*.o
Expand Down
File renamed without changes.
File renamed without changes.
70 changes: 68 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,68 @@
# Unikernel---Proof-of-Concept
Unikernel Proof of Concept
# Unikernel - Proof of Concept

## What is a Unikernel?

A Unikernel is a specialised image that contains only the parts of the operating system and libraries needed to run a specific application. Unlike traditional operating systems, which are general, they support a wide range of functionalities. Unikernels are highly minimalist, focused on providing only the essentials for a single purpose, which makes them lighter and more secure.

## Motivation

Based on the Unikernel concept and what currently exists, it was decided to use the Linux kernel to turn it into a Unikernel.

This project was born from an idea explored in the context of a Master's thesis.

## Supported Execution Environments

| Environment | :heavy_check_mark: / :x:|
|--|--|
|QEMU| :heavy_check_mark: |
|VMware | :heavy_check_mark: |
|VMware ESXi | :heavy_check_mark: |

## Installation Guide

Follow the steps below to configure and run the project:

### Requirements
- Install the necessary development tools and libraries:
```bash
sudo apt-get update
sudo apt install build-essential
sudo apt install flex
sudo apt install bison
sudo apt install libelf-dev
sudo apt install libssl-dev
sudo apt install bc
sudo apt install busybox-static
sudo apt install qemu
```

#### Clone the project repository
```bash
git clone https://github.com/ItzPires/Unikernel---Proof-of-Concept.git
cd Unikernel---Proof-of-Concept
git submodule update --init --recursive
```

#### Compilation
- To compile the Unikernel, run the following command:
```bash
./Scripts/build.sh <path_to_binary> [additional_parameters]
```

#### Execution
- To run the Unikernel in QEMU:
```bash
./Scripts/run.sh -t qemu
```
This command will open a monitor showing the execution of the proof of concept in a virtual machine.

- To run the Unikernel in VMware:
```bash
./Scripts/run.sh -t vmware
```

This command will generate a file called disk.vmx and disk.vmdk in the "/Output/vmware" folder. To run the kernel in VMware, open vmware with file '/Output/vmware/disk.vmx' and start the VM".

- To run the Unikernel in VMware ESXi:

You must follow all the creation steps in VMware and then export the virtual machine in OVF. With these export files, you can create a virtual machine in VMware ESXi.
7 changes: 7 additions & 0 deletions Scripts/Kernel/init
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "[Unikernel Proof of Concept]"
./scripts/mount_all_disks
{{BINARY_AND_ARGS}}
poweroff -f
36 changes: 36 additions & 0 deletions Scripts/Kernel/mount_all_disks
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/sh

# Base directory for mount points
MOUNT_DIR_BASE="/mnt/disk"

# Function to format and mount a disk
setup_and_mount_disk() {
local disk=$1
local mount_point="${MOUNT_DIR_BASE}${disk: -1}"

major_minor=$(cat /sys/class/block/$disk/dev)
mknod /dev/$disk b ${major_minor%:*} ${major_minor#*:}

# Create a partition on the disk
echo -e "n\np\n1\n\n\nw" | fdisk /dev/$disk

# Create a new block device for the partition
mknod /dev/${disk}1 b ${major_minor%:*} $((${major_minor#*:} + 1))

# Format the partition as ext4
./sbin/mke2fs -t ext4 /dev/${disk}1

# Create the mount point and mount the partition
mkdir -p $mount_point
mount -t ext4 /dev/${disk}1 $mount_point
echo "Disc $disk mounted at $mount_point"
}

# Init
mknod /dev/null c 1 3
mknod /dev/sda b 8 0

# Get the list of available disks
for disk in $(ls /sys/class/block/ | grep -E '^sd[b-z]$'); do
setup_and_mount_disk $disk
done
37 changes: 37 additions & 0 deletions Scripts/Templates/vmware.vmx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.encoding = "windows-1252"
config.version = "8"
virtualHW.version = "8"
pciBridge0.present = "TRUE"
pciBridge4.present = "TRUE"
pciBridge4.virtualDev = "pcieRootPort"
pciBridge4.functions = "8"
pciBridge5.present = "TRUE"
pciBridge5.virtualDev = "pcieRootPort"
pciBridge5.functions = "8"
pciBridge6.present = "TRUE"
pciBridge6.virtualDev = "pcieRootPort"
pciBridge6.functions = "8"
pciBridge7.present = "TRUE"
pciBridge7.virtualDev = "pcieRootPort"
pciBridge7.functions = "8"
vmci0.present = "TRUE"
hpet0.present = "TRUE"
nvram = "{{IMAGE_NAME}}.nvram"
virtualHW.productCompatibility = "hosted"
powerType.powerOff = "soft"
powerType.powerOn = "soft"
powerType.suspend = "soft"
powerType.reset = "soft"
displayName = "{{IMAGE_NAME}}"
guestOS = "other"
tools.syncTime = "FALSE"
cpuid.coresPerSocket = "1"
memsize = "1024"
ide0:0.fileName = "{{IMAGE_NAME}}.vmdk"
ide0:0.present = "TRUE"
ethernet0.virtualDev = "vmxnet3"
ethernet0.connectionType = "nat"
ethernet0.addressType = "generated"
ethernet0.present = "TRUE"
extendedConfigFile = "{{IMAGE_NAME}}.vmxf"
floppy0.present = "FALSE"
48 changes: 34 additions & 14 deletions Scripts/build.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#!/bin/bash

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

SCRIPTS_KERNEL_DIR="$SCRIPT_DIR/Kernel/"

if [ -z "$1" ] || [ "$1" == "-h" ]; then
echo "Usage: $0 <path_to_binary> [additional_parameters]"
exit 1
Expand All @@ -25,7 +29,7 @@ Compile the executable with the -static flag."
fi

# Go to kernel folder
if ! cd ../kernel 2>/dev/null; then
if ! cd $SCRIPT_DIR/../kernel 2>/dev/null; then
echo "Error: Could not access the 'kernel' directory. Please check if the submodules are initialized."
exit 1
fi
Expand All @@ -37,24 +41,40 @@ make -j$(nproc)

# Image creation
echo "Preparing the image..."
mkdir -p ../Output/initramfs/{bin,sbin,etc,proc,sys,newroot}
cp "$BINARY" ../Output/initramfs/bin/
mkdir -p $SCRIPT_DIR/../Output/RAW/initramfs/{bin,sbin,etc,proc,sys,newroot,scripts,lib,lib64}
cp "$BINARY" $SCRIPT_DIR/../Output/RAW/initramfs/bin/
chmod 777 $SCRIPT_DIR/../Output/RAW/initramfs/$BINARY_BIN

# Copy the scripts to scripts folder
cp "$SCRIPTS_KERNEL_DIR/mount_all_disks" "$SCRIPT_DIR/../Output/RAW/initramfs/scripts/"
chmod +x $SCRIPT_DIR/../Output/RAW/initramfs/scripts/mount_all_disks

# Program initiation file in unikernel
cat << EOF > ../Output/initramfs/init
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "[Unikernel Proof of Concept]"
$BINARY_BIN $@
poweroff -f
EOF
INIT_TEMPLATE_FILE="$SCRIPTS_KERNEL_DIR/init"
INIT_FILE="$SCRIPT_DIR/../Output/RAW/initramfs/init"
BINARY_AND_ARGS="$BINARY_BIN $@"
sed "s#{{BINARY_AND_ARGS}}#$BINARY_AND_ARGS#g" "$INIT_TEMPLATE_FILE" > "$INIT_FILE"
chmod 777 $INIT_FILE

chmod +x ../Output/initramfs/init
chmod +x $SCRIPT_DIR/../Output/RAW/initramfs/init

# Image compilation
cp -a /bin/busybox ../Output/initramfs/bin/
cd ../Output/initramfs/bin
cp -a /bin/busybox $SCRIPT_DIR/../Output/RAW/initramfs/bin/
cd $SCRIPT_DIR/../Output/RAW/initramfs/bin

sudo cp $(which mke2fs) $SCRIPT_DIR/../Output/RAW/initramfs/sbin/
sudo cp /lib/x86_64-linux-gnu/libext2fs.so.2 $SCRIPT_DIR/../Output/RAW/initramfs/lib/
sudo cp /lib/x86_64-linux-gnu/libcom_err.so.2 $SCRIPT_DIR/../Output/RAW/initramfs/lib/
sudo cp /lib/x86_64-linux-gnu/libblkid.so.1 $SCRIPT_DIR/../Output/RAW/initramfs/lib/
sudo cp /lib/x86_64-linux-gnu/libuuid.so.1 $SCRIPT_DIR/../Output/RAW/initramfs/lib/
sudo cp /lib/x86_64-linux-gnu/libe2p.so.2 $SCRIPT_DIR/../Output/RAW/initramfs/lib/
sudo cp /lib/x86_64-linux-gnu/libc.so.6 $SCRIPT_DIR/../Output/RAW/initramfs/lib/
sudo cp /lib64/ld-linux-x86-64.so.2 $SCRIPT_DIR/../Output/RAW/initramfs/lib64/

sudo chmod 777 $SCRIPT_DIR/../Output/RAW/initramfs/sbin/mke2fs
sudo chmod 777 $SCRIPT_DIR/../Output/RAW/initramfs/lib/*
sudo chmod 777 $SCRIPT_DIR/../Output/RAW/initramfs/lib64/*

for i in $(./busybox --list); do ln -s busybox $i; done
cd ../
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../image.img
Expand Down
132 changes: 128 additions & 4 deletions Scripts/run.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,126 @@
#!/bin/bash

ERROR_FOUND=false
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

KERNEL="../kernel/arch/x86_64/boot/bzImage"
IMAGE="../Output/image.img"
TEMPLATE_DIR="$SCRIPT_DIR/Templates/"
OUTPUT_DIR="$SCRIPT_DIR/../Output/"

KERNEL="$SCRIPT_DIR/../kernel/arch/x86_64/boot/bzImage"
KERNEL=$(readlink -f "$KERNEL")
IMAGE="$OUTPUT_DIR/RAW/image.img"

# Functions
usage() {
echo "Usage: $0 -t <target>"
echo "Targets: qemu, vmware"
echo
echo "Example: $0 -t qemu"
exit 1
}

run_qemu() {
echo "Running Unikernel"
qemu-system-x86_64 -kernel "$KERNEL" -initrd "$IMAGE" -append "console=ttyS0" -enable-kvm -nographic
}

build_vmware() {
echo "Building Unikernel to VMWare"
IMAGE_NAME="disk"
IMAGE_DNAME="$OUTPUT_DIR/TEMP/$IMAGE_NAME.img"
MOUNT_DIR="/mnt/disk"

# Calculate the size of the image in KB
BOOT_DIR_TEMP="$OUTPUT_DIR/TEMP/boot_contents_temp"
GRUB_DIR="/usr/lib/grub/i386-pc"

# Create a temporary directory for boot contents
mkdir -p "$BOOT_DIR_TEMP"
cp "$KERNEL" "$BOOT_DIR_TEMP/" # Copy the kernel
cp "$IMAGE" "$BOOT_DIR_TEMP/" # Copy the image
cp -r "$GRUB_DIR" "$BOOT_DIR_TEMP/grub/i386-pc" # Copy the grub files

# Calculate the size of the image in KB
TOTAL_SIZE_KB=$(du -sk "$BOOT_DIR_TEMP" | awk '{print $1}')
TOTAL_SIZE_KB=$((TOTAL_SIZE_KB + 102400)) # 2 MB for extra space

echo "Creating a disk image"
dd if=/dev/zero of=$IMAGE_DNAME bs=1K count=$TOTAL_SIZE_KB

LOOP_DEVICE=$(sudo losetup -fP --show $IMAGE_DNAME)

(
echo n # New partition
echo p # Primary partition
echo 1 # One partition
echo # Default start sector
echo # Default end sector
echo w # Write
) | sudo fdisk $LOOP_DEVICE

sudo mkfs.ext4 ${LOOP_DEVICE}p1
sudo mkdir -p $MOUNT_DIR
sudo mount ${LOOP_DEVICE}p1 $MOUNT_DIR

sudo grub-install --boot-directory=$MOUNT_DIR/boot --target=i386-pc $LOOP_DEVICE

echo "Copying files to Image"
sudo cp $KERNEL $MOUNT_DIR/boot/
sudo cp $IMAGE $MOUNT_DIR/boot/

cat <<EOF > $MOUNT_DIR/boot/grub/grub.cfg
set default=0
set timeout=0
menuentry "Unikernel" {
linux /boot/bzImage
initrd /boot/image.img
}
EOF

sudo umount $MOUNT_DIR

mkdir -p "$OUTPUT_DIR/vmware"
qemu-img convert -f raw -O vmdk $IMAGE_DNAME "$OUTPUT_DIR/vmware/$IMAGE_NAME".vmdk

rm -r $IMAGE_DNAME
rm -rf $BOOT_DIR_TEMP
}

create_vmx() {
local output_file="${OUTPUT_DIR}/vmware/${IMAGE_NAME}.vmx"
local template_vmx="$TEMPLATE_DIR/vmware.vmx"

# Replace placeholders with actual values
sed "s/{{IMAGE_NAME}}/$IMAGE_NAME/g" "$template_vmx" > "$output_file"
}

define_owner() {
CURRENT_USER=${SUDO_USER:-$(whoami)}
echo "Defining owner to $CURRENT_USER"
sudo chmod 664 "$OUTPUT_DIR/vmware/"*
sudo chown -R "$CURRENT_USER":"$CURRENT_USER" "$OUTPUT_DIR/vmware"
}

# Init

# Check number of arguments
if [ "$#" -ne 2 ]; then
usage
fi

if [ "$EUID" -ne 0 ]; then
echo "Require root privileges"
exec sudo "$0" "$@"
fi

# Process arguments
while getopts "t:" opt; do
case $opt in
t) TARGET="$OPTARG" ;;
*) usage ;;
esac
done

if [ ! -f "$KERNEL" ]; then
echo "Error: The Kernel does not exist."
Expand All @@ -20,5 +137,12 @@ if [ "$ERROR_FOUND" = true ]; then
exit 1
fi

echo "Running Unikernel"
qemu-system-x86_64 -kernel "$KERNEL" -initrd "$IMAGE" -append "console=ttyS0" -nographic
if [ "$TARGET" = "qemu" ]; then
run_qemu
elif [ "$TARGET" = "vmware" ]; then
build_vmware
create_vmx
define_owner
echo
echo "Usage: Open vmware with file '/Output/vmware/$IMAGE_NAME.vmx' and '/Output/vmware/$IMAGE_NAME.vmdk' and start the VM"
fi

0 comments on commit 2493fb3

Please sign in to comment.