Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ ifeq ($(TDX),1)
endif

HOSTARCH = $(shell uname -m)
OS = $(shell uname -s)
ifeq ($(origin OS),command line)
# Use the explicit OS value from command line (e.g. make OS=Windows)
else
OS := $(shell uname -s)
endif
ifeq ($(ARCH),)
GUESTARCH := $(HOSTARCH)
STRIP := strip
Expand All @@ -41,6 +45,11 @@ else
STRIP := $(CROSS_COMPILE)strip
endif

ifeq ($(OS),Windows)
CC := x86_64-w64-mingw32-gcc
VARIANT := -windows
endif

KBUNDLE_TYPE_x86_64 = vmlinux
KBUNDLE_TYPE_aarch64 = Image
KBUNDLE_TYPE_riscv64 = Image
Expand All @@ -59,8 +68,14 @@ KRUNFW_SONAME_Darwin = libkrunfw.$(ABI_VERSION).dylib
KRUNFW_BASE_Darwin = libkrunfw.dylib
SONAME_Darwin =

KRUNFW_BINARY_Windows = libkrunfw.dll
KRUNFW_SONAME_Windows = libkrunfw.dll
KRUNFW_BASE_Windows = libkrunfw.dll
SONAME_Windows =

LIBDIR_Linux = lib64
LIBDIR_Darwin = lib
LIBDIR_Windows = bin

ifeq ($(PREFIX),)
PREFIX := /usr/local
Expand Down Expand Up @@ -103,7 +118,7 @@ $(KERNEL_C_BUNDLE):
else
$(KERNEL_C_BUNDLE): $(KERNEL_BINARY_$(GUESTARCH))
@echo "Generating $(KERNEL_C_BUNDLE) from $(KERNEL_BINARY_$(GUESTARCH))..."
@python3 bin2cbundle.py -t $(KBUNDLE_TYPE_$(GUESTARCH)) $(KERNEL_BINARY_$(GUESTARCH)) kernel.c
@python3 bin2cbundle.py --os $(OS) -t $(KBUNDLE_TYPE_$(GUESTARCH)) $(KERNEL_BINARY_$(GUESTARCH)) kernel.c
endif

ifeq ($(SEV),1)
Expand All @@ -127,16 +142,22 @@ $(INITRD_C_BUNDLE): $(INITRD_BINARY)
endif

$(KRUNFW_BINARY_$(OS)): $(KERNEL_C_BUNDLE) $(QBOOT_C_BUNDLE) $(INITRD_C_BUNDLE)
ifeq ($(OS),Windows)
$(CC) -shared -DABI_VERSION=$(ABI_VERSION) -O2 -o $@ $(KERNEL_C_BUNDLE) $(QBOOT_C_BUNDLE) $(INITRD_C_BUNDLE) -Wl,--kill-at -Wl,--nxcompat
else
$(CC) -fPIC -DABI_VERSION=$(ABI_VERSION) -shared $(SONAME_$(OS)) -o $@ $(KERNEL_C_BUNDLE) $(QBOOT_C_BUNDLE) $(INITRD_C_BUNDLE)
ifeq ($(OS),Linux)
$(STRIP) $(KRUNFW_BINARY_$(OS))
endif
endif

install:
install -d $(DESTDIR)$(PREFIX)/$(LIBDIR_$(OS))/
install -m 755 $(KRUNFW_BINARY_$(OS)) $(DESTDIR)$(PREFIX)/$(LIBDIR_$(OS))/
ifeq ($(OS),Darwin)
cd $(DESTDIR)$(PREFIX)/$(LIBDIR_$(OS))/ ; ln -sf $(KRUNFW_BINARY_$(OS)) $(KRUNFW_BASE_$(OS))
else ifeq ($(OS),Windows)
# Windows doesn't need soname symlinks
else
cd $(DESTDIR)$(PREFIX)/$(LIBDIR_$(OS))/ ; ln -sf $(KRUNFW_BINARY_$(OS)) $(KRUNFW_SONAME_$(OS)) ; ln -sf $(KRUNFW_SONAME_$(OS)) $(KRUNFW_BASE_$(OS))
endif
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,33 @@ BUILDER=debian ./build_on_krunvm.sh

In general, `./build_on_krunvm.sh` will always delegate to `./build_on_krunvm_${BUILDER}.sh` so additional environments can be added like this if needed.

### Windows (cross-compilation from Linux)

#### Requirements
* A Linux host with the toolchain needed to build a Linux kernel.
* The MinGW-w64 cross-compiler (`x86_64-w64-mingw32-gcc`), available as `mingw-w64-gcc` (Fedora) or `gcc-mingw-w64-x86-64` (Debian/Ubuntu).
* Python 3
* ```pyelftools``` (package ```python3-pyelftools``` in Fedora and Ubuntu)

