Compare commits

...

26 Commits

Author SHA1 Message Date
f59976eb80 chore: release (#16)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-04-03 00:27:16 +00:00
3641bc55f4 workflows: publish on release 2024-04-03 00:08:28 +00:00
4b6272f49d chore: release-plz on main branch 2024-04-02 23:52:26 +00:00
7c55e63f24 chore: implement automatic releases 2024-04-02 23:50:45 +00:00
2083ec0604 docs: update README 2024-04-02 20:17:34 +00:00
5ad2e40a7b krata: reimplement console to utilize channels, and provide logs support 2024-04-02 08:57:34 +00:00
0fd6318c5f xenstore: use read thread to avoid need for non-blocking I/O 2024-04-02 03:02:00 +00:00
7940eea588 build(deps): bump async-compression from 0.4.6 to 0.4.7 (#14)
Bumps [async-compression](https://github.com/Nullus157/async-compression) from 0.4.6 to 0.4.7.
- [Release notes](https://github.com/Nullus157/async-compression/releases)
- [Changelog](https://github.com/Nullus157/async-compression/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Nullus157/async-compression/compare/async-compression-v0.4.6...async-compression-v0.4.7)

---
updated-dependencies:
- dependency-name: async-compression
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 18:44:13 -07:00
45aa4914bb build(deps): bump etherparse from 0.14.2 to 0.14.3 (#15)
Bumps [etherparse](https://github.com/JulianSchmid/etherparse) from 0.14.2 to 0.14.3.
- [Release notes](https://github.com/JulianSchmid/etherparse/releases)
- [Changelog](https://github.com/JulianSchmid/etherparse/blob/master/changelog.md)
- [Commits](https://github.com/JulianSchmid/etherparse/compare/v0.14.2...v0.14.3)

---
updated-dependencies:
- dependency-name: etherparse
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 18:43:37 -07:00
8dd3cc7692 krata: work on parallel reconciliation 2024-04-02 00:56:18 +00:00
6a2f1e6517 dependabot: fix config file 2024-04-01 15:46:39 -07:00
d433cd49e2 krata: unvendor dependencies 2024-04-01 15:45:43 -07:00
0fd32e84cd workflows: fix client workflow spec 2024-03-31 16:30:22 -07:00
ab8941326a workflows: client build on windows should use lf checkout 2024-03-31 16:29:00 -07:00
8be75a722e workflows: use submodules checkout everywhere 2024-03-31 16:21:25 -07:00
e6f35eb3d0 cargo: fix vendor path 2024-03-31 16:09:32 -07:00
58c6413ca2 workflows: checkout submodules 2024-03-31 16:07:57 -07:00
e1f1f8579c vendor dependencies using krata-vendor repository 2024-03-31 15:53:10 -07:00
6bf1d3f88c krata: implement parallel guest reconciliation 2024-03-31 10:10:06 +00:00
377b837db9 guest: set hostname from launch config 2024-03-31 03:18:56 +00:00
6cd8cc12db guest: remove device restriction 2024-03-31 02:33:58 +00:00
c68f367e4a krata: log when a guest start failures occurs 2024-03-31 01:44:28 +00:00
15d5ed5a45 krata: implement event stream retries 2024-03-31 01:11:50 +00:00
6d6bdade87 kernel: update to 6.8.2 2024-03-31 00:28:21 +00:00
693d62a41a guest: setup loopback interface 2024-03-30 23:46:01 +00:00
8ec7042ea4 guest: place running tasks in cgroup 2024-03-30 23:25:00 +00:00
71 changed files with 4800 additions and 657 deletions

View File

@ -1,11 +1,13 @@
name: check
on: [push, pull_request]
on: [push, pull_request, merge_group]
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 +18,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- run: ./hack/code/shellcheck.sh

View File

@ -1,5 +1,5 @@
name: client
on: [push, pull_request]
on: [push, pull_request, merge_group]
jobs:
build:
strategy:
@ -21,7 +21,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

View File

@ -8,10 +8,13 @@ on:
paths:
- "kernel/**"
- "hack/ci/**"
merge_group:
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
arch:
- x86_64
@ -21,6 +24,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

View File

@ -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' }}

View File

@ -10,10 +10,12 @@ on:
- "os/**"
- "hack/os/**"
- "hack/ci/**"
merge_group:
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
arch:
- x86_64
@ -23,6 +25,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
View 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 }}"

31
.github/workflows/release-plz.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: release-plz
permissions:
pull-requests: write
contents: write
on:
push:
branches:
- main
concurrency:
group: "${{ github.workflow }}"
cancel-in-progress: true
jobs:
release-plz:
name: release-plz
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- 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: "${{ secrets.GITHUB_TOKEN }}"
CARGO_REGISTRY_TOKEN: "${{ secrets.KRATA_RELEASE_CARGO_TOKEN }}"

View File

@ -1,9 +1,10 @@
name: server
on: [push, pull_request]
on: [push, pull_request, merge_group]
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
arch:
- x86_64
@ -13,6 +14,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 +31,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 +48,8 @@ jobs:
name: clippy ${{ matrix.arch }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
@ -60,6 +67,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
View File

@ -1,2 +1 @@
/target
Cargo.lock

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "vendor"]
path = vendor
url = https://github.com/edera-dev/krata-vendor.git

21
CHANGELOG.md Normal file
View File

@ -0,0 +1,21 @@
# 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.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

3320
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,7 @@ members = [
resolver = "2"
[workspace.package]
version = "0.0.3"
version = "0.0.4"
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.7"
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.0"
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"

View File

@ -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 |

View File

@ -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.4" }
log = { workspace = true }
prost-reflect = { workspace = true, features = ["serde"] }
serde_json = { workspace = true }

View File

@ -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,
@ -29,7 +29,7 @@ enum ListFormat {
#[derive(Parser)]
pub struct ListCommand {
#[arg(short, long, default_value = "cli-table")]
#[arg(short, long, default_value = "table")]
format: ListFormat,
#[arg()]
guest: Option<String>,
@ -70,24 +70,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 +114,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(())
}

View File

@ -0,0 +1,57 @@
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)]
pub struct LogsCommand {
#[arg()]
guest: String,
#[arg(short, long)]
follow: bool,
}
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));
}
}

View File

@ -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,15 +11,13 @@ 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)]
@ -37,20 +36,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 +59,10 @@ impl ControlCommand {
attach.run(client, events).await?;
}
Commands::Logs(logs) => {
logs.run(client, events).await?;
}
Commands::List(list) => {
list.run(client, events).await?;
}

View File

@ -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 {
@ -45,12 +45,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));
}
}

