os: build bootable images

This commit is contained in:
Alex Zenla 2024-03-10 00:22:24 +00:00
parent 817509bcef
commit 4894bd9d1c
No known key found for this signature in database
GPG Key ID: 067B238899B51269
28 changed files with 384 additions and 82 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
/target
/kernel/linux-*

View File

@ -13,4 +13,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- run: ./hack/ci/install-deps.sh
- run: ./hack/kernel/build.sh -j5
- run: ./hack/kernel/build.sh
env:
KRATA_KERNEL_BUILD_JOBS: "5"

View File

@ -16,17 +16,32 @@ jobs:
- run: ./hack/dist/bundle.sh
env:
KRATA_KERNEL_BUILD_JOBS: "5"
- uses: actions/upload-artifact@v4
with:
name: krata-bundle-systemd-x86_64
path: "target/dist/bundle-systemd.tgz"
compression-level: 0
- run: ./hack/dist/deb.sh
env:
KRATA_BUNDLE_SKIP_KERNEL_BUILD: "1"
KRATA_KERNEL_BUILD_SKIP: "1"
- uses: actions/upload-artifact@v4
with:
name: krata-nightly-bundle-x86_64
path: "target/dist/bundle.tgz"
name: krata-debian-x86_64
path: "target/dist/*_amd64.deb"
compression-level: 0
- run: ./hack/dist/apk.sh
env:
KRATA_KERNEL_BUILD_SKIP: "1"
- uses: actions/upload-artifact@v4
with:
name: krata-nightly-debian-x86_64
path: "target/dist/*_amd64.deb"
compression-level: 0
name: krata-alpine-x86_64
path: "target/dist/*_amd64.apk"
compression-level: 0
- run: ./hack/os/build.sh
env:
KRATA_KERNEL_BUILD_SKIP: "1"
- uses: actions/upload-artifact@v4
with:
name: krata-os-x86_64
path: "target/os/krata.qcow2"
compression-level: 0

View File

@ -75,6 +75,8 @@ features = ["derive"]
[workspace.dependencies.reqwest]
version = "0.11.24"
default-features = false
features = ["rustls-tls"]
[workspace.dependencies.sys-mount]
version = "3.0.0"

View File