#### How it works

The Windows build uses a dedicated kernel configuration (`config-libkrunfw-windows_x86_64`) that enables Hyper-V guest enlightenments (`CONFIG_HYPERV`, `CONFIG_HYPERV_TIMER`, `CONFIG_HYPERV_UTILS`), allowing the guest kernel to take advantage of the Windows Hypervisor Platform (WHP).

The kernel bundle is aligned to 4K pages (instead of 64K on Linux) to match the page granularity used by x86_64 Windows and WHP, avoiding alignment mismatches when the VMM maps the kernel into guest memory.

The resulting `libkrunfw.dll` is produced using the MinGW-w64 toolchain and can be consumed by the Windows build of [libkrun](https://github.com/containers/libkrun).

#### Building the library
```
make OS=Windows
```

This will:
1. Download and patch the kernel sources.
2. Build the kernel using the Windows-specific configuration.
3. Generate the C bundle with 4K page alignment.
4. Cross-compile `libkrunfw.dll` using `x86_64-w64-mingw32-gcc`.

## Known limitations

* To save memory, the embedded kernel is configured with ```CONFIG_NR_CPUS=8```, which limits the maximum number of supported CPUs to 8. If this kernel runs in a VM with more CPUs, only the first 8 will be initialized and used.
Expand Down
26 changes: 15 additions & 11 deletions bin2cbundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@

from elftools.elf.elffile import ELFFile

# Use 64k page size for rounding. This should cover 4k/16k/64k kernels
PAGE_SIZE = 65536
PAGE_SIZE_DEFAULT = 65536 # 64k covers 4k/16k/64k Linux kernels
PAGE_SIZE_WINDOWS = 4096 # x86_64 Windows / WHP uses 4k pages
AARCH64_LOAD_ADDR = '0x80000000'

def write_header(ofile, bundle_name):
def write_header(ofile, bundle_name, page_size):
ofile.write('#include <stddef.h>\n')
ofile.write('__attribute__ ((aligned ({}))) char {}_BUNDLE[] = \n"'.format(PAGE_SIZE, bundle_name))
ofile.write('__attribute__ ((aligned ({}))) char {}_BUNDLE[] = \n"'.format(page_size, bundle_name))


def write_padding(ofile, padding, col):
Expand All @@ -25,7 +25,7 @@ def write_padding(ofile, padding, col):
padding = padding - 1


def write_elf_cbundle(ifile, ofile) -> int:
def write_elf_cbundle(ifile, ofile, page_size) -> int:
elffile = ELFFile(ifile)
entry_addr = elffile['e_entry']

Expand Down Expand Up @@ -61,14 +61,14 @@ def write_elf_cbundle(ifile, ofile) -> int:
prev_filesz = segment['p_filesz']
total_size = total_size + prev_filesz

rounded_size = int((total_size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE
rounded_size = int((total_size + page_size - 1) / page_size) * page_size
padding = rounded_size - total_size
write_padding(ofile, padding, col)

return load_addr, entry_addr


def write_raw_cbundle(ifile, ofile) -> int:
def write_raw_cbundle(ifile, ofile, page_size) -> int:
col = 0
total_size = 0
byte = ifile.read(1)
Expand All @@ -84,7 +84,7 @@ def write_raw_cbundle(ifile, ofile) -> int:
total_size = total_size + 1
byte = ifile.read(1)

rounded_size = int((total_size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE
rounded_size = int((total_size + page_size - 1) / page_size) * page_size
padding = rounded_size - total_size
write_padding(ofile, padding, col)

Expand Down Expand Up @@ -128,9 +128,13 @@ def main() -> int:
parser.add_argument('output_file', type=str,
help='Output file')
parser.add_argument('-t', type=str, help='Bundle type (vmlinux, Image, qboot, initrd)')
parser.add_argument('--os', type=str, default='Linux',
help='Target OS (Linux, Darwin, Windows)')

args = parser.parse_args()

page_size = PAGE_SIZE_WINDOWS if args.os == 'Windows' else PAGE_SIZE_DEFAULT

bundle_name = None
ifmt = None
if args.t == 'vmlinux':
Expand All @@ -152,12 +156,12 @@ def main() -> int:
ifile = open(args.input_file, 'rb')
ofile = open(args.output_file, 'w')

write_header(ofile, bundle_name)
write_header(ofile, bundle_name, page_size)

if ifmt == 'elf':
load_addr, entry_addr = write_elf_cbundle(ifile, ofile)
load_addr, entry_addr = write_elf_cbundle(ifile, ofile, page_size)
elif ifmt == 'raw':
write_raw_cbundle(ifile, ofile)
write_raw_cbundle(ifile, ofile, page_size)

if bundle_name == 'KERNEL':
if ifmt == 'raw':
Expand Down
Loading
Loading