mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-03 13:11:31 +00:00
Compare commits
40 Commits
Author | SHA1 | Date | |
---|---|---|---|
2526065d74 | |||
a509f69398 | |||
d021fb2147 | |||
d33079c7c5 | |||
bea6d1e6fe | |||
bc27eeb286 | |||
42bb289421 | |||
f2ab03711e | |||
2f3daad80a | |||
8f7e47a218 | |||
19683b80c1 | |||
ae486e347f | |||
9f4db08e07 | |||
d674a69c17 | |||
f59976eb80 | |||
3641bc55f4 | |||
4b6272f49d | |||
7c55e63f24 | |||
2083ec0604 | |||
5ad2e40a7b | |||
0fd6318c5f | |||
7940eea588 | |||
45aa4914bb | |||
8dd3cc7692 | |||
6a2f1e6517 | |||
d433cd49e2 | |||
0fd32e84cd | |||
ab8941326a | |||
8be75a722e | |||
e6f35eb3d0 | |||
58c6413ca2 | |||
e1f1f8579c | |||
6bf1d3f88c | |||
377b837db9 | |||
6cd8cc12db | |||
c68f367e4a | |||
15d5ed5a45 | |||
6d6bdade87 | |||
693d62a41a | |||
8ec7042ea4 |
12
.github/workflows/check.yml
vendored
12
.github/workflows/check.yml
vendored
@ -1,11 +1,19 @@
|
||||
name: check
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
merge_group:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
fmt:
|
||||
name: fmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
@ -16,4 +24,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- run: ./hack/code/shellcheck.sh
|
||||
|
12
.github/workflows/client.yml
vendored
12
.github/workflows/client.yml
vendored
@ -1,5 +1,11 @@
|
||||
name: client
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
merge_group:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
@ -21,7 +27,11 @@ jobs:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- run: git config --global core.autocrlf false && git config --global core.eol lf
|
||||
if: ${{ matrix.platform.os == 'windows' }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
if: ${{ matrix.platform.os != 'darwin' }}
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
12
.github/workflows/kernel.yml
vendored
12
.github/workflows/kernel.yml
vendored
@ -1,17 +1,19 @@
|
||||
name: kernel
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "kernel/**"
|
||||
- "hack/ci/**"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "kernel/**"
|
||||
- "hack/ci/**"
|
||||
merge_group:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch:
|
||||
- x86_64
|
||||
@ -21,6 +23,8 @@ jobs:
|
||||
name: build ${{ matrix.arch }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: ./hack/ci/install-linux-deps.sh
|
||||
- run: ./hack/kernel/build.sh
|
||||
|
14
.github/workflows/nightly.yml
vendored
14
.github/workflows/nightly.yml
vendored
@ -17,6 +17,8 @@ jobs:
|
||||
name: server ${{ matrix.arch }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: "${{ matrix.arch }}-unknown-linux-gnu,${{ matrix.arch }}-unknown-linux-musl"
|
||||
@ -73,7 +75,11 @@ jobs:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- run: git config --global core.autocrlf false && git config --global core.eol lf
|
||||
if: ${{ matrix.platform.os == 'windows' }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
if: ${{ matrix.platform.os != 'darwin' }}
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
@ -87,4 +93,10 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kratactl-${{ matrix.platform.os }}-${{ matrix.platform.arch }}
|
||||
path: "target/*/release/kratactl*"
|
||||
path: "target/*/release/kratactl"
|
||||
if: ${{ matrix.platform.os != 'windows' }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kratactl-${{ matrix.platform.os }}-${{ matrix.platform.arch }}
|
||||
path: "target/*/release/kratactl.exe"
|
||||
if: ${{ matrix.platform.os == 'windows' }}
|
||||
|
13
.github/workflows/os.yml
vendored
13
.github/workflows/os.yml
vendored
@ -1,19 +1,20 @@
|
||||
name: os
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "os/**"
|
||||
- "hack/os/**"
|
||||
- "hack/ci/**"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "os/**"
|
||||
- "hack/os/**"
|
||||
- "hack/ci/**"
|
||||
merge_group:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch:
|
||||
- x86_64
|
||||
@ -23,6 +24,8 @@ jobs:
|
||||
name: build ${{ matrix.arch }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: "${{ matrix.arch }}-unknown-linux-gnu,${{ matrix.arch }}-unknown-linux-musl"
|
||||
|
91
.github/workflows/release-binaries.yml
vendored
Normal file
91
.github/workflows/release-binaries.yml
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
name: release-binaries
|
||||
permissions:
|
||||
contents: write
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_BACKTRACE: 1
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
jobs:
|
||||
server:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch:
|
||||
- x86_64
|
||||
- aarch64
|
||||
env:
|
||||
TARGET_ARCH: "${{ matrix.arch }}"
|
||||
name: server ${{ matrix.arch }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: "${{ matrix.arch }}-unknown-linux-gnu,${{ matrix.arch }}-unknown-linux-musl"
|
||||
- run: ./hack/ci/install-linux-deps.sh
|
||||
- run: ./hack/dist/bundle.sh
|
||||
env:
|
||||
KRATA_KERNEL_BUILD_JOBS: "5"
|
||||
- run: "./hack/ci/assemble-release-assets.sh bundle-systemd ${{ github.event.release.tag_name }} ${{ matrix.arch }} target/dist/bundle-systemd-${{ matrix.arch }}.tgz"
|
||||
- run: ./hack/dist/deb.sh
|
||||
env:
|
||||
KRATA_KERNEL_BUILD_SKIP: "1"
|
||||
- run: "./hack/ci/assemble-release-assets.sh debian ${{ github.event.release.tag_name }} ${{ matrix.arch }} target/dist/*.deb"
|
||||
- run: ./hack/dist/apk.sh
|
||||
env:
|
||||
KRATA_KERNEL_BUILD_SKIP: "1"
|
||||
- run: "./hack/ci/assemble-release-assets.sh alpine ${{ github.event.release.tag_name }} ${{ matrix.arch }} target/dist/*_${{ matrix.arch }}.apk"
|
||||
- run: ./hack/os/build.sh
|
||||
env:
|
||||
KRATA_KERNEL_BUILD_SKIP: "1"
|
||||
- run: "./hack/ci/assemble-release-assets.sh os ${{ github.event.release.tag_name }} ${{ matrix.arch }} target/os/krata-${{ matrix.arch }}.qcow2"
|
||||
client:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- { os: linux, arch: x86_64, on: ubuntu-latest, deps: linux }
|
||||
- { os: linux, arch: aarch64, on: ubuntu-latest, deps: linux }
|
||||
- { os: darwin, arch: x86_64, on: macos-14, deps: darwin }
|
||||
- { os: darwin, arch: aarch64, on: macos-14, deps: darwin }
|
||||
- { os: freebsd, arch: x86_64, on: ubuntu-latest, deps: linux }
|
||||
- { os: windows, arch: x86_64, on: windows-latest, deps: windows }
|
||||
env:
|
||||
TARGET_OS: "${{ matrix.platform.os }}"
|
||||
TARGET_ARCH: "${{ matrix.platform.arch }}"
|
||||
runs-on: "${{ matrix.platform.on }}"
|
||||
name: client ${{ matrix.platform.os }}-${{ matrix.platform.arch }}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
if: ${{ matrix.platform.os != 'darwin' }}
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: "${{ matrix.platform.arch }}-apple-darwin"
|
||||
if: ${{ matrix.platform.os == 'darwin' }}
|
||||
- uses: homebrew/actions/setup-homebrew@master
|
||||
if: ${{ matrix.platform.os == 'darwin' }}
|
||||
- run: ./hack/ci/install-${{ matrix.platform.deps }}-deps.sh
|
||||
- run: ./hack/build/cargo.sh build --release --bin kratactl
|
||||
- run: "./hack/ci/assemble-release-assets.sh kratactl ${{ github.event.release.tag_name }} ${{ matrix.platform.os }}-${{ matrix.platform.arch }} target/*/release/kratactl"
|
||||
if: ${{ matrix.platform.os != 'windows' }}
|
||||
- run: "./hack/ci/assemble-release-assets.sh kratactl ${{ github.event.release.tag_name }} ${{ matrix.platform.os }}-${{ matrix.platform.arch }} target/*/release/kratactl.exe"
|
||||
if: ${{ matrix.platform.os == 'windows' }}
|
||||
- run: "./hack/ci/upload-release-assets.sh ${{ github.event.release.tag_name }}"
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
32
.github/workflows/release-plz.yml
vendored
Normal file
32
.github/workflows/release-plz.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: release-plz
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
concurrency:
|
||||
group: "${{ github.workflow }}"
|
||||
jobs:
|
||||
release-plz:
|
||||
name: release-plz
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v1
|
||||
id: generate-token
|
||||
with:
|
||||
app-id: "${{ secrets.EDERA_CULTIVATION_APP_ID }}"
|
||||
private-key: "${{ secrets.EDERA_CULTIVATION_APP_PRIVATE_KEY }}"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
token: "${{ steps.generate-token.outputs.token }}"
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: ./hack/ci/install-linux-deps.sh
|
||||
- name: release-plz
|
||||
uses: MarcoIeni/release-plz-action@v0.5
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ steps.generate-token.outputs.token }}"
|
||||
CARGO_REGISTRY_TOKEN: "${{ secrets.KRATA_RELEASE_CARGO_TOKEN }}"
|
17
.github/workflows/server.yml
vendored
17
.github/workflows/server.yml
vendored
@ -1,9 +1,16 @@
|
||||
name: server
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
merge_group:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch:
|
||||
- x86_64
|
||||
@ -13,6 +20,8 @@ jobs:
|
||||
name: build ${{ matrix.arch }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: ./hack/ci/install-linux-deps.sh
|
||||
- run: ./hack/build/cargo.sh build
|
||||
@ -28,6 +37,8 @@ jobs:
|
||||
name: test ${{ matrix.arch }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: ./hack/ci/install-linux-deps.sh
|
||||
- run: ./hack/build/cargo.sh test
|
||||
@ -43,6 +54,8 @@ jobs:
|
||||
name: clippy ${{ matrix.arch }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
@ -60,6 +73,8 @@ jobs:
|
||||
name: initrd ${{ matrix.arch }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: "${{ matrix.arch }}-unknown-linux-gnu,${{ matrix.arch }}-unknown-linux-musl"
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "vendor"]
|
||||
path = vendor
|
||||
url = https://github.com/edera-dev/krata-vendor.git
|
30
CHANGELOG.md
Normal file
30
CHANGELOG.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.0.5](https://github.com/edera-dev/krata/compare/v0.0.4...v0.0.5) - 2024-04-09
|
||||
|
||||
### Added
|
||||
- *(ctl)* add help and about to commands and arguments ([#25](https://github.com/edera-dev/krata/pull/25))
|
||||
|
||||
### Other
|
||||
- update Cargo.toml dependencies
|
||||
- update Cargo.lock dependencies
|
||||
|
||||
## [0.0.4](https://github.com/edera-dev/krata/releases/tag/v${version}) - 2024-04-03
|
||||
|
||||
### Other
|
||||
- implement automatic releases
|
||||
- reimplement console to utilize channels, and provide logs support
|
||||
- set hostname from launch config
|
||||
- implement event stream retries
|
||||
- work on parallel reconciliation
|
||||
- implement parallel guest reconciliation
|
||||
- log when a guest start failures occurs
|
||||
- remove device restriction
|
||||
- setup loopback interface
|
||||
- place running tasks in cgroup
|
3317
Cargo.lock
generated
Normal file
3317
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
Cargo.toml
18
Cargo.toml
@ -16,7 +16,7 @@ members = [
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.0.3"
|
||||
version = "0.0.5"
|
||||
homepage = "https://krata.dev"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/edera-dev/krata"
|
||||
@ -24,18 +24,20 @@ repository = "https://github.com/edera-dev/krata"
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0"
|
||||
arrayvec = "0.7.4"
|
||||
async-compression = "0.4.6"
|
||||
async-compression = "0.4.8"
|
||||
async-stream = "0.3.5"
|
||||
async-trait = "0.1.77"
|
||||
backhand = "0.15.0"
|
||||
byteorder = "1"
|
||||
bytes = "1.5.0"
|
||||
cli-tables = "0.2.1"
|
||||
cgroups-rs = "0.3.4"
|
||||
circular-buffer = "0.1.7"
|
||||
comfy-table = "7.1.1"
|
||||
crossterm = "0.27.0"
|
||||
ctrlc = "3.4.4"
|
||||
elf = "0.7.4"
|
||||
env_logger = "0.11.0"
|
||||
etherparse = "0.14.2"
|
||||
etherparse = "0.14.3"
|
||||
flate2 = "1.0"
|
||||
futures = "0.3.30"
|
||||
ipnetwork = "0.20.0"
|
||||
@ -50,7 +52,7 @@ oci-spec = "0.6.4"
|
||||
once_cell = "1.19.0"
|
||||
path-absolutize = "3.1.1"
|
||||
path-clean = "1.0.1"
|
||||
prost = "0.12.3"
|
||||
prost = "0.12.4"
|
||||
prost-build = "0.12.3"
|
||||
prost-reflect-build = "0.13.0"
|
||||
rand = "0.8.5"
|
||||
@ -63,7 +65,7 @@ signal-hook = "0.3.17"
|
||||
slice-copy = "0.3.0"
|
||||
smoltcp = "0.11.0"
|
||||
thiserror = "1.0"
|
||||
tokio-tun = "0.11.2"
|
||||
tokio-tun = "0.11.4"
|
||||
tonic-build = "0.11.0"
|
||||
tower = "0.4.13"
|
||||
udp-stream = "0.0.11"
|
||||
@ -76,11 +78,11 @@ version = "4.4.18"
|
||||
features = ["derive"]
|
||||
|
||||
[workspace.dependencies.prost-reflect]
|
||||
version = "0.13.0"
|
||||
version = "0.13.1"
|
||||
features = ["derive"]
|
||||
|
||||
[workspace.dependencies.reqwest]
|
||||
version = "0.12.0"
|
||||
version = "0.12.3"
|
||||
default-features = false
|
||||
features = ["rustls-tls"]
|
||||
|
||||
|
11
README.md
11
README.md
@ -16,4 +16,13 @@ The Edera Hypervisor
|
||||
|
||||
## Introduction
|
||||
|
||||
krata is a single-host hypervisor service built primarily for OCI containers.
|
||||
krata is a single-host hypervisor service built for OCI-compliant containers. It isolates containers using a type-1 hypervisor, providing workload isolation that can exceed the security level of KVM-based OCI-compliant runtimes.
|
||||
|
||||
krata utilizes the core of the Xen hypervisor, with a fully memory-safe Rust control plane to bring Xen tooling into a new secure era.
|
||||
|
||||
## Hardware Support
|
||||
|
||||
| Architecture | Completion Level | Virtualization Technology |
|
||||
| ------------ | ---------------- | ------------------------- |
|
||||
| x86_64 | 100% Completed | Intel VT-x, AMD-V |
|
||||
| aarch64 | 30% Completed | AArch64 virtualization |
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "krata-ctl"
|
||||
description = "Command-line tool to control the krata hypervisor"
|
||||
license.workspace = true
|
||||
version= "0.0.3"
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition = "2021"
|
||||
@ -12,11 +12,11 @@ resolver = "2"
|
||||
anyhow = { workspace = true }
|
||||
async-stream = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
cli-tables = { workspace = true }
|
||||
comfy-table = { workspace = true }
|
||||
crossterm = { workspace = true }
|
||||
ctrlc = { workspace = true, features = ["termination"] }
|
||||
env_logger = { workspace = true }
|
||||
krata = { path = "../krata", version = "^0.0.3" }
|
||||
krata = { path = "../krata", version = "^0.0.5" }
|
||||
log = { workspace = true }
|
||||
prost-reflect = { workspace = true, features = ["serde"] }
|
||||
serde_json = { workspace = true }
|
||||
|
@ -10,8 +10,9 @@ use crate::console::StdioConsoleStream;
|
||||
use super::resolve_guest;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(about = "Attach to the guest console")]
|
||||
pub struct AttachCommand {
|
||||
#[arg()]
|
||||
#[arg(help = "Guest to attach to, either the name or the uuid")]
|
||||
guest: String,
|
||||
}
|
||||
|
||||
|
@ -17,10 +17,15 @@ use tonic::{transport::Channel, Request};
|
||||
use crate::cli::resolve_guest;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(about = "Destroy a guest")]
|
||||
pub struct DestroyCommand {
|
||||
#[arg(short = 'W', long)]
|
||||
#[arg(
|
||||
short = 'W',
|
||||
long,
|
||||
help = "Wait for the destruction of the guest to complete"
|
||||
)]
|
||||
wait: bool,
|
||||
#[arg()]
|
||||
#[arg(help = "Guest to destroy, either the name or the uuid")]
|
||||
guest: String,
|
||||
}
|
||||
|
||||
|
@ -22,23 +22,46 @@ use tonic::{transport::Channel, Request};
|
||||
use crate::console::StdioConsoleStream;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(about = "Launch a new guest")]
|
||||
pub struct LauchCommand {
|
||||
#[arg(short, long)]
|
||||
#[arg(short, long, help = "Name of the guest")]
|
||||
name: Option<String>,
|
||||
#[arg(short, long, default_value_t = 1)]
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
default_value_t = 1,
|
||||
help = "vCPUs available to the guest"
|
||||
)]
|
||||
cpus: u32,
|
||||
#[arg(short, long, default_value_t = 512)]
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
default_value_t = 512,
|
||||
help = "Memory available to the guest, in megabytes"
|
||||
)]
|
||||
mem: u64,
|
||||
#[arg[short, long]]
|
||||
#[arg[short, long, help = "Environment variables set in the guest"]]
|
||||
env: Option<Vec<String>>,
|
||||
#[arg(short, long)]
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
help = "Attach to the guest after guest starts, implies --wait"
|
||||
)]
|
||||
attach: bool,
|
||||
#[arg(short = 'W', long)]
|
||||
#[arg(
|
||||
short = 'W',
|
||||
long,
|
||||
help = "Wait for the guest to start, implied by --attach"
|
||||
)]
|
||||
wait: bool,
|
||||
#[arg()]
|
||||
#[arg(help = "Container image for guest to use")]
|
||||
oci: String,
|
||||
#[arg(allow_hyphen_values = true, trailing_var_arg = true)]
|
||||
run: Vec<String>,
|
||||
#[arg(
|
||||
allow_hyphen_values = true,
|
||||
trailing_var_arg = true,
|
||||
help = "Command to run inside the guest"
|
||||
)]
|
||||
command: Vec<String>,
|
||||
}
|
||||
|
||||
impl LauchCommand {
|
||||
@ -63,7 +86,7 @@ impl LauchCommand {
|
||||
value: value.clone(),
|
||||
})
|
||||
.collect(),
|
||||
command: self.run,
|
||||
command: self.command,
|
||||
}),
|
||||
annotations: vec![],
|
||||
}),
|
||||
|
@ -1,10 +1,10 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use clap::{Parser, ValueEnum};
|
||||
use cli_tables::Table;
|
||||
use comfy_table::{presets::UTF8_FULL_CONDENSED, Cell, Color, Table};
|
||||
use krata::{
|
||||
events::EventStream,
|
||||
v1::{
|
||||
common::{guest_image_spec::Image, Guest, GuestStatus},
|
||||
common::{Guest, GuestStatus},
|
||||
control::{
|
||||
control_service_client::ControlServiceClient, ListGuestsRequest, ResolveGuestRequest,
|
||||
},
|
||||
@ -14,11 +14,11 @@ use krata::{
|
||||
use serde_json::Value;
|
||||
use tonic::{transport::Channel, Request};
|
||||
|
||||
use crate::format::{guest_state_text, guest_status_text, kv2line, proto2dynamic, proto2kv};
|
||||
use crate::format::{guest_simple_line, guest_status_text, kv2line, proto2dynamic, proto2kv};
|
||||
|
||||
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
|
||||
enum ListFormat {
|
||||
CliTable,
|
||||
Table,
|
||||
Json,
|
||||
JsonPretty,
|
||||
Jsonl,
|
||||
@ -28,10 +28,11 @@ enum ListFormat {
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(about = "List the guests on the hypervisor")]
|
||||
pub struct ListCommand {
|
||||
#[arg(short, long, default_value = "cli-table")]
|
||||
#[arg(short, long, default_value = "table", help = "Output format")]
|
||||
format: ListFormat,
|
||||
#[arg()]
|
||||
#[arg(help = "Limit to a single guest, either the name or the uuid")]
|
||||
guest: Option<String>,
|
||||
}
|
||||
|
||||
@ -70,24 +71,13 @@ impl ListCommand {
|
||||
});
|
||||
|
||||
match self.format {
|
||||
ListFormat::CliTable => {
|
||||
ListFormat::Table => {
|
||||
self.print_guest_table(guests)?;
|
||||
}
|
||||
|
||||
ListFormat::Simple => {
|
||||
for guest in guests {
|
||||
let state = guest_status_text(
|
||||
guest
|
||||
.state
|
||||
.as_ref()
|
||||
.map(|x| x.status())
|
||||
.unwrap_or(GuestStatus::Unknown),
|
||||
);
|
||||
let name = guest.spec.as_ref().map(|x| x.name.as_str()).unwrap_or("");
|
||||
let network = guest.state.as_ref().and_then(|x| x.network.as_ref());
|
||||
let ipv4 = network.map(|x| x.guest_ipv4.as_str()).unwrap_or("");
|
||||
let ipv6 = network.map(|x| x.guest_ipv6.as_str()).unwrap_or("");
|
||||
println!("{}\t{}\t{}\t{}\t{}", guest.id, state, name, ipv4, ipv6);
|
||||
println!("{}", guest_simple_line(&guest));
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,49 +115,51 @@ impl ListCommand {
|
||||
|
||||
fn print_guest_table(&self, guests: Vec<Guest>) -> Result<()> {
|
||||
let mut table = Table::new();
|
||||
let header = vec!["name", "uuid", "state", "ipv4", "ipv6", "image"];
|
||||
table.push_row(&header)?;
|
||||
table.load_preset(UTF8_FULL_CONDENSED);
|
||||
table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
|
||||
table.set_header(vec!["name", "uuid", "status", "ipv4", "ipv6"]);
|
||||
for guest in guests {
|
||||
let ipv4 = guest
|
||||
.state
|
||||
.as_ref()
|
||||
.and_then(|x| x.network.as_ref())
|
||||
.map(|x| x.guest_ipv4.as_str())
|
||||
.unwrap_or("unknown");
|
||||
.unwrap_or("n/a");
|
||||
let ipv6 = guest
|
||||
.state
|
||||
.as_ref()
|
||||
.and_then(|x| x.network.as_ref())
|
||||
.map(|x| x.guest_ipv6.as_str())
|
||||
.unwrap_or("unknown");
|
||||
.unwrap_or("n/a");
|
||||
let Some(spec) = guest.spec else {
|
||||
continue;
|
||||
};
|
||||
let image = spec
|
||||
.image
|
||||
.map(|x| {
|
||||
x.image
|
||||
.map(|y| match y {
|
||||
Image::Oci(oci) => oci.image,
|
||||
})
|
||||
.unwrap_or("unknown".to_string())
|
||||
})
|
||||
.unwrap_or("unknown".to_string());
|
||||
table.push_row_string(&vec![
|
||||
spec.name,
|
||||
guest.id,
|
||||
format!("{}", guest_state_text(guest.state.as_ref())),
|
||||
ipv4.to_string(),
|
||||
ipv6.to_string(),
|
||||
image,
|
||||
])?;
|
||||
let status = guest.state.as_ref().cloned().unwrap_or_default().status();
|
||||
let status_text = guest_status_text(status);
|
||||
|
||||
let status_color = match status {
|
||||
GuestStatus::Destroyed | GuestStatus::Failed => Color::Red,
|
||||
GuestStatus::Destroying | GuestStatus::Exited | GuestStatus::Starting => {
|
||||
Color::Yellow
|
||||
}
|
||||
GuestStatus::Started => Color::Green,
|
||||
_ => Color::Reset,
|
||||
};
|
||||
|
||||
table.add_row(vec![
|
||||
Cell::new(spec.name),
|
||||
Cell::new(guest.id),
|
||||
Cell::new(status_text).fg(status_color),
|
||||
Cell::new(ipv4.to_string()),
|
||||
Cell::new(ipv6.to_string()),
|
||||
]);
|
||||
}
|
||||
if table.num_records() == 1 {
|
||||
if table.is_empty() {
|
||||
if self.guest.is_none() {
|
||||
println!("no guests have been launched");
|
||||
}
|
||||
} else {
|
||||
println!("{}", table.to_string());
|
||||
println!("{}", table);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
58
crates/ctl/src/cli/logs.rs
Normal file
58
crates/ctl/src/cli/logs.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use anyhow::Result;
|
||||
use async_stream::stream;
|
||||
use clap::Parser;
|
||||
use krata::{
|
||||
events::EventStream,
|
||||
v1::control::{control_service_client::ControlServiceClient, ConsoleDataRequest},
|
||||
};
|
||||
|
||||
use tokio::select;
|
||||
use tokio_stream::{pending, StreamExt};
|
||||
use tonic::transport::Channel;
|
||||
|
||||
use crate::console::StdioConsoleStream;
|
||||
|
||||
use super::resolve_guest;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(about = "View the logs of a guest")]
|
||||
pub struct LogsCommand {
|
||||
#[arg(short, long, help = "Follow output from the guest")]
|
||||
follow: bool,
|
||||
#[arg(help = "Guest to show logs for, either the name or the uuid")]
|
||||
guest: String,
|
||||
}
|
||||
|
||||
impl LogsCommand {
|
||||
pub async fn run(
|
||||
self,
|
||||
mut client: ControlServiceClient<Channel>,
|
||||
events: EventStream,
|
||||
) -> Result<()> {
|
||||
let guest_id: String = resolve_guest(&mut client, &self.guest).await?;
|
||||
let guest_id_stream = guest_id.clone();
|
||||
let follow = self.follow;
|
||||
let input = stream! {
|
||||
yield ConsoleDataRequest { guest_id: guest_id_stream, data: Vec::new() };
|
||||
if follow {
|
||||
let mut pending = pending::<ConsoleDataRequest>();
|
||||
while let Some(x) = pending.next().await {
|
||||
yield x;
|
||||
}
|
||||
}
|
||||
};
|
||||
let output = client.console_data(input).await?.into_inner();
|
||||
let stdout_handle =
|
||||
tokio::task::spawn(async move { StdioConsoleStream::stdout(output).await });
|
||||
let exit_hook_task = StdioConsoleStream::guest_exit_hook(guest_id.clone(), events).await?;
|
||||
let code = select! {
|
||||
x = stdout_handle => {
|
||||
x??;
|
||||
None
|
||||
},
|
||||
x = exit_hook_task => x?
|
||||
};
|
||||
StdioConsoleStream::restore_terminal_mode();
|
||||
std::process::exit(code.unwrap_or(0));
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ pub mod attach;
|
||||
pub mod destroy;
|
||||
pub mod launch;
|
||||
pub mod list;
|
||||
pub mod logs;
|
||||
pub mod resolve;
|
||||
pub mod watch;
|
||||
|
||||
@ -10,21 +11,27 @@ use clap::{Parser, Subcommand};
|
||||
use krata::{
|
||||
client::ControlClientProvider,
|
||||
events::EventStream,
|
||||
v1::control::{
|
||||
control_service_client::ControlServiceClient, ResolveGuestRequest, WatchEventsRequest,
|
||||
},
|
||||
v1::control::{control_service_client::ControlServiceClient, ResolveGuestRequest},
|
||||
};
|
||||
use tonic::{transport::Channel, Request};
|
||||
|
||||
use self::{
|
||||
attach::AttachCommand, destroy::DestroyCommand, launch::LauchCommand, list::ListCommand,
|
||||
resolve::ResolveCommand, watch::WatchCommand,
|
||||
logs::LogsCommand, resolve::ResolveCommand, watch::WatchCommand,
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(version, about)]
|
||||
#[command(
|
||||
version,
|
||||
about = "Control the krata hypervisor, a secure platform for running containers"
|
||||
)]
|
||||
pub struct ControlCommand {
|
||||
#[arg(short, long, default_value = "unix:///var/lib/krata/daemon.socket")]
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
help = "The connection URL to the krata hypervisor",
|
||||
default_value = "unix:///var/lib/krata/daemon.socket"
|
||||
)]
|
||||
connection: String,
|
||||
|
||||
#[command(subcommand)]
|
||||
@ -37,20 +44,15 @@ pub enum Commands {
|
||||
Destroy(DestroyCommand),
|
||||
List(ListCommand),
|
||||
Attach(AttachCommand),
|
||||
Logs(LogsCommand),
|
||||
Watch(WatchCommand),
|
||||
Resolve(ResolveCommand),
|
||||
}
|
||||
|
||||
impl ControlCommand {
|
||||
pub async fn run(self) -> Result<()> {
|
||||
let mut client = ControlClientProvider::dial(self.connection.parse()?).await?;
|
||||
let events = EventStream::open(
|
||||
client
|
||||
.watch_events(WatchEventsRequest {})
|
||||
.await?
|
||||
.into_inner(),
|
||||
)
|
||||
.await?;
|
||||
let client = ControlClientProvider::dial(self.connection.parse()?).await?;
|
||||
let events = EventStream::open(client.clone()).await?;
|
||||
|
||||
match self.command {
|
||||
Commands::Launch(launch) => {
|
||||
@ -65,6 +67,10 @@ impl ControlCommand {
|
||||
attach.run(client, events).await?;
|
||||
}
|
||||
|
||||
Commands::Logs(logs) => {
|
||||
logs.run(client, events).await?;
|
||||
}
|
||||
|
||||
Commands::List(list) => {
|
||||
list.run(client, events).await?;
|
||||
}
|
||||
|
@ -5,8 +5,9 @@ use krata::v1::control::{control_service_client::ControlServiceClient, ResolveGu
|
||||
use tonic::{transport::Channel, Request};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(about = "Resolve a guest name to a uuid")]
|
||||
pub struct ResolveCommand {
|
||||
#[arg()]
|
||||
#[arg(help = "Guest name")]
|
||||
guest: String,
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ use krata::{
|
||||
use prost_reflect::ReflectMessage;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::format::{guest_state_text, kv2line, proto2dynamic, proto2kv};
|
||||
use crate::format::{guest_simple_line, kv2line, proto2dynamic, proto2kv};
|
||||
|
||||
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
|
||||
enum WatchFormat {
|
||||
@ -17,8 +17,9 @@ enum WatchFormat {
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(about = "Watch for guest changes")]
|
||||
pub struct WatchCommand {
|
||||
#[arg(short, long, default_value = "simple")]
|
||||
#[arg(short, long, default_value = "simple", help = "Output format")]
|
||||
format: WatchFormat,
|
||||
}
|
||||
|
||||
@ -45,12 +46,7 @@ impl WatchCommand {
|
||||
match self.format {
|
||||
WatchFormat::Simple => {
|
||||
if let Some(guest) = guest {
|
||||
println!(
|
||||
"{} guest={} status=\"{}\"",
|
||||
typ,
|
||||
guest.id,
|
||||
guest_state_text(guest.state.as_ref()).replace('"', "\\\"")
|
||||
);
|
||||
println!("{}", guest_simple_line(&guest));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use krata::v1::common::{GuestState, GuestStatus};
|
||||
use krata::v1::common::{Guest, GuestStatus};
|
||||
use prost_reflect::{DynamicMessage, ReflectMessage, Value};
|
||||
|
||||
pub fn proto2dynamic(proto: impl ReflectMessage) -> Result<DynamicMessage> {
|
||||
@ -71,16 +71,17 @@ pub fn guest_status_text(status: GuestStatus) -> String {
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn guest_state_text(state: Option<&GuestState>) -> String {
|
||||
let state = state.cloned().unwrap_or_default();
|
||||
let mut text = guest_status_text(state.status());
|
||||
|
||||
if let Some(exit) = state.exit_info {
|
||||
text.push_str(&format!(" (exit code: {})", exit.code));
|
||||
}
|
||||
|
||||
if let Some(error) = state.error_info {
|
||||
text.push_str(&format!(" (error: {})", error.message));
|
||||
}
|
||||
text
|
||||
pub fn guest_simple_line(guest: &Guest) -> String {
|
||||
let state = guest_status_text(
|
||||
guest
|
||||
.state
|
||||
.as_ref()
|
||||
.map(|x| x.status())
|
||||
.unwrap_or(GuestStatus::Unknown),
|
||||
);
|
||||
let name = guest.spec.as_ref().map(|x| x.name.as_str()).unwrap_or("");
|
||||
let network = guest.state.as_ref().and_then(|x| x.network.as_ref());
|
||||
let ipv4 = network.map(|x| x.guest_ipv4.as_str()).unwrap_or("");
|
||||
let ipv6 = network.map(|x| x.guest_ipv6.as_str()).unwrap_or("");
|
||||
format!("{}\t{}\t{}\t{}\t{}", guest.id, state, name, ipv4, ipv6)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "krata-daemon"
|
||||
description = "Daemon for the krata hypervisor."
|
||||
license.workspace = true
|
||||
version= "0.0.3"
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition = "2021"
|
||||
@ -13,11 +13,12 @@ anyhow = { workspace = true }
|
||||
async-stream = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
circular-buffer = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
krata = { path = "../krata", version = "^0.0.3" }
|
||||
krata-runtime = { path = "../runtime", version = "^0.0.3" }
|
||||
krata = { path = "../krata", version = "^0.0.5" }
|
||||
krata-runtime = { path = "../runtime", version = "^0.0.5" }
|
||||
log = { workspace = true }
|
||||
prost = { workspace = true }
|
||||
redb = { workspace = true }
|
||||
|
147
crates/daemon/src/console.rs
Normal file
147
crates/daemon/src/console.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use circular_buffer::CircularBuffer;
|
||||
use kratart::channel::ChannelService;
|
||||
use log::error;
|
||||
use tokio::{
|
||||
sync::{
|
||||
mpsc::{error::TrySendError, Receiver, Sender},
|
||||
Mutex,
|
||||
},
|
||||
task::JoinHandle,
|
||||
};
|
||||
|
||||
const CONSOLE_BUFFER_SIZE: usize = 1024 * 1024;
|
||||
type RawConsoleBuffer = CircularBuffer<CONSOLE_BUFFER_SIZE, u8>;
|
||||
type ConsoleBuffer = Box<RawConsoleBuffer>;
|
||||
|
||||
type ListenerMap = Arc<Mutex<HashMap<u32, Vec<Sender<Vec<u8>>>>>>;
|
||||
type BufferMap = Arc<Mutex<HashMap<u32, ConsoleBuffer>>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DaemonConsoleHandle {
|
||||
listeners: ListenerMap,
|
||||
buffers: BufferMap,
|
||||
sender: Sender<(u32, Vec<u8>)>,
|
||||
task: Arc<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DaemonConsoleAttachHandle {
|
||||
pub initial: Vec<u8>,
|
||||
listeners: ListenerMap,
|
||||
sender: Sender<(u32, Vec<u8>)>,
|
||||
domid: u32,
|
||||
}
|
||||
|
||||
impl DaemonConsoleAttachHandle {
|
||||
pub async fn unsubscribe(&self) -> Result<()> {
|
||||
let mut guard = self.listeners.lock().await;
|
||||
let _ = guard.remove(&self.domid);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send(&self, data: Vec<u8>) -> Result<()> {
|
||||
Ok(self.sender.send((self.domid, data)).await?)
|
||||
}
|
||||
}
|
||||
|
||||
impl DaemonConsoleHandle {
|
||||
pub async fn attach(
|
||||
&self,
|
||||
domid: u32,
|
||||
sender: Sender<Vec<u8>>,
|
||||
) -> Result<DaemonConsoleAttachHandle> {
|
||||
let buffers = self.buffers.lock().await;
|
||||
let buffer = buffers.get(&domid).map(|x| x.to_vec()).unwrap_or_default();
|
||||
drop(buffers);
|
||||
let mut listeners = self.listeners.lock().await;
|
||||
let senders = listeners.entry(domid).or_default();
|
||||
senders.push(sender);
|
||||
Ok(DaemonConsoleAttachHandle {
|
||||
initial: buffer,
|
||||
sender: self.sender.clone(),
|
||||
listeners: self.listeners.clone(),
|
||||
domid,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DaemonConsoleHandle {
|
||||
fn drop(&mut self) {
|
||||
if Arc::strong_count(&self.task) <= 1 {
|
||||
self.task.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DaemonConsole {
|
||||
listeners: ListenerMap,
|
||||
buffers: BufferMap,
|
||||
receiver: Receiver<(u32, Vec<u8>)>,
|
||||
sender: Sender<(u32, Vec<u8>)>,
|
||||
task: JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl DaemonConsole {
|
||||
pub async fn new() -> Result<DaemonConsole> {
|
||||
let (service, sender, receiver) =
|
||||
ChannelService::new("krata-console".to_string(), Some(0)).await?;
|
||||
let task = service.launch().await?;
|
||||
let listeners = Arc::new(Mutex::new(HashMap::new()));
|
||||
let buffers = Arc::new(Mutex::new(HashMap::new()));
|
||||
Ok(DaemonConsole {
|
||||
listeners,
|
||||
buffers,
|
||||
receiver,
|
||||
sender,
|
||||
task,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn launch(mut self) -> Result<DaemonConsoleHandle> {
|
||||
let listeners = self.listeners.clone();
|
||||
let buffers = self.buffers.clone();
|
||||
let sender = self.sender.clone();
|
||||
let task = tokio::task::spawn(async move {
|
||||
if let Err(error) = self.process().await {
|
||||
error!("failed to process console: {}", error);
|
||||
}
|
||||
});
|
||||
Ok(DaemonConsoleHandle {
|
||||
listeners,
|
||||
buffers,
|
||||
sender,
|
||||
task: Arc::new(task),
|
||||
})
|
||||
}
|
||||
|
||||
async fn process(&mut self) -> Result<()> {
|
||||
loop {
|
||||
let Some((domid, data)) = self.receiver.recv().await else {
|
||||
break;
|
||||
};
|
||||
|
||||
let mut buffers = self.buffers.lock().await;
|
||||
let buffer = buffers
|
||||
.entry(domid)
|
||||
.or_insert_with_key(|_| RawConsoleBuffer::boxed());
|
||||
buffer.extend_from_slice(&data);
|
||||
drop(buffers);
|
||||
let mut listeners = self.listeners.lock().await;
|
||||
if let Some(senders) = listeners.get_mut(&domid) {
|
||||
senders.retain(|sender| {
|
||||
!matches!(sender.try_send(data.to_vec()), Err(TrySendError::Closed(_)))
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DaemonConsole {
|
||||
fn drop(&mut self) {
|
||||
self.task.abort();
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use std::{io, pin::Pin, str::FromStr};
|
||||
use std::{pin::Pin, str::FromStr};
|
||||
|
||||
use async_stream::try_stream;
|
||||
use futures::Stream;
|
||||
@ -11,17 +11,15 @@ use krata::v1::{
|
||||
WatchEventsReply, WatchEventsRequest,
|
||||
},
|
||||
};
|
||||
use kratart::Runtime;
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
select,
|
||||
sync::mpsc::Sender,
|
||||
sync::mpsc::{channel, Sender},
|
||||
};
|
||||
use tokio_stream::StreamExt;
|
||||
use tonic::{Request, Response, Status, Streaming};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{db::GuestStore, event::DaemonEventContext};
|
||||
use crate::{console::DaemonConsoleHandle, db::GuestStore, event::DaemonEventContext};
|
||||
|
||||
pub struct ApiError {
|
||||
message: String,
|
||||
@ -44,7 +42,7 @@ impl From<ApiError> for Status {
|
||||
#[derive(Clone)]
|
||||
pub struct RuntimeControlService {
|
||||
events: DaemonEventContext,
|
||||
runtime: Runtime,
|
||||
console: DaemonConsoleHandle,
|
||||
guests: GuestStore,
|
||||
guest_reconciler_notify: Sender<Uuid>,
|
||||
}
|
||||
@ -52,13 +50,13 @@ pub struct RuntimeControlService {
|
||||
impl RuntimeControlService {
|
||||
pub fn new(
|
||||
events: DaemonEventContext,
|
||||
runtime: Runtime,
|
||||
console: DaemonConsoleHandle,
|
||||
guests: GuestStore,
|
||||
guest_reconciler_notify: Sender<Uuid>,
|
||||
) -> Self {
|
||||
Self {
|
||||
events,
|
||||
runtime,
|
||||
console,
|
||||
guests,
|
||||
guest_reconciler_notify,
|
||||
}
|
||||
@ -66,7 +64,7 @@ impl RuntimeControlService {
|
||||
}
|
||||
|
||||
enum ConsoleDataSelect {
|
||||
Read(io::Result<usize>),
|
||||
Read(Option<Vec<u8>>),
|
||||
Write(Option<Result<ConsoleDataRequest, tonic::Status>>),
|
||||
}
|
||||
|
||||
@ -200,27 +198,64 @@ impl ControlService for RuntimeControlService {
|
||||
let uuid = Uuid::from_str(&request.guest_id).map_err(|error| ApiError {
|
||||
message: error.to_string(),
|
||||
})?;
|
||||
let mut console = self.runtime.console(uuid).await.map_err(ApiError::from)?;
|
||||
let guest = self
|
||||
.guests
|
||||
.read(uuid)
|
||||
.await
|
||||
.map_err(|error| ApiError {
|
||||
message: error.to_string(),
|
||||
})?
|
||||
.ok_or_else(|| ApiError {
|
||||
message: "guest did not exist in the database".to_string(),
|
||||
})?;
|
||||
|
||||
let Some(ref state) = guest.state else {
|
||||
return Err(ApiError {
|
||||
message: "guest did not have state".to_string(),
|
||||
}
|
||||
.into());
|
||||
};
|
||||
|
||||
let domid = state.domid;
|
||||
if domid == 0 {
|
||||
return Err(ApiError {
|
||||
message: "invalid domid on the guest".to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
let (sender, mut receiver) = channel(100);
|
||||
let console = self
|
||||
.console
|
||||
.attach(domid, sender)
|
||||
.await
|
||||
.map_err(|error| ApiError {
|
||||
message: format!("failed to attach to console: {}", error),
|
||||
})?;
|
||||
|
||||
let output = try_stream! {
|
||||
let mut buffer: Vec<u8> = vec![0u8; 256];
|
||||
yield ConsoleDataReply { data: console.initial.clone(), };
|
||||
loop {
|
||||
let what = select! {
|
||||
x = console.read_handle.read(&mut buffer) => ConsoleDataSelect::Read(x),
|
||||
x = receiver.recv() => ConsoleDataSelect::Read(x),
|
||||
x = input.next() => ConsoleDataSelect::Write(x),
|
||||
};
|
||||
|
||||
match what {
|
||||
ConsoleDataSelect::Read(result) => {
|
||||
let size = result?;
|
||||
let data = buffer[0..size].to_vec();
|
||||
ConsoleDataSelect::Read(Some(data)) => {
|
||||
yield ConsoleDataReply { data, };
|
||||
},
|
||||
|
||||
ConsoleDataSelect::Read(None) => {
|
||||
break;
|
||||
}
|
||||
|
||||
ConsoleDataSelect::Write(Some(request)) => {
|
||||
let request = request?;
|
||||
if !request.data.is_empty() {
|
||||
console.write_handle.write_all(&request.data).await?;
|
||||
console.send(request.data).await.map_err(|error| ApiError {
|
||||
message: error.to_string(),
|
||||
})?;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -67,7 +67,7 @@ pub struct DaemonIdm {
|
||||
|
||||
impl DaemonIdm {
|
||||
pub async fn new() -> Result<DaemonIdm> {
|
||||
let (service, receiver) = ChannelService::new("krata-channel".to_string()).await?;
|
||||
let (service, _, receiver) = ChannelService::new("krata-channel".to_string(), None).await?;
|
||||
let task = service.launch().await?;
|
||||
let listeners = Arc::new(Mutex::new(HashMap::new()));
|
||||
Ok(DaemonIdm {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::{net::SocketAddr, path::PathBuf, str::FromStr};
|
||||
|
||||
use anyhow::Result;
|
||||
use console::{DaemonConsole, DaemonConsoleHandle};
|
||||
use control::RuntimeControlService;
|
||||
use db::GuestStore;
|
||||
use event::{DaemonEventContext, DaemonEventGenerator};
|
||||
@ -18,6 +19,7 @@ use tokio_stream::wrappers::UnixListenerStream;
|
||||
use tonic::transport::{Identity, Server, ServerTlsConfig};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub mod console;
|
||||
pub mod control;
|
||||
pub mod db;
|
||||
pub mod event;
|
||||
@ -26,13 +28,13 @@ pub mod reconcile;
|
||||
|
||||
pub struct Daemon {
|
||||
store: String,
|
||||
runtime: Runtime,
|
||||
guests: GuestStore,
|
||||
events: DaemonEventContext,
|
||||
guest_reconciler_task: JoinHandle<()>,
|
||||
guest_reconciler_notify: Sender<Uuid>,
|
||||
generator_task: JoinHandle<()>,
|
||||
_idm: DaemonIdmHandle,
|
||||
console: DaemonConsoleHandle,
|
||||
}
|
||||
|
||||
const GUEST_RECONCILER_QUEUE_LEN: usize = 1000;
|
||||
@ -45,31 +47,37 @@ impl Daemon {
|
||||
channel::<Uuid>(GUEST_RECONCILER_QUEUE_LEN);
|
||||
let idm = DaemonIdm::new().await?;
|
||||
let idm = idm.launch().await?;
|
||||
let console = DaemonConsole::new().await?;
|
||||
let console = console.launch().await?;
|
||||
let (events, generator) =
|
||||
DaemonEventGenerator::new(guests.clone(), guest_reconciler_notify.clone(), idm.clone())
|
||||
.await?;
|
||||
let runtime_for_reconciler = runtime.dupe().await?;
|
||||
let guest_reconciler =
|
||||
GuestReconciler::new(guests.clone(), events.clone(), runtime_for_reconciler)?;
|
||||
let guest_reconciler = GuestReconciler::new(
|
||||
guests.clone(),
|
||||
events.clone(),
|
||||
runtime_for_reconciler,
|
||||
guest_reconciler_notify.clone(),
|
||||
)?;
|
||||
|
||||
let guest_reconciler_task = guest_reconciler.launch(guest_reconciler_receiver).await?;
|
||||
let generator_task = generator.launch().await?;
|
||||
Ok(Self {
|
||||
store,
|
||||
runtime,
|
||||
guests,
|
||||
events,
|
||||
guest_reconciler_task,
|
||||
guest_reconciler_notify,
|
||||
generator_task,
|
||||
_idm: idm,
|
||||
console,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn listen(&mut self, addr: ControlDialAddress) -> Result<()> {
|
||||
let control_service = RuntimeControlService::new(
|
||||
self.events.clone(),
|
||||
self.runtime.clone(),
|
||||
self.console.clone(),
|
||||
self.guests.clone(),
|
||||
self.guest_reconciler_notify.clone(),
|
||||
);
|
||||
|
@ -1,4 +1,8 @@
|
||||
use std::{collections::HashMap, time::Duration};
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use krata::v1::{
|
||||
@ -10,7 +14,15 @@ use krata::v1::{
|
||||
};
|
||||
use kratart::{launch::GuestLaunchRequest, GuestInfo, Runtime};
|
||||
use log::{error, info, trace, warn};
|
||||
use tokio::{select, sync::mpsc::Receiver, task::JoinHandle, time::sleep};
|
||||
use tokio::{
|
||||
select,
|
||||
sync::{
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
Mutex, RwLock,
|
||||
},
|
||||
task::JoinHandle,
|
||||
time::sleep,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
@ -18,18 +30,49 @@ use crate::{
|
||||
event::{DaemonEvent, DaemonEventContext},
|
||||
};
|
||||
|
||||
const PARALLEL_LIMIT: u32 = 5;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum GuestReconcilerResult {
|
||||
Unchanged,
|
||||
Changed { rerun: bool },
|
||||
}
|
||||
|
||||
struct GuestReconcilerEntry {
|
||||
task: JoinHandle<()>,
|
||||
sender: Sender<()>,
|
||||
}
|
||||
|
||||
impl Drop for GuestReconcilerEntry {
|
||||
fn drop(&mut self) {
|
||||
self.task.abort();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GuestReconciler {
|
||||
guests: GuestStore,
|
||||
events: DaemonEventContext,
|
||||
runtime: Runtime,
|
||||
tasks: Arc<Mutex<HashMap<Uuid, GuestReconcilerEntry>>>,
|
||||
guest_reconciler_notify: Sender<Uuid>,
|
||||
reconcile_lock: Arc<RwLock<()>>,
|
||||
}
|
||||
|
||||
impl GuestReconciler {
|
||||
pub fn new(guests: GuestStore, events: DaemonEventContext, runtime: Runtime) -> Result<Self> {
|
||||
pub fn new(
|
||||
guests: GuestStore,
|
||||
events: DaemonEventContext,
|
||||
runtime: Runtime,
|
||||
guest_reconciler_notify: Sender<Uuid>,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
guests,
|
||||
events,
|
||||
runtime,
|
||||
tasks: Arc::new(Mutex::new(HashMap::new())),
|
||||
guest_reconciler_notify,
|
||||
reconcile_lock: Arc::new(RwLock::with_max_readers((), PARALLEL_LIMIT)),
|
||||
})
|
||||
}
|
||||
|
||||
@ -47,8 +90,15 @@ impl GuestReconciler {
|
||||
},
|
||||
|
||||
Some(uuid) => {
|
||||
if let Err(error) = self.reconcile(uuid).await {
|
||||
error!("failed to reconcile guest {}: {}", uuid, error);
|
||||
if let Err(error) = self.launch_task_if_needed(uuid).await {
|
||||
error!("failed to start guest reconciler task {}: {}", uuid, error);
|
||||
}
|
||||
|
||||
let map = self.tasks.lock().await;
|
||||
if let Some(entry) = map.get(&uuid) {
|
||||
if let Err(error) = entry.sender.send(()).await {
|
||||
error!("failed to notify guest reconciler task {}: {}", uuid, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -64,6 +114,7 @@ impl GuestReconciler {
|
||||
}
|
||||
|
||||
pub async fn reconcile_runtime(&self, initial: bool) -> Result<()> {
|
||||
let _permit = self.reconcile_lock.write().await;
|
||||
trace!("reconciling runtime");
|
||||
let runtime_guests = self.runtime.list().await?;
|
||||
let stored_guests = self.guests.list().await?;
|
||||
@ -96,21 +147,20 @@ impl GuestReconciler {
|
||||
|
||||
if changed || initial {
|
||||
self.guests.update(uuid, stored_guest).await?;
|
||||
if let Err(error) = self.reconcile(uuid).await {
|
||||
error!("failed to reconcile guest {}: {}", uuid, error);
|
||||
}
|
||||
let _ = self.guest_reconciler_notify.try_send(uuid);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn reconcile(&self, uuid: Uuid) -> Result<()> {
|
||||
pub async fn reconcile(&self, uuid: Uuid) -> Result<bool> {
|
||||
let _runtime_reconcile_permit = self.reconcile_lock.read().await;
|
||||
let Some(mut guest) = self.guests.read(uuid).await? else {
|
||||
warn!(
|
||||
"notified of reconcile for guest {} but it didn't exist",
|
||||
uuid
|
||||
);
|
||||
return Ok(());
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
info!("reconciling guest {}", uuid);
|
||||
@ -120,47 +170,55 @@ impl GuestReconciler {
|
||||
guest: Some(guest.clone()),
|
||||
}))?;
|
||||
|
||||
let result = match guest.state.as_ref().map(|x| x.status()).unwrap_or_default() {
|
||||
let start_status = guest.state.as_ref().map(|x| x.status()).unwrap_or_default();
|
||||
let result = match start_status {
|
||||
GuestStatus::Starting => self.start(uuid, &mut guest).await,
|
||||
GuestStatus::Destroying | GuestStatus::Exited => self.destroy(uuid, &mut guest).await,
|
||||
_ => Ok(false),
|
||||
GuestStatus::Exited => self.exited(&mut guest).await,
|
||||
GuestStatus::Destroying => self.destroy(uuid, &mut guest).await,
|
||||
_ => Ok(GuestReconcilerResult::Unchanged),
|
||||
};
|
||||
|
||||
let changed = match result {
|
||||
Ok(changed) => changed,
|
||||
let result = match result {
|
||||
Ok(result) => result,
|
||||
Err(error) => {
|
||||
guest.state = Some(guest.state.as_mut().cloned().unwrap_or_default());
|
||||
guest.state.as_mut().unwrap().status = GuestStatus::Failed.into();
|
||||
guest.state.as_mut().unwrap().error_info = Some(GuestErrorInfo {
|
||||
message: error.to_string(),
|
||||
});
|
||||
true
|
||||
warn!("failed to start guest {}: {}", guest.id, error);
|
||||
GuestReconcilerResult::Changed { rerun: false }
|
||||
}
|
||||
};
|
||||
|
||||
info!("reconciled guest {}", uuid);
|
||||
|
||||
let status = guest.state.as_ref().map(|x| x.status()).unwrap_or_default();
|
||||
let destroyed = status == GuestStatus::Destroyed || status == GuestStatus::Failed;
|
||||
let destroyed = status == GuestStatus::Destroyed;
|
||||
|
||||
if changed {
|
||||
let rerun = if let GuestReconcilerResult::Changed { rerun } = result {
|
||||
let event = DaemonEvent::GuestChanged(GuestChangedEvent {
|
||||
guest: Some(guest.clone()),
|
||||
});
|
||||
|
||||
if destroyed {
|
||||
self.guests.remove(uuid).await?;
|
||||
let mut map = self.tasks.lock().await;
|
||||
map.remove(&uuid);
|
||||
} else {
|
||||
self.guests.update(uuid, guest.clone()).await?;
|
||||
}
|
||||
|
||||
self.events.send(event)?;
|
||||
}
|
||||
rerun
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
Ok(())
|
||||
Ok(rerun)
|
||||
}
|
||||
|
||||
async fn start(&self, uuid: Uuid, guest: &mut Guest) -> Result<bool> {
|
||||
async fn start(&self, uuid: Uuid, guest: &mut Guest) -> Result<GuestReconcilerResult> {
|
||||
let Some(ref spec) = guest.spec else {
|
||||
return Err(anyhow!("guest spec not specified"));
|
||||
};
|
||||
@ -206,10 +264,19 @@ impl GuestReconciler {
|
||||
error_info: None,
|
||||
domid: info.domid,
|
||||
});
|
||||
Ok(true)
|
||||
Ok(GuestReconcilerResult::Changed { rerun: false })
|
||||
}
|
||||
|
||||
async fn destroy(&self, uuid: Uuid, guest: &mut Guest) -> Result<bool> {
|
||||
async fn exited(&self, guest: &mut Guest) -> Result<GuestReconcilerResult> {
|
||||
if let Some(ref mut state) = guest.state {
|
||||
state.set_status(GuestStatus::Destroying);
|
||||
Ok(GuestReconcilerResult::Changed { rerun: true })
|
||||
} else {
|
||||
Ok(GuestReconcilerResult::Unchanged)
|
||||
}
|
||||
}
|
||||
|
||||
async fn destroy(&self, uuid: Uuid, guest: &mut Guest) -> Result<GuestReconcilerResult> {
|
||||
if let Err(error) = self.runtime.destroy(uuid).await {
|
||||
trace!("failed to destroy runtime guest {}: {}", uuid, error);
|
||||
}
|
||||
@ -222,7 +289,46 @@ impl GuestReconciler {
|
||||
error_info: None,
|
||||
domid: guest.state.as_ref().map(|x| x.domid).unwrap_or(u32::MAX),
|
||||
});
|
||||
Ok(true)
|
||||
Ok(GuestReconcilerResult::Changed { rerun: false })
|
||||
}
|
||||
|
||||
async fn launch_task_if_needed(&self, uuid: Uuid) -> Result<()> {
|
||||
let mut map = self.tasks.lock().await;
|
||||
match map.entry(uuid) {
|
||||
Entry::Occupied(_) => {}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(self.launch_task(uuid).await?);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn launch_task(&self, uuid: Uuid) -> Result<GuestReconcilerEntry> {
|
||||
let this = self.clone();
|
||||
let (sender, mut receiver) = channel(10);
|
||||
let task = tokio::task::spawn(async move {
|
||||
'notify_loop: loop {
|
||||
if receiver.recv().await.is_none() {
|
||||
break 'notify_loop;
|
||||
}
|
||||
|
||||
'rerun_loop: loop {
|
||||
let rerun = match this.reconcile(uuid).await {
|
||||
Ok(rerun) => rerun,
|
||||
Err(error) => {
|
||||
error!("failed to reconcile guest {}: {}", uuid, error);
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if rerun {
|
||||
continue 'rerun_loop;
|
||||
}
|
||||
break 'rerun_loop;
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(GuestReconcilerEntry { task, sender })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "krata-guest"
|
||||
description = "Guest services for the krata hypervisor."
|
||||
license.workspace = true
|
||||
version= "0.0.3"
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition = "2021"
|
||||
@ -10,11 +10,12 @@ resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
cgroups-rs = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
ipnetwork = { workspace = true }
|
||||
krata = { path = "../krata", version = "^0.0.3" }
|
||||
krata-xenstore = { path = "../xen/xenstore", version = "^0.0.3" }
|
||||
krata = { path = "../krata", version = "^0.0.5" }
|
||||
krata-xenstore = { path = "../xen/xenstore", version = "^0.0.5" }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
nix = { workspace = true, features = ["ioctl", "process", "fs"] }
|
||||
|
@ -3,6 +3,7 @@ use crate::{
|
||||
death,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use cgroups_rs::Cgroup;
|
||||
use krata::idm::{
|
||||
client::IdmClient,
|
||||
protocol::{idm_event::Event, IdmEvent, IdmExitEvent, IdmPacket},
|
||||
@ -14,14 +15,16 @@ use tokio::select;
|
||||
pub struct GuestBackground {
|
||||
idm: IdmClient,
|
||||
child: Pid,
|
||||
_cgroup: Cgroup,
|
||||
wait: ChildWait,
|
||||
}
|
||||
|
||||
impl GuestBackground {
|
||||
pub async fn new(idm: IdmClient, child: Pid) -> Result<GuestBackground> {
|
||||
pub async fn new(idm: IdmClient, cgroup: Cgroup, child: Pid) -> Result<GuestBackground> {
|
||||
Ok(GuestBackground {
|
||||
idm,
|
||||
child,
|
||||
_cgroup: cgroup,
|
||||
wait: ChildWait::new()?,
|
||||
})
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use cgroups_rs::{Cgroup, CgroupPid};
|
||||
use futures::stream::TryStreamExt;
|
||||
use ipnetwork::IpNetwork;
|
||||
use krata::ethtool::EthtoolHandle;
|
||||
use krata::idm::client::IdmClient;
|
||||
use krata::launchcfg::{LaunchInfo, LaunchNetwork};
|
||||
use libc::{setsid, TIOCSCTTY};
|
||||
use libc::{sethostname, setsid, TIOCSCTTY};
|
||||
use log::{trace, warn};
|
||||
use nix::ioctl_write_int_bad;
|
||||
use nix::unistd::{dup2, execve, fork, ForkResult, Pid};
|
||||
@ -18,7 +19,7 @@ use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::fs::{chroot, symlink, PermissionsExt};
|
||||
use std::os::unix::fs::{chroot, PermissionsExt};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use sys_mount::{FilesystemType, Mount, MountFlags};
|
||||
@ -90,6 +91,18 @@ impl GuestInit {
|
||||
self.nuke_initrd().await?;
|
||||
self.bind_new_root().await?;
|
||||
|
||||
if let Some(hostname) = launch.hostname.clone() {
|
||||
let result = unsafe {
|
||||
sethostname(
|
||||
hostname.as_bytes().as_ptr() as *mut libc::c_char,
|
||||
hostname.len(),
|
||||
)
|
||||
};
|
||||
if result != 0 {
|
||||
warn!("failed to set hostname: {}", result);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(network) = &launch.network {
|
||||
trace!("initializing network");
|
||||
if let Err(error) = self.network_setup(network).await {
|
||||
@ -112,14 +125,20 @@ impl GuestInit {
|
||||
trace!("early init");
|
||||
self.create_dir("/dev", Some(0o0755)).await?;
|
||||
self.create_dir("/proc", None).await?;
|
||||
self.create_dir("/sys", None).await?;
|
||||
self.create_dir("/sys", Some(0o0555)).await?;
|
||||
self.create_dir("/root", Some(0o0700)).await?;
|
||||
self.create_dir("/tmp", None).await?;
|
||||
self.mount_kernel_fs("devtmpfs", "/dev", "mode=0755")
|
||||
self.create_dir("/run", Some(0o0755)).await?;
|
||||
self.mount_kernel_fs("devtmpfs", "/dev", "mode=0755", None)
|
||||
.await?;
|
||||
self.mount_kernel_fs("proc", "/proc", "", None).await?;
|
||||
self.mount_kernel_fs("sysfs", "/sys", "", None).await?;
|
||||
fs::symlink("/proc/self/fd", "/dev/fd").await?;
|
||||
fs::symlink("/proc/self/fd/0", "/dev/stdin").await?;
|
||||
fs::symlink("/proc/self/fd/1", "/dev/stdout").await?;
|
||||
fs::symlink("/proc/self/fd/2", "/dev/stderr").await?;
|
||||
self.mount_kernel_fs("cgroup2", "/sys/fs/cgroup", "", Some(MountFlags::RELATIME))
|
||||
.await?;
|
||||
self.mount_kernel_fs("proc", "/proc", "").await?;
|
||||
self.mount_kernel_fs("sysfs", "/sys", "").await?;
|
||||
symlink("/proc/self/fd", "/dev/fd")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -137,16 +156,19 @@ impl GuestInit {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn mount_kernel_fs(&mut self, fstype: &str, path: &str, data: &str) -> Result<()> {
|
||||
let metadata = fs::metadata(path).await?;
|
||||
if metadata.st_dev() == fs::metadata("/").await?.st_dev() {
|
||||
trace!("mounting kernel fs {} to {}", fstype, path);
|
||||
Mount::builder()
|
||||
.fstype(FilesystemType::Manual(fstype))
|
||||
.flags(MountFlags::NOEXEC | MountFlags::NOSUID)
|
||||
.data(data)
|
||||
.mount(fstype, path)?;
|
||||
}
|
||||
async fn mount_kernel_fs(
|
||||
&mut self,
|
||||
fstype: &str,
|
||||
path: &str,
|
||||
data: &str,
|
||||
flags: Option<MountFlags>,
|
||||
) -> Result<()> {
|
||||
trace!("mounting kernel fs {} to {}", fstype, path);
|
||||
Mount::builder()
|
||||
.fstype(FilesystemType::Manual(fstype))
|
||||
.flags(MountFlags::NOEXEC | MountFlags::NOSUID | flags.unwrap_or(MountFlags::empty()))
|
||||
.data(data)
|
||||
.mount(fstype, path)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -327,6 +349,14 @@ impl GuestInit {
|
||||
let (connection, handle, _) = rtnetlink::new_connection()?;
|
||||
tokio::spawn(connection);
|
||||
|
||||
let mut links = handle.link().get().match_name("lo".to_string()).execute();
|
||||
let Some(link) = links.try_next().await? else {
|
||||
warn!("unable to find link named lo");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
handle.link().set(link.header.index).up().execute().await?;
|
||||
|
||||
let ipv4_network: IpNetwork = network.ipv4.address.parse()?;
|
||||
let ipv4_gateway: Ipv4Addr = network.ipv4.gateway.parse()?;
|
||||
let ipv6_network: IpNetwork = network.ipv6.address.parse()?;
|
||||
@ -406,7 +436,7 @@ impl GuestInit {
|
||||
};
|
||||
|
||||
if launch.run.is_some() {
|
||||
cmd = launch.run.as_ref().unwrap().clone();
|
||||
cmd.clone_from(launch.run.as_ref().unwrap());
|
||||
}
|
||||
|
||||
if let Some(entrypoint) = config.entrypoint() {
|
||||
@ -454,10 +484,21 @@ impl GuestInit {
|
||||
working_dir = "/".to_string();
|
||||
}
|
||||
|
||||
self.fork_and_exec(idm, working_dir, path, cmd, env).await?;
|
||||
let cgroup = self.init_cgroup().await?;
|
||||
self.fork_and_exec(idm, cgroup, working_dir, path, cmd, env)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn init_cgroup(&self) -> Result<Cgroup> {
|
||||
trace!("initializing cgroup");
|
||||
let hierarchy = cgroups_rs::hierarchies::auto();
|
||||
let cgroup = Cgroup::new(hierarchy, "krata-guest-task")?;
|
||||
cgroup.set_cgroup_type("threaded")?;
|
||||
trace!("initialized cgroup");
|
||||
Ok(cgroup)
|
||||
}
|
||||
|
||||
fn strings_as_cstrings(values: Vec<String>) -> Result<Vec<CString>> {
|
||||
let mut results: Vec<CString> = vec![];
|
||||
for value in values {
|
||||
@ -506,19 +547,21 @@ impl GuestInit {
|
||||
async fn fork_and_exec(
|
||||
&mut self,
|
||||
idm: IdmClient,
|
||||
cgroup: Cgroup,
|
||||
working_dir: String,
|
||||
path: CString,
|
||||
cmd: Vec<CString>,
|
||||
env: Vec<CString>,
|
||||
) -> Result<()> {
|
||||
match unsafe { fork()? } {
|
||||
ForkResult::Parent { child } => self.background(idm, child).await,
|
||||
ForkResult::Child => self.foreground(working_dir, path, cmd, env).await,
|
||||
ForkResult::Parent { child } => self.background(idm, cgroup, child).await,
|
||||
ForkResult::Child => self.foreground(cgroup, working_dir, path, cmd, env).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn foreground(
|
||||
&mut self,
|
||||
cgroup: Cgroup,
|
||||
working_dir: String,
|
||||
path: CString,
|
||||
cmd: Vec<CString>,
|
||||
@ -526,6 +569,7 @@ impl GuestInit {
|
||||
) -> Result<()> {
|
||||
GuestInit::set_controlling_terminal()?;
|
||||
std::env::set_current_dir(working_dir)?;
|
||||
cgroup.add_task(CgroupPid::from(std::process::id() as u64))?;
|
||||
execve(&path, &cmd, &env)?;
|
||||
Ok(())
|
||||
}
|
||||
@ -538,8 +582,8 @@ impl GuestInit {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn background(&mut self, idm: IdmClient, executed: Pid) -> Result<()> {
|
||||
let mut background = GuestBackground::new(idm, executed).await?;
|
||||
async fn background(&mut self, idm: IdmClient, cgroup: Cgroup, executed: Pid) -> Result<()> {
|
||||
let mut background = GuestBackground::new(idm, cgroup, executed).await?;
|
||||
background.run().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "krata"
|
||||
description = "Client library and common services for the krata hypervisor."
|
||||
license.workspace = true
|
||||
version= "0.0.3"
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition = "2021"
|
||||
|
@ -1,11 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use crate::v1::control::{watch_events_reply::Event, WatchEventsReply};
|
||||
use crate::v1::control::{
|
||||
control_service_client::ControlServiceClient, watch_events_reply::Event, WatchEventsReply,
|
||||
WatchEventsRequest,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use log::trace;
|
||||
use tokio::{sync::broadcast, task::JoinHandle};
|
||||
use log::{error, trace, warn};
|
||||
use tokio::{sync::broadcast, task::JoinHandle, time::sleep};
|
||||
use tokio_stream::StreamExt;
|
||||
use tonic::Streaming;
|
||||
use tonic::{transport::Channel, Streaming};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventStream {
|
||||
@ -14,27 +17,12 @@ pub struct EventStream {
|
||||
}
|
||||
|
||||
impl EventStream {
|
||||
pub async fn open(mut events: Streaming<WatchEventsReply>) -> Result<Self> {
|
||||
pub async fn open(client: ControlServiceClient<Channel>) -> Result<Self> {
|
||||
let (sender, _) = broadcast::channel(1000);
|
||||
let emit = sender.clone();
|
||||
let task = tokio::task::spawn(async move {
|
||||
loop {
|
||||
let Some(result) = events.next().await else {
|
||||
break;
|
||||
};
|
||||
|
||||
let reply = match result {
|
||||
Ok(reply) => reply,
|
||||
Err(error) => {
|
||||
trace!("event stream processing failed: {}", error);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
let Some(event) = reply.event else {
|
||||
continue;
|
||||
};
|
||||
let _ = emit.send(event);
|
||||
if let Err(error) = EventStream::process(client, emit).await {
|
||||
error!("failed to process event stream: {}", error);
|
||||
}
|
||||
});
|
||||
Ok(Self {
|
||||
@ -43,6 +31,48 @@ impl EventStream {
|
||||
})
|
||||
}
|
||||
|
||||
async fn process(
|
||||
mut client: ControlServiceClient<Channel>,
|
||||
emit: broadcast::Sender<Event>,
|
||||
) -> Result<()> {
|
||||
let mut events: Option<Streaming<WatchEventsReply>> = None;
|
||||
loop {
|
||||
let mut stream = match events {
|
||||
Some(stream) => stream,
|
||||
None => {
|
||||
let result = client.watch_events(WatchEventsRequest {}).await;
|
||||
if let Err(error) = result {
|
||||
warn!("failed to watch events: {}", error);
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
continue;
|
||||
}
|
||||
result.unwrap().into_inner()
|
||||
}
|
||||
};
|
||||
|
||||
let Some(result) = stream.next().await else {
|
||||
events = None;
|
||||
continue;
|
||||
};
|
||||
|
||||
let reply = match result {
|
||||
Ok(reply) => reply,
|
||||
Err(error) => {
|
||||
trace!("event stream processing failed: {}", error);
|
||||
events = None;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let Some(event) = reply.event else {
|
||||
events = Some(stream);
|
||||
continue;
|
||||
};
|
||||
let _ = emit.send(event);
|
||||
events = Some(stream);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe(&self) -> broadcast::Receiver<Event> {
|
||||
self.sender.subscribe()
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use std::path::Path;
|
||||
use super::protocol::IdmPacket;
|
||||
use anyhow::{anyhow, Result};
|
||||
use bytes::BytesMut;
|
||||
use log::error;
|
||||
use log::{debug, error};
|
||||
use nix::sys::termios::{cfmakeraw, tcgetattr, tcsetattr, SetArg};
|
||||
use prost::Message;
|
||||
use tokio::{
|
||||
@ -41,7 +41,7 @@ impl IdmClient {
|
||||
let (tx_sender, tx_receiver) = channel(IDM_PACKET_QUEUE_LEN);
|
||||
let task = tokio::task::spawn(async move {
|
||||
if let Err(error) = IdmClient::process(file, rx_sender, tx_receiver).await {
|
||||
error!("failed to handle idm client processing: {}", error);
|
||||
debug!("failed to handle idm client processing: {}", error);
|
||||
}
|
||||
});
|
||||
Ok(IdmClient {
|
||||
|
@ -29,6 +29,7 @@ pub struct LaunchNetwork {
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct LaunchInfo {
|
||||
pub hostname: Option<String>,
|
||||
pub network: Option<LaunchNetwork>,
|
||||
pub env: HashMap<String, String>,
|
||||
pub run: Option<Vec<String>>,
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "krata-network"
|
||||
description = "Networking services for the krata hypervisor."
|
||||
license.workspace = true
|
||||
version= "0.0.3"
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition = "2021"
|
||||
@ -16,7 +16,7 @@ clap = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
etherparse = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
krata = { path = "../krata", version = "^0.0.3" }
|
||||
krata = { path = "../krata", version = "^0.0.5" }
|
||||
krata-advmac = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
@ -5,7 +5,7 @@ use krata::{
|
||||
common::Guest,
|
||||
control::{
|
||||
control_service_client::ControlServiceClient, watch_events_reply::Event,
|
||||
ListGuestsRequest, WatchEventsRequest,
|
||||
ListGuestsRequest,
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -50,12 +50,11 @@ pub struct AutoNetworkChangeset {
|
||||
}
|
||||
|
||||
impl AutoNetworkWatcher {
|
||||
pub async fn new(mut control: ControlServiceClient<Channel>) -> Result<AutoNetworkWatcher> {
|
||||
let watch_events_response = control.watch_events(WatchEventsRequest {}).await?;
|
||||
|
||||
pub async fn new(control: ControlServiceClient<Channel>) -> Result<AutoNetworkWatcher> {
|
||||
let client = control.clone();
|
||||
Ok(AutoNetworkWatcher {
|
||||
control,
|
||||
events: EventStream::open(watch_events_response.into_inner()).await?,
|
||||
events: EventStream::open(client).await?,
|
||||
known: HashMap::new(),
|
||||
})
|
||||
}
|
||||
@ -136,7 +135,15 @@ impl AutoNetworkWatcher {
|
||||
let mut added: Vec<NetworkMetadata> = Vec::new();
|
||||
let mut removed: Vec<NetworkMetadata> = Vec::new();
|
||||
|
||||
for network in self.read().await? {
|
||||
let networks = match self.read().await {
|
||||
Ok(networks) => networks,
|
||||
Err(error) => {
|
||||
warn!("failed to read network changes: {}", error);
|
||||
return Ok(AutoNetworkChangeset { added, removed });
|
||||
}
|
||||
};
|
||||
|
||||
for network in networks {
|
||||
seen.push(network.uuid);
|
||||
if self.known.contains_key(&network.uuid) {
|
||||
continue;
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "krata-oci"
|
||||
description = "OCI services for the krata hypervisor."
|
||||
license.workspace = true
|
||||
version= "0.0.3"
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition = "2021"
|
||||
|
@ -5,6 +5,7 @@ use oci_spec::image::{ImageConfiguration, ImageManifest};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::fs;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ImageCache {
|
||||
cache_dir: PathBuf,
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "krata-runtime"
|
||||
description = "Runtime for running guests on the krata hypervisor."
|
||||
license.workspace = true
|
||||
version= "0.0.3"
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition = "2021"
|
||||
@ -12,18 +12,18 @@ resolver = "2"
|
||||
anyhow = { workspace = true }
|
||||
backhand = { workspace = true }
|
||||
ipnetwork = { workspace = true }
|
||||
krata = { path = "../krata", version = "^0.0.3" }
|
||||
krata = { path = "../krata", version = "^0.0.5" }
|
||||
krata-advmac = { workspace = true }
|
||||
krata-oci = { path = "../oci", version = "^0.0.3" }
|
||||
krata-oci = { path = "../oci", version = "^0.0.5" }
|
||||
log = { workspace = true }
|
||||
loopdev-3 = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
krata-xenclient = { path = "../xen/xenclient", version = "^0.0.3" }
|
||||
krata-xenevtchn = { path = "../xen/xenevtchn", version = "^0.0.3" }
|
||||
krata-xengnt = { path = "../xen/xengnt", version = "^0.0.3" }
|
||||
krata-xenstore = { path = "../xen/xenstore", version = "^0.0.3" }
|
||||
krata-xenclient = { path = "../xen/xenclient", version = "^0.0.5" }
|
||||
krata-xenevtchn = { path = "../xen/xenevtchn", version = "^0.0.5" }
|
||||
krata-xengnt = { path = "../xen/xengnt", version = "^0.0.5" }
|
||||
krata-xenstore = { path = "../xen/xenstore", version = "^0.0.5" }
|
||||
|
||||
[lib]
|
||||
name = "kratart"
|
||||
|
@ -6,7 +6,7 @@ use kratart::channel::ChannelService;
|
||||
async fn main() -> Result<()> {
|
||||
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
|
||||
|
||||
let (service, mut receiver) = ChannelService::new("krata-channel".to_string()).await?;
|
||||
let (service, _, mut receiver) = ChannelService::new("krata-channel".to_string(), None).await?;
|
||||
let task = service.launch().await?;
|
||||
|
||||
loop {
|
||||
|
@ -1,17 +1,25 @@
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use log::debug;
|
||||
use loopdev::{LoopControl, LoopDevice};
|
||||
use tokio::time::sleep;
|
||||
use xenclient::BlockDeviceRef;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AutoLoop {
|
||||
control: LoopControl,
|
||||
control: Arc<LoopControl>,
|
||||
}
|
||||
|
||||
impl AutoLoop {
|
||||
pub fn new(control: LoopControl) -> AutoLoop {
|
||||
AutoLoop { control }
|
||||
AutoLoop {
|
||||
control: Arc::new(control),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn loopify(&self, file: &str) -> Result<BlockDeviceRef> {
|
||||
debug!("creating loop for file {}", file);
|
||||
let device = self.control.next_free()?;
|
||||
device.with().read_only(true).attach(file)?;
|
||||
let path = device
|
||||
@ -25,9 +33,10 @@ impl AutoLoop {
|
||||
Ok(BlockDeviceRef { path, major, minor })
|
||||
}
|
||||
|
||||
pub fn unloop(&self, device: &str) -> Result<()> {
|
||||
pub async fn unloop(&self, device: &str) -> Result<()> {
|
||||
let device = LoopDevice::open(device)?;
|
||||
device.detach()?;
|
||||
sleep(Duration::from_millis(200)).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ impl XenConsoleInterface {
|
||||
|
||||
pub struct ChannelService {
|
||||
typ: String,
|
||||
use_reserved_ref: Option<u64>,
|
||||
backends: HashMap<u32, ChannelBackend>,
|
||||
evtchn: EventChannel,
|
||||
store: XsdClient,
|
||||
@ -51,20 +52,29 @@ pub struct ChannelService {
|
||||
}
|
||||
|
||||
impl ChannelService {
|
||||
pub async fn new(typ: String) -> Result<(ChannelService, Receiver<(u32, Vec<u8>)>)> {
|
||||
pub async fn new(
|
||||
typ: String,
|
||||
use_reserved_ref: Option<u64>,
|
||||
) -> Result<(
|
||||
ChannelService,
|
||||
Sender<(u32, Vec<u8>)>,
|
||||
Receiver<(u32, Vec<u8>)>,
|
||||
)> {
|
||||
let (input_sender, input_receiver) = channel(GROUPED_CHANNEL_QUEUE_LEN);
|
||||
let (output_sender, output_receiver) = channel(GROUPED_CHANNEL_QUEUE_LEN);
|
||||
Ok((
|
||||
ChannelService {
|
||||
typ,
|
||||
use_reserved_ref,
|
||||
backends: HashMap::new(),
|
||||
evtchn: EventChannel::open().await?,
|
||||
store: XsdClient::open().await?,
|
||||
gnttab: GrantTab::open()?,
|
||||
input_sender,
|
||||
input_sender: input_sender.clone(),
|
||||
input_receiver,
|
||||
output_sender,
|
||||
},
|
||||
input_sender,
|
||||
output_receiver,
|
||||
))
|
||||
}
|
||||
@ -148,6 +158,7 @@ impl ChannelService {
|
||||
self.evtchn.clone(),
|
||||
self.gnttab.clone(),
|
||||
self.output_sender.clone(),
|
||||
self.use_reserved_ref,
|
||||
)
|
||||
.await?;
|
||||
self.backends.insert(domid, backend);
|
||||
@ -216,6 +227,7 @@ impl ChannelBackend {
|
||||
evtchn: EventChannel,
|
||||
gnttab: GrantTab,
|
||||
output_sender: Sender<(u32, Vec<u8>)>,
|
||||
use_reserved_ref: Option<u64>,
|
||||
) -> Result<ChannelBackend> {
|
||||
let processor = KrataChannelBackendProcessor {
|
||||
backend,
|
||||
@ -225,6 +237,7 @@ impl ChannelBackend {
|
||||
store,
|
||||
evtchn,
|
||||
gnttab,
|
||||
use_reserved_ref,
|
||||
};
|
||||
|
||||
let (input_sender, input_receiver) = channel(SINGLE_CHANNEL_QUEUE_LEN);
|
||||
@ -241,6 +254,7 @@ impl ChannelBackend {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct KrataChannelBackendProcessor {
|
||||
use_reserved_ref: Option<u64>,
|
||||
backend: String,
|
||||
frontend: String,
|
||||
id: u32,
|
||||
@ -347,7 +361,7 @@ impl KrataChannelBackendProcessor {
|
||||
return Err(anyhow!("frontend did not give ring-ref and port"));
|
||||
}
|
||||
|
||||
let Ok(ring_ref) = ring_ref.unwrap().parse::<u64>() else {
|
||||
let Ok(mut ring_ref) = ring_ref.unwrap().parse::<u64>() else {
|
||||
return Err(anyhow!("frontend gave invalid ring-ref"));
|
||||
};
|
||||
|
||||
@ -355,6 +369,8 @@ impl KrataChannelBackendProcessor {
|
||||
return Err(anyhow!("frontend gave invalid port"));
|
||||
};
|
||||
|
||||
ring_ref = self.use_reserved_ref.unwrap_or(ring_ref);
|
||||
|
||||
break (ring_ref, port);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv6Addr};
|
||||
use std::sync::Arc;
|
||||
use std::{fs, net::Ipv4Addr, str::FromStr};
|
||||
|
||||
use advmac::MacAddr6;
|
||||
@ -8,6 +9,7 @@ use ipnetwork::{IpNetwork, Ipv4Network};
|
||||
use krata::launchcfg::{
|
||||
LaunchInfo, LaunchNetwork, LaunchNetworkIpv4, LaunchNetworkIpv6, LaunchNetworkResolver,
|
||||
};
|
||||
use tokio::sync::Semaphore;
|
||||
use uuid::Uuid;
|
||||
use xenclient::{DomainChannel, DomainConfig, DomainDisk, DomainNetworkInterface};
|
||||
use xenstore::XsdInterface;
|
||||
@ -33,16 +35,18 @@ pub struct GuestLaunchRequest<'a> {
|
||||
pub debug: bool,
|
||||
}
|
||||
|
||||
pub struct GuestLauncher {}
|
||||
pub struct GuestLauncher {
|
||||
pub launch_semaphore: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
impl GuestLauncher {
|
||||
pub fn new() -> Result<Self> {
|
||||
Ok(Self {})
|
||||
pub fn new(launch_semaphore: Arc<Semaphore>) -> Result<Self> {
|
||||
Ok(Self { launch_semaphore })
|
||||
}
|
||||
|
||||
pub async fn launch<'r>(
|
||||
&mut self,
|
||||
context: &mut RuntimeContext,
|
||||
context: &RuntimeContext,
|
||||
request: GuestLaunchRequest<'r>,
|
||||
) -> Result<GuestInfo> {
|
||||
let uuid = request.uuid.unwrap_or_else(Uuid::new_v4);
|
||||
@ -56,6 +60,7 @@ impl GuestLauncher {
|
||||
container_mac.set_local(true);
|
||||
container_mac.set_multicast(false);
|
||||
|
||||
let _launch_permit = self.launch_semaphore.acquire().await?;
|
||||
let guest_ipv4 = self.allocate_ipv4(context).await?;
|
||||
let guest_ipv6 = container_mac.to_link_local_ipv6();
|
||||
let gateway_ipv4 = "10.75.70.1";
|
||||
@ -64,6 +69,12 @@ impl GuestLauncher {
|
||||
let ipv6_network_mask: u32 = 10;
|
||||
|
||||
let launch_config = LaunchInfo {
|
||||
hostname: Some(
|
||||
request
|
||||
.name
|
||||
.map(|x| x.to_string())
|
||||
.unwrap_or_else(|| format!("krata-{}", uuid)),
|
||||
),
|
||||
network: Some(LaunchNetwork {
|
||||
link: "eth0".to_string(),
|
||||
ipv4: LaunchNetworkIpv4 {
|
||||
@ -168,6 +179,7 @@ impl GuestLauncher {
|
||||
kernel_path: &context.kernel,
|
||||
initrd_path: &context.initrd,
|
||||
cmdline: &cmdline,
|
||||
use_console_backend: Some("krata-console"),
|
||||
disks: vec![
|
||||
DomainDisk {
|
||||
vdev: "xvda",
|
||||
@ -217,14 +229,14 @@ impl GuestLauncher {
|
||||
)?),
|
||||
gateway_ipv6: Some(IpNetwork::new(
|
||||
IpAddr::V6(Ipv6Addr::from_str(gateway_ipv6)?),
|
||||
ipv4_network_mask as u8,
|
||||
ipv6_network_mask as u8,
|
||||
)?),
|
||||
gateway_mac: Some(gateway_mac_string.clone()),
|
||||
state: GuestState { exit_code: None },
|
||||
}),
|
||||
Err(error) => {
|
||||
let _ = context.autoloop.unloop(&image_squashfs_loop.path);
|
||||
let _ = context.autoloop.unloop(&cfgblk_squashfs_loop.path);
|
||||
let _ = context.autoloop.unloop(&image_squashfs_loop.path).await;
|
||||
let _ = context.autoloop.unloop(&cfgblk_squashfs_loop.path).await;
|
||||
let _ = fs::remove_dir(&cfgblk.dir);
|
||||
Err(error.into())
|
||||
}
|
||||
@ -237,7 +249,7 @@ impl GuestLauncher {
|
||||
compiler.compile(&image).await
|
||||
}
|
||||
|
||||
async fn allocate_ipv4(&mut self, context: &mut RuntimeContext) -> Result<Ipv4Addr> {
|
||||
async fn allocate_ipv4(&self, context: &RuntimeContext) -> Result<Ipv4Addr> {
|
||||
let network = Ipv4Network::new(Ipv4Addr::new(10, 75, 80, 0), 24)?;
|
||||
let mut used: Vec<Ipv4Addr> = vec![];
|
||||
for domid_candidate in context.xen.store.list("/local/domain").await? {
|
||||
@ -264,7 +276,7 @@ impl GuestLauncher {
|
||||
|
||||
if found.is_none() {
|
||||
return Err(anyhow!(
|
||||
"unable to find ipv4 to allocate to container, ipv4 addresses are exhausted"
|
||||
"unable to find ipv4 to allocate to guest, ipv4 addresses are exhausted"
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use std::{
|
||||
use anyhow::{anyhow, Result};
|
||||
use ipnetwork::IpNetwork;
|
||||
use loopdev::LoopControl;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::Semaphore;
|
||||
use uuid::Uuid;
|
||||
use xenclient::XenClient;
|
||||
use xenstore::{XsdClient, XsdInterface};
|
||||
@ -51,6 +51,7 @@ pub struct GuestInfo {
|
||||
pub state: GuestState,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RuntimeContext {
|
||||
pub image_cache: ImageCache,
|
||||
pub autoloop: AutoLoop,
|
||||
@ -94,7 +95,7 @@ impl RuntimeContext {
|
||||
Err(anyhow!("unable to find required guest file: {}", name))
|
||||
}
|
||||
|
||||
pub async fn list(&mut self) -> Result<Vec<GuestInfo>> {
|
||||
pub async fn list(&self) -> Result<Vec<GuestInfo>> {
|
||||
let mut guests: Vec<GuestInfo> = Vec::new();
|
||||
for domid_candidate in self.xen.store.list("/local/domain").await? {
|
||||
if domid_candidate == "0" {
|
||||
@ -218,7 +219,7 @@ impl RuntimeContext {
|
||||
Ok(guests)
|
||||
}
|
||||
|
||||
pub async fn resolve(&mut self, uuid: Uuid) -> Result<Option<GuestInfo>> {
|
||||
pub async fn resolve(&self, uuid: Uuid) -> Result<Option<GuestInfo>> {
|
||||
for guest in self.list().await? {
|
||||
if guest.uuid == uuid {
|
||||
return Ok(Some(guest));
|
||||
@ -254,7 +255,8 @@ impl RuntimeContext {
|
||||
#[derive(Clone)]
|
||||
pub struct Runtime {
|
||||
store: Arc<String>,
|
||||
context: Arc<Mutex<RuntimeContext>>,
|
||||
context: RuntimeContext,
|
||||
launch_semaphore: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
@ -262,24 +264,24 @@ impl Runtime {
|
||||
let context = RuntimeContext::new(store.clone()).await?;
|
||||
Ok(Self {
|
||||
store: Arc::new(store),
|
||||
context: Arc::new(Mutex::new(context)),
|
||||
context,
|
||||
launch_semaphore: Arc::new(Semaphore::new(1)),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn launch<'a>(&self, request: GuestLaunchRequest<'a>) -> Result<GuestInfo> {
|
||||
let mut context = self.context.lock().await;
|
||||
let mut launcher = GuestLauncher::new()?;
|
||||
launcher.launch(&mut context, request).await
|
||||
let mut launcher = GuestLauncher::new(self.launch_semaphore.clone())?;
|
||||
launcher.launch(&self.context, request).await
|
||||
}
|
||||
|
||||
pub async fn destroy(&self, uuid: Uuid) -> Result<Uuid> {
|
||||
let mut context = self.context.lock().await;
|
||||
let info = context
|
||||
let info = self
|
||||
.context
|
||||
.resolve(uuid)
|
||||
.await?
|
||||
.ok_or_else(|| anyhow!("unable to resolve guest: {}", uuid))?;
|
||||
let domid = info.domid;
|
||||
let mut store = XsdClient::open().await?;
|
||||
let store = XsdClient::open().await?;
|
||||
let dom_path = store.get_domain_path(domid).await?;
|
||||
let uuid = match store
|
||||
.read_string(format!("{}/krata/uuid", dom_path).as_str())
|
||||
@ -301,9 +303,9 @@ impl Runtime {
|
||||
.read_string(format!("{}/krata/loops", dom_path).as_str())
|
||||
.await?;
|
||||
let loops = RuntimeContext::parse_loop_set(&loops);
|
||||
context.xen.destroy(domid).await?;
|
||||
self.context.xen.destroy(domid).await?;
|
||||
for info in &loops {
|
||||
context.autoloop.unloop(&info.device)?;
|
||||
self.context.autoloop.unloop(&info.device).await?;
|
||||
match &info.delete {
|
||||
None => {}
|
||||
Some(delete) => {
|
||||
@ -320,19 +322,18 @@ impl Runtime {
|
||||
}
|
||||
|
||||
pub async fn console(&self, uuid: Uuid) -> Result<XenConsole> {
|
||||
let mut context = self.context.lock().await;
|
||||
let info = context
|
||||
let info = self
|
||||
.context
|
||||
.resolve(uuid)
|
||||
.await?
|
||||
.ok_or_else(|| anyhow!("unable to resolve guest: {}", uuid))?;
|
||||
let domid = info.domid;
|
||||
let tty = context.xen.get_console_path(domid).await?;
|
||||
let tty = self.context.xen.get_console_path(domid).await?;
|
||||
XenConsole::new(&tty).await
|
||||
}
|
||||
|
||||
pub async fn list(&self) -> Result<Vec<GuestInfo>> {
|
||||
let mut context = self.context.lock().await;
|
||||
context.list().await
|
||||
self.context.list().await
|
||||
}
|
||||
|
||||
pub async fn dupe(&self) -> Result<Runtime> {
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "krata-xencall"
|
||||
description = "An implementation of direct interfacing to xen privcmd for krata."
|
||||
license.workspace = true
|
||||
version= "0.0.3"
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition = "2021"
|
||||
@ -13,6 +13,7 @@ libc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
nix = { workspace = true, features = ["ioctl"] }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
|
||||
[lib]
|
||||
|
@ -2,11 +2,12 @@ use xencall::error::Result;
|
||||
use xencall::sys::CreateDomain;
|
||||
use xencall::XenCall;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let call = XenCall::open(0)?;
|
||||
let domid = call.create_domain(CreateDomain::default())?;
|
||||
let domid = call.create_domain(CreateDomain::default()).await?;
|
||||
println!("created domain {}", domid);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
use xencall::error::Result;
|
||||
use xencall::XenCall;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let call = XenCall::open(0)?;
|
||||
let info = call.get_domain_info(1)?;
|
||||
let info = call.get_domain_info(1).await?;
|
||||
println!("{:?}", info);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
use xencall::error::Result;
|
||||
use xencall::XenCall;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let call = XenCall::open(0)?;
|
||||
let context = call.get_vcpu_context(224, 0)?;
|
||||
let context = call.get_vcpu_context(224, 0).await?;
|
||||
println!("{:?}", context);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
use xencall::error::Result;
|
||||
use xencall::XenCall;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let call = XenCall::open(0)?;
|
||||
let info = call.get_version_capabilities()?;
|
||||
let info = call.get_version_capabilities().await?;
|
||||
println!("{:?}", info);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -8,8 +8,12 @@ pub enum Error {
|
||||
Kernel(#[from] nix::errno::Errno),
|
||||
#[error("io issue encountered: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("failed to acquire semaphore: {0}")]
|
||||
AcquireSemaphoreFailed(#[from] tokio::sync::AcquireError),
|
||||
#[error("populate physmap failed")]
|
||||
PopulatePhysmapFailed,
|
||||
#[error("mmap batch failed: {0}")]
|
||||
MmapBatchFailed(nix::errno::Errno),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
@ -18,15 +18,19 @@ 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 std::sync::Arc;
|
||||
use sys::{XEN_DOMCTL_MAX_INTERFACE_VERSION, XEN_DOMCTL_MIN_INTERFACE_VERSION};
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::ptr::addr_of_mut;
|
||||
use std::slice;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct XenCall {
|
||||
pub handle: File,
|
||||
pub handle: Arc<File>,
|
||||
semaphore: Arc<Semaphore>,
|
||||
domctl_interface_version: u32,
|
||||
}
|
||||
|
||||
@ -39,7 +43,8 @@ impl XenCall {
|
||||
let domctl_interface_version =
|
||||
XenCall::detect_domctl_interface_version(&handle, current_domid)?;
|
||||
Ok(XenCall {
|
||||
handle,
|
||||
handle: Arc::new(handle),
|
||||
semaphore: Arc::new(Semaphore::new(1)),
|
||||
domctl_interface_version,
|
||||
})
|
||||
}
|
||||
@ -68,7 +73,8 @@ impl XenCall {
|
||||
Err(Error::XenVersionUnsupported)
|
||||
}
|
||||
|
||||
pub fn mmap(&self, addr: u64, len: u64) -> Option<u64> {
|
||||
pub async fn mmap(&self, addr: u64, len: u64) -> Option<u64> {
|
||||
let _permit = self.semaphore.acquire().await.ok()?;
|
||||
trace!(
|
||||
"call fd={} mmap addr={:#x} len={}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -87,12 +93,20 @@ impl XenCall {
|
||||
if ptr == MAP_FAILED {
|
||||
None
|
||||
} else {
|
||||
trace!(
|
||||
"call fd={} mmap addr={:#x} len={} = {:#x}",
|
||||
self.handle.as_raw_fd(),
|
||||
addr,
|
||||
len,
|
||||
ptr as u64,
|
||||
);
|
||||
Some(ptr as u64)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hypercall(&self, op: c_ulong, arg: [c_ulong; 5]) -> Result<c_long> {
|
||||
pub async fn hypercall(&self, op: c_ulong, arg: [c_ulong; 5]) -> Result<c_long> {
|
||||
let _permit = self.semaphore.acquire().await?;
|
||||
trace!(
|
||||
"call fd={} hypercall op={:#x} arg={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -106,29 +120,29 @@ impl XenCall {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hypercall0(&self, op: c_ulong) -> Result<c_long> {
|
||||
self.hypercall(op, [0, 0, 0, 0, 0])
|
||||
pub async fn hypercall0(&self, op: c_ulong) -> Result<c_long> {
|
||||
self.hypercall(op, [0, 0, 0, 0, 0]).await
|
||||
}
|
||||
|
||||
pub fn hypercall1(&self, op: c_ulong, arg1: c_ulong) -> Result<c_long> {
|
||||
self.hypercall(op, [arg1, 0, 0, 0, 0])
|
||||
pub async fn hypercall1(&self, op: c_ulong, arg1: c_ulong) -> Result<c_long> {
|
||||
self.hypercall(op, [arg1, 0, 0, 0, 0]).await
|
||||
}
|
||||
|
||||
pub fn hypercall2(&self, op: c_ulong, arg1: c_ulong, arg2: c_ulong) -> Result<c_long> {
|
||||
self.hypercall(op, [arg1, arg2, 0, 0, 0])
|
||||
pub async fn hypercall2(&self, op: c_ulong, arg1: c_ulong, arg2: c_ulong) -> Result<c_long> {
|
||||
self.hypercall(op, [arg1, arg2, 0, 0, 0]).await
|
||||
}
|
||||
|
||||
pub fn hypercall3(
|
||||
pub async fn hypercall3(
|
||||
&self,
|
||||
op: c_ulong,
|
||||
arg1: c_ulong,
|
||||
arg2: c_ulong,
|
||||
arg3: c_ulong,
|
||||
) -> Result<c_long> {
|
||||
self.hypercall(op, [arg1, arg2, arg3, 0, 0])
|
||||
self.hypercall(op, [arg1, arg2, arg3, 0, 0]).await
|
||||
}
|
||||
|
||||
pub fn hypercall4(
|
||||
pub async fn hypercall4(
|
||||
&self,
|
||||
op: c_ulong,
|
||||
arg1: c_ulong,
|
||||
@ -136,10 +150,10 @@ impl XenCall {
|
||||
arg3: c_ulong,
|
||||
arg4: c_ulong,
|
||||
) -> Result<c_long> {
|
||||
self.hypercall(op, [arg1, arg2, arg3, arg4, 0])
|
||||
self.hypercall(op, [arg1, arg2, arg3, arg4, 0]).await
|
||||
}
|
||||
|
||||
pub fn hypercall5(
|
||||
pub async fn hypercall5(
|
||||
&self,
|
||||
op: c_ulong,
|
||||
arg1: c_ulong,
|
||||
@ -148,10 +162,10 @@ impl XenCall {
|
||||
arg4: c_ulong,
|
||||
arg5: c_ulong,
|
||||
) -> Result<c_long> {
|
||||
self.hypercall(op, [arg1, arg2, arg3, arg4, arg5])
|
||||
self.hypercall(op, [arg1, arg2, arg3, arg4, arg5]).await
|
||||
}
|
||||
|
||||
pub fn multicall(&self, calls: &mut [MultiCallEntry]) -> Result<()> {
|
||||
pub async fn multicall(&self, calls: &mut [MultiCallEntry]) -> Result<()> {
|
||||
trace!(
|
||||
"call fd={} multicall calls={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -161,11 +175,12 @@ impl XenCall {
|
||||
HYPERVISOR_MULTICALL,
|
||||
calls.as_mut_ptr() as c_ulong,
|
||||
calls.len() as c_ulong,
|
||||
)?;
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn map_resource(
|
||||
pub async fn map_resource(
|
||||
&self,
|
||||
domid: u32,
|
||||
typ: u32,
|
||||
@ -174,6 +189,7 @@ impl XenCall {
|
||||
num: u64,
|
||||
addr: u64,
|
||||
) -> Result<()> {
|
||||
let _permit = self.semaphore.acquire().await?;
|
||||
let mut resource = MmapResource {
|
||||
dom: domid as u16,
|
||||
typ,
|
||||
@ -188,7 +204,14 @@ impl XenCall {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mmap_batch(&self, domid: u32, num: u64, addr: u64, mfns: Vec<u64>) -> Result<c_long> {
|
||||
pub async fn mmap_batch(
|
||||
&self,
|
||||
domid: u32,
|
||||
num: u64,
|
||||
addr: u64,
|
||||
mfns: Vec<u64>,
|
||||
) -> Result<c_long> {
|
||||
let _permit = self.semaphore.acquire().await?;
|
||||
trace!(
|
||||
"call fd={} mmap_batch domid={} num={} addr={:#x} mfns={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -211,7 +234,7 @@ impl XenCall {
|
||||
let result = sys::mmapbatch(self.handle.as_raw_fd(), &mut batch);
|
||||
if let Err(errno) = result {
|
||||
if errno != Errno::ENOENT {
|
||||
return Err(errno)?;
|
||||
return Err(Error::MmapBatchFailed(errno))?;
|
||||
}
|
||||
|
||||
usleep(100);
|
||||
@ -246,7 +269,7 @@ impl XenCall {
|
||||
let result = sys::mmapbatch(self.handle.as_raw_fd(), &mut batch);
|
||||
if let Err(n) = result {
|
||||
if n != Errno::ENOENT {
|
||||
return Err(n)?;
|
||||
return Err(Error::MmapBatchFailed(n))?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,7 +289,7 @@ impl XenCall {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_version_capabilities(&self) -> Result<XenCapabilitiesInfo> {
|
||||
pub async fn get_version_capabilities(&self) -> Result<XenCapabilitiesInfo> {
|
||||
trace!(
|
||||
"call fd={} get_version_capabilities",
|
||||
self.handle.as_raw_fd()
|
||||
@ -278,26 +301,29 @@ impl XenCall {
|
||||
HYPERVISOR_XEN_VERSION,
|
||||
XENVER_CAPABILITIES,
|
||||
addr_of_mut!(info) as c_ulong,
|
||||
)?;
|
||||
)
|
||||
.await?;
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
pub fn evtchn_op(&self, cmd: c_int, arg: u64) -> Result<()> {
|
||||
self.hypercall2(HYPERVISOR_EVENT_CHANNEL_OP, cmd as c_ulong, arg)?;
|
||||
pub async fn evtchn_op(&self, cmd: c_int, arg: u64) -> Result<()> {
|
||||
self.hypercall2(HYPERVISOR_EVENT_CHANNEL_OP, cmd as c_ulong, arg)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn evtchn_alloc_unbound(&self, domid: u32, remote_domid: u32) -> Result<u32> {
|
||||
pub async fn evtchn_alloc_unbound(&self, domid: u32, remote_domid: u32) -> Result<u32> {
|
||||
let mut alloc_unbound = EvtChnAllocUnbound {
|
||||
dom: domid as u16,
|
||||
remote_dom: remote_domid as u16,
|
||||
port: 0,
|
||||
};
|
||||
self.evtchn_op(6, addr_of_mut!(alloc_unbound) as c_ulong)?;
|
||||
self.evtchn_op(6, addr_of_mut!(alloc_unbound) as c_ulong)
|
||||
.await?;
|
||||
Ok(alloc_unbound.port)
|
||||
}
|
||||
|
||||
pub fn get_domain_info(&self, domid: u32) -> Result<GetDomainInfo> {
|
||||
pub async fn get_domain_info(&self, domid: u32) -> Result<GetDomainInfo> {
|
||||
trace!(
|
||||
"domctl fd={} get_domain_info domid={}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -311,11 +337,12 @@ impl XenCall {
|
||||
get_domain_info: GetDomainInfo::default(),
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||
.await?;
|
||||
Ok(unsafe { domctl.value.get_domain_info })
|
||||
}
|
||||
|
||||
pub fn create_domain(&self, create_domain: CreateDomain) -> Result<u32> {
|
||||
pub async fn create_domain(&self, create_domain: CreateDomain) -> Result<u32> {
|
||||
trace!(
|
||||
"domctl fd={} create_domain create_domain={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -327,11 +354,12 @@ impl XenCall {
|
||||
domid: 0,
|
||||
value: DomCtlValue { create_domain },
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||
.await?;
|
||||
Ok(domctl.domid)
|
||||
}
|
||||
|
||||
pub fn pause_domain(&self, domid: u32) -> Result<()> {
|
||||
pub async fn pause_domain(&self, domid: u32) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} pause_domain domid={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -343,11 +371,12 @@ impl XenCall {
|
||||
domid,
|
||||
value: DomCtlValue { pad: [0; 128] },
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unpause_domain(&self, domid: u32) -> Result<()> {
|
||||
pub async fn unpause_domain(&self, domid: u32) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} unpause_domain domid={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -359,11 +388,12 @@ impl XenCall {
|
||||
domid,
|
||||
value: DomCtlValue { pad: [0; 128] },
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_max_mem(&self, domid: u32, memkb: u64) -> Result<()> {
|
||||
pub async fn set_max_mem(&self, domid: u32, memkb: u64) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} set_max_mem domid={} memkb={}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -378,11 +408,12 @@ impl XenCall {
|
||||
max_mem: MaxMem { max_memkb: memkb },
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_max_vcpus(&self, domid: u32, max_vcpus: u32) -> Result<()> {
|
||||
pub async fn set_max_vcpus(&self, domid: u32, max_vcpus: u32) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} set_max_vcpus domid={} max_vcpus={}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -397,11 +428,12 @@ impl XenCall {
|
||||
max_cpus: MaxVcpus { max_vcpus },
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_address_size(&self, domid: u32, size: u32) -> Result<()> {
|
||||
pub async fn set_address_size(&self, domid: u32, size: u32) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} set_address_size domid={} size={}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -416,11 +448,12 @@ impl XenCall {
|
||||
address_size: AddressSize { size },
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_vcpu_context(&self, domid: u32, vcpu: u32) -> Result<VcpuGuestContext> {
|
||||
pub async fn get_vcpu_context(&self, domid: u32, vcpu: u32) -> Result<VcpuGuestContext> {
|
||||
trace!(
|
||||
"domctl fd={} get_vcpu_context domid={}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -440,11 +473,12 @@ impl XenCall {
|
||||
},
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||
.await?;
|
||||
Ok(unsafe { wrapper.value })
|
||||
}
|
||||
|
||||
pub fn set_vcpu_context(
|
||||
pub async fn set_vcpu_context(
|
||||
&self,
|
||||
domid: u32,
|
||||
vcpu: u32,
|
||||
@ -469,11 +503,12 @@ impl XenCall {
|
||||
},
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_page_frame_info(&self, domid: u32, frames: &[u64]) -> Result<Vec<u64>> {
|
||||
pub async fn get_page_frame_info(&self, domid: u32, frames: &[u64]) -> Result<Vec<u64>> {
|
||||
let mut buffer: Vec<u64> = frames.to_vec();
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_GETPAGEFRAMEINFO3,
|
||||
@ -486,7 +521,8 @@ impl XenCall {
|
||||
},
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||
.await?;
|
||||
let slice = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
domctl.value.get_page_frame_info.array as *mut u64,
|
||||
@ -496,7 +532,7 @@ impl XenCall {
|
||||
Ok(slice.to_vec())
|
||||
}
|
||||
|
||||
pub fn hypercall_init(&self, domid: u32, gmfn: u64) -> Result<()> {
|
||||
pub async fn hypercall_init(&self, domid: u32, gmfn: u64) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} hypercall_init domid={} gmfn={}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -511,11 +547,12 @@ impl XenCall {
|
||||
hypercall_init: HypercallInit { gmfn },
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn destroy_domain(&self, domid: u32) -> Result<()> {
|
||||
pub async fn destroy_domain(&self, domid: u32) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} destroy_domain domid={}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -527,11 +564,12 @@ impl XenCall {
|
||||
domid,
|
||||
value: DomCtlValue { pad: [0; 128] },
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_memory_map(&self, size_of_entry: usize) -> Result<Vec<u8>> {
|
||||
pub async fn get_memory_map(&self, size_of_entry: usize) -> Result<Vec<u8>> {
|
||||
let mut memory_map = MemoryMap {
|
||||
count: 0,
|
||||
buffer: 0,
|
||||
@ -540,18 +578,20 @@ impl XenCall {
|
||||
HYPERVISOR_MEMORY_OP,
|
||||
XEN_MEM_MEMORY_MAP as c_ulong,
|
||||
addr_of_mut!(memory_map) as c_ulong,
|
||||
)?;
|
||||
)
|
||||
.await?;
|
||||
let mut buffer = vec![0u8; memory_map.count as usize * size_of_entry];
|
||||
memory_map.buffer = buffer.as_mut_ptr() as c_ulong;
|
||||
self.hypercall2(
|
||||
HYPERVISOR_MEMORY_OP,
|
||||
XEN_MEM_MEMORY_MAP as c_ulong,
|
||||
addr_of_mut!(memory_map) as c_ulong,
|
||||
)?;
|
||||
)
|
||||
.await?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn populate_physmap(
|
||||
pub async fn populate_physmap(
|
||||
&self,
|
||||
domid: u32,
|
||||
nr_extents: u64,
|
||||
@ -583,7 +623,7 @@ impl XenCall {
|
||||
0,
|
||||
],
|
||||
}];
|
||||
self.multicall(calls)?;
|
||||
self.multicall(calls).await?;
|
||||
let code = calls[0].result;
|
||||
if code > !0xfff {
|
||||
return Err(Error::PopulatePhysmapFailed);
|
||||
@ -595,7 +635,7 @@ impl XenCall {
|
||||
Ok(extents)
|
||||
}
|
||||
|
||||
pub fn claim_pages(&self, domid: u32, pages: u64) -> Result<()> {
|
||||
pub async fn claim_pages(&self, domid: u32, pages: u64) -> Result<()> {
|
||||
trace!(
|
||||
"memory fd={} claim_pages domid={} pages={}",
|
||||
self.handle.as_raw_fd(),
|
||||
@ -613,11 +653,12 @@ impl XenCall {
|
||||
HYPERVISOR_MEMORY_OP,
|
||||
XEN_MEM_CLAIM_PAGES as c_ulong,
|
||||
addr_of_mut!(reservation) as c_ulong,
|
||||
)?;
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mmuext(&self, domid: u32, cmd: c_uint, arg1: u64, arg2: u64) -> Result<()> {
|
||||
pub async fn mmuext(&self, domid: u32, cmd: c_uint, arg1: u64, arg2: u64) -> Result<()> {
|
||||
let mut ops = MmuExtOp { cmd, arg1, arg2 };
|
||||
|
||||
self.hypercall4(
|
||||
@ -627,6 +668,7 @@ impl XenCall {
|
||||
0,
|
||||
domid as c_ulong,
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
@ -2,20 +2,22 @@
|
||||
name = "krata-xenclient"
|
||||
description = "An implementation of Xen userspace for krata."
|
||||
license.workspace = true
|
||||
version= "0.0.3"
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
elf = { workspace = true }
|
||||
flate2 = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
krata-xencall = { path = "../xencall", version = "^0.0.3" }
|
||||
krata-xenstore = { path = "../xenstore", version = "^0.0.3" }
|
||||
krata-xencall = { path = "../xencall", version = "^0.0.5" }
|
||||
krata-xenstore = { path = "../xenstore", version = "^0.0.5" }
|
||||
memchr = { workspace = true }
|
||||
nix = { workspace = true }
|
||||
slice-copy = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
@ -24,6 +26,7 @@ xz2 = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
||||
[lib]
|
||||
name = "xenclient"
|
||||
|
@ -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(0).await?;
|
||||
let client = XenClient::open(0).await?;
|
||||
let config = DomainConfig {
|
||||
backend_domid: 0,
|
||||
name: "xenclient-test",
|
||||
@ -22,6 +22,7 @@ async fn main() -> Result<()> {
|
||||
kernel_path: kernel_image_path.as_str(),
|
||||
initrd_path: initrd_path.as_str(),
|
||||
cmdline: "debug elevator=noop",
|
||||
use_console_backend: None,
|
||||
disks: vec![],
|
||||
channels: vec![],
|
||||
vifs: vec![],
|
||||
|
@ -41,9 +41,9 @@ impl Arm64BootSetup {
|
||||
Arm64BootSetup {}
|
||||
}
|
||||
|
||||
fn populate_one_size(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
async fn populate_one_size(
|
||||
&self,
|
||||
setup: &mut BootSetup<'_>,
|
||||
pfn_shift: u64,
|
||||
base_pfn: u64,
|
||||
pfn_count: u64,
|
||||
@ -78,20 +78,23 @@ impl Arm64BootSetup {
|
||||
extents[i as usize] = base_pfn + (i << pfn_shift);
|
||||
}
|
||||
|
||||
let result_extents = setup.call.populate_physmap(
|
||||
setup.domid,
|
||||
count,
|
||||
pfn_shift as u32,
|
||||
0,
|
||||
&extents[0usize..count as usize],
|
||||
)?;
|
||||
let result_extents = setup
|
||||
.call
|
||||
.populate_physmap(
|
||||
setup.domid,
|
||||
count,
|
||||
pfn_shift as u32,
|
||||
0,
|
||||
&extents[0usize..count as usize],
|
||||
)
|
||||
.await?;
|
||||
slice_copy::copy(extents, &result_extents);
|
||||
Ok((result_extents.len() as u64) << pfn_shift)
|
||||
}
|
||||
|
||||
fn populate_guest_memory(
|
||||
async fn populate_guest_memory(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
setup: &mut BootSetup<'_>,
|
||||
base_pfn: u64,
|
||||
pfn_count: u64,
|
||||
) -> Result<()> {
|
||||
@ -99,43 +102,51 @@ impl Arm64BootSetup {
|
||||
|
||||
for pfn in 0..extents.len() {
|
||||
let mut allocsz = (1024 * 1024).min(pfn_count - pfn as u64);
|
||||
allocsz = self.populate_one_size(
|
||||
setup,
|
||||
PFN_512G_SHIFT,
|
||||
base_pfn + pfn as u64,
|
||||
allocsz,
|
||||
&mut extents,
|
||||
)?;
|
||||
allocsz = self
|
||||
.populate_one_size(
|
||||
setup,
|
||||
PFN_512G_SHIFT,
|
||||
base_pfn + pfn as u64,
|
||||
allocsz,
|
||||
&mut extents,
|
||||
)
|
||||
.await?;
|
||||
if allocsz > 0 {
|
||||
continue;
|
||||
}
|
||||
allocsz = self.populate_one_size(
|
||||
setup,
|
||||
PFN_1G_SHIFT,
|
||||
base_pfn + pfn as u64,
|
||||
allocsz,
|
||||
&mut extents,
|
||||
)?;
|
||||
allocsz = self
|
||||
.populate_one_size(
|
||||
setup,
|
||||
PFN_1G_SHIFT,
|
||||
base_pfn + pfn as u64,
|
||||
allocsz,
|
||||
&mut extents,
|
||||
)
|
||||
.await?;
|
||||
if allocsz > 0 {
|
||||
continue;
|
||||
}
|
||||
allocsz = self.populate_one_size(
|
||||
setup,
|
||||
PFN_2M_SHIFT,
|
||||
base_pfn + pfn as u64,
|
||||
allocsz,
|
||||
&mut extents,
|
||||
)?;
|
||||
allocsz = self
|
||||
.populate_one_size(
|
||||
setup,
|
||||
PFN_2M_SHIFT,
|
||||
base_pfn + pfn as u64,
|
||||
allocsz,
|
||||
&mut extents,
|
||||
)
|
||||
.await?;
|
||||
if allocsz > 0 {
|
||||
continue;
|
||||
}
|
||||
allocsz = self.populate_one_size(
|
||||
setup,
|
||||
PFN_4K_SHIFT,
|
||||
base_pfn + pfn as u64,
|
||||
allocsz,
|
||||
&mut extents,
|
||||
)?;
|
||||
allocsz = self
|
||||
.populate_one_size(
|
||||
setup,
|
||||
PFN_4K_SHIFT,
|
||||
base_pfn + pfn as u64,
|
||||
allocsz,
|
||||
&mut extents,
|
||||
)
|
||||
.await?;
|
||||
if allocsz == 0 {
|
||||
return Err(Error::MemorySetupFailed("allocsz is zero"));
|
||||
}
|
||||
@ -145,6 +156,7 @@ impl Arm64BootSetup {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ArchBootSetup for Arm64BootSetup {
|
||||
fn page_size(&mut self) -> u64 {
|
||||
ARM_PAGE_SIZE
|
||||
@ -158,15 +170,15 @@ impl ArchBootSetup for Arm64BootSetup {
|
||||
true
|
||||
}
|
||||
|
||||
fn setup_shared_info(&mut self, _: &mut BootSetup, _: u64) -> Result<()> {
|
||||
async fn setup_shared_info(&mut self, _: &mut BootSetup, _: u64) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_start_info(&mut self, _: &mut BootSetup, _: &BootState, _: &str) -> Result<()> {
|
||||
async fn setup_start_info(&mut self, _: &mut BootSetup, _: &BootState, _: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn meminit(
|
||||
async fn meminit(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
total_pages: u64,
|
||||
@ -176,7 +188,7 @@ impl ArchBootSetup for Arm64BootSetup {
|
||||
let kernel_segment = kernel_segment
|
||||
.as_ref()
|
||||
.ok_or(Error::MemorySetupFailed("kernel_segment missing"))?;
|
||||
setup.call.claim_pages(setup.domid, total_pages)?;
|
||||
setup.call.claim_pages(setup.domid, total_pages).await?;
|
||||
let mut ramsize = total_pages << XEN_PAGE_SHIFT;
|
||||
|
||||
let bankbase = GUEST_RAM_BANK_BASES;
|
||||
@ -214,7 +226,8 @@ impl ArchBootSetup for Arm64BootSetup {
|
||||
}
|
||||
|
||||
for i in 0..2 {
|
||||
self.populate_guest_memory(setup, bankbase[i] >> XEN_PAGE_SHIFT, rambank_size[i])?;
|
||||
self.populate_guest_memory(setup, bankbase[i] >> XEN_PAGE_SHIFT, rambank_size[i])
|
||||
.await?;
|
||||
}
|
||||
|
||||
let bank0end = bankbase[0] + (rambank_size[0] << XEN_PAGE_SHIFT);
|
||||
@ -227,15 +240,15 @@ impl ArchBootSetup for Arm64BootSetup {
|
||||
} else {
|
||||
return Err(Error::MemorySetupFailed("unable to determine modbase"));
|
||||
};
|
||||
setup.call.claim_pages(setup.domid, 0)?;
|
||||
setup.call.claim_pages(setup.domid, 0).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bootlate(&mut self, _: &mut BootSetup, _: &mut BootState) -> Result<()> {
|
||||
async fn bootlate(&mut self, _: &mut BootSetup, _: &mut BootState) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
async fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
let mut vcpu = VcpuGuestContext::default();
|
||||
vcpu.user_regs.pc = state.image_info.virt_entry;
|
||||
vcpu.user_regs.x0 = 0xffffffff;
|
||||
@ -249,11 +262,11 @@ impl ArchBootSetup for Arm64BootSetup {
|
||||
vcpu.user_regs.cpsr = PSR_GUEST64_INIT;
|
||||
vcpu.flags = 1 << 0; // VGCF_ONLINE
|
||||
trace!("vcpu context: {:?}", vcpu);
|
||||
setup.call.set_vcpu_context(setup.domid, 0, &vcpu)?;
|
||||
setup.call.set_vcpu_context(setup.domid, 0, &vcpu).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn alloc_p2m_segment(
|
||||
async fn alloc_p2m_segment(
|
||||
&mut self,
|
||||
_: &mut BootSetup,
|
||||
_: &BootImageInfo,
|
||||
@ -261,7 +274,7 @@ impl ArchBootSetup for Arm64BootSetup {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn alloc_page_tables(
|
||||
async fn alloc_page_tables(
|
||||
&mut self,
|
||||
_: &mut BootSetup,
|
||||
_: &BootImageInfo,
|
||||
@ -269,7 +282,7 @@ impl ArchBootSetup for Arm64BootSetup {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn setup_page_tables(&mut self, _: &mut BootSetup, _: &mut BootState) -> Result<()> {
|
||||
async fn setup_page_tables(&mut self, _: &mut BootSetup, _: &mut BootState) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ use crate::sys::{GrantEntry, XEN_PAGE_SHIFT};
|
||||
use crate::Error;
|
||||
use libc::munmap;
|
||||
use log::debug;
|
||||
use nix::errno::Errno;
|
||||
use slice_copy::copy;
|
||||
|
||||
use crate::mem::ARCH_PAGE_SHIFT;
|
||||
@ -83,33 +84,34 @@ impl BootSetup<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_memory(
|
||||
async fn initialize_memory(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
arch: &mut Box<dyn ArchBootSetup + Send + Sync>,
|
||||
total_pages: u64,
|
||||
kernel_segment: &Option<DomainSegment>,
|
||||
initrd_segment: &Option<DomainSegment>,
|
||||
) -> Result<()> {
|
||||
self.call.set_address_size(self.domid, 64)?;
|
||||
arch.meminit(self, total_pages, kernel_segment, initrd_segment)?;
|
||||
self.call.set_address_size(self.domid, 64).await?;
|
||||
arch.meminit(self, total_pages, kernel_segment, initrd_segment)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_hypercall_page(&mut self, image_info: &BootImageInfo) -> Result<()> {
|
||||
async fn setup_hypercall_page(&mut self, image_info: &BootImageInfo) -> Result<()> {
|
||||
if image_info.virt_hypercall == XEN_UNSET_ADDR {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let pfn = (image_info.virt_hypercall - image_info.virt_base) >> ARCH_PAGE_SHIFT;
|
||||
let mfn = self.phys.p2m[pfn as usize];
|
||||
self.call.hypercall_init(self.domid, mfn)?;
|
||||
self.call.hypercall_init(self.domid, mfn).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn initialize(
|
||||
pub async fn initialize<I: BootImageLoader + Send + Sync>(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
image_loader: &dyn BootImageLoader,
|
||||
arch: &mut Box<dyn ArchBootSetup + Send + Sync>,
|
||||
image_loader: &I,
|
||||
initrd: &[u8],
|
||||
max_vcpus: u32,
|
||||
mem_mb: u64,
|
||||
@ -117,60 +119,68 @@ impl BootSetup<'_> {
|
||||
) -> Result<BootState> {
|
||||
debug!("initialize max_vcpus={:?} mem_mb={:?}", max_vcpus, mem_mb);
|
||||
|
||||
let page_size = arch.page_size();
|
||||
let image_info = image_loader.parse()?;
|
||||
debug!("initialize image_info={:?}", image_info);
|
||||
let mut kernel_segment: Option<DomainSegment> = None;
|
||||
let mut initrd_segment: Option<DomainSegment> = None;
|
||||
if !image_info.unmapped_initrd {
|
||||
initrd_segment = Some(self.alloc_module(arch, initrd)?);
|
||||
initrd_segment = Some(self.alloc_module(page_size, initrd).await?);
|
||||
}
|
||||
|
||||
if arch.needs_early_kernel() {
|
||||
kernel_segment = Some(self.load_kernel_segment(arch, image_loader, &image_info)?);
|
||||
kernel_segment = Some(
|
||||
self.load_kernel_segment(page_size, image_loader, &image_info)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
|
||||
let total_pages = mem_mb << (20 - arch.page_shift());
|
||||
self.initialize_memory(arch, total_pages, &kernel_segment, &initrd_segment)?;
|
||||
self.initialize_memory(arch, total_pages, &kernel_segment, &initrd_segment)
|
||||
.await?;
|
||||
self.virt_alloc_end = image_info.virt_base;
|
||||
|
||||
if kernel_segment.is_none() {
|
||||
kernel_segment = Some(self.load_kernel_segment(arch, image_loader, &image_info)?);
|
||||
kernel_segment = Some(
|
||||
self.load_kernel_segment(page_size, image_loader, &image_info)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
|
||||
let mut p2m_segment: Option<DomainSegment> = None;
|
||||
if image_info.virt_p2m_base >= image_info.virt_base
|
||||
|| (image_info.virt_p2m_base & ((1 << arch.page_shift()) - 1)) != 0
|
||||
{
|
||||
p2m_segment = arch.alloc_p2m_segment(self, &image_info)?;
|
||||
p2m_segment = arch.alloc_p2m_segment(self, &image_info).await?;
|
||||
}
|
||||
let start_info_segment = self.alloc_page(arch)?;
|
||||
let xenstore_segment = self.alloc_page(arch)?;
|
||||
let start_info_segment = self.alloc_page(page_size)?;
|
||||
let xenstore_segment = self.alloc_page(page_size)?;
|
||||
let mut consoles: Vec<(u32, DomainSegment)> = Vec::new();
|
||||
for _ in 0..console_count {
|
||||
let evtchn = self.call.evtchn_alloc_unbound(self.domid, 0)?;
|
||||
let page = self.alloc_page(arch)?;
|
||||
let evtchn = self.call.evtchn_alloc_unbound(self.domid, 0).await?;
|
||||
let page = self.alloc_page(page_size)?;
|
||||
consoles.push((evtchn, page));
|
||||
}
|
||||
let page_table_segment = arch.alloc_page_tables(self, &image_info)?;
|
||||
let boot_stack_segment = self.alloc_page(arch)?;
|
||||
let page_table_segment = arch.alloc_page_tables(self, &image_info).await?;
|
||||
let boot_stack_segment = self.alloc_page(page_size)?;
|
||||
|
||||
if self.virt_pgtab_end > 0 {
|
||||
self.alloc_padding_pages(arch, self.virt_pgtab_end)?;
|
||||
self.alloc_padding_pages(page_size, self.virt_pgtab_end)?;
|
||||
}
|
||||
|
||||
if p2m_segment.is_none() {
|
||||
if let Some(mut segment) = arch.alloc_p2m_segment(self, &image_info)? {
|
||||
if let Some(mut segment) = arch.alloc_p2m_segment(self, &image_info).await? {
|
||||
segment.vstart = image_info.virt_p2m_base;
|
||||
p2m_segment = Some(segment);
|
||||
}
|
||||
}
|
||||
|
||||
if image_info.unmapped_initrd {
|
||||
initrd_segment = Some(self.alloc_module(arch, initrd)?);
|
||||
initrd_segment = Some(self.alloc_module(page_size, initrd).await?);
|
||||
}
|
||||
|
||||
let initrd_segment = initrd_segment.unwrap();
|
||||
let store_evtchn = self.call.evtchn_alloc_unbound(self.domid, 0)?;
|
||||
let store_evtchn = self.call.evtchn_alloc_unbound(self.domid, 0).await?;
|
||||
|
||||
let kernel_segment =
|
||||
kernel_segment.ok_or(Error::MemorySetupFailed("kernel_segment missing"))?;
|
||||
@ -192,35 +202,37 @@ impl BootSetup<'_> {
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub fn boot(
|
||||
pub async fn boot(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
arch: &mut Box<dyn ArchBootSetup + Send + Sync>,
|
||||
state: &mut BootState,
|
||||
cmdline: &str,
|
||||
) -> Result<()> {
|
||||
let domain_info = self.call.get_domain_info(self.domid)?;
|
||||
let domain_info = self.call.get_domain_info(self.domid).await?;
|
||||
let shared_info_frame = domain_info.shared_info_frame;
|
||||
state.shared_info_frame = shared_info_frame;
|
||||
arch.setup_page_tables(self, state)?;
|
||||
arch.setup_start_info(self, state, cmdline)?;
|
||||
self.setup_hypercall_page(&state.image_info)?;
|
||||
arch.bootlate(self, state)?;
|
||||
arch.setup_shared_info(self, state.shared_info_frame)?;
|
||||
arch.vcpu(self, state)?;
|
||||
arch.setup_page_tables(self, state).await?;
|
||||
arch.setup_start_info(self, state, cmdline).await?;
|
||||
self.setup_hypercall_page(&state.image_info).await?;
|
||||
arch.bootlate(self, state).await?;
|
||||
arch.setup_shared_info(self, state.shared_info_frame)
|
||||
.await?;
|
||||
arch.vcpu(self, state).await?;
|
||||
self.phys.unmap_all()?;
|
||||
self.gnttab_seed(state)?;
|
||||
self.gnttab_seed(state).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gnttab_seed(&mut self, state: &mut BootState) -> Result<()> {
|
||||
async fn gnttab_seed(&mut self, state: &mut BootState) -> Result<()> {
|
||||
let console_gfn =
|
||||
self.phys.p2m[state.consoles.first().map(|x| x.1.pfn).unwrap_or(0) as usize];
|
||||
let xenstore_gfn = self.phys.p2m[state.xenstore_segment.pfn as usize];
|
||||
let addr = self
|
||||
.call
|
||||
.mmap(0, 1 << XEN_PAGE_SHIFT)
|
||||
.await
|
||||
.ok_or(Error::MmapFailed)?;
|
||||
self.call.map_resource(self.domid, 1, 0, 0, 1, addr)?;
|
||||
self.call.map_resource(self.domid, 1, 0, 0, 1, addr).await?;
|
||||
let entries = unsafe { slice::from_raw_parts_mut(addr as *mut GrantEntry, 2) };
|
||||
entries[0].flags = 1 << 0;
|
||||
entries[0].domid = 0;
|
||||
@ -231,23 +243,25 @@ impl BootSetup<'_> {
|
||||
unsafe {
|
||||
let result = munmap(addr as *mut c_void, 1 << XEN_PAGE_SHIFT);
|
||||
if result != 0 {
|
||||
return Err(Error::UnmapFailed);
|
||||
return Err(Error::UnmapFailed(Errno::from_raw(result)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_kernel_segment(
|
||||
async fn load_kernel_segment<I: BootImageLoader + Send + Sync>(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
image_loader: &dyn BootImageLoader,
|
||||
page_size: u64,
|
||||
image_loader: &I,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<DomainSegment> {
|
||||
let kernel_segment = self.alloc_segment(
|
||||
arch,
|
||||
image_info.virt_kstart,
|
||||
image_info.virt_kend - image_info.virt_kstart,
|
||||
)?;
|
||||
let kernel_segment = self
|
||||
.alloc_segment(
|
||||
page_size,
|
||||
image_info.virt_kstart,
|
||||
image_info.virt_kend - image_info.virt_kstart,
|
||||
)
|
||||
.await?;
|
||||
let kernel_segment_ptr = kernel_segment.addr as *mut u8;
|
||||
let kernel_segment_slice =
|
||||
unsafe { slice::from_raw_parts_mut(kernel_segment_ptr, kernel_segment.size as usize) };
|
||||
@ -264,18 +278,19 @@ impl BootSetup<'_> {
|
||||
(1 << bits) - 1
|
||||
}
|
||||
|
||||
pub(crate) fn alloc_segment(
|
||||
pub(crate) async fn alloc_segment(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
page_size: u64,
|
||||
start: u64,
|
||||
size: u64,
|
||||
) -> Result<DomainSegment> {
|
||||
debug!("alloc_segment {:#x} {:#x}", start, size);
|
||||
if start > 0 {
|
||||
self.alloc_padding_pages(arch, start)?;
|
||||
self.alloc_padding_pages(page_size, start)?;
|
||||
}
|
||||
|
||||
let page_size: u32 = (1i64 << XEN_PAGE_SHIFT) as u32;
|
||||
let pages = (size + page_size as u64 - 1) / page_size as u64;
|
||||
let local_page_size: u32 = (1i64 << XEN_PAGE_SHIFT) as u32;
|
||||
let pages = (size + local_page_size as u64 - 1) / local_page_size as u64;
|
||||
let start = self.virt_alloc_end;
|
||||
|
||||
let mut segment = DomainSegment {
|
||||
@ -288,12 +303,12 @@ impl BootSetup<'_> {
|
||||
pages,
|
||||
};
|
||||
|
||||
self.chk_alloc_pages(arch, pages)?;
|
||||
self.chk_alloc_pages(page_size, pages)?;
|
||||
|
||||
let ptr = self.phys.pfn_to_ptr(segment.pfn, pages)?;
|
||||
let ptr = self.phys.pfn_to_ptr(segment.pfn, pages).await?;
|
||||
segment.addr = ptr;
|
||||
let slice = unsafe {
|
||||
slice::from_raw_parts_mut(ptr as *mut u8, (pages * page_size as u64) as usize)
|
||||
slice::from_raw_parts_mut(ptr as *mut u8, (pages * local_page_size as u64) as usize)
|
||||
};
|
||||
slice.fill(0);
|
||||
segment.vend = self.virt_alloc_end;
|
||||
@ -304,15 +319,15 @@ impl BootSetup<'_> {
|
||||
Ok(segment)
|
||||
}
|
||||
|
||||
fn alloc_page(&mut self, arch: &mut dyn ArchBootSetup) -> Result<DomainSegment> {
|
||||
fn alloc_page(&mut self, page_size: u64) -> Result<DomainSegment> {
|
||||
let start = self.virt_alloc_end;
|
||||
let pfn = self.pfn_alloc_end;
|
||||
|
||||
self.chk_alloc_pages(arch, 1)?;
|
||||
self.chk_alloc_pages(page_size, 1)?;
|
||||
debug!("alloc_page {:#x} (pfn {:#x})", start, pfn);
|
||||
Ok(DomainSegment {
|
||||
vstart: start,
|
||||
vend: (start + arch.page_size()) - 1,
|
||||
vend: (start + page_size) - 1,
|
||||
pfn,
|
||||
addr: 0,
|
||||
size: 0,
|
||||
@ -321,31 +336,29 @@ impl BootSetup<'_> {
|
||||
})
|
||||
}
|
||||
|
||||
fn alloc_module(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
buffer: &[u8],
|
||||
) -> Result<DomainSegment> {
|
||||
let segment = self.alloc_segment(arch, 0, buffer.len() as u64)?;
|
||||
async fn alloc_module(&mut self, page_size: u64, buffer: &[u8]) -> Result<DomainSegment> {
|
||||
let segment = self
|
||||
.alloc_segment(page_size, 0, buffer.len() as u64)
|
||||
.await?;
|
||||
let slice = unsafe { slice::from_raw_parts_mut(segment.addr as *mut u8, buffer.len()) };
|
||||
copy(slice, buffer);
|
||||
Ok(segment)
|
||||
}
|
||||
|
||||
fn alloc_padding_pages(&mut self, arch: &mut dyn ArchBootSetup, boundary: u64) -> Result<()> {
|
||||
if (boundary & (arch.page_size() - 1)) != 0 {
|
||||
fn alloc_padding_pages(&mut self, page_size: u64, boundary: u64) -> Result<()> {
|
||||
if (boundary & (page_size - 1)) != 0 {
|
||||
return Err(Error::MemorySetupFailed("boundary is incorrect"));
|
||||
}
|
||||
|
||||
if boundary < self.virt_alloc_end {
|
||||
return Err(Error::MemorySetupFailed("boundary is below allocation end"));
|
||||
}
|
||||
let pages = (boundary - self.virt_alloc_end) / arch.page_size();
|
||||
self.chk_alloc_pages(arch, pages)?;
|
||||
let pages = (boundary - self.virt_alloc_end) / page_size;
|
||||
self.chk_alloc_pages(page_size, pages)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn chk_alloc_pages(&mut self, arch: &mut dyn ArchBootSetup, pages: u64) -> Result<()> {
|
||||
fn chk_alloc_pages(&mut self, page_size: u64, pages: u64) -> Result<()> {
|
||||
if pages > self.total_pages
|
||||
|| self.pfn_alloc_end > self.total_pages
|
||||
|| pages > self.total_pages - self.pfn_alloc_end
|
||||
@ -354,47 +367,56 @@ impl BootSetup<'_> {
|
||||
}
|
||||
|
||||
self.pfn_alloc_end += pages;
|
||||
self.virt_alloc_end += pages * arch.page_size();
|
||||
self.virt_alloc_end += pages * page_size;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait ArchBootSetup {
|
||||
fn page_size(&mut self) -> u64;
|
||||
fn page_shift(&mut self) -> u64;
|
||||
|
||||
fn needs_early_kernel(&mut self) -> bool;
|
||||
|
||||
fn alloc_p2m_segment(
|
||||
async fn alloc_p2m_segment(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<Option<DomainSegment>>;
|
||||
|
||||
fn alloc_page_tables(
|
||||
async fn alloc_page_tables(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<Option<DomainSegment>>;
|
||||
|
||||
fn setup_page_tables(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>;
|
||||
async fn setup_page_tables(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
state: &mut BootState,
|
||||
) -> Result<()>;
|
||||
|
||||
fn setup_start_info(
|
||||
async fn setup_start_info(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
state: &BootState,
|
||||
cmdline: &str,
|
||||
) -> Result<()>;
|
||||
|
||||
fn setup_shared_info(&mut self, setup: &mut BootSetup, shared_info_frame: u64) -> Result<()>;
|
||||
async fn setup_shared_info(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
shared_info_frame: u64,
|
||||
) -> Result<()>;
|
||||
|
||||
fn meminit(
|
||||
async fn meminit(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
total_pages: u64,
|
||||
kernel_segment: &Option<DomainSegment>,
|
||||
initrd_segment: &Option<DomainSegment>,
|
||||
) -> Result<()>;
|
||||
fn bootlate(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>;
|
||||
fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>;
|
||||
async fn bootlate(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>;
|
||||
async fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>;
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ pub enum Error {
|
||||
ElfParseFailed(#[from] elf::ParseError),
|
||||
#[error("mmap failed")]
|
||||
MmapFailed,
|
||||
#[error("munmap failed")]
|
||||
UnmapFailed,
|
||||
#[error("munmap failed: {0}")]
|
||||
UnmapFailed(nix::errno::Errno),
|
||||
#[error("memory setup failed: {0}")]
|
||||
MemorySetupFailed(&'static str),
|
||||
#[error("populate physmap failed: wanted={0}, received={1}, input_extents={2}")]
|
||||
|
@ -16,7 +16,7 @@ pub mod arm64;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arm64::Arm64BootSetup;
|
||||
|
||||
use crate::boot::BootSetup;
|
||||
use crate::boot::{ArchBootSetup, BootSetup};
|
||||
use crate::elfloader::ElfImageLoader;
|
||||
use crate::error::{Error, Result};
|
||||
use boot::BootState;
|
||||
@ -34,6 +34,7 @@ use xenstore::{
|
||||
XsPermission, XsdClient, XsdInterface, XS_PERM_NONE, XS_PERM_READ, XS_PERM_READ_WRITE,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct XenClient {
|
||||
pub store: XsdClient,
|
||||
call: XenCall,
|
||||
@ -88,6 +89,7 @@ pub struct DomainConfig<'a> {
|
||||
pub initrd_path: &'a str,
|
||||
pub cmdline: &'a str,
|
||||
pub disks: Vec<DomainDisk<'a>>,
|
||||
pub use_console_backend: Option<&'a str>,
|
||||
pub channels: Vec<DomainChannel>,
|
||||
pub vifs: Vec<DomainNetworkInterface<'a>>,
|
||||
pub filesystems: Vec<DomainFilesystem<'a>>,
|
||||
@ -115,7 +117,7 @@ impl XenClient {
|
||||
Ok(XenClient { store, call })
|
||||
}
|
||||
|
||||
pub async fn create(&mut self, config: &DomainConfig<'_>) -> Result<CreatedDomain> {
|
||||
pub async fn create(&self, config: &DomainConfig<'_>) -> Result<CreatedDomain> {
|
||||
let mut domain = CreateDomain {
|
||||
max_vcpus: config.max_vcpus,
|
||||
..Default::default()
|
||||
@ -125,7 +127,7 @@ impl XenClient {
|
||||
domain.flags = XEN_DOMCTL_CDF_HVM_GUEST | XEN_DOMCTL_CDF_HAP;
|
||||
}
|
||||
|
||||
let domid = self.call.create_domain(domain)?;
|
||||
let domid = self.call.create_domain(domain).await?;
|
||||
match self.init(domid, &domain, config).await {
|
||||
Ok(created) => Ok(created),
|
||||
Err(err) => {
|
||||
@ -138,7 +140,7 @@ impl XenClient {
|
||||
}
|
||||
|
||||
async fn init(
|
||||
&mut self,
|
||||
&self,
|
||||
domid: u32,
|
||||
domain: &CreateDomain,
|
||||
config: &DomainConfig<'_>,
|
||||
@ -253,8 +255,8 @@ impl XenClient {
|
||||
tx.commit().await?;
|
||||
}
|
||||
|
||||
self.call.set_max_vcpus(domid, config.max_vcpus)?;
|
||||
self.call.set_max_mem(domid, config.mem_mb * 1024)?;
|
||||
self.call.set_max_vcpus(domid, config.max_vcpus).await?;
|
||||
self.call.set_max_mem(domid, config.mem_mb * 1024).await?;
|
||||
let image_loader = ElfImageLoader::load_file_kernel(config.kernel_path)?;
|
||||
|
||||
let xenstore_evtchn: u32;
|
||||
@ -265,19 +267,21 @@ impl XenClient {
|
||||
{
|
||||
let mut boot = BootSetup::new(&self.call, domid);
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let mut arch = X86BootSetup::new();
|
||||
let mut arch = Box::new(X86BootSetup::new()) as Box<dyn ArchBootSetup + Send + Sync>;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let mut arch = Arm64BootSetup::new();
|
||||
let mut arch = Box::new(Arm64BootSetup::new()) as Box<dyn ArchBootSetup + Send + Sync>;
|
||||
let initrd = read(config.initrd_path)?;
|
||||
state = boot.initialize(
|
||||
&mut arch,
|
||||
&image_loader,
|
||||
initrd.as_slice(),
|
||||
config.max_vcpus,
|
||||
config.mem_mb,
|
||||
1,
|
||||
)?;
|
||||
boot.boot(&mut arch, &mut state, config.cmdline)?;
|
||||
state = boot
|
||||
.initialize(
|
||||
&mut arch,
|
||||
&image_loader,
|
||||
initrd.as_slice(),
|
||||
config.max_vcpus,
|
||||
config.mem_mb,
|
||||
1,
|
||||
)
|
||||
.await?;
|
||||
boot.boot(&mut arch, &mut state, config.cmdline).await?;
|
||||
xenstore_evtchn = state.store_evtchn;
|
||||
xenstore_mfn = boot.phys.p2m[state.xenstore_segment.pfn as usize];
|
||||
p2m = boot.phys.p2m;
|
||||
@ -346,7 +350,10 @@ impl XenClient {
|
||||
}
|
||||
self.console_device_add(
|
||||
&DomainChannel {
|
||||
typ: "xenconsoled".to_string(),
|
||||
typ: config
|
||||
.use_console_backend
|
||||
.unwrap_or("xenconsoled")
|
||||
.to_string(),
|
||||
initialized: true,
|
||||
},
|
||||
&p2m,
|
||||
@ -418,7 +425,8 @@ impl XenClient {
|
||||
for channel in &config.event_channels {
|
||||
let id = self
|
||||
.call
|
||||
.evtchn_alloc_unbound(domid, config.backend_domid)?;
|
||||
.evtchn_alloc_unbound(domid, config.backend_domid)
|
||||
.await?;
|
||||
let channel_path = format!("{}/evtchn/{}", dom_path, channel.name);
|
||||
self.store
|
||||
.write_string(&format!("{}/name", channel_path), channel.name)
|
||||
@ -428,12 +436,12 @@ impl XenClient {
|
||||
.await?;
|
||||
}
|
||||
|
||||
self.call.unpause_domain(domid)?;
|
||||
self.call.unpause_domain(domid).await?;
|
||||
Ok(CreatedDomain { domid, channels })
|
||||
}
|
||||
|
||||
async fn disk_device_add(
|
||||
&mut self,
|
||||
&self,
|
||||
dom_path: &str,
|
||||
backend_dom_path: &str,
|
||||
backend_domid: u32,
|
||||
@ -486,7 +494,7 @@ impl XenClient {
|
||||
|
||||
#[allow(clippy::too_many_arguments, clippy::unnecessary_unwrap)]
|
||||
async fn console_device_add(
|
||||
&mut self,
|
||||
&self,
|
||||
channel: &DomainChannel,
|
||||
p2m: &[u64],
|
||||
state: &BootState,
|
||||
@ -553,7 +561,7 @@ impl XenClient {
|
||||
}
|
||||
|
||||
async fn fs_9p_device_add(
|
||||
&mut self,
|
||||
&self,
|
||||
dom_path: &str,
|
||||
backend_dom_path: &str,
|
||||
backend_domid: u32,
|
||||
@ -591,7 +599,7 @@ impl XenClient {
|
||||
}
|
||||
|
||||
async fn vif_device_add(
|
||||
&mut self,
|
||||
&self,
|
||||
dom_path: &str,
|
||||
backend_dom_path: &str,
|
||||
backend_domid: u32,
|
||||
@ -650,7 +658,7 @@ impl XenClient {
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn device_add(
|
||||
&mut self,
|
||||
&self,
|
||||
typ: &str,
|
||||
id: u64,
|
||||
dom_path: &str,
|
||||
@ -713,15 +721,15 @@ impl XenClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn destroy(&mut self, domid: u32) -> Result<()> {
|
||||
pub async fn destroy(&self, domid: u32) -> Result<()> {
|
||||
if let Err(err) = self.destroy_store(domid).await {
|
||||
warn!("failed to destroy store for domain {}: {}", domid, err);
|
||||
}
|
||||
self.call.destroy_domain(domid)?;
|
||||
self.call.destroy_domain(domid).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn destroy_store(&mut self, domid: u32) -> Result<()> {
|
||||
async fn destroy_store(&self, domid: u32) -> Result<()> {
|
||||
let dom_path = self.store.get_domain_path(domid).await?;
|
||||
let vm_path = self.store.read_string(&format!("{}/vm", dom_path)).await?;
|
||||
if vm_path.is_none() {
|
||||
@ -813,7 +821,7 @@ impl XenClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_console_path(&mut self, domid: u32) -> Result<String> {
|
||||
pub async fn get_console_path(&self, domid: u32) -> Result<String> {
|
||||
let dom_path = self.store.get_domain_path(domid).await?;
|
||||
let console_tty_path = format!("{}/console/tty", dom_path);
|
||||
let mut tty: Option<String> = None;
|
||||
|
@ -3,6 +3,7 @@ use crate::sys::{XEN_PAGE_SHIFT, XEN_PAGE_SIZE};
|
||||
use crate::Error;
|
||||
use libc::munmap;
|
||||
use log::debug;
|
||||
use nix::errno::Errno;
|
||||
use std::ffi::c_void;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
@ -45,7 +46,7 @@ impl PhysicalPages<'_> {
|
||||
self.p2m.len() as u64
|
||||
}
|
||||
|
||||
pub fn pfn_to_ptr(&mut self, pfn: u64, count: u64) -> Result<u64> {
|
||||
pub async fn pfn_to_ptr(&mut self, pfn: u64, count: u64) -> Result<u64> {
|
||||
for page in &self.pages {
|
||||
if pfn >= page.pfn + page.count {
|
||||
continue;
|
||||
@ -76,10 +77,10 @@ impl PhysicalPages<'_> {
|
||||
return Err(Error::MemorySetupFailed("page count is zero"));
|
||||
}
|
||||
|
||||
self.pfn_alloc(pfn, count)
|
||||
self.pfn_alloc(pfn, count).await
|
||||
}
|
||||
|
||||
fn pfn_alloc(&mut self, pfn: u64, count: u64) -> Result<u64> {
|
||||
async fn pfn_alloc(&mut self, pfn: u64, count: u64) -> Result<u64> {
|
||||
let mut entries = vec![MmapEntry::default(); count as usize];
|
||||
for (i, entry) in entries.iter_mut().enumerate() {
|
||||
entry.mfn = self.p2m[pfn as usize + i];
|
||||
@ -98,9 +99,13 @@ impl PhysicalPages<'_> {
|
||||
let addr = self
|
||||
.call
|
||||
.mmap(0, actual_mmap_len)
|
||||
.await
|
||||
.ok_or(Error::MmapFailed)?;
|
||||
debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr);
|
||||
let result = self.call.mmap_batch(self.domid, num as u64, addr, pfns)?;
|
||||
let result = self
|
||||
.call
|
||||
.mmap_batch(self.domid, num as u64, addr, pfns)
|
||||
.await?;
|
||||
if result != 0 {
|
||||
return Err(Error::MmapFailed);
|
||||
}
|
||||
@ -117,7 +122,7 @@ impl PhysicalPages<'_> {
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
pub fn map_foreign_pages(&mut self, mfn: u64, size: u64) -> Result<u64> {
|
||||
pub async fn map_foreign_pages(&mut self, mfn: u64, size: u64) -> Result<u64> {
|
||||
let num = ((size + XEN_PAGE_SIZE - 1) >> XEN_PAGE_SHIFT) as usize;
|
||||
let mut pfns = vec![u64::MAX; num];
|
||||
for (i, item) in pfns.iter_mut().enumerate().take(num) {
|
||||
@ -128,9 +133,13 @@ impl PhysicalPages<'_> {
|
||||
let addr = self
|
||||
.call
|
||||
.mmap(0, actual_mmap_len)
|
||||
.await
|
||||
.ok_or(Error::MmapFailed)?;
|
||||
debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr);
|
||||
let result = self.call.mmap_batch(self.domid, num as u64, addr, pfns)?;
|
||||
let result = self
|
||||
.call
|
||||
.mmap_batch(self.domid, num as u64, addr, pfns)
|
||||
.await?;
|
||||
if result != 0 {
|
||||
return Err(Error::MmapFailed);
|
||||
}
|
||||
@ -155,7 +164,7 @@ impl PhysicalPages<'_> {
|
||||
(page.count << ARCH_PAGE_SHIFT) as usize,
|
||||
);
|
||||
if err != 0 {
|
||||
return Err(Error::UnmapFailed);
|
||||
return Err(Error::UnmapFailed(Errno::from_raw(err)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -181,7 +190,7 @@ impl PhysicalPages<'_> {
|
||||
page.ptr
|
||||
);
|
||||
if err != 0 {
|
||||
return Err(Error::UnmapFailed);
|
||||
return Err(Error::UnmapFailed(Errno::from_raw(err)));
|
||||
}
|
||||
self.pages.remove(i);
|
||||
}
|
||||
|
@ -275,6 +275,7 @@ impl X86BootSetup {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ArchBootSetup for X86BootSetup {
|
||||
fn page_size(&mut self) -> u64 {
|
||||
X86_PAGE_SIZE
|
||||
@ -288,7 +289,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
false
|
||||
}
|
||||
|
||||
fn alloc_p2m_segment(
|
||||
async fn alloc_p2m_segment(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
@ -310,11 +311,13 @@ impl ArchBootSetup for X86BootSetup {
|
||||
}
|
||||
self.table.mappings_count += 1;
|
||||
p2m_alloc_size += (pgtables << X86_PAGE_SHIFT) as u64;
|
||||
let p2m_segment = setup.alloc_segment(self, 0, p2m_alloc_size)?;
|
||||
let p2m_segment = setup
|
||||
.alloc_segment(self.page_size(), 0, p2m_alloc_size)
|
||||
.await?;
|
||||
Ok(Some(p2m_segment))
|
||||
}
|
||||
|
||||
fn alloc_page_tables(
|
||||
async fn alloc_page_tables(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
@ -341,7 +344,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
self.table.mappings_count += 1;
|
||||
setup.virt_pgtab_end = try_virt_end + 1;
|
||||
let size = self.table.mappings[m].area.pgtables as u64 * X86_PAGE_SIZE;
|
||||
let segment = setup.alloc_segment(self, 0, size)?;
|
||||
let segment = setup.alloc_segment(self.page_size(), 0, size).await?;
|
||||
debug!(
|
||||
"alloc_page_tables table={:?} segment={:?}",
|
||||
self.table, segment
|
||||
@ -349,7 +352,11 @@ impl ArchBootSetup for X86BootSetup {
|
||||
Ok(Some(segment))
|
||||
}
|
||||
|
||||
fn setup_page_tables(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
async fn setup_page_tables(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
state: &mut BootState,
|
||||
) -> Result<()> {
|
||||
let p2m_segment = state
|
||||
.p2m_segment
|
||||
.as_ref()
|
||||
@ -364,7 +371,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
let map1 = &self.table.mappings[m1];
|
||||
let from = map1.levels[l].from;
|
||||
let to = map1.levels[l].to;
|
||||
let pg_ptr = setup.phys.pfn_to_ptr(map1.levels[l].pfn, 0)? as *mut u64;
|
||||
let pg_ptr = setup.phys.pfn_to_ptr(map1.levels[l].pfn, 0).await? as *mut u64;
|
||||
for m2 in 0usize..self.table.mappings_count {
|
||||
let map2 = &self.table.mappings[m2];
|
||||
let lvl = if l > 0 {
|
||||
@ -407,13 +414,16 @@ impl ArchBootSetup for X86BootSetup {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_start_info(
|
||||
async fn setup_start_info(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
state: &BootState,
|
||||
cmdline: &str,
|
||||
) -> Result<()> {
|
||||
let ptr = setup.phys.pfn_to_ptr(state.start_info_segment.pfn, 1)?;
|
||||
let ptr = setup
|
||||
.phys
|
||||
.pfn_to_ptr(state.start_info_segment.pfn, 1)
|
||||
.await?;
|
||||
let byte_slice =
|
||||
unsafe { slice::from_raw_parts_mut(ptr as *mut u8, X86_PAGE_SIZE as usize) };
|
||||
byte_slice.fill(0);
|
||||
@ -456,11 +466,15 @@ impl ArchBootSetup for X86BootSetup {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_shared_info(&mut self, setup: &mut BootSetup, shared_info_frame: u64) -> Result<()> {
|
||||
async fn setup_shared_info(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
shared_info_frame: u64,
|
||||
) -> Result<()> {
|
||||
let info = setup
|
||||
.phys
|
||||
.map_foreign_pages(shared_info_frame, X86_PAGE_SIZE)?
|
||||
as *mut SharedInfo;
|
||||
.map_foreign_pages(shared_info_frame, X86_PAGE_SIZE)
|
||||
.await? as *mut SharedInfo;
|
||||
unsafe {
|
||||
let size = size_of::<SharedInfo>();
|
||||
let info_as_buff = slice::from_raw_parts_mut(info as *mut u8, size);
|
||||
@ -473,14 +487,14 @@ impl ArchBootSetup for X86BootSetup {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn meminit(
|
||||
async fn meminit(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
total_pages: u64,
|
||||
_: &Option<DomainSegment>,
|
||||
_: &Option<DomainSegment>,
|
||||
) -> Result<()> {
|
||||
setup.call.claim_pages(setup.domid, total_pages)?;
|
||||
setup.call.claim_pages(setup.domid, total_pages).await?;
|
||||
let mut vmemranges: Vec<VmemRange> = Vec::new();
|
||||
let stub = VmemRange {
|
||||
start: 0,
|
||||
@ -530,13 +544,16 @@ impl ArchBootSetup for X86BootSetup {
|
||||
}
|
||||
|
||||
let extents_init_slice = extents_init.as_slice();
|
||||
let extents = setup.call.populate_physmap(
|
||||
setup.domid,
|
||||
count,
|
||||
SUPERPAGE_2MB_SHIFT as u32,
|
||||
0,
|
||||
&extents_init_slice[0usize..count as usize],
|
||||
)?;
|
||||
let extents = setup
|
||||
.call
|
||||
.populate_physmap(
|
||||
setup.domid,
|
||||
count,
|
||||
SUPERPAGE_2MB_SHIFT as u32,
|
||||
0,
|
||||
&extents_init_slice[0usize..count as usize],
|
||||
)
|
||||
.await?;
|
||||
|
||||
pfn = pfn_base_idx;
|
||||
for mfn in extents {
|
||||
@ -558,10 +575,10 @@ impl ArchBootSetup for X86BootSetup {
|
||||
let p2m_idx = (pfn_base + j) as usize;
|
||||
let p2m_end_idx = p2m_idx + allocsz as usize;
|
||||
let input_extent_starts = &p2m[p2m_idx..p2m_end_idx];
|
||||
let result =
|
||||
setup
|
||||
.call
|
||||
.populate_physmap(setup.domid, allocsz, 0, 0, input_extent_starts)?;
|
||||
let result = setup
|
||||
.call
|
||||
.populate_physmap(setup.domid, allocsz, 0, 0, input_extent_starts)
|
||||
.await?;
|
||||
|
||||
if result.len() != allocsz as usize {
|
||||
return Err(Error::PopulatePhysmapFailed(
|
||||
@ -581,11 +598,11 @@ impl ArchBootSetup for X86BootSetup {
|
||||
}
|
||||
|
||||
setup.phys.load_p2m(p2m);
|
||||
setup.call.claim_pages(setup.domid, 0)?;
|
||||
setup.call.claim_pages(setup.domid, 0).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bootlate(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
async fn bootlate(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
let p2m_segment = state
|
||||
.p2m_segment
|
||||
.as_ref()
|
||||
@ -600,11 +617,12 @@ impl ArchBootSetup for X86BootSetup {
|
||||
setup.phys.unmap(p2m_segment.pfn)?;
|
||||
setup
|
||||
.call
|
||||
.mmuext(setup.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)?;
|
||||
.mmuext(setup.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
async fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
let page_table_segment = state
|
||||
.page_table_segment
|
||||
.as_ref()
|
||||
@ -633,7 +651,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
vcpu.kernel_ss = vcpu.user_regs.ss as u64;
|
||||
vcpu.kernel_sp = vcpu.user_regs.rsp;
|
||||
trace!("vcpu context: {:?}", vcpu);
|
||||
setup.call.set_vcpu_context(setup.domid, 0, &vcpu)?;
|
||||
setup.call.set_vcpu_context(setup.domid, 0, &vcpu).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "krata-xenevtchn"
|
||||
description = "An implementation of xen evtchn for krata."
|
||||
license.workspace = true
|
||||
version= "0.0.3"
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition = "2021"
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "krata-xengnt"
|
||||
description = "An implementation of xen grant interfaces for krata."
|
||||
license.workspace = true
|
||||
version= "0.0.3"
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition = "2021"
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "krata-xenstore"
|
||||
description = "A client that interacts with xenstore for krata."
|
||||
license.workspace = true
|
||||
version= "0.0.3"
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition = "2021"
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ffi::CString,
|
||||
io::ErrorKind,
|
||||
io::Read,
|
||||
os::{
|
||||
fd::{AsRawFd, FromRawFd, IntoRawFd},
|
||||
unix::fs::FileTypeExt,
|
||||
@ -9,11 +9,10 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use libc::{fcntl, F_GETFL, F_SETFL, O_NONBLOCK};
|
||||
use log::warn;
|
||||
use log::{debug, warn};
|
||||
use tokio::{
|
||||
fs::{metadata, File},
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
io::AsyncWriteExt,
|
||||
net::UnixStream,
|
||||
select,
|
||||
sync::{
|
||||
@ -64,8 +63,8 @@ pub struct XsdSocket {
|
||||
next_request_id: Arc<Mutex<u32>>,
|
||||
next_watch_id: Arc<Mutex<u32>>,
|
||||
processor_task: Arc<JoinHandle<()>>,
|
||||
rx_task: Arc<JoinHandle<()>>,
|
||||
unwatch_sender: Sender<(u32, String)>,
|
||||
_rx_task: Arc<std::thread::JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl XsdSocket {
|
||||
@ -78,15 +77,10 @@ impl XsdSocket {
|
||||
let file = if socket {
|
||||
let stream = UnixStream::connect(path).await?;
|
||||
let stream = stream.into_std()?;
|
||||
stream.set_nonblocking(true)?;
|
||||
stream.set_nonblocking(false)?;
|
||||
unsafe { File::from_raw_fd(stream.into_raw_fd()) }
|
||||
} else {
|
||||
File::options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.custom_flags(O_NONBLOCK)
|
||||
.open(path)
|
||||
.await?
|
||||
File::options().read(true).write(true).open(path).await?
|
||||
};
|
||||
|
||||
XsdSocket::from_handle(file).await
|
||||
@ -101,7 +95,7 @@ impl XsdSocket {
|
||||
let (rx_sender, rx_receiver) = channel::<XsdMessage>(10);
|
||||
let (tx_sender, tx_receiver) = channel::<XsdMessage>(10);
|
||||
let (unwatch_sender, unwatch_receiver) = channel::<(u32, String)>(1000);
|
||||
let read: File = handle.try_clone().await?;
|
||||
let read: std::fs::File = unsafe { std::fs::File::from_raw_fd(handle.as_raw_fd()) };
|
||||
|
||||
let mut processor = XsdSocketProcessor {
|
||||
handle,
|
||||
@ -119,11 +113,13 @@ impl XsdSocket {
|
||||
}
|
||||
});
|
||||
|
||||
let rx_task = tokio::task::spawn(async move {
|
||||
if let Err(error) = XsdSocketProcessor::process_rx(read, rx_sender).await {
|
||||
warn!("failed to process xen store responses: {}", error);
|
||||
}
|
||||
});
|
||||
let rx_task = std::thread::Builder::new()
|
||||
.name("xenstore-reader".to_string())
|
||||
.spawn(move || {
|
||||
if let Err(error) = XsdSocketProcessor::process_rx(read, rx_sender) {
|
||||
debug!("failed to process xen store bus: {}", error);
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(XsdSocket {
|
||||
tx_sender,
|
||||
@ -132,8 +128,8 @@ impl XsdSocket {
|
||||
next_request_id,
|
||||
next_watch_id: Arc::new(Mutex::new(0u32)),
|
||||
processor_task: Arc::new(processor_task),
|
||||
rx_task: Arc::new(rx_task),
|
||||
unwatch_sender,
|
||||
_rx_task: Arc::new(rx_task),
|
||||
})
|
||||
}
|
||||
|
||||
@ -201,80 +197,28 @@ struct XsdSocketProcessor {
|
||||
}
|
||||
|
||||
impl XsdSocketProcessor {
|
||||
async fn process_rx(mut read: File, rx_sender: Sender<XsdMessage>) -> Result<()> {
|
||||
fn process_rx(mut read: std::fs::File, rx_sender: Sender<XsdMessage>) -> Result<()> {
|
||||
let mut header_buffer: Vec<u8> = vec![0u8; XsdMessageHeader::SIZE];
|
||||
let mut buffer: Vec<u8> = vec![0u8; XEN_BUS_MAX_PACKET_SIZE - XsdMessageHeader::SIZE];
|
||||
loop {
|
||||
select! {
|
||||
message = XsdSocketProcessor::read_message(&mut header_buffer, &mut buffer, &mut read) => {
|
||||
let message = message?;
|
||||
rx_sender.send(message).await?;
|
||||
},
|
||||
|
||||
_ = rx_sender.closed() => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
let message =
|
||||
XsdSocketProcessor::read_message(&mut header_buffer, &mut buffer, &mut read)?;
|
||||
rx_sender.blocking_send(message)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_nonblocking(fd: i32, nonblock: bool) -> Result<()> {
|
||||
let mut flags = unsafe { fcntl(fd, F_GETFL) };
|
||||
if flags == -1 {
|
||||
return Err(Error::Io(std::io::Error::new(
|
||||
ErrorKind::Unsupported,
|
||||
"failed to get fd flags",
|
||||
)));
|
||||
}
|
||||
if nonblock {
|
||||
flags |= O_NONBLOCK;
|
||||
} else {
|
||||
flags &= !O_NONBLOCK;
|
||||
}
|
||||
let result = unsafe { fcntl(fd, F_SETFL, flags) };
|
||||
if result == -1 {
|
||||
return Err(Error::Io(std::io::Error::new(
|
||||
ErrorKind::Unsupported,
|
||||
"failed to set fd flags",
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn read_message(
|
||||
fn read_message(
|
||||
header_buffer: &mut [u8],
|
||||
buffer: &mut [u8],
|
||||
read: &mut File,
|
||||
read: &mut std::fs::File,
|
||||
) -> Result<XsdMessage> {
|
||||
XsdSocketProcessor::set_nonblocking(read.as_raw_fd(), true)?;
|
||||
let header_size = loop {
|
||||
match read.read_exact(header_buffer).await {
|
||||
Ok(size) => break size,
|
||||
Err(error) => {
|
||||
if error.kind() == ErrorKind::WouldBlock {
|
||||
tokio::task::yield_now().await;
|
||||
continue;
|
||||
}
|
||||
return Err(error.into());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
if header_size < XsdMessageHeader::SIZE {
|
||||
return Err(Error::InvalidBusData);
|
||||
}
|
||||
|
||||
read.read_exact(header_buffer)?;
|
||||
let header = XsdMessageHeader::decode(header_buffer)?;
|
||||
if header.len as usize > buffer.len() {
|
||||
return Err(Error::InvalidBusData);
|
||||
}
|
||||
let payload_buffer = &mut buffer[0..header.len as usize];
|
||||
XsdSocketProcessor::set_nonblocking(read.as_raw_fd(), false)?;
|
||||
let payload_size = read.read_exact(payload_buffer).await?;
|
||||
if payload_size != header.len as usize {
|
||||
return Err(Error::InvalidBusData);
|
||||
}
|
||||
read.read_exact(payload_buffer)?;
|
||||
Ok(XsdMessage {
|
||||
header,
|
||||
payload: payload_buffer.to_vec(),
|
||||
@ -392,10 +336,6 @@ impl XsdMessage {
|
||||
|
||||
impl Drop for XsdSocket {
|
||||
fn drop(&mut self) {
|
||||
if Arc::strong_count(&self.rx_task) <= 1 {
|
||||
self.rx_task.abort();
|
||||
}
|
||||
|
||||
if Arc::strong_count(&self.processor_task) <= 1 {
|
||||
self.processor_task.abort();
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ impl XsdClient {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_domain_path(&mut self, domid: u32) -> Result<String> {
|
||||
pub async fn get_domain_path(&self, domid: u32) -> Result<String> {
|
||||
let response = self
|
||||
.socket
|
||||
.send(0, XSD_GET_DOMAIN_PATH, &[&domid.to_string()])
|
||||
@ -176,7 +176,7 @@ impl XsdClient {
|
||||
response.parse_string()
|
||||
}
|
||||
|
||||
pub async fn introduce_domain(&mut self, domid: u32, mfn: u64, evtchn: u32) -> Result<bool> {
|
||||
pub async fn introduce_domain(&self, domid: u32, mfn: u64, evtchn: u32) -> Result<bool> {
|
||||
trace!("introduce domain domid={domid} mfn={mfn} evtchn={evtchn}");
|
||||
let response = self
|
||||
.socket
|
||||
|
@ -1,7 +1,5 @@
|
||||
/// Handwritten protocol definitions for XenStore.
|
||||
/// Used xen/include/public/io/xs_wire.h as a reference.
|
||||
use libc;
|
||||
|
||||
use crate::error::Result;
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use std::io::Cursor;
|
||||
|
57
hack/ci/assemble-release-assets.sh
Executable file
57
hack/ci/assemble-release-assets.sh
Executable file
@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
checksum_sha256() {
|
||||
if type sha256sum > /dev/null 2>&1
|
||||
then
|
||||
sha256sum "${1}"
|
||||
else
|
||||
shasum -a 256 "${1}"
|
||||
fi
|
||||
}
|
||||
|
||||
asset() {
|
||||
cp "${1}" "${2}"
|
||||
PREVIOUS="${PWD}"
|
||||
cd "$(dirname "${2}")"
|
||||
BASE_FILE_NAME="$(basename "${2}")"
|
||||
checksum_sha256 "${BASE_FILE_NAME}" > "${BASE_FILE_NAME}.sha256"
|
||||
cd "${PREVIOUS}"
|
||||
}
|
||||
|
||||
FORM="${1}"
|
||||
shift
|
||||
TAG_NAME="${1}"
|
||||
shift
|
||||
PLATFORM="${1}"
|
||||
shift
|
||||
|
||||
mkdir -p target/assets
|
||||
|
||||
for SOURCE_FILE_PATH in "${@}"
|
||||
do
|
||||
if [ "${FORM}" = "kratactl" ]
|
||||
then
|
||||
SUFFIX=""
|
||||
if echo "${PLATFORM}" | grep "^windows-" > /dev/null
|
||||
then
|
||||
SUFFIX=".exe"
|
||||
fi
|
||||
asset "${SOURCE_FILE_PATH}" "target/assets/kratactl_${TAG_NAME}_${PLATFORM}${SUFFIX}"
|
||||
elif [ "${FORM}" = "debian" ]
|
||||
then
|
||||
asset "${SOURCE_FILE_PATH}" "target/assets/krata_${TAG_NAME}_${PLATFORM}.deb"
|
||||
elif [ "${FORM}" = "alpine" ]
|
||||
then
|
||||
asset "${SOURCE_FILE_PATH}" "target/assets/krata_${TAG_NAME}_${PLATFORM}.deb"
|
||||
elif [ "${FORM}" = "bundle-systemd" ]
|
||||
then
|
||||
asset "${SOURCE_FILE_PATH}" "target/assets/krata-systemd_${TAG_NAME}_${PLATFORM}.tgz"
|
||||
elif [ "${FORM}" = "os" ]
|
||||
then
|
||||
asset "${SOURCE_FILE_PATH}" "target/assets/krata_${TAG_NAME}_${PLATFORM}.qcow2"
|
||||
else
|
||||
echo "ERROR: Unknown form '${FORM}'"
|
||||
exit 1
|
||||
fi
|
||||
done
|
0
hack/ci/install-windows-deps.sh
Normal file → Executable file
0
hack/ci/install-windows-deps.sh
Normal file → Executable file
22
hack/ci/upload-release-assets.sh
Executable file
22
hack/ci/upload-release-assets.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
retry() {
|
||||
for i in $(seq 1 10)
|
||||
do
|
||||
if "${@}"
|
||||
then
|
||||
return 0
|
||||
else
|
||||
sleep "${i}"
|
||||
fi
|
||||
done
|
||||
"${@}"
|
||||
}
|
||||
|
||||
TAG="${1}"
|
||||
shift
|
||||
|
||||
cd target/assets
|
||||
|
||||
retry gh release upload "${TAG}" --clobber ./*
|
@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
KERNEL_VERSION="6.8.1"
|
||||
KERNEL_VERSION="6.7.2"
|
||||
KERNEL_SRC_URL="https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${KERNEL_VERSION}.tar.xz"
|
||||
|
@ -1,23 +1,23 @@
|
||||
#
|
||||
# Automatically generated file; DO NOT EDIT.
|
||||
# Linux/arm64 6.7.8 Kernel Configuration
|
||||
# Linux/arm64 6.7.2 Kernel Configuration
|
||||
#
|
||||
CONFIG_CC_VERSION_TEXT="gcc (GCC) 13.2.1 20231205 (Red Hat 13.2.1-6)"
|
||||
CONFIG_CC_VERSION_TEXT="aarch64-linux-gnu-gcc (Debian 13.2.0-12) 13.2.0"
|
||||
CONFIG_CC_IS_GCC=y
|
||||
CONFIG_GCC_VERSION=130201
|
||||
CONFIG_GCC_VERSION=130200
|
||||
CONFIG_CLANG_VERSION=0
|
||||
CONFIG_AS_IS_GNU=y
|
||||
CONFIG_AS_VERSION=24000
|
||||
CONFIG_AS_VERSION=24200
|
||||
CONFIG_LD_IS_BFD=y
|
||||
CONFIG_LD_VERSION=24000
|
||||
CONFIG_LD_VERSION=24200
|
||||
CONFIG_LLD_VERSION=0
|
||||
CONFIG_CC_CAN_LINK=y
|
||||
CONFIG_CC_CAN_LINK_STATIC=y
|
||||
CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y
|
||||
CONFIG_CC_HAS_ASM_GOTO_TIED_OUTPUT=y
|
||||
CONFIG_GCC_ASM_GOTO_OUTPUT_WORKAROUND=y
|
||||
CONFIG_CC_HAS_ASM_INLINE=y
|
||||
CONFIG_CC_HAS_NO_PROFILE_FN_ATTR=y
|
||||
CONFIG_PAHOLE_VERSION=0
|
||||
CONFIG_PAHOLE_VERSION=124
|
||||
CONFIG_IRQ_WORK=y
|
||||
CONFIG_BUILDTIME_TABLE_SORT=y
|
||||
CONFIG_THREAD_INFO_IN_TASK=y
|
||||
@ -401,9 +401,7 @@ CONFIG_ARM64_ERRATUM_2067961=y
|
||||
CONFIG_ARM64_ERRATUM_2441009=y
|
||||
CONFIG_ARM64_ERRATUM_2457168=y
|
||||
CONFIG_ARM64_ERRATUM_2645198=y
|
||||
CONFIG_ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD=y
|
||||
CONFIG_ARM64_ERRATUM_2966298=y
|
||||
CONFIG_ARM64_ERRATUM_3117295=y
|
||||
CONFIG_CAVIUM_ERRATUM_22375=y
|
||||
CONFIG_CAVIUM_ERRATUM_23144=y
|
||||
CONFIG_CAVIUM_ERRATUM_23154=y
|
||||
@ -3475,6 +3473,7 @@ CONFIG_BCMA_POSSIBLE=y
|
||||
# CONFIG_MFD_SM501 is not set
|
||||
# CONFIG_MFD_SKY81452 is not set
|
||||
# CONFIG_MFD_SYSCON is not set
|
||||
# CONFIG_MFD_TI_AM335X_TSCADC is not set
|
||||
# CONFIG_MFD_LP3943 is not set
|
||||
# CONFIG_MFD_TI_LMU is not set
|
||||
# CONFIG_TPS6105X is not set
|
||||
@ -5509,7 +5508,11 @@ CONFIG_DEBUG_INFO_DWARF5=y
|
||||
# CONFIG_DEBUG_INFO_REDUCED is not set
|
||||
CONFIG_DEBUG_INFO_COMPRESSED_NONE=y
|
||||
# CONFIG_DEBUG_INFO_COMPRESSED_ZLIB is not set
|
||||
# CONFIG_DEBUG_INFO_COMPRESSED_ZSTD is not set
|
||||
# CONFIG_DEBUG_INFO_SPLIT is not set
|
||||
# CONFIG_DEBUG_INFO_BTF is not set
|
||||
CONFIG_PAHOLE_HAS_SPLIT_BTF=y
|
||||
CONFIG_PAHOLE_HAS_LANG_EXCLUDE=y
|
||||
# CONFIG_GDB_SCRIPTS is not set
|
||||
CONFIG_FRAME_WARN=1280
|
||||
# CONFIG_STRIP_ASM_SYMS is not set
|
||||
|
@ -1,8 +1,8 @@
|
||||
#
|
||||
# Automatically generated file; DO NOT EDIT.
|
||||
# Linux/x86 6.7.3 Kernel Configuration
|
||||
# Linux/x86_64 6.8.2 Kernel Configuration
|
||||
#
|
||||
CONFIG_CC_VERSION_TEXT="gcc (Debian 13.2.0-13) 13.2.0"
|
||||
CONFIG_CC_VERSION_TEXT="gcc (Debian 13.2.0-23) 13.2.0"
|
||||
CONFIG_CC_IS_GCC=y
|
||||
CONFIG_GCC_VERSION=130200
|
||||
CONFIG_CLANG_VERSION=0
|
||||
@ -15,6 +15,7 @@ CONFIG_CC_CAN_LINK=y
|
||||
CONFIG_CC_CAN_LINK_STATIC=y
|
||||
CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y
|
||||
CONFIG_CC_HAS_ASM_GOTO_TIED_OUTPUT=y
|
||||
CONFIG_GCC_ASM_GOTO_OUTPUT_WORKAROUND=y
|
||||
CONFIG_TOOLS_SUPPORT_RELR=y
|
||||
CONFIG_CC_HAS_ASM_INLINE=y
|
||||
CONFIG_CC_HAS_NO_PROFILE_FN_ATTR=y
|
||||
@ -120,7 +121,6 @@ CONFIG_BPF_JIT=y
|
||||
CONFIG_BPF_JIT_ALWAYS_ON=y
|
||||
CONFIG_BPF_JIT_DEFAULT_ON=y
|
||||
CONFIG_BPF_UNPRIV_DEFAULT_OFF=y
|
||||
CONFIG_USERMODE_DRIVER=y
|
||||
# CONFIG_BPF_PRELOAD is not set
|
||||
# CONFIG_BPF_LSM is not set
|
||||
# end of BPF subsystem
|
||||
@ -188,8 +188,10 @@ CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y
|
||||
CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y
|
||||
CONFIG_CC_HAS_INT128=y
|
||||
CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5"
|
||||
CONFIG_GCC11_NO_ARRAY_BOUNDS=y
|
||||
CONFIG_GCC10_NO_ARRAY_BOUNDS=y
|
||||
CONFIG_CC_NO_ARRAY_BOUNDS=y
|
||||
CONFIG_GCC_NO_STRINGOP_OVERFLOW=y
|
||||
CONFIG_CC_NO_STRINGOP_OVERFLOW=y
|
||||
CONFIG_ARCH_SUPPORTS_INT128=y
|
||||
CONFIG_CGROUPS=y
|
||||
CONFIG_PAGE_COUNTER=y
|
||||
@ -268,19 +270,19 @@ CONFIG_AIO=y
|
||||
CONFIG_IO_URING=y
|
||||
CONFIG_ADVISE_SYSCALLS=y
|
||||
CONFIG_MEMBARRIER=y
|
||||
CONFIG_KCMP=y
|
||||
CONFIG_RSEQ=y
|
||||
# CONFIG_DEBUG_RSEQ is not set
|
||||
CONFIG_CACHESTAT_SYSCALL=y
|
||||
# CONFIG_PC104 is not set
|
||||
CONFIG_KALLSYMS=y
|
||||
# CONFIG_KALLSYMS_SELFTEST is not set
|
||||
CONFIG_KALLSYMS_ALL=y
|
||||
CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y
|
||||
CONFIG_KALLSYMS_BASE_RELATIVE=y
|
||||
CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y
|
||||
CONFIG_KCMP=y
|
||||
CONFIG_RSEQ=y
|
||||
CONFIG_CACHESTAT_SYSCALL=y
|
||||
# CONFIG_DEBUG_RSEQ is not set
|
||||
CONFIG_HAVE_PERF_EVENTS=y
|
||||
CONFIG_GUEST_PERF_EVENTS=y
|
||||
# CONFIG_PC104 is not set
|
||||
|
||||
#
|
||||
# Kernel Performance Events And Counters
|
||||
@ -380,6 +382,7 @@ CONFIG_GENERIC_CPU=y
|
||||
CONFIG_X86_INTERNODE_CACHE_SHIFT=6
|
||||
CONFIG_X86_L1_CACHE_SHIFT=6
|
||||
CONFIG_X86_TSC=y
|
||||
CONFIG_X86_HAVE_PAE=y
|
||||
CONFIG_X86_CMPXCHG64=y
|
||||
CONFIG_X86_CMOV=y
|
||||
CONFIG_X86_MINIMUM_CPU_FAMILY=64
|
||||
@ -457,7 +460,6 @@ CONFIG_X86_INTEL_TSX_MODE_OFF=y
|
||||
# CONFIG_X86_INTEL_TSX_MODE_AUTO is not set
|
||||
# CONFIG_X86_SGX is not set
|
||||
# CONFIG_X86_USER_SHADOW_STACK is not set
|
||||
# CONFIG_INTEL_TDX_HOST is not set
|
||||
CONFIG_EFI=y
|
||||
CONFIG_EFI_STUB=y
|
||||
CONFIG_EFI_HANDOVER_PROTOCOL=y
|
||||
@ -520,6 +522,7 @@ CONFIG_CPU_IBRS_ENTRY=y
|
||||
CONFIG_CPU_SRSO=y
|
||||
# CONFIG_SLS is not set
|
||||
# CONFIG_GDS_FORCE_MITIGATION is not set
|
||||
CONFIG_MITIGATION_RFDS=y
|
||||
CONFIG_ARCH_HAS_ADD_PAGES=y
|
||||
|
||||
#
|
||||
@ -544,6 +547,7 @@ CONFIG_ACPI=y
|
||||
CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y
|
||||
CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC=y
|
||||
CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y
|
||||
CONFIG_ACPI_THERMAL_LIB=y
|
||||
# CONFIG_ACPI_DEBUGGER is not set
|
||||
CONFIG_ACPI_SPCR_TABLE=y
|
||||
# CONFIG_ACPI_FPDT is not set
|
||||
@ -670,14 +674,13 @@ CONFIG_COMPAT_FOR_U64_ALIGNMENT=y
|
||||
# end of Binary Emulations
|
||||
|
||||
CONFIG_HAVE_KVM=y
|
||||
CONFIG_KVM_COMMON=y
|
||||
CONFIG_HAVE_KVM_PFNCACHE=y
|
||||
CONFIG_HAVE_KVM_IRQCHIP=y
|
||||
CONFIG_HAVE_KVM_IRQFD=y
|
||||
CONFIG_HAVE_KVM_IRQ_ROUTING=y
|
||||
CONFIG_HAVE_KVM_DIRTY_RING=y
|
||||
CONFIG_HAVE_KVM_DIRTY_RING_TSO=y
|
||||
CONFIG_HAVE_KVM_DIRTY_RING_ACQ_REL=y
|
||||
CONFIG_HAVE_KVM_EVENTFD=y
|
||||
CONFIG_KVM_MMIO=y
|
||||
CONFIG_KVM_ASYNC_PF=y
|
||||
CONFIG_HAVE_KVM_MSI=y
|
||||
@ -690,13 +693,16 @@ CONFIG_HAVE_KVM_NO_POLL=y
|
||||
CONFIG_KVM_XFER_TO_GUEST_WORK=y
|
||||
CONFIG_HAVE_KVM_PM_NOTIFIER=y
|
||||
CONFIG_KVM_GENERIC_HARDWARE_ENABLING=y
|
||||
CONFIG_KVM_GENERIC_MMU_NOTIFIER=y
|
||||
CONFIG_VIRTUALIZATION=y
|
||||
CONFIG_KVM=m
|
||||
CONFIG_KVM_WERROR=y
|
||||
# CONFIG_KVM_SW_PROTECTED_VM is not set
|
||||
CONFIG_KVM_INTEL=m
|
||||
CONFIG_KVM_AMD=m
|
||||
CONFIG_KVM_AMD_SEV=y
|
||||
CONFIG_KVM_SMM=y
|
||||
CONFIG_KVM_HYPERV=y
|
||||
# CONFIG_KVM_XEN is not set
|
||||
# CONFIG_KVM_PROVE_MMU is not set
|
||||
CONFIG_KVM_MAX_NR_VCPUS=1024
|
||||
@ -847,6 +853,7 @@ CONFIG_ARCH_SUPPORTS_PAGE_TABLE_CHECK=y
|
||||
CONFIG_ARCH_HAS_ELFCORE_COMPAT=y
|
||||
CONFIG_ARCH_HAS_PARANOID_L1D_FLUSH=y
|
||||
CONFIG_DYNAMIC_SIGFRAME=y
|
||||
CONFIG_ARCH_HAS_HW_PTE_YOUNG=y
|
||||
CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG=y
|
||||
|
||||
#
|
||||
@ -903,6 +910,7 @@ CONFIG_BLK_ICQ=y
|
||||
CONFIG_BLK_DEV_BSGLIB=y
|
||||
CONFIG_BLK_DEV_INTEGRITY=y
|
||||
CONFIG_BLK_DEV_INTEGRITY_T10=m
|
||||
CONFIG_BLK_DEV_WRITE_MOUNTED=y
|
||||
# CONFIG_BLK_DEV_ZONED is not set
|
||||
CONFIG_BLK_DEV_THROTTLING=y
|
||||
# CONFIG_BLK_DEV_THROTTLING_LOW is not set
|
||||
@ -992,6 +1000,7 @@ CONFIG_SWAP=y
|
||||
CONFIG_ZSWAP=y
|
||||
# CONFIG_ZSWAP_DEFAULT_ON is not set
|
||||
# CONFIG_ZSWAP_EXCLUSIVE_LOADS_DEFAULT_ON is not set
|
||||
# CONFIG_ZSWAP_SHRINKER_DEFAULT_ON is not set
|
||||
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_DEFLATE is not set
|
||||
CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZO=y
|
||||
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_842 is not set
|
||||
@ -1010,9 +1019,8 @@ CONFIG_ZSMALLOC=y
|
||||
CONFIG_ZSMALLOC_CHAIN_SIZE=8
|
||||
|
||||
#
|
||||
# SLAB allocator options
|
||||
# Slab allocator options
|
||||
#
|
||||
# CONFIG_SLAB_DEPRECATED is not set
|
||||
CONFIG_SLUB=y
|
||||
# CONFIG_SLUB_TINY is not set
|
||||
CONFIG_SLAB_MERGE_DEFAULT=y
|
||||
@ -1021,7 +1029,7 @@ CONFIG_SLAB_FREELIST_RANDOM=y
|
||||
# CONFIG_SLUB_STATS is not set
|
||||
CONFIG_SLUB_CPU_PARTIAL=y
|
||||
# CONFIG_RANDOM_KMALLOC_CACHES is not set
|
||||
# end of SLAB allocator options
|
||||
# end of Slab allocator options
|
||||
|
||||
# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set
|
||||
# CONFIG_COMPAT_BRK is not set
|
||||
@ -1063,6 +1071,7 @@ CONFIG_ARCH_WANTS_THP_SWAP=y
|
||||
CONFIG_TRANSPARENT_HUGEPAGE=y
|
||||
CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
|
||||
# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set
|
||||
# CONFIG_TRANSPARENT_HUGEPAGE_NEVER is not set
|
||||
CONFIG_THP_SWAP=y
|
||||
# CONFIG_READ_ONLY_THP_FOR_FS is not set
|
||||
CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y
|
||||
@ -1093,6 +1102,7 @@ CONFIG_SECRETMEM=y
|
||||
CONFIG_LRU_GEN=y
|
||||
CONFIG_LRU_GEN_ENABLED=y
|
||||
# CONFIG_LRU_GEN_STATS is not set
|
||||
CONFIG_LRU_GEN_WALKS_MMU=y
|
||||
CONFIG_ARCH_SUPPORTS_PER_VMA_LOCK=y
|
||||
CONFIG_PER_VMA_LOCK=y
|
||||
CONFIG_LOCK_MM_AND_FIND_VMA=y
|
||||
@ -1590,8 +1600,6 @@ CONFIG_BRIDGE_EBT_REDIRECT=m
|
||||
CONFIG_BRIDGE_EBT_SNAT=m
|
||||
CONFIG_BRIDGE_EBT_LOG=m
|
||||
CONFIG_BRIDGE_EBT_NFLOG=m
|
||||
CONFIG_BPFILTER=y
|
||||
CONFIG_BPFILTER_UMH=m
|
||||
CONFIG_IP_DCCP=m
|
||||
CONFIG_INET_DCCP_DIAG=m
|
||||
|
||||
@ -1789,10 +1797,10 @@ CONFIG_STREAM_PARSER=y
|
||||
CONFIG_FIB_RULES=y
|
||||
# CONFIG_WIRELESS is not set
|
||||
# CONFIG_RFKILL is not set
|
||||
CONFIG_NET_9P=m
|
||||
CONFIG_NET_9P_FD=m
|
||||
CONFIG_NET_9P_VIRTIO=m
|
||||
CONFIG_NET_9P_XEN=m
|
||||
CONFIG_NET_9P=y
|
||||
CONFIG_NET_9P_FD=y
|
||||
CONFIG_NET_9P_VIRTIO=y
|
||||
CONFIG_NET_9P_XEN=y
|
||||
# CONFIG_NET_9P_DEBUG is not set
|
||||
# CONFIG_CAIF is not set
|
||||
CONFIG_CEPH_LIB=m
|
||||
@ -1927,6 +1935,7 @@ CONFIG_ALLOW_DEV_COREDUMP=y
|
||||
# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set
|
||||
# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set
|
||||
CONFIG_SYS_HYPERVISOR=y
|
||||
CONFIG_GENERIC_CPU_DEVICES=y
|
||||
CONFIG_GENERIC_CPU_AUTOPROBE=y
|
||||
CONFIG_GENERIC_CPU_VULNERABILITIES=y
|
||||
CONFIG_REGMAP=y
|
||||
@ -2031,6 +2040,7 @@ CONFIG_ZRAM_DEF_COMP_LZ4=y
|
||||
# CONFIG_ZRAM_DEF_COMP_LZ4HC is not set
|
||||
CONFIG_ZRAM_DEF_COMP="lz4"
|
||||
# CONFIG_ZRAM_WRITEBACK is not set
|
||||
# CONFIG_ZRAM_TRACK_ENTRY_ACTIME is not set
|
||||
# CONFIG_ZRAM_MEMORY_TRACKING is not set
|
||||
# CONFIG_ZRAM_MULTI_COMP is not set
|
||||
CONFIG_BLK_DEV_LOOP=m
|
||||
@ -2091,6 +2101,7 @@ CONFIG_VMWARE_BALLOON=m
|
||||
# CONFIG_DW_XDATA_PCIE is not set
|
||||
# CONFIG_PCI_ENDPOINT_TEST is not set
|
||||
# CONFIG_XILINX_SDFEC is not set
|
||||
# CONFIG_NSM is not set
|
||||
# CONFIG_C2PORT is not set
|
||||
|
||||
#
|
||||
@ -2117,8 +2128,6 @@ CONFIG_VMWARE_BALLOON=m
|
||||
#
|
||||
# CONFIG_ALTERA_STAPL is not set
|
||||
# CONFIG_INTEL_MEI is not set
|
||||
# CONFIG_INTEL_MEI_ME is not set
|
||||
# CONFIG_INTEL_MEI_TXE is not set
|
||||
CONFIG_VMWARE_VMCI=m
|
||||
# CONFIG_GENWQE is not set
|
||||
# CONFIG_ECHO is not set
|
||||
@ -2332,13 +2341,10 @@ CONFIG_MD=y
|
||||
CONFIG_BLK_DEV_MD=y
|
||||
# CONFIG_MD_AUTODETECT is not set
|
||||
CONFIG_MD_BITMAP_FILE=y
|
||||
CONFIG_MD_LINEAR=m
|
||||
CONFIG_MD_RAID0=m
|
||||
CONFIG_MD_RAID1=m
|
||||
CONFIG_MD_RAID10=m
|
||||
CONFIG_MD_RAID456=m
|
||||
CONFIG_MD_MULTIPATH=m
|
||||
CONFIG_MD_FAULTY=m
|
||||
# CONFIG_MD_CLUSTER is not set
|
||||
CONFIG_BCACHE=m
|
||||
# CONFIG_BCACHE_DEBUG is not set
|
||||
@ -2558,7 +2564,6 @@ CONFIG_NET_VENDOR_WANGXUN=y
|
||||
# CONFIG_NET_VENDOR_WIZNET is not set
|
||||
CONFIG_NET_VENDOR_XILINX=y
|
||||
# CONFIG_XILINX_EMACLITE is not set
|
||||
# CONFIG_XILINX_AXI_EMAC is not set
|
||||
# CONFIG_XILINX_LL_TEMAC is not set
|
||||
# CONFIG_FDDI is not set
|
||||
# CONFIG_HIPPI is not set
|
||||
@ -2617,6 +2622,7 @@ CONFIG_FIXED_PHY=m
|
||||
# CONFIG_DP83867_PHY is not set
|
||||
# CONFIG_DP83869_PHY is not set
|
||||
# CONFIG_DP83TD510_PHY is not set
|
||||
# CONFIG_DP83TG720_PHY is not set
|
||||
# CONFIG_VITESSE_PHY is not set
|
||||
# CONFIG_XILINX_GMII2RGMII is not set
|
||||
# CONFIG_PSE_CONTROLLER is not set
|
||||
@ -3075,13 +3081,13 @@ CONFIG_HWMON=m
|
||||
# CONFIG_SENSORS_DRIVETEMP is not set
|
||||
# CONFIG_SENSORS_DS620 is not set
|
||||
# CONFIG_SENSORS_DS1621 is not set
|
||||
# CONFIG_SENSORS_DELL_SMM is not set
|
||||
# CONFIG_SENSORS_I5K_AMB is not set
|
||||
# CONFIG_SENSORS_F71805F is not set
|
||||
# CONFIG_SENSORS_F71882FG is not set
|
||||
# CONFIG_SENSORS_F75375S is not set
|
||||
# CONFIG_SENSORS_FSCHMD is not set
|
||||
# CONFIG_SENSORS_FTSTEUTATES is not set
|
||||
# CONFIG_SENSORS_GIGABYTE_WATERFORCE is not set
|
||||
# CONFIG_SENSORS_GL518SM is not set
|
||||
# CONFIG_SENSORS_GL520SM is not set
|
||||
# CONFIG_SENSORS_G760A is not set
|
||||
@ -3213,6 +3219,7 @@ CONFIG_SENSORS_ACPI_POWER=m
|
||||
CONFIG_THERMAL=y
|
||||
# CONFIG_THERMAL_NETLINK is not set
|
||||
# CONFIG_THERMAL_STATISTICS is not set
|
||||
# CONFIG_THERMAL_DEBUGFS is not set
|
||||
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
|
||||
# CONFIG_THERMAL_WRITABLE_TRIPS is not set
|
||||
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
|
||||
@ -3362,7 +3369,6 @@ CONFIG_BCMA_POSSIBLE=y
|
||||
# CONFIG_MFD_SM501 is not set
|
||||
# CONFIG_MFD_SKY81452 is not set
|
||||
# CONFIG_MFD_SYSCON is not set
|
||||
# CONFIG_MFD_TI_AM335X_TSCADC is not set
|
||||
# CONFIG_MFD_LP3943 is not set
|
||||
# CONFIG_MFD_TI_LMU is not set
|
||||
# CONFIG_TPS6105X is not set
|
||||
@ -3430,6 +3436,7 @@ CONFIG_DRM_GEM_SHMEM_HELPER=m
|
||||
# CONFIG_DRM_AMDGPU is not set
|
||||
# CONFIG_DRM_NOUVEAU is not set
|
||||
# CONFIG_DRM_I915 is not set
|
||||
# CONFIG_DRM_XE is not set
|
||||
# CONFIG_DRM_VGEM is not set
|
||||
# CONFIG_DRM_VKMS is not set
|
||||
CONFIG_DRM_VMWGFX=m
|
||||
@ -3457,7 +3464,6 @@ CONFIG_DRM_PANEL_BRIDGE=y
|
||||
# CONFIG_DRM_ANALOGIX_ANX78XX is not set
|
||||
# end of Display Interface Bridges
|
||||
|
||||
# CONFIG_DRM_LOONGSON is not set
|
||||
# CONFIG_DRM_ETNAVIV is not set
|
||||
CONFIG_DRM_BOCHS=m
|
||||
CONFIG_DRM_CIRRUS_QEMU=m
|
||||
@ -3469,7 +3475,6 @@ CONFIG_DRM_VBOXVIDEO=m
|
||||
# CONFIG_DRM_GUD is not set
|
||||
# CONFIG_DRM_SSD130X is not set
|
||||
CONFIG_DRM_HYPERV=m
|
||||
# CONFIG_DRM_LEGACY is not set
|
||||
CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=m
|
||||
|
||||
#
|
||||
@ -3489,7 +3494,6 @@ CONFIG_FB=m
|
||||
# CONFIG_FB_NVIDIA is not set
|
||||
# CONFIG_FB_RIVA is not set
|
||||
# CONFIG_FB_I740 is not set
|
||||
# CONFIG_FB_LE80578 is not set
|
||||
# CONFIG_FB_MATROX is not set
|
||||
# CONFIG_FB_RADEON is not set
|
||||
# CONFIG_FB_ATY128 is not set
|
||||
@ -3524,9 +3528,8 @@ CONFIG_FB_SYS_FILLRECT=m
|
||||
CONFIG_FB_SYS_COPYAREA=m
|
||||
CONFIG_FB_SYS_IMAGEBLIT=m
|
||||
# CONFIG_FB_FOREIGN_ENDIAN is not set
|
||||
CONFIG_FB_SYS_FOPS=m
|
||||
CONFIG_FB_SYSMEM_FOPS=m
|
||||
CONFIG_FB_DEFERRED_IO=y
|
||||
CONFIG_FB_IOMEM_FOPS=m
|
||||
CONFIG_FB_SYSMEM_HELPERS=y
|
||||
CONFIG_FB_SYSMEM_HELPERS_DEFERRED=y
|
||||
# CONFIG_FB_MODE_HELPERS is not set
|
||||
@ -4129,6 +4132,7 @@ CONFIG_VIRTIO_PCI_LIB=y
|
||||
CONFIG_VIRTIO_PCI_LIB_LEGACY=y
|
||||
CONFIG_VIRTIO_MENU=y
|
||||
CONFIG_VIRTIO_PCI=y
|
||||
CONFIG_VIRTIO_PCI_ADMIN_LEGACY=y
|
||||
CONFIG_VIRTIO_PCI_LEGACY=y
|
||||
CONFIG_VIRTIO_VDPA=m
|
||||
# CONFIG_VIRTIO_PMEM is not set
|
||||
@ -4308,6 +4312,7 @@ CONFIG_RPMSG_VIRTIO=m
|
||||
#
|
||||
# Qualcomm SoC drivers
|
||||
#
|
||||
# CONFIG_QCOM_PMIC_PDCHARGER_ULOG is not set
|
||||
# end of Qualcomm SoC drivers
|
||||
|
||||
# CONFIG_SOC_TI is not set
|
||||
@ -4382,6 +4387,7 @@ CONFIG_MEMORY=y
|
||||
#
|
||||
# Performance monitor support
|
||||
#
|
||||
# CONFIG_DWC_PCIE_PMU is not set
|
||||
# end of Performance monitor support
|
||||
|
||||
# CONFIG_RAS is not set
|
||||
@ -4402,14 +4408,7 @@ CONFIG_DAX=y
|
||||
# CONFIG_DEV_DAX is not set
|
||||
CONFIG_NVMEM=y
|
||||
CONFIG_NVMEM_SYSFS=y
|
||||
|
||||
#
|
||||
# Layout Types
|
||||
#
|
||||
# CONFIG_NVMEM_LAYOUT_SL28_VPD is not set
|
||||
# CONFIG_NVMEM_LAYOUT_ONIE_TLV is not set
|
||||
# end of Layout Types
|
||||
|
||||
# CONFIG_NVMEM_LAYOUTS is not set
|
||||
# CONFIG_NVMEM_RMEM is not set
|
||||
|
||||
#
|
||||
@ -4436,6 +4435,7 @@ CONFIG_NVMEM_SYSFS=y
|
||||
CONFIG_DCACHE_WORD_ACCESS=y
|
||||
# CONFIG_VALIDATE_FS_PARSER is not set
|
||||
CONFIG_FS_IOMAP=y
|
||||
CONFIG_FS_STACK=y
|
||||
CONFIG_BUFFER_HEAD=y
|
||||
CONFIG_LEGACY_DIRECT_IO=y
|
||||
CONFIG_EXT2_FS=m
|
||||
@ -4541,9 +4541,9 @@ CONFIG_OVERLAY_FS=y
|
||||
#
|
||||
# Caches
|
||||
#
|
||||
CONFIG_NETFS_SUPPORT=m
|
||||
CONFIG_NETFS_SUPPORT=y
|
||||
CONFIG_NETFS_STATS=y
|
||||
CONFIG_FSCACHE=m
|
||||
CONFIG_FSCACHE=y
|
||||
CONFIG_FSCACHE_STATS=y
|
||||
# CONFIG_FSCACHE_DEBUG is not set
|
||||
CONFIG_CACHEFILES=m
|
||||
@ -4598,9 +4598,9 @@ CONFIG_TMPFS_XATTR=y
|
||||
CONFIG_TMPFS_INODE64=y
|
||||
# CONFIG_TMPFS_QUOTA is not set
|
||||
CONFIG_HUGETLBFS=y
|
||||
# CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON is not set
|
||||
CONFIG_HUGETLB_PAGE=y
|
||||
CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP=y
|
||||
# CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON is not set
|
||||
CONFIG_ARCH_HAS_GIGANTIC_PAGE=y
|
||||
CONFIG_CONFIGFS_FS=y
|
||||
CONFIG_EFIVAR_FS=y
|
||||
@ -4688,6 +4688,7 @@ CONFIG_NFSD_SCSILAYOUT=y
|
||||
CONFIG_NFSD_FLEXFILELAYOUT=y
|
||||
# CONFIG_NFSD_V4_2_INTER_SSC is not set
|
||||
# CONFIG_NFSD_V4_SECURITY_LABEL is not set
|
||||
# CONFIG_NFSD_LEGACY_CLIENT_TRACKING is not set
|
||||
CONFIG_GRACE_PERIOD=m
|
||||
CONFIG_LOCKD=m
|
||||
CONFIG_LOCKD_V4=y
|
||||
@ -4723,7 +4724,7 @@ CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN=y
|
||||
CONFIG_SMBFS=m
|
||||
# CONFIG_CODA_FS is not set
|
||||
# CONFIG_AFS_FS is not set
|
||||
CONFIG_9P_FS=m
|
||||
CONFIG_9P_FS=y
|
||||
CONFIG_9P_FSCACHE=y
|
||||
CONFIG_9P_FS_POSIX_ACL=y
|
||||
# CONFIG_9P_FS_SECURITY is not set
|
||||
@ -4947,14 +4948,12 @@ CONFIG_CRYPTO_TWOFISH_COMMON=m
|
||||
CONFIG_CRYPTO_ADIANTUM=m
|
||||
CONFIG_CRYPTO_CHACHA20=m
|
||||
CONFIG_CRYPTO_CBC=y
|
||||
CONFIG_CRYPTO_CFB=m
|
||||
CONFIG_CRYPTO_CTR=m
|
||||
CONFIG_CRYPTO_CTS=y
|
||||
CONFIG_CRYPTO_ECB=y
|
||||
CONFIG_CRYPTO_HCTR2=m
|
||||
CONFIG_CRYPTO_KEYWRAP=m
|
||||
CONFIG_CRYPTO_LRW=m
|
||||
# CONFIG_CRYPTO_OFB is not set
|
||||
CONFIG_CRYPTO_PCBC=m
|
||||
CONFIG_CRYPTO_XCTR=m
|
||||
CONFIG_CRYPTO_XTS=y
|
||||
@ -5109,6 +5108,7 @@ CONFIG_CRYPTO_DEV_QAT=m
|
||||
# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set
|
||||
# CONFIG_CRYPTO_DEV_QAT_C62X is not set
|
||||
CONFIG_CRYPTO_DEV_QAT_4XXX=m
|
||||
# CONFIG_CRYPTO_DEV_QAT_420XX is not set
|
||||
CONFIG_CRYPTO_DEV_QAT_DH895xCCVF=m
|
||||
# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set
|
||||
# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set
|
||||
@ -5312,7 +5312,7 @@ CONFIG_DEBUG_MISC=y
|
||||
# Compile-time checks and compiler options
|
||||
#
|
||||
CONFIG_DEBUG_INFO=y
|
||||
CONFIG_AS_HAS_NON_CONST_LEB128=y
|
||||
CONFIG_AS_HAS_NON_CONST_ULEB128=y
|
||||
# CONFIG_DEBUG_INFO_NONE is not set
|
||||
# CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set
|
||||
# CONFIG_DEBUG_INFO_DWARF4 is not set
|
||||
|
41
release-plz.toml
Normal file
41
release-plz.toml
Normal file
@ -0,0 +1,41 @@
|
||||
[workspace]
|
||||
git_release_enable = false
|
||||
git_tag_enable = false
|
||||
changelog_update = false
|
||||
|
||||
[[package]]
|
||||
name = "krata"
|
||||
git_release_name = "v{{ version }}"
|
||||
git_tag_name = "v{{ version }}"
|
||||
git_tag_enable = true
|
||||
git_release_enable = true
|
||||
changelog_update = true
|
||||
changelog_path = "./CHANGELOG.md"
|
||||
changelog_include = [
|
||||
"krata-daemon",
|
||||
"krata-ctl",
|
||||
"krata-guest",
|
||||
"krata-network",
|
||||
"krata-runtime",
|
||||
"krata-oci",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "krata-xencall"
|
||||
semver_check = false
|
||||
|
||||
[[package]]
|
||||
name = "krata-xenclient"
|
||||
semver_check = false
|
||||
|
||||
[[package]]
|
||||
name = "krata-xenevtchn"
|
||||
semver_check = false
|
||||
|
||||
[[package]]
|
||||
name = "krata-xengnt"
|
||||
semver_check = false
|
||||
|
||||
[[package]]
|
||||
name = "krata-xenstore"
|
||||
semver_check = false
|
@ -6,7 +6,6 @@ output_log="/var/log/kratad.log"
|
||||
error_log="/var/log/kratad.err"
|
||||
|
||||
depend() {
|
||||
use xenconsoled
|
||||
use xenstored
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ output_log="/var/log/kratanet.log"
|
||||
error_log="/var/log/kratanet.err"
|
||||
|
||||
depend() {
|
||||
use xenstored
|
||||
use kratad
|
||||
}
|
||||
|
||||
export RUST_LOG=info
|
||||
|
@ -2,6 +2,8 @@
|
||||
Description=Krata Networking Daemon
|
||||
|
||||
[Service]
|
||||
Wants=kratad.service
|
||||
After=kratad.service
|
||||
Restart=on-failure
|
||||
Type=simple
|
||||
ExecStart=/usr/libexec/kratanet
|
||||
|
Reference in New Issue
Block a user