diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..bc7151a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +/target +/kernel/linux-* diff --git a/.github/workflows/kernel.yml b/.github/workflows/kernel.yml index 51f5196..3465c69 100644 --- a/.github/workflows/kernel.yml +++ b/.github/workflows/kernel.yml @@ -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" diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index ca59134..308ba64 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -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 - \ No newline at end of file + 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 diff --git a/Cargo.toml b/Cargo.toml index 4da9d62..dc042e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/crates/kratart/src/lib.rs b/crates/kratart/src/lib.rs index 7518140..3b267e8 100644 --- a/crates/kratart/src/lib.rs +++ b/crates/kratart/src/lib.rs @@ -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)?; diff --git a/crates/xen/xencall/examples/domain_create.rs b/crates/xen/xencall/examples/domain_create.rs index 3755df4..02ee2d3 100644 --- a/crates/xen/xencall/examples/domain_create.rs +++ b/crates/xen/xencall/examples/domain_create.rs @@ -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(()) diff --git a/crates/xen/xencall/examples/domain_info.rs b/crates/xen/xencall/examples/domain_info.rs index 260c038..a676a19 100644 --- a/crates/xen/xencall/examples/domain_info.rs +++ b/crates/xen/xencall/examples/domain_info.rs @@ -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(()) diff --git a/crates/xen/xencall/examples/vcpu_context.rs b/crates/xen/xencall/examples/vcpu_context.rs index b144aa8..940d689 100644 --- a/crates/xen/xencall/examples/vcpu_context.rs +++ b/crates/xen/xencall/examples/vcpu_context.rs @@ -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(()) diff --git a/crates/xen/xencall/examples/version_capabilities.rs b/crates/xen/xencall/examples/version_capabilities.rs index 64c1983..fb3c46e 100644 --- a/crates/xen/xencall/examples/version_capabilities.rs +++ b/crates/xen/xencall/examples/version_capabilities.rs @@ -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(()) diff --git a/crates/xen/xencall/src/error.rs b/crates/xen/xencall/src/error.rs index 331c0f3..68eee5b 100644 --- a/crates/xen/xencall/src/error.rs +++ b/crates/xen/xencall/src/error.rs @@ -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, diff --git a/crates/xen/xencall/src/lib.rs b/crates/xen/xencall/src/lib.rs index 9082497..76f926c 100644 --- a/crates/xen/xencall/src/lib.rs +++ b/crates/xen/xencall/src/lib.rs @@ -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 { - let file = OpenOptions::new() + pub fn open(current_domid: u32) -> Result { + 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 { + 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 { @@ -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 = 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] }, }; diff --git a/crates/xen/xencall/src/sys.rs b/crates/xen/xencall/src/sys.rs index 3af3ea0..c42eb76 100644 --- a/crates/xen/xencall/src/sys.rs +++ b/crates/xen/xencall/src/sys.rs @@ -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)] diff --git a/crates/xen/xenclient/examples/boot.rs b/crates/xen/xenclient/examples/boot.rs index dbd4142..3ade6d1 100644 --- a/crates/xen/xenclient/examples/boot.rs +++ b/crates/xen/xenclient/examples/boot.rs @@ -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", diff --git a/crates/xen/xenclient/src/lib.rs b/crates/xen/xenclient/src/lib.rs index 6a720e3..1c29114 100644 --- a/crates/xen/xenclient/src/lib.rs +++ b/crates/xen/xenclient/src/lib.rs @@ -83,9 +83,9 @@ pub struct DomainConfig<'a> { } impl XenClient { - pub async fn open() -> Result { + pub async fn open(current_domid: u32) -> Result { let store = XsdClient::open().await?; - let call = XenCall::open()?; + let call = XenCall::open(current_domid)?; Ok(XenClient { store, call }) } diff --git a/hack/build/cargo.sh b/hack/build/cargo.sh index 2e4d6ae..7440348 100755 --- a/hack/build/cargo.sh +++ b/hack/build/cargo.sh @@ -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 "${@}" diff --git a/hack/code/shellcheck.sh b/hack/code/shellcheck.sh index 559448e..3a3c417 100755 --- a/hack/code/shellcheck.sh +++ b/hack/code/shellcheck.sh @@ -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 diff --git a/hack/dist/apk.sh b/hack/dist/apk.sh new file mode 100755 index 0000000..b772e46 --- /dev/null +++ b/hack/dist/apk.sh @@ -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 " \ + "${OUTPUT_DIR}/system-openrc.tgz" diff --git a/hack/dist/bundle.sh b/hack/dist/bundle.sh index 645d7a8..c2dc0c0 100755 --- a/hack/dist/bundle.sh +++ b/hack/dist/bundle.sh @@ -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 diff --git a/hack/dist/deb.sh b/hack/dist/deb.sh index 45218c9..eb80b89 100755 --- a/hack/dist/deb.sh +++ b/hack/dist/deb.sh @@ -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" diff --git a/hack/dist/systar.sh b/hack/dist/systar.sh index 3853fcf..e4ac6d3 100755 --- a/hack/dist/systar.sh +++ b/hack/dist/systar.sh @@ -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 diff --git a/hack/initrd/build.sh b/hack/initrd/build.sh index fad9dd2..6d985e1 100755 --- a/hack/initrd/build.sh +++ b/hack/initrd/build.sh @@ -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}" diff --git a/hack/kernel/build.sh b/hack/kernel/build.sh index 9d489eb..822b763 100755 --- a/hack/kernel/build.sh +++ b/hack/kernel/build.sh @@ -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" ] diff --git a/hack/os/build.sh b/hack/os/build.sh new file mode 100755 index 0000000..56f41bf --- /dev/null +++ b/hack/os/build.sh @@ -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}" diff --git a/os/internal/stage1.sh b/os/internal/stage1.sh new file mode 100755 index 0000000..0deae1a --- /dev/null +++ b/os/internal/stage1.sh @@ -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/**' . diff --git a/os/internal/stage2.sh b/os/internal/stage2.sh new file mode 100755 index 0000000..bccf72f --- /dev/null +++ b/os/internal/stage2.sh @@ -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/* diff --git a/resources/openrc/kratad b/resources/openrc/kratad new file mode 100644 index 0000000..c4b3b3e --- /dev/null +++ b/resources/openrc/kratad @@ -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 diff --git a/resources/openrc/kratanet b/resources/openrc/kratanet new file mode 100644 index 0000000..95c5d8e --- /dev/null +++ b/resources/openrc/kratanet @@ -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 diff --git a/resources/systemd/kratad.service b/resources/systemd/kratad.service index ce603db..4ea46d6 100644 --- a/resources/systemd/kratad.service +++ b/resources/systemd/kratad.service @@ -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