@ -60,7 +60,7 @@ impl RuntimeContext {
image_cache_path.push("cache");
fs::create_dir_all(&image_cache_path)?;
let xen = XenClient::open().await?;
let xen = XenClient::open(0).await?;
image_cache_path.push("image");
fs::create_dir_all(&image_cache_path)?;
let image_cache = ImageCache::new(&image_cache_path)?;

View File

@ -5,7 +5,7 @@ use xencall::XenCall;
fn main() -> Result<()> {
env_logger::init();
let call = XenCall::open()?;
let call = XenCall::open(0)?;
let domid = call.create_domain(CreateDomain::default())?;
println!("created domain {}", domid);
Ok(())

View File

@ -4,7 +4,7 @@ use xencall::XenCall;
fn main() -> Result<()> {
env_logger::init();
let call = XenCall::open()?;
let call = XenCall::open(0)?;
let info = call.get_domain_info(1)?;
println!("{:?}", info);
Ok(())

View File

@ -4,7 +4,7 @@ use xencall::XenCall;
fn main() -> Result<()> {
env_logger::init();
let call = XenCall::open()?;
let call = XenCall::open(0)?;
let context = call.get_vcpu_context(224, 0)?;
println!("{:?}", context);
Ok(())

View File

@ -4,7 +4,7 @@ use xencall::XenCall;
fn main() -> Result<()> {
env_logger::init();
let call = XenCall::open()?;
let call = XenCall::open(0)?;
let info = call.get_version_capabilities()?;
println!("{:?}", info);
Ok(())

View File

@ -2,9 +2,11 @@ use std::io;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("kernel error")]
#[error("version of xen is not supported")]
XenVersionUnsupported,
#[error("kernel error: {0}")]
Kernel(#[from] nix::errno::Errno),
#[error("io issue encountered")]
#[error("io issue encountered: {0}")]
Io(#[from] io::Error),
#[error("populate physmap failed")]
PopulatePhysmapFailed,

View File

@ -3,22 +3,22 @@ pub mod sys;
use crate::error::{Error, Result};
use crate::sys::{
AddressSize, ArchDomainConfig, CreateDomain, DomCtl, DomCtlValue, DomCtlVcpuContext,
EvtChnAllocUnbound, GetDomainInfo, GetPageFrameInfo3, Hypercall, HypercallInit, MaxMem,
MaxVcpus, MemoryMap, MemoryReservation, MmapBatch, MmapResource, MmuExtOp, MultiCallEntry,
VcpuGuestContext, VcpuGuestContextAny, XenCapabilitiesInfo, HYPERVISOR_DOMCTL,
HYPERVISOR_EVENT_CHANNEL_OP, HYPERVISOR_MEMORY_OP, HYPERVISOR_MMUEXT_OP, HYPERVISOR_MULTICALL,
HYPERVISOR_XEN_VERSION, XENVER_CAPABILITIES, XEN_DOMCTL_CREATEDOMAIN, XEN_DOMCTL_DESTROYDOMAIN,
AddressSize, CreateDomain, DomCtl, DomCtlValue, DomCtlVcpuContext, EvtChnAllocUnbound,
GetDomainInfo, GetPageFrameInfo3, Hypercall, HypercallInit, MaxMem, MaxVcpus, MemoryMap,
MemoryReservation, MmapBatch, MmapResource, MmuExtOp, MultiCallEntry, VcpuGuestContext,
VcpuGuestContextAny, XenCapabilitiesInfo, HYPERVISOR_DOMCTL, HYPERVISOR_EVENT_CHANNEL_OP,
HYPERVISOR_MEMORY_OP, HYPERVISOR_MMUEXT_OP, HYPERVISOR_MULTICALL, HYPERVISOR_XEN_VERSION,
XENVER_CAPABILITIES, XEN_DOMCTL_CREATEDOMAIN, XEN_DOMCTL_DESTROYDOMAIN,
XEN_DOMCTL_GETDOMAININFO, XEN_DOMCTL_GETPAGEFRAMEINFO3, XEN_DOMCTL_GETVCPUCONTEXT,
XEN_DOMCTL_HYPERCALL_INIT, XEN_DOMCTL_INTERFACE_VERSION, XEN_DOMCTL_MAX_MEM,
XEN_DOMCTL_MAX_VCPUS, XEN_DOMCTL_PAUSEDOMAIN, XEN_DOMCTL_SETVCPUCONTEXT,
XEN_DOMCTL_SET_ADDRESS_SIZE, XEN_DOMCTL_UNPAUSEDOMAIN, XEN_MEM_CLAIM_PAGES, XEN_MEM_MEMORY_MAP,
XEN_MEM_POPULATE_PHYSMAP,
XEN_DOMCTL_HYPERCALL_INIT, XEN_DOMCTL_MAX_MEM, XEN_DOMCTL_MAX_VCPUS, XEN_DOMCTL_PAUSEDOMAIN,
XEN_DOMCTL_SETVCPUCONTEXT, XEN_DOMCTL_SET_ADDRESS_SIZE, XEN_DOMCTL_UNPAUSEDOMAIN,
XEN_MEM_CLAIM_PAGES, XEN_MEM_MEMORY_MAP, XEN_MEM_POPULATE_PHYSMAP,
};
use libc::{c_int, mmap, usleep, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE};
use log::trace;
use nix::errno::Errno;
use std::ffi::{c_long, c_uint, c_ulong, c_void};
use sys::{XEN_DOMCTL_MAX_INTERFACE_VERSION, XEN_DOMCTL_MIN_INTERFACE_VERSION};
use std::fs::{File, OpenOptions};
use std::os::fd::AsRawFd;
@ -27,15 +27,45 @@ use std::slice;
pub struct XenCall {
pub handle: File,
domctl_interface_version: u32,
}
impl XenCall {
pub fn open() -> Result<XenCall> {
let file = OpenOptions::new()
pub fn open(current_domid: u32) -> Result<XenCall> {
let handle = OpenOptions::new()
.read(true)
.write(true)
.open("/dev/xen/privcmd")?;
Ok(XenCall { handle: file })
let domctl_interface_version =
XenCall::detect_domctl_interface_version(&handle, current_domid)?;
Ok(XenCall {
handle,
domctl_interface_version,
})
}
fn detect_domctl_interface_version(handle: &File, current_domid: u32) -> Result<u32> {
for version in XEN_DOMCTL_MIN_INTERFACE_VERSION..XEN_DOMCTL_MAX_INTERFACE_VERSION + 1 {
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_GETDOMAININFO,
interface_version: version,
domid: current_domid,
value: DomCtlValue {
get_domain_info: GetDomainInfo::default(),
},
};
unsafe {
let mut call = Hypercall {
op: HYPERVISOR_DOMCTL,
arg: [addr_of_mut!(domctl) as u64, 0, 0, 0, 0],
};
let result = sys::hypercall(handle.as_raw_fd(), &mut call).unwrap_or(-1);
if result == 0 {
return Ok(version);
}
}
}
Err(Error::XenVersionUnsupported)
}
pub fn mmap(&self, addr: u64, len: u64) -> Option<u64> {
@ -275,32 +305,10 @@ impl XenCall {
);
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_GETDOMAININFO,
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
interface_version: self.domctl_interface_version,
domid,
value: DomCtlValue {
get_domain_info: GetDomainInfo {
domid: 0,
pad1: 0,
flags: 0,
total_pages: 0,
max_pages: 0,
outstanding_pages: 0,
shr_pages: 0,
paged_pages: 0,
shared_info_frame: 0,
cpu_time: 0,
number_online_vcpus: 0,
max_vcpu_id: 0,
ssidref: 0,
handle: [0; 16],
cpupool: 0,
gpaddr_bits: 0,
pad2: [0; 7],
arch: ArchDomainConfig {
emulation_flags: 0,
misc_flags: 0,
},
},
get_domain_info: GetDomainInfo::default(),
},
};
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
@ -315,7 +323,7 @@ impl XenCall {
);
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_CREATEDOMAIN,
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
interface_version: self.domctl_interface_version,
domid: 0,
value: DomCtlValue { create_domain },
};
@ -331,7 +339,7 @@ impl XenCall {
);
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_PAUSEDOMAIN,
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
interface_version: self.domctl_interface_version,
domid,
value: DomCtlValue { pad: [0; 128] },
};
@ -347,7 +355,7 @@ impl XenCall {
);
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_UNPAUSEDOMAIN,
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
interface_version: self.domctl_interface_version,
domid,
value: DomCtlValue { pad: [0; 128] },
};
@ -364,7 +372,7 @@ impl XenCall {
);
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_MAX_MEM,
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
interface_version: self.domctl_interface_version,
domid,
value: DomCtlValue {
max_mem: MaxMem { max_memkb: memkb },
@ -383,7 +391,7 @@ impl XenCall {
);
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_MAX_VCPUS,
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
interface_version: self.domctl_interface_version,
domid,
value: DomCtlValue {
max_cpus: MaxVcpus { max_vcpus },
@ -402,7 +410,7 @@ impl XenCall {
);
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_SET_ADDRESS_SIZE,
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
interface_version: self.domctl_interface_version,
domid,
value: DomCtlValue {
address_size: AddressSize { size },
@ -423,7 +431,7 @@ impl XenCall {
};
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_GETVCPUCONTEXT,
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
interface_version: self.domctl_interface_version,
domid,
value: DomCtlValue {
vcpu_context: DomCtlVcpuContext {
@ -452,7 +460,7 @@ impl XenCall {
let mut value = VcpuGuestContextAny { value: *context };
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_SETVCPUCONTEXT,
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
interface_version: self.domctl_interface_version,
domid,
value: DomCtlValue {
vcpu_context: DomCtlVcpuContext {
@ -469,7 +477,7 @@ impl XenCall {
let mut buffer: Vec<u64> = frames.to_vec();
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_GETPAGEFRAMEINFO3,
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
interface_version: self.domctl_interface_version,
domid,
value: DomCtlValue {
get_page_frame_info: GetPageFrameInfo3 {
@ -497,7 +505,7 @@ impl XenCall {
);
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_HYPERCALL_INIT,
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
interface_version: self.domctl_interface_version,
domid,
value: DomCtlValue {
hypercall_init: HypercallInit { gmfn },
@ -515,7 +523,7 @@ impl XenCall {
);
let mut domctl = DomCtl {
cmd: XEN_DOMCTL_DESTROYDOMAIN,
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
interface_version: self.domctl_interface_version,
domid,
value: DomCtlValue { pad: [0; 128] },
};

View File

@ -280,7 +280,7 @@ impl Default for CreateDomain {
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, Default)]
pub struct GetDomainInfo {
pub domid: u16,
pub pad1: u16,
@ -310,7 +310,7 @@ pub struct GetPageFrameInfo3 {
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, Default)]
pub struct ArchDomainConfig {
pub emulation_flags: u32,
pub misc_flags: u32,
@ -334,7 +334,9 @@ pub struct HypercallInit {
pub gmfn: u64,
}
pub const XEN_DOMCTL_INTERFACE_VERSION: u32 = 0x00000015;
pub const XEN_DOMCTL_MIN_INTERFACE_VERSION: u32 = 0x00000015;
pub const XEN_DOMCTL_MAX_INTERFACE_VERSION: u32 = 0x00000016;
pub const SECINITSID_DOMU: u32 = 12;
#[repr(C)]

View File

@ -13,7 +13,7 @@ async fn main() -> Result<()> {
}
let kernel_image_path = args.get(1).expect("argument not specified");
let initrd_path = args.get(2).expect("argument not specified");
let mut client = XenClient::open().await?;
let mut client = XenClient::open(0).await?;
let config = DomainConfig {
backend_domid: 0,
name: "xenclient-test",

View File

@ -83,9 +83,9 @@ pub struct DomainConfig<'a> {
}
impl XenClient {
pub async fn open() -> Result<XenClient> {
pub async fn open(current_domid: u32) -> Result<XenClient> {
let store = XsdClient::open().await?;
let call = XenCall::open()?;
let call = XenCall::open(current_domid)?;
Ok(XenClient { store, call })
}

View File

@ -4,5 +4,10 @@ set -e
TOOLS_DIR="$(dirname "${0}")"
RUST_TARGET="$("${TOOLS_DIR}/target.sh")"
if [ "${RUST_LIBC}" = "musl" ] && [ -f "/etc/alpine-release" ]
then
export RUSTFLAGS="-Ctarget-feature=-crt-static"
fi
export CARGO_BUILD_TARGET="${RUST_TARGET}"
exec cargo "${@}"

View File

@ -5,3 +5,4 @@ REAL_SCRIPT="$(realpath "${0}")"
cd "$(dirname "${REAL_SCRIPT}")/../.."
find hack -type f -name '*.sh' -print0 | xargs -0 shellcheck -x
find os/internal -type f -name '*.sh' -print0 | xargs -0 shellcheck -x

25
hack/dist/apk.sh vendored Executable file
View File

@ -0,0 +1,25 @@
#!/bin/sh
set -e
# shellcheck source-path=SCRIPTDIR source=common.sh
. "$(dirname "${0}")/common.sh"
export RUST_LIBC="musl"
KRATA_SYSTAR_OPENRC=1 "${KRATA_DIR}/hack/dist/systar.sh"
KRATA_VERSION="$("${KRATA_DIR}/hack/dist/version.sh")"
TARGET_ARCH="$("${KRATA_DIR}/hack/build/arch.sh")"
cd "${OUTPUT_DIR}"
rm -f "krata_${KRATA_VERSION}_${TARGET_ARCH}.apk"
fpm -s tar -t apk \
--name krata \
--license agpl3 \
--version "${KRATA_VERSION}" \
--architecture "${TARGET_ARCH}" \
--description "Krata Hypervisor" \
--url "https://krata.dev" \
--maintainer "Edera Team <contact@edera.dev>" \
"${OUTPUT_DIR}/system-openrc.tgz"

4
hack/dist/bundle.sh vendored
View File

@ -9,7 +9,7 @@ then
KRATA_KERNEL_BUILD_JOBS="2"
fi
BUNDLE_TAR="${OUTPUT_DIR}/bundle.tgz"
BUNDLE_TAR="${OUTPUT_DIR}/bundle-systemd.tgz"
rm -f "${BUNDLE_TAR}"
BUNDLE_DIR="$(mktemp -d /tmp/krata-bundle.XXXXXXXXXXXXX)"
BUNDLE_DIR="${BUNDLE_DIR}/krata"
@ -21,7 +21,7 @@ do
cp "${KRATA_DIR}/target/${RUST_TARGET}/release/${X}" "${BUNDLE_DIR}/${X}"
done
./hack/initrd/build.sh
if [ "${KRATA_BUNDLE_SKIP_KERNEL_BUILD}" != "1" ]
if [ "${KRATA_KERNEL_BUILD_SKIP}" != "1" ]
then
./hack/kernel/build.sh "-j${KRATA_KERNEL_BUILD_JOBS}"
fi

2
hack/dist/deb.sh vendored
View File

@ -27,4 +27,4 @@ fpm -s tar -t deb \
--deb-systemd "${KRATA_DIR}/resources/systemd/kratanet.service" \
--deb-systemd-enable \
--deb-systemd-auto-start \
"${OUTPUT_DIR}/system.tgz"
"${OUTPUT_DIR}/system-systemd.tgz"

23
hack/dist/systar.sh vendored
View File

@ -6,11 +6,17 @@ set -e
"${KRATA_DIR}/hack/dist/bundle.sh"
SYSTAR="${OUTPUT_DIR}/system.tgz"
SYSTAR_VARIANT="systemd"
if [ "${KRATA_SYSTAR_OPENRC}" = "1" ]
then
SYSTAR_VARIANT="openrc"
fi
SYSTAR="${OUTPUT_DIR}/system-${SYSTAR_VARIANT}.tgz"
rm -f "${SYSTAR}"
SYSTAR_DIR="$(mktemp -d /tmp/krata-systar.XXXXXXXXXXXXX)"
cd "${SYSTAR_DIR}"
tar xf "${OUTPUT_DIR}/bundle.tgz"
tar xf "${OUTPUT_DIR}/bundle-systemd.tgz"
mkdir sys
cd sys
@ -19,8 +25,17 @@ mkdir -p usr/bin usr/libexec
mv ../krata/kratactl usr/bin
mv ../krata/kratanet ../krata/kratad usr/libexec/
mkdir -p usr/lib/systemd/system
mv ../krata/kratad.service ../krata/kratanet.service usr/lib/systemd/system/
if [ "${SYSTAR_VARIANT}" = "openrc" ]
then
mkdir -p etc/init.d
cp "${KRATA_DIR}/resources/openrc/kratad" etc/init.d/kratad
cp "${KRATA_DIR}/resources/openrc/kratanet" etc/init.d/kratanet
chmod +x etc/init.d/kratad
chmod +x etc/init.d/kratanet
else
mkdir -p usr/lib/systemd/system
mv ../krata/kratad.service ../krata/kratanet.service usr/lib/systemd/system/
fi
mkdir -p usr/share/krata/guest
mv ../krata/kernel ../krata/initrd usr/share/krata/guest

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/sh
set -e
REAL_SCRIPT="$(realpath "${0}")"
@ -16,5 +16,5 @@ cp "target/${RUST_TARGET}/release/krataguest" "${INITRD_DIR}/init"
chmod +x "${INITRD_DIR}/init"
cd "${INITRD_DIR}"
mkdir -p "${KRATA_DIR}/target/initrd"
find . | cpio -R 0:0 --reproducible -o -H newc --quiet > "${KRATA_DIR}/target/initrd/initrd"
find . | cpio -R 0:0 --ignore-devno --renumber-inodes -o -H newc --quiet > "${KRATA_DIR}/target/initrd/initrd"
rm -rf "${INITRD_DIR}"

View File

@ -12,6 +12,11 @@ cd "${KRATA_DIR}"
. "${KERNEL_DIR}/config.sh"
KERNEL_SRC="${KERNEL_DIR}/linux-${KERNEL_VERSION}"
if [ -z "${KRATA_KERNEL_BUILD_JOBS}" ]
then
KRATA_KERNEL_BUILD_JOBS="2"
fi
if [ ! -f "${KERNEL_SRC}/Makefile" ]
then
rm -rf "${KERNEL_SRC}"
@ -34,14 +39,14 @@ then
fi
cp "${KERNEL_CONFIG_FILE}" "${KERNEL_SRC}/.config"
make -C "${KERNEL_SRC}" ARCH="${TARGET_ARCH}" "${@}" olddefconfig
make -C "${KERNEL_SRC}" ARCH="${TARGET_ARCH}" olddefconfig
if [ "${TARGET_ARCH}" = "x86_64" ]
then
make -C "${KERNEL_SRC}" ARCH="${TARGET_ARCH}" "${@}" bzImage
make -C "${KERNEL_SRC}" ARCH="${TARGET_ARCH}" -j"${KRATA_KERNEL_BUILD_JOBS}" bzImage
elif [ "${TARGET_ARCH}" = "arm64" ]
then
make -C "${KERNEL_SRC}" ARCH="${TARGET_ARCH}" "${@}" Image.gz
make -C "${KERNEL_SRC}" ARCH="${TARGET_ARCH}" -j"${KRATA_KERNEL_BUILD_JOBS}" Image.gz
fi
if [ "${TARGET_ARCH}" = "x86_64" ]

95
hack/os/build.sh Executable file
View File

@ -0,0 +1,95 @@
#!/bin/sh
set -e
REAL_SCRIPT="$(realpath "${0}")"
cd "$(dirname "${REAL_SCRIPT}")/../.."
./hack/dist/apk.sh
KRATA_VERSION="$(./hack/dist/version.sh)"
TARGET_ARCH="$(./hack/build/arch.sh)"
TARGET_DIR="${PWD}/target"
TARGET_OS_DIR="${TARGET_DIR}/os"
mkdir -p "${TARGET_OS_DIR}"
cp "${TARGET_DIR}/dist/krata_${KRATA_VERSION}_${TARGET_ARCH}.apk" "${TARGET_OS_DIR}/krata.apk"
docker run --rm --privileged -v "${PWD}:/mnt" -it alpine:latest "/mnt/os/internal/stage1.sh"
sudo chown "${USER}:${GROUP}" "${TARGET_OS_DIR}/rootfs.tgz"
sudo modprobe nbd
NBD_DEVICE="$(find /dev -maxdepth 2 -name 'nbd[0-9]*' | while read -r DEVICE
do
if [ "$(sudo blockdev --getsize64 "${DEVICE}")" = "0" ]
then
echo "${DEVICE}"
break
fi
done)"
if [ -z "${NBD_DEVICE}" ]
then
echo "ERROR: unable to allocate nbd device" > /dev/stderr
exit 1
fi
OS_IMAGE="${TARGET_OS_DIR}/krata.qcow2"
EFI_PART="${NBD_DEVICE}p1"
ROOT_PART="${NBD_DEVICE}p2"
ROOT_DIR="${TARGET_OS_DIR}/root"
EFI_DIR="${ROOT_DIR}/boot/efi"
cleanup() {
trap '' EXIT HUP INT TERM
sudo umount -R "${ROOT_DIR}" > /dev/null 2>&1 || true
sudo umount "${EFI_PART}" > /dev/null 2>&1 || true
sudo umount "${ROOT_PART}" > /dev/null 2>&1 || true
sudo qemu-nbd --disconnect "${NBD_DEVICE}" > /dev/null 2>&1 || true
sudo rm -rf "${ROOT_DIR}"
}
rm -f "${OS_IMAGE}"
qemu-img create -f qcow2 "${TARGET_OS_DIR}/krata.qcow2" "2G"
trap cleanup EXIT HUP INT TERM
sudo qemu-nbd --connect="${NBD_DEVICE}" --cache=writeback -f qcow2 "${OS_IMAGE}"
printf '%s\n' \
'label: gpt' \
'name=efi,type=U,size=128M,bootable' \
'name=system,type=L' | sudo sfdisk "${NBD_DEVICE}"
sudo mkfs.fat -F32 -n EFI "${EFI_PART}"
sudo mkfs.ext4 -L root -E discard "${ROOT_PART}"
mkdir -p "${ROOT_DIR}"
sudo mount -t ext4 "${ROOT_PART}" "${ROOT_DIR}"
sudo mkdir -p "${EFI_DIR}"
sudo mount -t vfat "${EFI_PART}" "${EFI_DIR}"
sudo tar xf "${TARGET_OS_DIR}/rootfs.tar" -C "${ROOT_DIR}"
ROOT_UUID="$(sudo blkid "${ROOT_PART}" | sed -En 's/.*\bUUID="([^"]+)".*/\1/p')"
EFI_UUID="$(sudo blkid "${EFI_PART}" | sed -En 's/.*\bUUID="([^"]+)".*/\1/p')"
echo "${ROOT_UUID}"
sudo mkdir -p "${ROOT_DIR}/proc" "${ROOT_DIR}/dev" "${ROOT_DIR}/sys"
sudo mount -t proc none "${ROOT_DIR}/proc"
sudo mount --bind /dev "${ROOT_DIR}/dev"
sudo mount --make-private "${ROOT_DIR}/dev"
sudo mount --bind /sys "${ROOT_DIR}/sys"
sudo mount --make-private "${ROOT_DIR}/sys"
sudo cp "${PWD}/os/internal/stage2.sh" "${ROOT_DIR}/stage2.sh"
echo "${ROOT_UUID}" | sudo tee "${ROOT_DIR}/root-uuid" > /dev/null
sudo chroot "${ROOT_DIR}" /bin/sh -c "/stage2.sh"
sudo rm -f "${ROOT_DIR}/stage2.sh"
sudo rm -f "${ROOT_DIR}/root-uuid"
{
echo "# krata fstab"
echo "UUID=${ROOT_UUID} / ext4 relatime 0 1"
echo "UUID=${EFI_UUID} / vfat rw,relatime,fmask=0133,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro 0 2"
} | sudo tee "${ROOT_DIR}/etc/fstab" > /dev/null
cleanup
OS_SMALL_IMAGE="${TARGET_OS_DIR}/krata.small.qcow2"
qemu-img convert -O qcow2 "${OS_IMAGE}" "${OS_SMALL_IMAGE}"
mv -f "${OS_SMALL_IMAGE}" "${OS_IMAGE}"

80
os/internal/stage1.sh Executable file
View File

@ -0,0 +1,80 @@
#!/bin/sh
set -e
apk add --update-cache alpine-base \
linux-lts linux-firmware-none \
mkinitfs dosfstools e2fsprogs \
tzdata chrony
apk add --allow-untrusted /mnt/target/os/krata.apk
for SERVICE in kratad kratanet
do
rc-update add "${SERVICE}" default
done
apk add xen xen-hypervisor
for SERVICE in xenconsoled xenstored
do
rc-update add "${SERVICE}" default
done
for MODULE in xen-netblock xen-blkback tun tap
do
echo "${MODULE}" >> /etc/modules
done
cat > /etc/network/interfaces <<-EOF
auto eth0
iface eth0 inet dhcp
EOF
for SERVICE in networking chronyd
do
rc-update add "${SERVICE}" default
done
for SERVICE in devfs dmesg mdev hwdrivers cgroups
do
rc-update add "${SERVICE}" sysinit
done
for SERVICE in modules hwclock swap hostname sysctl bootmisc syslog seedrng
do
rc-update add "${SERVICE}" boot
done
for SERVICE in killprocs savecache mount-ro
do
rc-update add "${SERVICE}" shutdown
done
echo 'root:krata' | chpasswd
echo 'krata' > /etc/hostname
{
echo '# krata resolver configuration'
echo 'nameserver 1.1.1.1'
echo 'nameserver 1.0.0.1'
echo 'nameserver 2606:4700:4700::1111'
echo 'nameserver 2606:4700:4700::1001'
} > /etc/resolv.conf
{
echo 'Welcome to krataOS!'
echo 'You may now login to the console to manage krata.'
} > /etc/issue
echo > /etc/motd
ln -s /usr/share/zoneinfo/UTC /etc/localtime
rm -rf /var/cache/apk/*
rm -rf /.dockerenv
cd /
rm -f /mnt/target/os/rootfs.tar
tar cf /mnt/target/os/rootfs.tar --numeric-owner \
--exclude 'mnt/**' --exclude 'proc/**' \
--exclude 'sys/**' --exclude 'dev/**' .

19
os/internal/stage2.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
set -e
apk add --update-cache grub-efi
grub-install --target=x86_64-efi --efi-directory=/boot/efi --no-nvram --skip-fs-probe --bootloader-id=BOOT
mv /boot/efi/EFI/BOOT/grubx64.efi /boot/efi/EFI/BOOT/BOOTX64.efi
ROOT_UUID="$(cat /root-uuid)"
{
echo 'GRUB_CMDLINE_XEN_DEFAULT="dom0_mem=1024M,max:1024M"'
echo "GRUB_CMDLINE_LINUX_DEFAULT=\"quiet rootfstype=ext4 root=UUID=${ROOT_UUID} modules=ext4\""
echo 'GRUB_DEFAULT="saved"'
echo 'GRUB_SAVEDEFAULT="true"'
} >> /etc/default/grub
grub-mkconfig -o /boot/grub/grub.cfg
grub-set-default "$(grep ^menuentry /boot/grub/grub.cfg | grep Xen | cut -d \' -f 2 | head -1)"
rm -rf /var/cache/apk/*

13
resources/openrc/kratad Normal file
View File

@ -0,0 +1,13 @@
#!/sbin/openrc-run
description="Krata Control Daemon"
command="/usr/libexec/kratad"
supervisor="supervise-daemon"
output_log="/var/log/kratad.log"
error_log="/var/log/kratad.err"
depend() {
use xenconsoled
use xenstored
}
export RUST_LOG=info

12
resources/openrc/kratanet Normal file
View File

@ -0,0 +1,12 @@
#!/sbin/openrc-run
description="Krata Networking Daemon"
command="/usr/libexec/kratanet"
supervisor="supervise-daemon"
output_log="/var/log/kratanet.log"
error_log="/var/log/kratanet.err"
depend() {
use xenstored
}
export RUST_LOG=info

View File

@ -4,7 +4,6 @@ Description=Krata Control Daemon
[Service]
Restart=on-failure
Type=simple
ExecStartPre=/bin/mkdir -p /var/lib/krata
ExecStart=/usr/libexec/kratad -l unix:///var/lib/krata/daemon.socket
Environment=RUST_LOG=info
User=root