View File

@ -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)
}

View File

@ -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.4" }
krata-runtime = { path = "../runtime", version = "^0.0.4" }
log = { workspace = true }
prost = { workspace = true }
redb = { workspace = true }

View 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();
}
}

View File

@ -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(),
})?;
}
},

View File

@ -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 {

View File

@ -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(),
);

View File

@ -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 })
}
}

View File

@ -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.4" }
krata-xenstore = { path = "../xen/xenstore", version = "^0.0.4" }
libc = { workspace = true }
log = { workspace = true }
nix = { workspace = true, features = ["ioctl", "process", "fs"] }

View File

@ -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()?,
})
}

View File

@ -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(())
}

View File

@ -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"

View File

@ -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()
}

View File

@ -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 {

View File

@ -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>>,

View File

@ -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.4" }
krata-advmac = { workspace = true }
libc = { workspace = true }
log = { workspace = true }

View File

@ -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;

View File

@ -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"

View File

@ -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,
}

View File

@ -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.4" }
krata-advmac = { workspace = true }
krata-oci = { path = "../oci", version = "^0.0.3" }
krata-oci = { path = "../oci", version = "^0.0.4" }
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.4" }
krata-xenevtchn = { path = "../xen/xenevtchn", version = "^0.0.4" }
krata-xengnt = { path = "../xen/xengnt", version = "^0.0.4" }
krata-xenstore = { path = "../xen/xenstore", version = "^0.0.4" }
[lib]
name = "kratart"

View File

@ -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 {

View File

@ -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(())
}
}

View File

@ -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);
}
}

View File

@ -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"
));
}

View File

@ -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> {

View File

@ -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]

View File

@ -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(())
}

View File

@ -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(())
}

View File

@ -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(())
}

View File

@ -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(())
}

View File

@ -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>;

View File

@ -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(|_| ())
}
}

View File

@ -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.4" }
krata-xenstore = { path = "../xenstore", version = "^0.0.4" }
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"

View File

@ -13,7 +13,7 @@ async fn main() -> Result<()> {
}
let kernel_image_path = args.get(1).expect("argument not specified");
let initrd_path = args.get(2).expect("argument not specified");
let mut client = XenClient::open(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![],

View File

@ -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(())
}
}

View File

@ -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<()>;
}

View File

@ -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}")]

View File

@ -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;

View File

@ -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);
}

View File

@ -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(())
}
}

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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();
}

View File

@ -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

View File

@ -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;

View 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
View File

View 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 ./*

View File

@ -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"

View File

@ -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

View File

@ -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

20
release-plz.toml Normal file
View File

@ -0,0 +1,20 @@
[workspace]
git_release_enable = false
git_tag_enable = false
changelog_update = false
[[package]]
name = "krata"
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"
]