Compare commits

..

40 Commits

Author SHA1 Message Date
2526065d74 chore: release (#32)
Co-authored-by: edera-cultivation[bot] <165992271+edera-cultivation[bot]@users.noreply.github.com>
2024-04-08 22:18:29 -07:00
a509f69398 chore: release (#24)
Signed-off-by: edera-cultivation[bot] <165992271+edera-cultivation[bot]@users.noreply.github.com>
Co-authored-by: edera-cultivation[bot] <165992271+edera-cultivation[bot]@users.noreply.github.com>
2024-04-08 22:12:38 -07:00
d021fb2147 build(deps): bump tokio-tun from 0.11.2 to 0.11.4 (#27)
Bumps [tokio-tun](https://github.com/yaa110/tokio-tun) from 0.11.2 to 0.11.4.
- [Commits](https://github.com/yaa110/tokio-tun/compare/0.11.2...0.11.4)

---
updated-dependencies:
- dependency-name: tokio-tun
  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-09 04:30:24 +00:00
d33079c7c5 build(deps): bump comfy-table from 7.1.0 to 7.1.1 (#28)
Bumps [comfy-table](https://github.com/nukesor/comfy-table) from 7.1.0 to 7.1.1.
- [Release notes](https://github.com/nukesor/comfy-table/releases)
- [Changelog](https://github.com/Nukesor/comfy-table/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nukesor/comfy-table/compare/v7.1.0...v7.1.1)

---
updated-dependencies:
- dependency-name: comfy-table
  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-09 04:30:14 +00:00
bea6d1e6fe build(deps): bump reqwest from 0.12.2 to 0.12.3 (#29)
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.12.2 to 0.12.3.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.2...v0.12.3)

---
updated-dependencies:
- dependency-name: reqwest
  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-09 04:30:06 +00:00
bc27eeb286 build(deps): bump prost from 0.12.3 to 0.12.4 (#30)
Bumps [prost](https://github.com/tokio-rs/prost) from 0.12.3 to 0.12.4.
- [Release notes](https://github.com/tokio-rs/prost/releases)
- [Commits](https://github.com/tokio-rs/prost/compare/v0.12.3...v0.12.4)

---
updated-dependencies:
- dependency-name: prost
  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-09 04:30:01 +00:00
42bb289421 build(deps): bump async-compression from 0.4.7 to 0.4.8 (#31)
Bumps [async-compression](https://github.com/Nullus157/async-compression) from 0.4.7 to 0.4.8.
- [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.7...async-compression-v0.4.8)

---
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-09 04:29:55 +00:00
f2ab03711e feat(ctl): add help and about to commands and arguments (#25) 2024-04-06 00:00:02 +00:00
2f3daad80a build(deps): bump h2 from 0.3.25 to 0.3.26 (#26)
Bumps [h2](https://github.com/hyperium/h2) from 0.3.25 to 0.3.26.
- [Release notes](https://github.com/hyperium/h2/releases)
- [Changelog](https://github.com/hyperium/h2/blob/v0.3.26/CHANGELOG.md)
- [Commits](https://github.com/hyperium/h2/compare/v0.3.25...v0.3.26)

---
updated-dependencies:
- dependency-name: h2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-05 22:15:12 +00:00
8f7e47a218 chore: release workflow fixes to improve utilization and fix checks (#23)
* chore: use edera-cultivation bot to push release changes

* chore: workflows now largely only run on pull requests or merge queues
2024-04-04 23:05:59 -07:00
19683b80c1 fix: service files should have kratanet depend on kratad (#18) 2024-04-04 23:44:57 +00:00
ae486e347f fix: release pr trigger problem (#21) 2024-04-03 23:59:07 -07:00
9f4db08e07 build(deps): bump prost-reflect from 0.13.0 to 0.13.1 (#19)
Bumps [prost-reflect](https://github.com/andrewhickman/prost-reflect) from 0.13.0 to 0.13.1.
- [Changelog](https://github.com/andrewhickman/prost-reflect/blob/main/CHANGELOG.md)
- [Commits](https://github.com/andrewhickman/prost-reflect/compare/0.13.0...0.13.1)

---
updated-dependencies:
- dependency-name: prost-reflect
  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-04 06:05:45 +00:00
d674a69c17 ci: fix tag names and disable semver checks for low-level crates (#17) 2024-04-03 17:16:30 +00:00
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
78 changed files with 4919 additions and 690 deletions

View File

@ -1,11 +1,19 @@
name: check name: check
on: [push, pull_request] on:
pull_request:
branches:
- main
merge_group:
branches:
- main
jobs: jobs:
fmt: fmt:
name: fmt name: fmt
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
components: rustfmt components: rustfmt
@ -16,4 +24,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
- run: ./hack/code/shellcheck.sh - run: ./hack/code/shellcheck.sh

View File

@ -1,5 +1,11 @@
name: client name: client
on: [push, pull_request] on:
pull_request:
branches:
- main
merge_group:
branches:
- main
jobs: jobs:
build: build:
strategy: strategy:
@ -21,7 +27,11 @@ jobs:
run: run:
shell: bash shell: bash
steps: steps:
- run: git config --global core.autocrlf false && git config --global core.eol lf
if: ${{ matrix.platform.os == 'windows' }}
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
if: ${{ matrix.platform.os != 'darwin' }} if: ${{ matrix.platform.os != 'darwin' }}
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable

View File

@ -1,17 +1,19 @@
name: kernel name: kernel
on: on:
push:
paths:
- "kernel/**"
- "hack/ci/**"
pull_request: pull_request:
branches:
- main
paths: paths:
- "kernel/**" - "kernel/**"
- "hack/ci/**" - "hack/ci/**"
merge_group:
branches:
- main
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false
matrix: matrix:
arch: arch:
- x86_64 - x86_64
@ -21,6 +23,8 @@ jobs:
name: build ${{ matrix.arch }} name: build ${{ matrix.arch }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- run: ./hack/ci/install-linux-deps.sh - run: ./hack/ci/install-linux-deps.sh
- run: ./hack/kernel/build.sh - run: ./hack/kernel/build.sh

View File

@ -17,6 +17,8 @@ jobs:
name: server ${{ matrix.arch }} name: server ${{ matrix.arch }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
targets: "${{ matrix.arch }}-unknown-linux-gnu,${{ matrix.arch }}-unknown-linux-musl" targets: "${{ matrix.arch }}-unknown-linux-gnu,${{ matrix.arch }}-unknown-linux-musl"
@ -73,7 +75,11 @@ jobs:
run: run:
shell: bash shell: bash
steps: steps:
- run: git config --global core.autocrlf false && git config --global core.eol lf
if: ${{ matrix.platform.os == 'windows' }}
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
if: ${{ matrix.platform.os != 'darwin' }} if: ${{ matrix.platform.os != 'darwin' }}
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
@ -87,4 +93,10 @@ jobs:
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: kratactl-${{ matrix.platform.os }}-${{ matrix.platform.arch }} 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

@ -1,19 +1,20 @@
name: os name: os
on: on:
push:
paths:
- "os/**"
- "hack/os/**"
- "hack/ci/**"
pull_request: pull_request:
branches:
- main
paths: paths:
- "os/**" - "os/**"
- "hack/os/**" - "hack/os/**"
- "hack/ci/**" - "hack/ci/**"
merge_group:
branches:
- main
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false
matrix: matrix:
arch: arch:
- x86_64 - x86_64
@ -23,6 +24,8 @@ jobs:
name: build ${{ matrix.arch }} name: build ${{ matrix.arch }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
targets: "${{ matrix.arch }}-unknown-linux-gnu,${{ matrix.arch }}-unknown-linux-musl" 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 }}"

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

@ -0,0 +1,32 @@
name: release-plz
permissions:
pull-requests: write
contents: write
on:
push:
branches:
- main
concurrency:
group: "${{ github.workflow }}"
jobs:
release-plz:
name: release-plz
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
id: generate-token
with:
app-id: "${{ secrets.EDERA_CULTIVATION_APP_ID }}"
private-key: "${{ secrets.EDERA_CULTIVATION_APP_PRIVATE_KEY }}"
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
token: "${{ steps.generate-token.outputs.token }}"
- uses: dtolnay/rust-toolchain@stable
- run: ./hack/ci/install-linux-deps.sh
- name: release-plz
uses: MarcoIeni/release-plz-action@v0.5
env:
GITHUB_TOKEN: "${{ steps.generate-token.outputs.token }}"
CARGO_REGISTRY_TOKEN: "${{ secrets.KRATA_RELEASE_CARGO_TOKEN }}"

View File

@ -1,9 +1,16 @@
name: server name: server
on: [push, pull_request] on:
pull_request:
branches:
- main
merge_group:
branches:
- main
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false
matrix: matrix:
arch: arch:
- x86_64 - x86_64
@ -13,6 +20,8 @@ jobs:
name: build ${{ matrix.arch }} name: build ${{ matrix.arch }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- run: ./hack/ci/install-linux-deps.sh - run: ./hack/ci/install-linux-deps.sh
- run: ./hack/build/cargo.sh build - run: ./hack/build/cargo.sh build
@ -28,6 +37,8 @@ jobs:
name: test ${{ matrix.arch }} name: test ${{ matrix.arch }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- run: ./hack/ci/install-linux-deps.sh - run: ./hack/ci/install-linux-deps.sh
- run: ./hack/build/cargo.sh test - run: ./hack/build/cargo.sh test
@ -43,6 +54,8 @@ jobs:
name: clippy ${{ matrix.arch }} name: clippy ${{ matrix.arch }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
components: clippy components: clippy
@ -60,6 +73,8 @@ jobs:
name: initrd ${{ matrix.arch }} name: initrd ${{ matrix.arch }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
targets: "${{ matrix.arch }}-unknown-linux-gnu,${{ matrix.arch }}-unknown-linux-musl" targets: "${{ matrix.arch }}-unknown-linux-gnu,${{ matrix.arch }}-unknown-linux-musl"

1
.gitignore vendored
View File

@ -1,2 +1 @@
/target /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

30
CHANGELOG.md Normal file
View File

@ -0,0 +1,30 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.0.5](https://github.com/edera-dev/krata/compare/v0.0.4...v0.0.5) - 2024-04-09
### Added
- *(ctl)* add help and about to commands and arguments ([#25](https://github.com/edera-dev/krata/pull/25))
### Other
- update Cargo.toml dependencies
- update Cargo.lock dependencies
## [0.0.4](https://github.com/edera-dev/krata/releases/tag/v${version}) - 2024-04-03
### Other
- implement automatic releases
- reimplement console to utilize channels, and provide logs support
- set hostname from launch config
- implement event stream retries
- work on parallel reconciliation
- implement parallel guest reconciliation
- log when a guest start failures occurs
- remove device restriction
- setup loopback interface
- place running tasks in cgroup

3317
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,7 @@ members = [
resolver = "2" resolver = "2"
[workspace.package] [workspace.package]
version = "0.0.3" version = "0.0.5"
homepage = "https://krata.dev" homepage = "https://krata.dev"
license = "Apache-2.0" license = "Apache-2.0"
repository = "https://github.com/edera-dev/krata" repository = "https://github.com/edera-dev/krata"
@ -24,18 +24,20 @@ repository = "https://github.com/edera-dev/krata"
[workspace.dependencies] [workspace.dependencies]
anyhow = "1.0" anyhow = "1.0"
arrayvec = "0.7.4" arrayvec = "0.7.4"
async-compression = "0.4.6" async-compression = "0.4.8"
async-stream = "0.3.5" async-stream = "0.3.5"
async-trait = "0.1.77" async-trait = "0.1.77"
backhand = "0.15.0" backhand = "0.15.0"
byteorder = "1" byteorder = "1"
bytes = "1.5.0" bytes = "1.5.0"
cli-tables = "0.2.1" cgroups-rs = "0.3.4"
circular-buffer = "0.1.7"
comfy-table = "7.1.1"
crossterm = "0.27.0" crossterm = "0.27.0"
ctrlc = "3.4.4" ctrlc = "3.4.4"
elf = "0.7.4" elf = "0.7.4"
env_logger = "0.11.0" env_logger = "0.11.0"
etherparse = "0.14.2" etherparse = "0.14.3"
flate2 = "1.0" flate2 = "1.0"
futures = "0.3.30" futures = "0.3.30"
ipnetwork = "0.20.0" ipnetwork = "0.20.0"
@ -50,7 +52,7 @@ oci-spec = "0.6.4"
once_cell = "1.19.0" once_cell = "1.19.0"
path-absolutize = "3.1.1" path-absolutize = "3.1.1"
path-clean = "1.0.1" path-clean = "1.0.1"
prost = "0.12.3" prost = "0.12.4"
prost-build = "0.12.3" prost-build = "0.12.3"
prost-reflect-build = "0.13.0" prost-reflect-build = "0.13.0"
rand = "0.8.5" rand = "0.8.5"
@ -63,7 +65,7 @@ signal-hook = "0.3.17"
slice-copy = "0.3.0" slice-copy = "0.3.0"
smoltcp = "0.11.0" smoltcp = "0.11.0"
thiserror = "1.0" thiserror = "1.0"
tokio-tun = "0.11.2" tokio-tun = "0.11.4"
tonic-build = "0.11.0" tonic-build = "0.11.0"
tower = "0.4.13" tower = "0.4.13"
udp-stream = "0.0.11" udp-stream = "0.0.11"
@ -76,11 +78,11 @@ version = "4.4.18"
features = ["derive"] features = ["derive"]
[workspace.dependencies.prost-reflect] [workspace.dependencies.prost-reflect]
version = "0.13.0" version = "0.13.1"
features = ["derive"] features = ["derive"]
[workspace.dependencies.reqwest] [workspace.dependencies.reqwest]
version = "0.12.0" version = "0.12.3"
default-features = false default-features = false
features = ["rustls-tls"] features = ["rustls-tls"]

View File

@ -16,4 +16,13 @@ The Edera Hypervisor
## Introduction ## 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" name = "krata-ctl"
description = "Command-line tool to control the krata hypervisor" description = "Command-line tool to control the krata hypervisor"
license.workspace = true license.workspace = true
version= "0.0.3" version.workspace = true
homepage.workspace = true homepage.workspace = true
repository.workspace = true repository.workspace = true
edition = "2021" edition = "2021"
@ -12,11 +12,11 @@ resolver = "2"
anyhow = { workspace = true } anyhow = { workspace = true }
async-stream = { workspace = true } async-stream = { workspace = true }
clap = { workspace = true } clap = { workspace = true }
cli-tables = { workspace = true } comfy-table = { workspace = true }
crossterm = { workspace = true } crossterm = { workspace = true }
ctrlc = { workspace = true, features = ["termination"] } ctrlc = { workspace = true, features = ["termination"] }
env_logger = { workspace = true } env_logger = { workspace = true }
krata = { path = "../krata", version = "^0.0.3" } krata = { path = "../krata", version = "^0.0.5" }
log = { workspace = true } log = { workspace = true }
prost-reflect = { workspace = true, features = ["serde"] } prost-reflect = { workspace = true, features = ["serde"] }
serde_json = { workspace = true } serde_json = { workspace = true }

View File

@ -10,8 +10,9 @@ use crate::console::StdioConsoleStream;
use super::resolve_guest; use super::resolve_guest;
#[derive(Parser)] #[derive(Parser)]
#[command(about = "Attach to the guest console")]
pub struct AttachCommand { pub struct AttachCommand {
#[arg()] #[arg(help = "Guest to attach to, either the name or the uuid")]
guest: String, guest: String,
} }

View File

@ -17,10 +17,15 @@ use tonic::{transport::Channel, Request};
use crate::cli::resolve_guest; use crate::cli::resolve_guest;
#[derive(Parser)] #[derive(Parser)]
#[command(about = "Destroy a guest")]
pub struct DestroyCommand { pub struct DestroyCommand {
#[arg(short = 'W', long)] #[arg(
short = 'W',
long,
help = "Wait for the destruction of the guest to complete"
)]
wait: bool, wait: bool,
#[arg()] #[arg(help = "Guest to destroy, either the name or the uuid")]
guest: String, guest: String,
} }

View File

@ -22,23 +22,46 @@ use tonic::{transport::Channel, Request};
use crate::console::StdioConsoleStream; use crate::console::StdioConsoleStream;
#[derive(Parser)] #[derive(Parser)]
#[command(about = "Launch a new guest")]
pub struct LauchCommand { pub struct LauchCommand {
#[arg(short, long)] #[arg(short, long, help = "Name of the guest")]
name: Option<String>, name: Option<String>,
#[arg(short, long, default_value_t = 1)] #[arg(
short,
long,
default_value_t = 1,
help = "vCPUs available to the guest"
)]
cpus: u32, cpus: u32,
#[arg(short, long, default_value_t = 512)] #[arg(
short,
long,
default_value_t = 512,
help = "Memory available to the guest, in megabytes"
)]
mem: u64, mem: u64,
#[arg[short, long]] #[arg[short, long, help = "Environment variables set in the guest"]]
env: Option<Vec<String>>, env: Option<Vec<String>>,
#[arg(short, long)] #[arg(
short,
long,
help = "Attach to the guest after guest starts, implies --wait"
)]
attach: bool, attach: bool,
#[arg(short = 'W', long)] #[arg(
short = 'W',
long,
help = "Wait for the guest to start, implied by --attach"
)]
wait: bool, wait: bool,
#[arg()] #[arg(help = "Container image for guest to use")]
oci: String, oci: String,
#[arg(allow_hyphen_values = true, trailing_var_arg = true)] #[arg(
run: Vec<String>, allow_hyphen_values = true,
trailing_var_arg = true,
help = "Command to run inside the guest"
)]
command: Vec<String>,
} }
impl LauchCommand { impl LauchCommand {
@ -63,7 +86,7 @@ impl LauchCommand {
value: value.clone(), value: value.clone(),
}) })
.collect(), .collect(),
command: self.run, command: self.command,
}), }),
annotations: vec![], annotations: vec![],
}), }),

View File

@ -1,10 +1,10 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use clap::{Parser, ValueEnum}; use clap::{Parser, ValueEnum};
use cli_tables::Table; use comfy_table::{presets::UTF8_FULL_CONDENSED, Cell, Color, Table};
use krata::{ use krata::{
events::EventStream, events::EventStream,
v1::{ v1::{
common::{guest_image_spec::Image, Guest, GuestStatus}, common::{Guest, GuestStatus},
control::{ control::{
control_service_client::ControlServiceClient, ListGuestsRequest, ResolveGuestRequest, control_service_client::ControlServiceClient, ListGuestsRequest, ResolveGuestRequest,
}, },
@ -14,11 +14,11 @@ use krata::{
use serde_json::Value; use serde_json::Value;
use tonic::{transport::Channel, Request}; 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)] #[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
enum ListFormat { enum ListFormat {
CliTable, Table,
Json, Json,
JsonPretty, JsonPretty,
Jsonl, Jsonl,
@ -28,10 +28,11 @@ enum ListFormat {
} }
#[derive(Parser)] #[derive(Parser)]
#[command(about = "List the guests on the hypervisor")]
pub struct ListCommand { pub struct ListCommand {
#[arg(short, long, default_value = "cli-table")] #[arg(short, long, default_value = "table", help = "Output format")]
format: ListFormat, format: ListFormat,
#[arg()] #[arg(help = "Limit to a single guest, either the name or the uuid")]
guest: Option<String>, guest: Option<String>,
} }
@ -70,24 +71,13 @@ impl ListCommand {
}); });
match self.format { match self.format {
ListFormat::CliTable => { ListFormat::Table => {
self.print_guest_table(guests)?; self.print_guest_table(guests)?;
} }
ListFormat::Simple => { ListFormat::Simple => {
for guest in guests { for guest in guests {
let state = guest_status_text( println!("{}", guest_simple_line(&guest));
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);
} }
} }
@ -125,49 +115,51 @@ impl ListCommand {
fn print_guest_table(&self, guests: Vec<Guest>) -> Result<()> { fn print_guest_table(&self, guests: Vec<Guest>) -> Result<()> {
let mut table = Table::new(); let mut table = Table::new();
let header = vec!["name", "uuid", "state", "ipv4", "ipv6", "image"]; table.load_preset(UTF8_FULL_CONDENSED);
table.push_row(&header)?; table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
table.set_header(vec!["name", "uuid", "status", "ipv4", "ipv6"]);
for guest in guests { for guest in guests {
let ipv4 = guest let ipv4 = guest
.state .state
.as_ref() .as_ref()
.and_then(|x| x.network.as_ref()) .and_then(|x| x.network.as_ref())
.map(|x| x.guest_ipv4.as_str()) .map(|x| x.guest_ipv4.as_str())
.unwrap_or("unknown"); .unwrap_or("n/a");
let ipv6 = guest let ipv6 = guest
.state .state
.as_ref() .as_ref()
.and_then(|x| x.network.as_ref()) .and_then(|x| x.network.as_ref())
.map(|x| x.guest_ipv6.as_str()) .map(|x| x.guest_ipv6.as_str())
.unwrap_or("unknown"); .unwrap_or("n/a");
let Some(spec) = guest.spec else { let Some(spec) = guest.spec else {
continue; continue;
}; };
let image = spec let status = guest.state.as_ref().cloned().unwrap_or_default().status();
.image let status_text = guest_status_text(status);
.map(|x| {
x.image let status_color = match status {
.map(|y| match y { GuestStatus::Destroyed | GuestStatus::Failed => Color::Red,
Image::Oci(oci) => oci.image, GuestStatus::Destroying | GuestStatus::Exited | GuestStatus::Starting => {
}) Color::Yellow
.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,
])?;
} }
if table.num_records() == 1 { 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.is_empty() {
if self.guest.is_none() { if self.guest.is_none() {
println!("no guests have been launched"); println!("no guests have been launched");
} }
} else { } else {
println!("{}", table.to_string()); println!("{}", table);
} }
Ok(()) Ok(())
} }

View File

@ -0,0 +1,58 @@
use anyhow::Result;
use async_stream::stream;
use clap::Parser;
use krata::{
events::EventStream,
v1::control::{control_service_client::ControlServiceClient, ConsoleDataRequest},
};
use tokio::select;
use tokio_stream::{pending, StreamExt};
use tonic::transport::Channel;
use crate::console::StdioConsoleStream;
use super::resolve_guest;
#[derive(Parser)]
#[command(about = "View the logs of a guest")]
pub struct LogsCommand {
#[arg(short, long, help = "Follow output from the guest")]
follow: bool,
#[arg(help = "Guest to show logs for, either the name or the uuid")]
guest: String,
}
impl LogsCommand {
pub async fn run(
self,
mut client: ControlServiceClient<Channel>,
events: EventStream,
) -> Result<()> {
let guest_id: String = resolve_guest(&mut client, &self.guest).await?;
let guest_id_stream = guest_id.clone();
let follow = self.follow;
let input = stream! {
yield ConsoleDataRequest { guest_id: guest_id_stream, data: Vec::new() };
if follow {
let mut pending = pending::<ConsoleDataRequest>();
while let Some(x) = pending.next().await {
yield x;
}
}
};
let output = client.console_data(input).await?.into_inner();
let stdout_handle =
tokio::task::spawn(async move { StdioConsoleStream::stdout(output).await });
let exit_hook_task = StdioConsoleStream::guest_exit_hook(guest_id.clone(), events).await?;
let code = select! {
x = stdout_handle => {
x??;
None
},
x = exit_hook_task => x?
};
StdioConsoleStream::restore_terminal_mode();
std::process::exit(code.unwrap_or(0));
}
}

View File

@ -2,6 +2,7 @@ pub mod attach;
pub mod destroy; pub mod destroy;
pub mod launch; pub mod launch;
pub mod list; pub mod list;
pub mod logs;
pub mod resolve; pub mod resolve;
pub mod watch; pub mod watch;
@ -10,21 +11,27 @@ use clap::{Parser, Subcommand};
use krata::{ use krata::{
client::ControlClientProvider, client::ControlClientProvider,
events::EventStream, events::EventStream,
v1::control::{ v1::control::{control_service_client::ControlServiceClient, ResolveGuestRequest},
control_service_client::ControlServiceClient, ResolveGuestRequest, WatchEventsRequest,
},
}; };
use tonic::{transport::Channel, Request}; use tonic::{transport::Channel, Request};
use self::{ use self::{
attach::AttachCommand, destroy::DestroyCommand, launch::LauchCommand, list::ListCommand, attach::AttachCommand, destroy::DestroyCommand, launch::LauchCommand, list::ListCommand,
resolve::ResolveCommand, watch::WatchCommand, logs::LogsCommand, resolve::ResolveCommand, watch::WatchCommand,
}; };
#[derive(Parser)] #[derive(Parser)]
#[command(version, about)] #[command(
version,
about = "Control the krata hypervisor, a secure platform for running containers"
)]
pub struct ControlCommand { pub struct ControlCommand {
#[arg(short, long, default_value = "unix:///var/lib/krata/daemon.socket")] #[arg(
short,
long,
help = "The connection URL to the krata hypervisor",
default_value = "unix:///var/lib/krata/daemon.socket"
)]
connection: String, connection: String,
#[command(subcommand)] #[command(subcommand)]
@ -37,20 +44,15 @@ pub enum Commands {
Destroy(DestroyCommand), Destroy(DestroyCommand),
List(ListCommand), List(ListCommand),
Attach(AttachCommand), Attach(AttachCommand),
Logs(LogsCommand),
Watch(WatchCommand), Watch(WatchCommand),
Resolve(ResolveCommand), Resolve(ResolveCommand),
} }
impl ControlCommand { impl ControlCommand {
pub async fn run(self) -> Result<()> { pub async fn run(self) -> Result<()> {
let mut client = ControlClientProvider::dial(self.connection.parse()?).await?; let client = ControlClientProvider::dial(self.connection.parse()?).await?;
let events = EventStream::open( let events = EventStream::open(client.clone()).await?;
client
.watch_events(WatchEventsRequest {})
.await?
.into_inner(),
)
.await?;
match self.command { match self.command {
Commands::Launch(launch) => { Commands::Launch(launch) => {
@ -65,6 +67,10 @@ impl ControlCommand {
attach.run(client, events).await?; attach.run(client, events).await?;
} }
Commands::Logs(logs) => {
logs.run(client, events).await?;
}
Commands::List(list) => { Commands::List(list) => {
list.run(client, events).await?; list.run(client, events).await?;
} }

View File

@ -5,8 +5,9 @@ use krata::v1::control::{control_service_client::ControlServiceClient, ResolveGu
use tonic::{transport::Channel, Request}; use tonic::{transport::Channel, Request};
#[derive(Parser)] #[derive(Parser)]
#[command(about = "Resolve a guest name to a uuid")]
pub struct ResolveCommand { pub struct ResolveCommand {
#[arg()] #[arg(help = "Guest name")]
guest: String, guest: String,
} }

View File

@ -7,7 +7,7 @@ use krata::{
use prost_reflect::ReflectMessage; use prost_reflect::ReflectMessage;
use serde_json::Value; 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)] #[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
enum WatchFormat { enum WatchFormat {
@ -17,8 +17,9 @@ enum WatchFormat {
} }
#[derive(Parser)] #[derive(Parser)]
#[command(about = "Watch for guest changes")]
pub struct WatchCommand { pub struct WatchCommand {
#[arg(short, long, default_value = "simple")] #[arg(short, long, default_value = "simple", help = "Output format")]
format: WatchFormat, format: WatchFormat,
} }
@ -45,12 +46,7 @@ impl WatchCommand {
match self.format { match self.format {
WatchFormat::Simple => { WatchFormat::Simple => {
if let Some(guest) = guest { if let Some(guest) = guest {
println!( println!("{}", guest_simple_line(&guest));
"{} guest={} status=\"{}\"",
typ,
guest.id,
guest_state_text(guest.state.as_ref()).replace('"', "\\\"")
);
} }
} }

View File

@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use anyhow::Result; use anyhow::Result;
use krata::v1::common::{GuestState, GuestStatus}; use krata::v1::common::{Guest, GuestStatus};
use prost_reflect::{DynamicMessage, ReflectMessage, Value}; use prost_reflect::{DynamicMessage, ReflectMessage, Value};
pub fn proto2dynamic(proto: impl ReflectMessage) -> Result<DynamicMessage> { pub fn proto2dynamic(proto: impl ReflectMessage) -> Result<DynamicMessage> {
@ -71,16 +71,17 @@ pub fn guest_status_text(status: GuestStatus) -> String {
.to_string() .to_string()
} }
pub fn guest_state_text(state: Option<&GuestState>) -> String { pub fn guest_simple_line(guest: &Guest) -> String {
let state = state.cloned().unwrap_or_default(); let state = guest_status_text(
let mut text = guest_status_text(state.status()); guest
.state
if let Some(exit) = state.exit_info { .as_ref()
text.push_str(&format!(" (exit code: {})", exit.code)); .map(|x| x.status())
} .unwrap_or(GuestStatus::Unknown),
);
if let Some(error) = state.error_info { let name = guest.spec.as_ref().map(|x| x.name.as_str()).unwrap_or("");
text.push_str(&format!(" (error: {})", error.message)); 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("");
text 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" name = "krata-daemon"
description = "Daemon for the krata hypervisor." description = "Daemon for the krata hypervisor."
license.workspace = true license.workspace = true
version= "0.0.3" version.workspace = true
homepage.workspace = true homepage.workspace = true
repository.workspace = true repository.workspace = true
edition = "2021" edition = "2021"
@ -13,11 +13,12 @@ anyhow = { workspace = true }
async-stream = { workspace = true } async-stream = { workspace = true }
async-trait = { workspace = true } async-trait = { workspace = true }
bytes = { workspace = true } bytes = { workspace = true }
circular-buffer = { workspace = true }
clap = { workspace = true } clap = { workspace = true }
env_logger = { workspace = true } env_logger = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
krata = { path = "../krata", version = "^0.0.3" } krata = { path = "../krata", version = "^0.0.5" }
krata-runtime = { path = "../runtime", version = "^0.0.3" } krata-runtime = { path = "../runtime", version = "^0.0.5" }
log = { workspace = true } log = { workspace = true }
prost = { workspace = true } prost = { workspace = true }
redb = { 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 async_stream::try_stream;
use futures::Stream; use futures::Stream;
@ -11,17 +11,15 @@ use krata::v1::{
WatchEventsReply, WatchEventsRequest, WatchEventsReply, WatchEventsRequest,
}, },
}; };
use kratart::Runtime;
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
select, select,
sync::mpsc::Sender, sync::mpsc::{channel, Sender},
}; };
use tokio_stream::StreamExt; use tokio_stream::StreamExt;
use tonic::{Request, Response, Status, Streaming}; use tonic::{Request, Response, Status, Streaming};
use uuid::Uuid; use uuid::Uuid;
use crate::{db::GuestStore, event::DaemonEventContext}; use crate::{console::DaemonConsoleHandle, db::GuestStore, event::DaemonEventContext};
pub struct ApiError { pub struct ApiError {
message: String, message: String,
@ -44,7 +42,7 @@ impl From<ApiError> for Status {
#[derive(Clone)] #[derive(Clone)]
pub struct RuntimeControlService { pub struct RuntimeControlService {
events: DaemonEventContext, events: DaemonEventContext,
runtime: Runtime, console: DaemonConsoleHandle,
guests: GuestStore, guests: GuestStore,
guest_reconciler_notify: Sender<Uuid>, guest_reconciler_notify: Sender<Uuid>,
} }
@ -52,13 +50,13 @@ pub struct RuntimeControlService {
impl RuntimeControlService { impl RuntimeControlService {
pub fn new( pub fn new(
events: DaemonEventContext, events: DaemonEventContext,
runtime: Runtime, console: DaemonConsoleHandle,
guests: GuestStore, guests: GuestStore,
guest_reconciler_notify: Sender<Uuid>, guest_reconciler_notify: Sender<Uuid>,
) -> Self { ) -> Self {
Self { Self {
events, events,
runtime, console,
guests, guests,
guest_reconciler_notify, guest_reconciler_notify,
} }
@ -66,7 +64,7 @@ impl RuntimeControlService {
} }
enum ConsoleDataSelect { enum ConsoleDataSelect {
Read(io::Result<usize>), Read(Option<Vec<u8>>),
Write(Option<Result<ConsoleDataRequest, tonic::Status>>), 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 { let uuid = Uuid::from_str(&request.guest_id).map_err(|error| ApiError {
message: error.to_string(), 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 output = try_stream! {
let mut buffer: Vec<u8> = vec![0u8; 256]; yield ConsoleDataReply { data: console.initial.clone(), };
loop { loop {
let what = select! { 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), x = input.next() => ConsoleDataSelect::Write(x),
}; };
match what { match what {
ConsoleDataSelect::Read(result) => { ConsoleDataSelect::Read(Some(data)) => {
let size = result?;
let data = buffer[0..size].to_vec();
yield ConsoleDataReply { data, }; yield ConsoleDataReply { data, };
}, },
ConsoleDataSelect::Read(None) => {
break;
}
ConsoleDataSelect::Write(Some(request)) => { ConsoleDataSelect::Write(Some(request)) => {
let request = request?; let request = request?;
if !request.data.is_empty() { 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 { impl DaemonIdm {
pub async fn new() -> Result<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 task = service.launch().await?;
let listeners = Arc::new(Mutex::new(HashMap::new())); let listeners = Arc::new(Mutex::new(HashMap::new()));
Ok(DaemonIdm { Ok(DaemonIdm {

View File

@ -1,6 +1,7 @@
use std::{net::SocketAddr, path::PathBuf, str::FromStr}; use std::{net::SocketAddr, path::PathBuf, str::FromStr};
use anyhow::Result; use anyhow::Result;
use console::{DaemonConsole, DaemonConsoleHandle};
use control::RuntimeControlService; use control::RuntimeControlService;
use db::GuestStore; use db::GuestStore;
use event::{DaemonEventContext, DaemonEventGenerator}; use event::{DaemonEventContext, DaemonEventGenerator};
@ -18,6 +19,7 @@ use tokio_stream::wrappers::UnixListenerStream;
use tonic::transport::{Identity, Server, ServerTlsConfig}; use tonic::transport::{Identity, Server, ServerTlsConfig};
use uuid::Uuid; use uuid::Uuid;
pub mod console;
pub mod control; pub mod control;
pub mod db; pub mod db;
pub mod event; pub mod event;
@ -26,13 +28,13 @@ pub mod reconcile;
pub struct Daemon { pub struct Daemon {
store: String, store: String,
runtime: Runtime,
guests: GuestStore, guests: GuestStore,
events: DaemonEventContext, events: DaemonEventContext,
guest_reconciler_task: JoinHandle<()>, guest_reconciler_task: JoinHandle<()>,
guest_reconciler_notify: Sender<Uuid>, guest_reconciler_notify: Sender<Uuid>,
generator_task: JoinHandle<()>, generator_task: JoinHandle<()>,
_idm: DaemonIdmHandle, _idm: DaemonIdmHandle,
console: DaemonConsoleHandle,
} }
const GUEST_RECONCILER_QUEUE_LEN: usize = 1000; const GUEST_RECONCILER_QUEUE_LEN: usize = 1000;
@ -45,31 +47,37 @@ impl Daemon {
channel::<Uuid>(GUEST_RECONCILER_QUEUE_LEN); channel::<Uuid>(GUEST_RECONCILER_QUEUE_LEN);
let idm = DaemonIdm::new().await?; let idm = DaemonIdm::new().await?;
let idm = idm.launch().await?; let idm = idm.launch().await?;
let console = DaemonConsole::new().await?;
let console = console.launch().await?;
let (events, generator) = let (events, generator) =
DaemonEventGenerator::new(guests.clone(), guest_reconciler_notify.clone(), idm.clone()) DaemonEventGenerator::new(guests.clone(), guest_reconciler_notify.clone(), idm.clone())
.await?; .await?;
let runtime_for_reconciler = runtime.dupe().await?; let runtime_for_reconciler = runtime.dupe().await?;
let guest_reconciler = let guest_reconciler = GuestReconciler::new(
GuestReconciler::new(guests.clone(), events.clone(), runtime_for_reconciler)?; guests.clone(),
events.clone(),
runtime_for_reconciler,
guest_reconciler_notify.clone(),
)?;
let guest_reconciler_task = guest_reconciler.launch(guest_reconciler_receiver).await?; let guest_reconciler_task = guest_reconciler.launch(guest_reconciler_receiver).await?;
let generator_task = generator.launch().await?; let generator_task = generator.launch().await?;
Ok(Self { Ok(Self {
store, store,
runtime,
guests, guests,
events, events,
guest_reconciler_task, guest_reconciler_task,
guest_reconciler_notify, guest_reconciler_notify,
generator_task, generator_task,
_idm: idm, _idm: idm,
console,
}) })
} }
pub async fn listen(&mut self, addr: ControlDialAddress) -> Result<()> { pub async fn listen(&mut self, addr: ControlDialAddress) -> Result<()> {
let control_service = RuntimeControlService::new( let control_service = RuntimeControlService::new(
self.events.clone(), self.events.clone(),
self.runtime.clone(), self.console.clone(),
self.guests.clone(), self.guests.clone(),
self.guest_reconciler_notify.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 anyhow::{anyhow, Result};
use krata::v1::{ use krata::v1::{
@ -10,7 +14,15 @@ use krata::v1::{
}; };
use kratart::{launch::GuestLaunchRequest, GuestInfo, Runtime}; use kratart::{launch::GuestLaunchRequest, GuestInfo, Runtime};
use log::{error, info, trace, warn}; 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 uuid::Uuid;
use crate::{ use crate::{
@ -18,18 +30,49 @@ use crate::{
event::{DaemonEvent, DaemonEventContext}, 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 { pub struct GuestReconciler {
guests: GuestStore, guests: GuestStore,
events: DaemonEventContext, events: DaemonEventContext,
runtime: Runtime, runtime: Runtime,
tasks: Arc<Mutex<HashMap<Uuid, GuestReconcilerEntry>>>,
guest_reconciler_notify: Sender<Uuid>,
reconcile_lock: Arc<RwLock<()>>,
} }
impl GuestReconciler { 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 { Ok(Self {
guests, guests,
events, events,
runtime, 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) => { Some(uuid) => {
if let Err(error) = self.reconcile(uuid).await { if let Err(error) = self.launch_task_if_needed(uuid).await {
error!("failed to reconcile guest {}: {}", uuid, error); 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<()> { pub async fn reconcile_runtime(&self, initial: bool) -> Result<()> {
let _permit = self.reconcile_lock.write().await;
trace!("reconciling runtime"); trace!("reconciling runtime");
let runtime_guests = self.runtime.list().await?; let runtime_guests = self.runtime.list().await?;
let stored_guests = self.guests.list().await?; let stored_guests = self.guests.list().await?;
@ -96,21 +147,20 @@ impl GuestReconciler {
if changed || initial { if changed || initial {
self.guests.update(uuid, stored_guest).await?; self.guests.update(uuid, stored_guest).await?;
if let Err(error) = self.reconcile(uuid).await { let _ = self.guest_reconciler_notify.try_send(uuid);
error!("failed to reconcile guest {}: {}", uuid, error);
}
} }
} }
Ok(()) 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 { let Some(mut guest) = self.guests.read(uuid).await? else {
warn!( warn!(
"notified of reconcile for guest {} but it didn't exist", "notified of reconcile for guest {} but it didn't exist",
uuid uuid
); );
return Ok(()); return Ok(false);
}; };
info!("reconciling guest {}", uuid); info!("reconciling guest {}", uuid);
@ -120,47 +170,55 @@ impl GuestReconciler {
guest: Some(guest.clone()), 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::Starting => self.start(uuid, &mut guest).await,
GuestStatus::Destroying | GuestStatus::Exited => self.destroy(uuid, &mut guest).await, GuestStatus::Exited => self.exited(&mut guest).await,
_ => Ok(false), GuestStatus::Destroying => self.destroy(uuid, &mut guest).await,
_ => Ok(GuestReconcilerResult::Unchanged),
}; };
let changed = match result { let result = match result {
Ok(changed) => changed, Ok(result) => result,
Err(error) => { Err(error) => {
guest.state = Some(guest.state.as_mut().cloned().unwrap_or_default()); 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().status = GuestStatus::Failed.into();
guest.state.as_mut().unwrap().error_info = Some(GuestErrorInfo { guest.state.as_mut().unwrap().error_info = Some(GuestErrorInfo {
message: error.to_string(), message: error.to_string(),
}); });
true warn!("failed to start guest {}: {}", guest.id, error);
GuestReconcilerResult::Changed { rerun: false }
} }
}; };
info!("reconciled guest {}", uuid); info!("reconciled guest {}", uuid);
let status = guest.state.as_ref().map(|x| x.status()).unwrap_or_default(); 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 { let event = DaemonEvent::GuestChanged(GuestChangedEvent {
guest: Some(guest.clone()), guest: Some(guest.clone()),
}); });
if destroyed { if destroyed {
self.guests.remove(uuid).await?; self.guests.remove(uuid).await?;
let mut map = self.tasks.lock().await;
map.remove(&uuid);
} else { } else {
self.guests.update(uuid, guest.clone()).await?; self.guests.update(uuid, guest.clone()).await?;
} }
self.events.send(event)?; self.events.send(event)?;
rerun
} else {
false
};
Ok(rerun)
} }
Ok(()) async fn start(&self, uuid: Uuid, guest: &mut Guest) -> Result<GuestReconcilerResult> {
}
async fn start(&self, uuid: Uuid, guest: &mut Guest) -> Result<bool> {
let Some(ref spec) = guest.spec else { let Some(ref spec) = guest.spec else {
return Err(anyhow!("guest spec not specified")); return Err(anyhow!("guest spec not specified"));
}; };
@ -206,10 +264,19 @@ impl GuestReconciler {
error_info: None, error_info: None,
domid: info.domid, 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 { if let Err(error) = self.runtime.destroy(uuid).await {
trace!("failed to destroy runtime guest {}: {}", uuid, error); trace!("failed to destroy runtime guest {}: {}", uuid, error);
} }
@ -222,7 +289,46 @@ impl GuestReconciler {
error_info: None, error_info: None,
domid: guest.state.as_ref().map(|x| x.domid).unwrap_or(u32::MAX), 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" name = "krata-guest"
description = "Guest services for the krata hypervisor." description = "Guest services for the krata hypervisor."
license.workspace = true license.workspace = true
version= "0.0.3" version.workspace = true
homepage.workspace = true homepage.workspace = true
repository.workspace = true repository.workspace = true
edition = "2021" edition = "2021"
@ -10,11 +10,12 @@ resolver = "2"
[dependencies] [dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }
cgroups-rs = { workspace = true }
env_logger = { workspace = true } env_logger = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
ipnetwork = { workspace = true } ipnetwork = { workspace = true }
krata = { path = "../krata", version = "^0.0.3" } krata = { path = "../krata", version = "^0.0.5" }
krata-xenstore = { path = "../xen/xenstore", version = "^0.0.3" } krata-xenstore = { path = "../xen/xenstore", version = "^0.0.5" }
libc = { workspace = true } libc = { workspace = true }
log = { workspace = true } log = { workspace = true }
nix = { workspace = true, features = ["ioctl", "process", "fs"] } nix = { workspace = true, features = ["ioctl", "process", "fs"] }

View File

@ -3,6 +3,7 @@ use crate::{
death, death,
}; };
use anyhow::Result; use anyhow::Result;
use cgroups_rs::Cgroup;
use krata::idm::{ use krata::idm::{
client::IdmClient, client::IdmClient,
protocol::{idm_event::Event, IdmEvent, IdmExitEvent, IdmPacket}, protocol::{idm_event::Event, IdmEvent, IdmExitEvent, IdmPacket},
@ -14,14 +15,16 @@ use tokio::select;
pub struct GuestBackground { pub struct GuestBackground {
idm: IdmClient, idm: IdmClient,
child: Pid, child: Pid,
_cgroup: Cgroup,
wait: ChildWait, wait: ChildWait,
} }
impl GuestBackground { 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 { Ok(GuestBackground {
idm, idm,
child, child,
_cgroup: cgroup,
wait: ChildWait::new()?, wait: ChildWait::new()?,
}) })
} }

View File

@ -1,10 +1,11 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use cgroups_rs::{Cgroup, CgroupPid};
use futures::stream::TryStreamExt; use futures::stream::TryStreamExt;
use ipnetwork::IpNetwork; use ipnetwork::IpNetwork;
use krata::ethtool::EthtoolHandle; use krata::ethtool::EthtoolHandle;
use krata::idm::client::IdmClient; use krata::idm::client::IdmClient;
use krata::launchcfg::{LaunchInfo, LaunchNetwork}; use krata::launchcfg::{LaunchInfo, LaunchNetwork};
use libc::{setsid, TIOCSCTTY}; use libc::{sethostname, setsid, TIOCSCTTY};
use log::{trace, warn}; use log::{trace, warn};
use nix::ioctl_write_int_bad; use nix::ioctl_write_int_bad;
use nix::unistd::{dup2, execve, fork, ForkResult, Pid}; 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::fd::AsRawFd;
use std::os::linux::fs::MetadataExt; use std::os::linux::fs::MetadataExt;
use std::os::unix::ffi::OsStrExt; 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::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use sys_mount::{FilesystemType, Mount, MountFlags}; use sys_mount::{FilesystemType, Mount, MountFlags};
@ -90,6 +91,18 @@ impl GuestInit {
self.nuke_initrd().await?; self.nuke_initrd().await?;
self.bind_new_root().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 { if let Some(network) = &launch.network {
trace!("initializing network"); trace!("initializing network");
if let Err(error) = self.network_setup(network).await { if let Err(error) = self.network_setup(network).await {
@ -112,14 +125,20 @@ impl GuestInit {
trace!("early init"); trace!("early init");
self.create_dir("/dev", Some(0o0755)).await?; self.create_dir("/dev", Some(0o0755)).await?;
self.create_dir("/proc", None).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("/root", Some(0o0700)).await?;
self.create_dir("/tmp", None).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?; .await?;
self.mount_kernel_fs("proc", "/proc", "").await?;
self.mount_kernel_fs("sysfs", "/sys", "").await?;
symlink("/proc/self/fd", "/dev/fd")?;
Ok(()) Ok(())
} }
@ -137,16 +156,19 @@ impl GuestInit {
Ok(()) Ok(())
} }
async fn mount_kernel_fs(&mut self, fstype: &str, path: &str, data: &str) -> Result<()> { async fn mount_kernel_fs(
let metadata = fs::metadata(path).await?; &mut self,
if metadata.st_dev() == fs::metadata("/").await?.st_dev() { fstype: &str,
path: &str,
data: &str,
flags: Option<MountFlags>,
) -> Result<()> {
trace!("mounting kernel fs {} to {}", fstype, path); trace!("mounting kernel fs {} to {}", fstype, path);
Mount::builder() Mount::builder()
.fstype(FilesystemType::Manual(fstype)) .fstype(FilesystemType::Manual(fstype))
.flags(MountFlags::NOEXEC | MountFlags::NOSUID) .flags(MountFlags::NOEXEC | MountFlags::NOSUID | flags.unwrap_or(MountFlags::empty()))
.data(data) .data(data)
.mount(fstype, path)?; .mount(fstype, path)?;
}
Ok(()) Ok(())
} }
@ -327,6 +349,14 @@ impl GuestInit {
let (connection, handle, _) = rtnetlink::new_connection()?; let (connection, handle, _) = rtnetlink::new_connection()?;
tokio::spawn(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_network: IpNetwork = network.ipv4.address.parse()?;
let ipv4_gateway: Ipv4Addr = network.ipv4.gateway.parse()?; let ipv4_gateway: Ipv4Addr = network.ipv4.gateway.parse()?;
let ipv6_network: IpNetwork = network.ipv6.address.parse()?; let ipv6_network: IpNetwork = network.ipv6.address.parse()?;
@ -406,7 +436,7 @@ impl GuestInit {
}; };
if launch.run.is_some() { 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() { if let Some(entrypoint) = config.entrypoint() {
@ -454,10 +484,21 @@ impl GuestInit {
working_dir = "/".to_string(); 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(()) 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>> { fn strings_as_cstrings(values: Vec<String>) -> Result<Vec<CString>> {
let mut results: Vec<CString> = vec![]; let mut results: Vec<CString> = vec![];
for value in values { for value in values {
@ -506,19 +547,21 @@ impl GuestInit {
async fn fork_and_exec( async fn fork_and_exec(
&mut self, &mut self,
idm: IdmClient, idm: IdmClient,
cgroup: Cgroup,
working_dir: String, working_dir: String,
path: CString, path: CString,
cmd: Vec<CString>, cmd: Vec<CString>,
env: Vec<CString>, env: Vec<CString>,
) -> Result<()> { ) -> Result<()> {
match unsafe { fork()? } { match unsafe { fork()? } {
ForkResult::Parent { child } => self.background(idm, child).await, ForkResult::Parent { child } => self.background(idm, cgroup, child).await,
ForkResult::Child => self.foreground(working_dir, path, cmd, env).await, ForkResult::Child => self.foreground(cgroup, working_dir, path, cmd, env).await,
} }
} }
async fn foreground( async fn foreground(
&mut self, &mut self,
cgroup: Cgroup,
working_dir: String, working_dir: String,
path: CString, path: CString,
cmd: Vec<CString>, cmd: Vec<CString>,
@ -526,6 +569,7 @@ impl GuestInit {
) -> Result<()> { ) -> Result<()> {
GuestInit::set_controlling_terminal()?; GuestInit::set_controlling_terminal()?;
std::env::set_current_dir(working_dir)?; std::env::set_current_dir(working_dir)?;
cgroup.add_task(CgroupPid::from(std::process::id() as u64))?;
execve(&path, &cmd, &env)?; execve(&path, &cmd, &env)?;
Ok(()) Ok(())
} }
@ -538,8 +582,8 @@ impl GuestInit {
Ok(()) Ok(())
} }
async fn background(&mut self, idm: IdmClient, executed: Pid) -> Result<()> { async fn background(&mut self, idm: IdmClient, cgroup: Cgroup, executed: Pid) -> Result<()> {
let mut background = GuestBackground::new(idm, executed).await?; let mut background = GuestBackground::new(idm, cgroup, executed).await?;
background.run().await?; background.run().await?;
Ok(()) Ok(())
} }

View File

@ -2,7 +2,7 @@
name = "krata" name = "krata"
description = "Client library and common services for the krata hypervisor." description = "Client library and common services for the krata hypervisor."
license.workspace = true license.workspace = true
version= "0.0.3" version.workspace = true
homepage.workspace = true homepage.workspace = true
repository.workspace = true repository.workspace = true
edition = "2021" 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 anyhow::Result;
use log::trace; use log::{error, trace, warn};
use tokio::{sync::broadcast, task::JoinHandle}; use tokio::{sync::broadcast, task::JoinHandle, time::sleep};
use tokio_stream::StreamExt; use tokio_stream::StreamExt;
use tonic::Streaming; use tonic::{transport::Channel, Streaming};
#[derive(Clone)] #[derive(Clone)]
pub struct EventStream { pub struct EventStream {
@ -14,27 +17,12 @@ pub struct EventStream {
} }
impl 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 (sender, _) = broadcast::channel(1000);
let emit = sender.clone(); let emit = sender.clone();
let task = tokio::task::spawn(async move { let task = tokio::task::spawn(async move {
loop { if let Err(error) = EventStream::process(client, emit).await {
let Some(result) = events.next().await else { error!("failed to process event stream: {}", error);
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);
} }
}); });
Ok(Self { 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> { pub fn subscribe(&self) -> broadcast::Receiver<Event> {
self.sender.subscribe() self.sender.subscribe()
} }

View File

@ -3,7 +3,7 @@ use std::path::Path;
use super::protocol::IdmPacket; use super::protocol::IdmPacket;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use bytes::BytesMut; use bytes::BytesMut;
use log::error; use log::{debug, error};
use nix::sys::termios::{cfmakeraw, tcgetattr, tcsetattr, SetArg}; use nix::sys::termios::{cfmakeraw, tcgetattr, tcsetattr, SetArg};
use prost::Message; use prost::Message;
use tokio::{ use tokio::{
@ -41,7 +41,7 @@ impl IdmClient {
let (tx_sender, tx_receiver) = channel(IDM_PACKET_QUEUE_LEN); let (tx_sender, tx_receiver) = channel(IDM_PACKET_QUEUE_LEN);
let task = tokio::task::spawn(async move { let task = tokio::task::spawn(async move {
if let Err(error) = IdmClient::process(file, rx_sender, tx_receiver).await { 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 { Ok(IdmClient {

View File

@ -29,6 +29,7 @@ pub struct LaunchNetwork {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct LaunchInfo { pub struct LaunchInfo {
pub hostname: Option<String>,
pub network: Option<LaunchNetwork>, pub network: Option<LaunchNetwork>,
pub env: HashMap<String, String>, pub env: HashMap<String, String>,
pub run: Option<Vec<String>>, pub run: Option<Vec<String>>,

View File

@ -2,7 +2,7 @@
name = "krata-network" name = "krata-network"
description = "Networking services for the krata hypervisor." description = "Networking services for the krata hypervisor."
license.workspace = true license.workspace = true
version= "0.0.3" version.workspace = true
homepage.workspace = true homepage.workspace = true
repository.workspace = true repository.workspace = true
edition = "2021" edition = "2021"
@ -16,7 +16,7 @@ clap = { workspace = true }
env_logger = { workspace = true } env_logger = { workspace = true }
etherparse = { workspace = true } etherparse = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
krata = { path = "../krata", version = "^0.0.3" } krata = { path = "../krata", version = "^0.0.5" }
krata-advmac = { workspace = true } krata-advmac = { workspace = true }
libc = { workspace = true } libc = { workspace = true }
log = { workspace = true } log = { workspace = true }

View File

@ -5,7 +5,7 @@ use krata::{
common::Guest, common::Guest,
control::{ control::{
control_service_client::ControlServiceClient, watch_events_reply::Event, control_service_client::ControlServiceClient, watch_events_reply::Event,
ListGuestsRequest, WatchEventsRequest, ListGuestsRequest,
}, },
}, },
}; };
@ -50,12 +50,11 @@ pub struct AutoNetworkChangeset {
} }
impl AutoNetworkWatcher { impl AutoNetworkWatcher {
pub async fn new(mut control: ControlServiceClient<Channel>) -> Result<AutoNetworkWatcher> { pub async fn new(control: ControlServiceClient<Channel>) -> Result<AutoNetworkWatcher> {
let watch_events_response = control.watch_events(WatchEventsRequest {}).await?; let client = control.clone();
Ok(AutoNetworkWatcher { Ok(AutoNetworkWatcher {
control, control,
events: EventStream::open(watch_events_response.into_inner()).await?, events: EventStream::open(client).await?,
known: HashMap::new(), known: HashMap::new(),
}) })
} }
@ -136,7 +135,15 @@ impl AutoNetworkWatcher {
let mut added: Vec<NetworkMetadata> = Vec::new(); let mut added: Vec<NetworkMetadata> = Vec::new();
let mut removed: 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); seen.push(network.uuid);
if self.known.contains_key(&network.uuid) { if self.known.contains_key(&network.uuid) {
continue; continue;

View File

@ -2,7 +2,7 @@
name = "krata-oci" name = "krata-oci"
description = "OCI services for the krata hypervisor." description = "OCI services for the krata hypervisor."
license.workspace = true license.workspace = true
version= "0.0.3" version.workspace = true
homepage.workspace = true homepage.workspace = true
repository.workspace = true repository.workspace = true
edition = "2021" edition = "2021"

View File

@ -5,6 +5,7 @@ use oci_spec::image::{ImageConfiguration, ImageManifest};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use tokio::fs; use tokio::fs;
#[derive(Clone)]
pub struct ImageCache { pub struct ImageCache {
cache_dir: PathBuf, cache_dir: PathBuf,
} }

View File

@ -2,7 +2,7 @@
name = "krata-runtime" name = "krata-runtime"
description = "Runtime for running guests on the krata hypervisor." description = "Runtime for running guests on the krata hypervisor."
license.workspace = true license.workspace = true
version= "0.0.3" version.workspace = true
homepage.workspace = true homepage.workspace = true
repository.workspace = true repository.workspace = true
edition = "2021" edition = "2021"
@ -12,18 +12,18 @@ resolver = "2"
anyhow = { workspace = true } anyhow = { workspace = true }
backhand = { workspace = true } backhand = { workspace = true }
ipnetwork = { workspace = true } ipnetwork = { workspace = true }
krata = { path = "../krata", version = "^0.0.3" } krata = { path = "../krata", version = "^0.0.5" }
krata-advmac = { workspace = true } krata-advmac = { workspace = true }
krata-oci = { path = "../oci", version = "^0.0.3" } krata-oci = { path = "../oci", version = "^0.0.5" }
log = { workspace = true } log = { workspace = true }
loopdev-3 = { workspace = true } loopdev-3 = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
uuid = { workspace = true } uuid = { workspace = true }
krata-xenclient = { path = "../xen/xenclient", version = "^0.0.3" } krata-xenclient = { path = "../xen/xenclient", version = "^0.0.5" }
krata-xenevtchn = { path = "../xen/xenevtchn", version = "^0.0.3" } krata-xenevtchn = { path = "../xen/xenevtchn", version = "^0.0.5" }
krata-xengnt = { path = "../xen/xengnt", version = "^0.0.3" } krata-xengnt = { path = "../xen/xengnt", version = "^0.0.5" }
krata-xenstore = { path = "../xen/xenstore", version = "^0.0.3" } krata-xenstore = { path = "../xen/xenstore", version = "^0.0.5" }
[lib] [lib]
name = "kratart" name = "kratart"

View File

@ -6,7 +6,7 @@ use kratart::channel::ChannelService;
async fn main() -> Result<()> { async fn main() -> Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); 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?; let task = service.launch().await?;
loop { loop {

View File

@ -1,17 +1,25 @@
use std::{sync::Arc, time::Duration};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use log::debug;
use loopdev::{LoopControl, LoopDevice}; use loopdev::{LoopControl, LoopDevice};
use tokio::time::sleep;
use xenclient::BlockDeviceRef; use xenclient::BlockDeviceRef;
#[derive(Clone)]
pub struct AutoLoop { pub struct AutoLoop {
control: LoopControl, control: Arc<LoopControl>,
} }
impl AutoLoop { impl AutoLoop {
pub fn new(control: LoopControl) -> AutoLoop { pub fn new(control: LoopControl) -> AutoLoop {
AutoLoop { control } AutoLoop {
control: Arc::new(control),
}
} }
pub fn loopify(&self, file: &str) -> Result<BlockDeviceRef> { pub fn loopify(&self, file: &str) -> Result<BlockDeviceRef> {
debug!("creating loop for file {}", file);
let device = self.control.next_free()?; let device = self.control.next_free()?;
device.with().read_only(true).attach(file)?; device.with().read_only(true).attach(file)?;
let path = device let path = device
@ -25,9 +33,10 @@ impl AutoLoop {
Ok(BlockDeviceRef { path, major, minor }) 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)?; let device = LoopDevice::open(device)?;
device.detach()?; device.detach()?;
sleep(Duration::from_millis(200)).await;
Ok(()) Ok(())
} }
} }

View File

@ -41,6 +41,7 @@ impl XenConsoleInterface {
pub struct ChannelService { pub struct ChannelService {
typ: String, typ: String,
use_reserved_ref: Option<u64>,
backends: HashMap<u32, ChannelBackend>, backends: HashMap<u32, ChannelBackend>,
evtchn: EventChannel, evtchn: EventChannel,
store: XsdClient, store: XsdClient,
@ -51,20 +52,29 @@ pub struct ChannelService {
} }
impl 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 (input_sender, input_receiver) = channel(GROUPED_CHANNEL_QUEUE_LEN);
let (output_sender, output_receiver) = channel(GROUPED_CHANNEL_QUEUE_LEN); let (output_sender, output_receiver) = channel(GROUPED_CHANNEL_QUEUE_LEN);
Ok(( Ok((
ChannelService { ChannelService {
typ, typ,
use_reserved_ref,
backends: HashMap::new(), backends: HashMap::new(),
evtchn: EventChannel::open().await?, evtchn: EventChannel::open().await?,
store: XsdClient::open().await?, store: XsdClient::open().await?,
gnttab: GrantTab::open()?, gnttab: GrantTab::open()?,
input_sender, input_sender: input_sender.clone(),
input_receiver, input_receiver,
output_sender, output_sender,
}, },
input_sender,
output_receiver, output_receiver,
)) ))
} }
@ -148,6 +158,7 @@ impl ChannelService {
self.evtchn.clone(), self.evtchn.clone(),
self.gnttab.clone(), self.gnttab.clone(),
self.output_sender.clone(), self.output_sender.clone(),
self.use_reserved_ref,
) )
.await?; .await?;
self.backends.insert(domid, backend); self.backends.insert(domid, backend);
@ -216,6 +227,7 @@ impl ChannelBackend {
evtchn: EventChannel, evtchn: EventChannel,
gnttab: GrantTab, gnttab: GrantTab,
output_sender: Sender<(u32, Vec<u8>)>, output_sender: Sender<(u32, Vec<u8>)>,
use_reserved_ref: Option<u64>,
) -> Result<ChannelBackend> { ) -> Result<ChannelBackend> {
let processor = KrataChannelBackendProcessor { let processor = KrataChannelBackendProcessor {
backend, backend,
@ -225,6 +237,7 @@ impl ChannelBackend {
store, store,
evtchn, evtchn,
gnttab, gnttab,
use_reserved_ref,
}; };
let (input_sender, input_receiver) = channel(SINGLE_CHANNEL_QUEUE_LEN); let (input_sender, input_receiver) = channel(SINGLE_CHANNEL_QUEUE_LEN);
@ -241,6 +254,7 @@ impl ChannelBackend {
#[derive(Clone)] #[derive(Clone)]
pub struct KrataChannelBackendProcessor { pub struct KrataChannelBackendProcessor {
use_reserved_ref: Option<u64>,
backend: String, backend: String,
frontend: String, frontend: String,
id: u32, id: u32,
@ -347,7 +361,7 @@ impl KrataChannelBackendProcessor {
return Err(anyhow!("frontend did not give ring-ref and port")); 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")); return Err(anyhow!("frontend gave invalid ring-ref"));
}; };
@ -355,6 +369,8 @@ impl KrataChannelBackendProcessor {
return Err(anyhow!("frontend gave invalid port")); return Err(anyhow!("frontend gave invalid port"));
}; };
ring_ref = self.use_reserved_ref.unwrap_or(ring_ref);
break (ring_ref, port); break (ring_ref, port);
} }
} }

View File

@ -1,5 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::net::{IpAddr, Ipv6Addr}; use std::net::{IpAddr, Ipv6Addr};
use std::sync::Arc;
use std::{fs, net::Ipv4Addr, str::FromStr}; use std::{fs, net::Ipv4Addr, str::FromStr};
use advmac::MacAddr6; use advmac::MacAddr6;
@ -8,6 +9,7 @@ use ipnetwork::{IpNetwork, Ipv4Network};
use krata::launchcfg::{ use krata::launchcfg::{
LaunchInfo, LaunchNetwork, LaunchNetworkIpv4, LaunchNetworkIpv6, LaunchNetworkResolver, LaunchInfo, LaunchNetwork, LaunchNetworkIpv4, LaunchNetworkIpv6, LaunchNetworkResolver,
}; };
use tokio::sync::Semaphore;
use uuid::Uuid; use uuid::Uuid;
use xenclient::{DomainChannel, DomainConfig, DomainDisk, DomainNetworkInterface}; use xenclient::{DomainChannel, DomainConfig, DomainDisk, DomainNetworkInterface};
use xenstore::XsdInterface; use xenstore::XsdInterface;
@ -33,16 +35,18 @@ pub struct GuestLaunchRequest<'a> {
pub debug: bool, pub debug: bool,
} }
pub struct GuestLauncher {} pub struct GuestLauncher {
pub launch_semaphore: Arc<Semaphore>,
}
impl GuestLauncher { impl GuestLauncher {
pub fn new() -> Result<Self> { pub fn new(launch_semaphore: Arc<Semaphore>) -> Result<Self> {
Ok(Self {}) Ok(Self { launch_semaphore })
} }
pub async fn launch<'r>( pub async fn launch<'r>(
&mut self, &mut self,
context: &mut RuntimeContext, context: &RuntimeContext,
request: GuestLaunchRequest<'r>, request: GuestLaunchRequest<'r>,
) -> Result<GuestInfo> { ) -> Result<GuestInfo> {
let uuid = request.uuid.unwrap_or_else(Uuid::new_v4); let uuid = request.uuid.unwrap_or_else(Uuid::new_v4);
@ -56,6 +60,7 @@ impl GuestLauncher {
container_mac.set_local(true); container_mac.set_local(true);
container_mac.set_multicast(false); container_mac.set_multicast(false);
let _launch_permit = self.launch_semaphore.acquire().await?;
let guest_ipv4 = self.allocate_ipv4(context).await?; let guest_ipv4 = self.allocate_ipv4(context).await?;
let guest_ipv6 = container_mac.to_link_local_ipv6(); let guest_ipv6 = container_mac.to_link_local_ipv6();
let gateway_ipv4 = "10.75.70.1"; let gateway_ipv4 = "10.75.70.1";
@ -64,6 +69,12 @@ impl GuestLauncher {
let ipv6_network_mask: u32 = 10; let ipv6_network_mask: u32 = 10;
let launch_config = LaunchInfo { let launch_config = LaunchInfo {
hostname: Some(
request
.name
.map(|x| x.to_string())
.unwrap_or_else(|| format!("krata-{}", uuid)),
),
network: Some(LaunchNetwork { network: Some(LaunchNetwork {
link: "eth0".to_string(), link: "eth0".to_string(),
ipv4: LaunchNetworkIpv4 { ipv4: LaunchNetworkIpv4 {
@ -168,6 +179,7 @@ impl GuestLauncher {
kernel_path: &context.kernel, kernel_path: &context.kernel,
initrd_path: &context.initrd, initrd_path: &context.initrd,
cmdline: &cmdline, cmdline: &cmdline,
use_console_backend: Some("krata-console"),
disks: vec![ disks: vec![
DomainDisk { DomainDisk {
vdev: "xvda", vdev: "xvda",
@ -217,14 +229,14 @@ impl GuestLauncher {
)?), )?),
gateway_ipv6: Some(IpNetwork::new( gateway_ipv6: Some(IpNetwork::new(
IpAddr::V6(Ipv6Addr::from_str(gateway_ipv6)?), IpAddr::V6(Ipv6Addr::from_str(gateway_ipv6)?),
ipv4_network_mask as u8, ipv6_network_mask as u8,
)?), )?),
gateway_mac: Some(gateway_mac_string.clone()), gateway_mac: Some(gateway_mac_string.clone()),
state: GuestState { exit_code: None }, state: GuestState { exit_code: None },
}), }),
Err(error) => { Err(error) => {
let _ = context.autoloop.unloop(&image_squashfs_loop.path); let _ = context.autoloop.unloop(&image_squashfs_loop.path).await;
let _ = context.autoloop.unloop(&cfgblk_squashfs_loop.path); let _ = context.autoloop.unloop(&cfgblk_squashfs_loop.path).await;
let _ = fs::remove_dir(&cfgblk.dir); let _ = fs::remove_dir(&cfgblk.dir);
Err(error.into()) Err(error.into())
} }
@ -237,7 +249,7 @@ impl GuestLauncher {
compiler.compile(&image).await 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 network = Ipv4Network::new(Ipv4Addr::new(10, 75, 80, 0), 24)?;
let mut used: Vec<Ipv4Addr> = vec![]; let mut used: Vec<Ipv4Addr> = vec![];
for domid_candidate in context.xen.store.list("/local/domain").await? { for domid_candidate in context.xen.store.list("/local/domain").await? {
@ -264,7 +276,7 @@ impl GuestLauncher {
if found.is_none() { if found.is_none() {
return Err(anyhow!( 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 anyhow::{anyhow, Result};
use ipnetwork::IpNetwork; use ipnetwork::IpNetwork;
use loopdev::LoopControl; use loopdev::LoopControl;
use tokio::sync::Mutex; use tokio::sync::Semaphore;
use uuid::Uuid; use uuid::Uuid;
use xenclient::XenClient; use xenclient::XenClient;
use xenstore::{XsdClient, XsdInterface}; use xenstore::{XsdClient, XsdInterface};
@ -51,6 +51,7 @@ pub struct GuestInfo {
pub state: GuestState, pub state: GuestState,
} }
#[derive(Clone)]
pub struct RuntimeContext { pub struct RuntimeContext {
pub image_cache: ImageCache, pub image_cache: ImageCache,
pub autoloop: AutoLoop, pub autoloop: AutoLoop,
@ -94,7 +95,7 @@ impl RuntimeContext {
Err(anyhow!("unable to find required guest file: {}", name)) 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(); let mut guests: Vec<GuestInfo> = Vec::new();
for domid_candidate in self.xen.store.list("/local/domain").await? { for domid_candidate in self.xen.store.list("/local/domain").await? {
if domid_candidate == "0" { if domid_candidate == "0" {
@ -218,7 +219,7 @@ impl RuntimeContext {
Ok(guests) 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? { for guest in self.list().await? {
if guest.uuid == uuid { if guest.uuid == uuid {
return Ok(Some(guest)); return Ok(Some(guest));
@ -254,7 +255,8 @@ impl RuntimeContext {
#[derive(Clone)] #[derive(Clone)]
pub struct Runtime { pub struct Runtime {
store: Arc<String>, store: Arc<String>,
context: Arc<Mutex<RuntimeContext>>, context: RuntimeContext,
launch_semaphore: Arc<Semaphore>,
} }
impl Runtime { impl Runtime {
@ -262,24 +264,24 @@ impl Runtime {
let context = RuntimeContext::new(store.clone()).await?; let context = RuntimeContext::new(store.clone()).await?;
Ok(Self { Ok(Self {
store: Arc::new(store), 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> { pub async fn launch<'a>(&self, request: GuestLaunchRequest<'a>) -> Result<GuestInfo> {
let mut context = self.context.lock().await; let mut launcher = GuestLauncher::new(self.launch_semaphore.clone())?;
let mut launcher = GuestLauncher::new()?; launcher.launch(&self.context, request).await
launcher.launch(&mut context, request).await
} }
pub async fn destroy(&self, uuid: Uuid) -> Result<Uuid> { pub async fn destroy(&self, uuid: Uuid) -> Result<Uuid> {
let mut context = self.context.lock().await; let info = self
let info = context .context
.resolve(uuid) .resolve(uuid)
.await? .await?
.ok_or_else(|| anyhow!("unable to resolve guest: {}", uuid))?; .ok_or_else(|| anyhow!("unable to resolve guest: {}", uuid))?;
let domid = info.domid; 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 dom_path = store.get_domain_path(domid).await?;
let uuid = match store let uuid = match store
.read_string(format!("{}/krata/uuid", dom_path).as_str()) .read_string(format!("{}/krata/uuid", dom_path).as_str())
@ -301,9 +303,9 @@ impl Runtime {
.read_string(format!("{}/krata/loops", dom_path).as_str()) .read_string(format!("{}/krata/loops", dom_path).as_str())
.await?; .await?;
let loops = RuntimeContext::parse_loop_set(&loops); let loops = RuntimeContext::parse_loop_set(&loops);
context.xen.destroy(domid).await?; self.context.xen.destroy(domid).await?;
for info in &loops { for info in &loops {
context.autoloop.unloop(&info.device)?; self.context.autoloop.unloop(&info.device).await?;
match &info.delete { match &info.delete {
None => {} None => {}
Some(delete) => { Some(delete) => {
@ -320,19 +322,18 @@ impl Runtime {
} }
pub async fn console(&self, uuid: Uuid) -> Result<XenConsole> { pub async fn console(&self, uuid: Uuid) -> Result<XenConsole> {
let mut context = self.context.lock().await; let info = self
let info = context .context
.resolve(uuid) .resolve(uuid)
.await? .await?
.ok_or_else(|| anyhow!("unable to resolve guest: {}", uuid))?; .ok_or_else(|| anyhow!("unable to resolve guest: {}", uuid))?;
let domid = info.domid; 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 XenConsole::new(&tty).await
} }
pub async fn list(&self) -> Result<Vec<GuestInfo>> { pub async fn list(&self) -> Result<Vec<GuestInfo>> {
let mut context = self.context.lock().await; self.context.list().await
context.list().await
} }
pub async fn dupe(&self) -> Result<Runtime> { pub async fn dupe(&self) -> Result<Runtime> {

View File

@ -2,7 +2,7 @@
name = "krata-xencall" name = "krata-xencall"
description = "An implementation of direct interfacing to xen privcmd for krata." description = "An implementation of direct interfacing to xen privcmd for krata."
license.workspace = true license.workspace = true
version= "0.0.3" version.workspace = true
homepage.workspace = true homepage.workspace = true
repository.workspace = true repository.workspace = true
edition = "2021" edition = "2021"
@ -13,6 +13,7 @@ libc = { workspace = true }
log = { workspace = true } log = { workspace = true }
nix = { workspace = true, features = ["ioctl"] } nix = { workspace = true, features = ["ioctl"] }
thiserror = { workspace = true } thiserror = { workspace = true }
tokio = { workspace = true }
uuid = { workspace = true } uuid = { workspace = true }
[lib] [lib]

View File

@ -2,11 +2,12 @@ use xencall::error::Result;
use xencall::sys::CreateDomain; use xencall::sys::CreateDomain;
use xencall::XenCall; use xencall::XenCall;
fn main() -> Result<()> { #[tokio::main]
async fn main() -> Result<()> {
env_logger::init(); env_logger::init();
let call = XenCall::open(0)?; let call = XenCall::open(0)?;
let domid = call.create_domain(CreateDomain::default())?; let domid = call.create_domain(CreateDomain::default()).await?;
println!("created domain {}", domid); println!("created domain {}", domid);
Ok(()) Ok(())
} }

View File

@ -1,11 +1,12 @@
use xencall::error::Result; use xencall::error::Result;
use xencall::XenCall; use xencall::XenCall;
fn main() -> Result<()> { #[tokio::main]
async fn main() -> Result<()> {
env_logger::init(); env_logger::init();
let call = XenCall::open(0)?; let call = XenCall::open(0)?;
let info = call.get_domain_info(1)?; let info = call.get_domain_info(1).await?;
println!("{:?}", info); println!("{:?}", info);
Ok(()) Ok(())
} }

View File

@ -1,11 +1,12 @@
use xencall::error::Result; use xencall::error::Result;
use xencall::XenCall; use xencall::XenCall;
fn main() -> Result<()> { #[tokio::main]
async fn main() -> Result<()> {
env_logger::init(); env_logger::init();
let call = XenCall::open(0)?; let call = XenCall::open(0)?;
let context = call.get_vcpu_context(224, 0)?; let context = call.get_vcpu_context(224, 0).await?;
println!("{:?}", context); println!("{:?}", context);
Ok(()) Ok(())
} }

View File

@ -1,11 +1,12 @@
use xencall::error::Result; use xencall::error::Result;
use xencall::XenCall; use xencall::XenCall;
fn main() -> Result<()> { #[tokio::main]
async fn main() -> Result<()> {
env_logger::init(); env_logger::init();
let call = XenCall::open(0)?; let call = XenCall::open(0)?;
let info = call.get_version_capabilities()?; let info = call.get_version_capabilities().await?;
println!("{:?}", info); println!("{:?}", info);
Ok(()) Ok(())
} }

View File

@ -8,8 +8,12 @@ pub enum Error {
Kernel(#[from] nix::errno::Errno), Kernel(#[from] nix::errno::Errno),
#[error("io issue encountered: {0}")] #[error("io issue encountered: {0}")]
Io(#[from] io::Error), Io(#[from] io::Error),
#[error("failed to acquire semaphore: {0}")]
AcquireSemaphoreFailed(#[from] tokio::sync::AcquireError),
#[error("populate physmap failed")] #[error("populate physmap failed")]
PopulatePhysmapFailed, PopulatePhysmapFailed,
#[error("mmap batch failed: {0}")]
MmapBatchFailed(nix::errno::Errno),
} }
pub type Result<T> = std::result::Result<T, Error>; 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 log::trace;
use nix::errno::Errno; use nix::errno::Errno;
use std::ffi::{c_long, c_uint, c_ulong, c_void}; 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 sys::{XEN_DOMCTL_MAX_INTERFACE_VERSION, XEN_DOMCTL_MIN_INTERFACE_VERSION};
use tokio::sync::Semaphore;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::os::fd::AsRawFd; use std::os::fd::AsRawFd;
use std::ptr::addr_of_mut; use std::ptr::addr_of_mut;
use std::slice; use std::slice;
#[derive(Clone)]
pub struct XenCall { pub struct XenCall {
pub handle: File, pub handle: Arc<File>,
semaphore: Arc<Semaphore>,
domctl_interface_version: u32, domctl_interface_version: u32,
} }
@ -39,7 +43,8 @@ impl XenCall {
let domctl_interface_version = let domctl_interface_version =
XenCall::detect_domctl_interface_version(&handle, current_domid)?; XenCall::detect_domctl_interface_version(&handle, current_domid)?;
Ok(XenCall { Ok(XenCall {
handle, handle: Arc::new(handle),
semaphore: Arc::new(Semaphore::new(1)),
domctl_interface_version, domctl_interface_version,
}) })
} }
@ -68,7 +73,8 @@ impl XenCall {
Err(Error::XenVersionUnsupported) 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!( trace!(
"call fd={} mmap addr={:#x} len={}", "call fd={} mmap addr={:#x} len={}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -87,12 +93,20 @@ impl XenCall {
if ptr == MAP_FAILED { if ptr == MAP_FAILED {
None None
} else { } else {
trace!(
"call fd={} mmap addr={:#x} len={} = {:#x}",
self.handle.as_raw_fd(),
addr,
len,
ptr as u64,
);
Some(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!( trace!(
"call fd={} hypercall op={:#x} arg={:?}", "call fd={} hypercall op={:#x} arg={:?}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -106,29 +120,29 @@ impl XenCall {
} }
} }
pub fn hypercall0(&self, op: c_ulong) -> Result<c_long> { pub async fn hypercall0(&self, op: c_ulong) -> Result<c_long> {
self.hypercall(op, [0, 0, 0, 0, 0]) self.hypercall(op, [0, 0, 0, 0, 0]).await
} }
pub fn hypercall1(&self, op: c_ulong, arg1: c_ulong) -> Result<c_long> { pub async fn hypercall1(&self, op: c_ulong, arg1: c_ulong) -> Result<c_long> {
self.hypercall(op, [arg1, 0, 0, 0, 0]) 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> { 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]) self.hypercall(op, [arg1, arg2, 0, 0, 0]).await
} }
pub fn hypercall3( pub async fn hypercall3(
&self, &self,
op: c_ulong, op: c_ulong,
arg1: c_ulong, arg1: c_ulong,
arg2: c_ulong, arg2: c_ulong,
arg3: c_ulong, arg3: c_ulong,
) -> Result<c_long> { ) -> 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, &self,
op: c_ulong, op: c_ulong,
arg1: c_ulong, arg1: c_ulong,
@ -136,10 +150,10 @@ impl XenCall {
arg3: c_ulong, arg3: c_ulong,
arg4: c_ulong, arg4: c_ulong,
) -> Result<c_long> { ) -> 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, &self,
op: c_ulong, op: c_ulong,
arg1: c_ulong, arg1: c_ulong,
@ -148,10 +162,10 @@ impl XenCall {
arg4: c_ulong, arg4: c_ulong,
arg5: c_ulong, arg5: c_ulong,
) -> Result<c_long> { ) -> 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!( trace!(
"call fd={} multicall calls={:?}", "call fd={} multicall calls={:?}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -161,11 +175,12 @@ impl XenCall {
HYPERVISOR_MULTICALL, HYPERVISOR_MULTICALL,
calls.as_mut_ptr() as c_ulong, calls.as_mut_ptr() as c_ulong,
calls.len() as c_ulong, calls.len() as c_ulong,
)?; )
.await?;
Ok(()) Ok(())
} }
pub fn map_resource( pub async fn map_resource(
&self, &self,
domid: u32, domid: u32,
typ: u32, typ: u32,
@ -174,6 +189,7 @@ impl XenCall {
num: u64, num: u64,
addr: u64, addr: u64,
) -> Result<()> { ) -> Result<()> {
let _permit = self.semaphore.acquire().await?;
let mut resource = MmapResource { let mut resource = MmapResource {
dom: domid as u16, dom: domid as u16,
typ, typ,
@ -188,7 +204,14 @@ impl XenCall {
Ok(()) 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!( trace!(
"call fd={} mmap_batch domid={} num={} addr={:#x} mfns={:?}", "call fd={} mmap_batch domid={} num={} addr={:#x} mfns={:?}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -211,7 +234,7 @@ impl XenCall {
let result = sys::mmapbatch(self.handle.as_raw_fd(), &mut batch); let result = sys::mmapbatch(self.handle.as_raw_fd(), &mut batch);
if let Err(errno) = result { if let Err(errno) = result {
if errno != Errno::ENOENT { if errno != Errno::ENOENT {
return Err(errno)?; return Err(Error::MmapBatchFailed(errno))?;
} }
usleep(100); usleep(100);
@ -246,7 +269,7 @@ impl XenCall {
let result = sys::mmapbatch(self.handle.as_raw_fd(), &mut batch); let result = sys::mmapbatch(self.handle.as_raw_fd(), &mut batch);
if let Err(n) = result { if let Err(n) = result {
if n != Errno::ENOENT { 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!( trace!(
"call fd={} get_version_capabilities", "call fd={} get_version_capabilities",
self.handle.as_raw_fd() self.handle.as_raw_fd()
@ -278,26 +301,29 @@ impl XenCall {
HYPERVISOR_XEN_VERSION, HYPERVISOR_XEN_VERSION,
XENVER_CAPABILITIES, XENVER_CAPABILITIES,
addr_of_mut!(info) as c_ulong, addr_of_mut!(info) as c_ulong,
)?; )
.await?;
Ok(info) Ok(info)
} }
pub fn evtchn_op(&self, cmd: c_int, arg: u64) -> Result<()> { pub async fn evtchn_op(&self, cmd: c_int, arg: u64) -> Result<()> {
self.hypercall2(HYPERVISOR_EVENT_CHANNEL_OP, cmd as c_ulong, arg)?; self.hypercall2(HYPERVISOR_EVENT_CHANNEL_OP, cmd as c_ulong, arg)
.await?;
Ok(()) 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 { let mut alloc_unbound = EvtChnAllocUnbound {
dom: domid as u16, dom: domid as u16,
remote_dom: remote_domid as u16, remote_dom: remote_domid as u16,
port: 0, 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) 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!( trace!(
"domctl fd={} get_domain_info domid={}", "domctl fd={} get_domain_info domid={}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -311,11 +337,12 @@ impl XenCall {
get_domain_info: GetDomainInfo::default(), 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 }) 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!( trace!(
"domctl fd={} create_domain create_domain={:?}", "domctl fd={} create_domain create_domain={:?}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -327,11 +354,12 @@ impl XenCall {
domid: 0, domid: 0,
value: DomCtlValue { create_domain }, 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) Ok(domctl.domid)
} }
pub fn pause_domain(&self, domid: u32) -> Result<()> { pub async fn pause_domain(&self, domid: u32) -> Result<()> {
trace!( trace!(
"domctl fd={} pause_domain domid={:?}", "domctl fd={} pause_domain domid={:?}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -343,11 +371,12 @@ impl XenCall {
domid, domid,
value: DomCtlValue { pad: [0; 128] }, 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(()) Ok(())
} }
pub fn unpause_domain(&self, domid: u32) -> Result<()> { pub async fn unpause_domain(&self, domid: u32) -> Result<()> {
trace!( trace!(
"domctl fd={} unpause_domain domid={:?}", "domctl fd={} unpause_domain domid={:?}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -359,11 +388,12 @@ impl XenCall {
domid, domid,
value: DomCtlValue { pad: [0; 128] }, 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(()) 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!( trace!(
"domctl fd={} set_max_mem domid={} memkb={}", "domctl fd={} set_max_mem domid={} memkb={}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -378,11 +408,12 @@ impl XenCall {
max_mem: MaxMem { max_memkb: memkb }, 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(()) 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!( trace!(
"domctl fd={} set_max_vcpus domid={} max_vcpus={}", "domctl fd={} set_max_vcpus domid={} max_vcpus={}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -397,11 +428,12 @@ impl XenCall {
max_cpus: MaxVcpus { max_vcpus }, 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(()) 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!( trace!(
"domctl fd={} set_address_size domid={} size={}", "domctl fd={} set_address_size domid={} size={}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -416,11 +448,12 @@ impl XenCall {
address_size: AddressSize { size }, 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(()) 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!( trace!(
"domctl fd={} get_vcpu_context domid={}", "domctl fd={} get_vcpu_context domid={}",
self.handle.as_raw_fd(), 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 }) Ok(unsafe { wrapper.value })
} }
pub fn set_vcpu_context( pub async fn set_vcpu_context(
&self, &self,
domid: u32, domid: u32,
vcpu: 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(()) 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 buffer: Vec<u64> = frames.to_vec();
let mut domctl = DomCtl { let mut domctl = DomCtl {
cmd: XEN_DOMCTL_GETPAGEFRAMEINFO3, 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 { let slice = unsafe {
slice::from_raw_parts_mut( slice::from_raw_parts_mut(
domctl.value.get_page_frame_info.array as *mut u64, domctl.value.get_page_frame_info.array as *mut u64,
@ -496,7 +532,7 @@ impl XenCall {
Ok(slice.to_vec()) 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!( trace!(
"domctl fd={} hypercall_init domid={} gmfn={}", "domctl fd={} hypercall_init domid={} gmfn={}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -511,11 +547,12 @@ impl XenCall {
hypercall_init: HypercallInit { gmfn }, 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(()) Ok(())
} }
pub fn destroy_domain(&self, domid: u32) -> Result<()> { pub async fn destroy_domain(&self, domid: u32) -> Result<()> {
trace!( trace!(
"domctl fd={} destroy_domain domid={}", "domctl fd={} destroy_domain domid={}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -527,11 +564,12 @@ impl XenCall {
domid, domid,
value: DomCtlValue { pad: [0; 128] }, 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(()) 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 { let mut memory_map = MemoryMap {
count: 0, count: 0,
buffer: 0, buffer: 0,
@ -540,18 +578,20 @@ impl XenCall {
HYPERVISOR_MEMORY_OP, HYPERVISOR_MEMORY_OP,
XEN_MEM_MEMORY_MAP as c_ulong, XEN_MEM_MEMORY_MAP as c_ulong,
addr_of_mut!(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]; let mut buffer = vec![0u8; memory_map.count as usize * size_of_entry];
memory_map.buffer = buffer.as_mut_ptr() as c_ulong; memory_map.buffer = buffer.as_mut_ptr() as c_ulong;
self.hypercall2( self.hypercall2(
HYPERVISOR_MEMORY_OP, HYPERVISOR_MEMORY_OP,
XEN_MEM_MEMORY_MAP as c_ulong, XEN_MEM_MEMORY_MAP as c_ulong,
addr_of_mut!(memory_map) as c_ulong, addr_of_mut!(memory_map) as c_ulong,
)?; )
.await?;
Ok(buffer) Ok(buffer)
} }
pub fn populate_physmap( pub async fn populate_physmap(
&self, &self,
domid: u32, domid: u32,
nr_extents: u64, nr_extents: u64,
@ -583,7 +623,7 @@ impl XenCall {
0, 0,
], ],
}]; }];
self.multicall(calls)?; self.multicall(calls).await?;
let code = calls[0].result; let code = calls[0].result;
if code > !0xfff { if code > !0xfff {
return Err(Error::PopulatePhysmapFailed); return Err(Error::PopulatePhysmapFailed);
@ -595,7 +635,7 @@ impl XenCall {
Ok(extents) Ok(extents)
} }
pub fn claim_pages(&self, domid: u32, pages: u64) -> Result<()> { pub async fn claim_pages(&self, domid: u32, pages: u64) -> Result<()> {
trace!( trace!(
"memory fd={} claim_pages domid={} pages={}", "memory fd={} claim_pages domid={} pages={}",
self.handle.as_raw_fd(), self.handle.as_raw_fd(),
@ -613,11 +653,12 @@ impl XenCall {
HYPERVISOR_MEMORY_OP, HYPERVISOR_MEMORY_OP,
XEN_MEM_CLAIM_PAGES as c_ulong, XEN_MEM_CLAIM_PAGES as c_ulong,
addr_of_mut!(reservation) as c_ulong, addr_of_mut!(reservation) as c_ulong,
)?; )
.await?;
Ok(()) 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 }; let mut ops = MmuExtOp { cmd, arg1, arg2 };
self.hypercall4( self.hypercall4(
@ -627,6 +668,7 @@ impl XenCall {
0, 0,
domid as c_ulong, domid as c_ulong,
) )
.await
.map(|_| ()) .map(|_| ())
} }
} }

View File

@ -2,20 +2,22 @@
name = "krata-xenclient" name = "krata-xenclient"
description = "An implementation of Xen userspace for krata." description = "An implementation of Xen userspace for krata."
license.workspace = true license.workspace = true
version= "0.0.3" version.workspace = true
homepage.workspace = true homepage.workspace = true
repository.workspace = true repository.workspace = true
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"
[dependencies] [dependencies]
async-trait = { workspace = true }
elf = { workspace = true } elf = { workspace = true }
flate2 = { workspace = true } flate2 = { workspace = true }
libc = { workspace = true } libc = { workspace = true }
log = { workspace = true } log = { workspace = true }
krata-xencall = { path = "../xencall", version = "^0.0.3" } krata-xencall = { path = "../xencall", version = "^0.0.5" }
krata-xenstore = { path = "../xenstore", version = "^0.0.3" } krata-xenstore = { path = "../xenstore", version = "^0.0.5" }
memchr = { workspace = true } memchr = { workspace = true }
nix = { workspace = true }
slice-copy = { workspace = true } slice-copy = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
@ -24,6 +26,7 @@ xz2 = { workspace = true }
[dev-dependencies] [dev-dependencies]
env_logger = { workspace = true } env_logger = { workspace = true }
tokio = { workspace = true }
[lib] [lib]
name = "xenclient" name = "xenclient"

View File

@ -13,7 +13,7 @@ async fn main() -> Result<()> {
} }
let kernel_image_path = args.get(1).expect("argument not specified"); let kernel_image_path = args.get(1).expect("argument not specified");
let initrd_path = args.get(2).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 { let config = DomainConfig {
backend_domid: 0, backend_domid: 0,
name: "xenclient-test", name: "xenclient-test",
@ -22,6 +22,7 @@ async fn main() -> Result<()> {
kernel_path: kernel_image_path.as_str(), kernel_path: kernel_image_path.as_str(),
initrd_path: initrd_path.as_str(), initrd_path: initrd_path.as_str(),
cmdline: "debug elevator=noop", cmdline: "debug elevator=noop",
use_console_backend: None,
disks: vec![], disks: vec![],
channels: vec![], channels: vec![],
vifs: vec![], vifs: vec![],

View File

@ -41,9 +41,9 @@ impl Arm64BootSetup {
Arm64BootSetup {} Arm64BootSetup {}
} }
fn populate_one_size( async fn populate_one_size(
&mut self, &self,
setup: &mut BootSetup, setup: &mut BootSetup<'_>,
pfn_shift: u64, pfn_shift: u64,
base_pfn: u64, base_pfn: u64,
pfn_count: u64, pfn_count: u64,
@ -78,20 +78,23 @@ impl Arm64BootSetup {
extents[i as usize] = base_pfn + (i << pfn_shift); extents[i as usize] = base_pfn + (i << pfn_shift);
} }
let result_extents = setup.call.populate_physmap( let result_extents = setup
.call
.populate_physmap(
setup.domid, setup.domid,
count, count,
pfn_shift as u32, pfn_shift as u32,
0, 0,
&extents[0usize..count as usize], &extents[0usize..count as usize],
)?; )
.await?;
slice_copy::copy(extents, &result_extents); slice_copy::copy(extents, &result_extents);
Ok((result_extents.len() as u64) << pfn_shift) Ok((result_extents.len() as u64) << pfn_shift)
} }
fn populate_guest_memory( async fn populate_guest_memory(
&mut self, &mut self,
setup: &mut BootSetup, setup: &mut BootSetup<'_>,
base_pfn: u64, base_pfn: u64,
pfn_count: u64, pfn_count: u64,
) -> Result<()> { ) -> Result<()> {
@ -99,43 +102,51 @@ impl Arm64BootSetup {
for pfn in 0..extents.len() { for pfn in 0..extents.len() {
let mut allocsz = (1024 * 1024).min(pfn_count - pfn as u64); let mut allocsz = (1024 * 1024).min(pfn_count - pfn as u64);
allocsz = self.populate_one_size( allocsz = self
.populate_one_size(
setup, setup,
PFN_512G_SHIFT, PFN_512G_SHIFT,
base_pfn + pfn as u64, base_pfn + pfn as u64,
allocsz, allocsz,
&mut extents, &mut extents,
)?; )
.await?;
if allocsz > 0 { if allocsz > 0 {
continue; continue;
} }
allocsz = self.populate_one_size( allocsz = self
.populate_one_size(
setup, setup,
PFN_1G_SHIFT, PFN_1G_SHIFT,
base_pfn + pfn as u64, base_pfn + pfn as u64,
allocsz, allocsz,
&mut extents, &mut extents,
)?; )
.await?;
if allocsz > 0 { if allocsz > 0 {
continue; continue;
} }
allocsz = self.populate_one_size( allocsz = self
.populate_one_size(
setup, setup,
PFN_2M_SHIFT, PFN_2M_SHIFT,
base_pfn + pfn as u64, base_pfn + pfn as u64,
allocsz, allocsz,
&mut extents, &mut extents,
)?; )
.await?;
if allocsz > 0 { if allocsz > 0 {
continue; continue;
} }
allocsz = self.populate_one_size( allocsz = self
.populate_one_size(
setup, setup,
PFN_4K_SHIFT, PFN_4K_SHIFT,
base_pfn + pfn as u64, base_pfn + pfn as u64,
allocsz, allocsz,
&mut extents, &mut extents,
)?; )
.await?;
if allocsz == 0 { if allocsz == 0 {
return Err(Error::MemorySetupFailed("allocsz is zero")); return Err(Error::MemorySetupFailed("allocsz is zero"));
} }
@ -145,6 +156,7 @@ impl Arm64BootSetup {
} }
} }
#[async_trait::async_trait]
impl ArchBootSetup for Arm64BootSetup { impl ArchBootSetup for Arm64BootSetup {
fn page_size(&mut self) -> u64 { fn page_size(&mut self) -> u64 {
ARM_PAGE_SIZE ARM_PAGE_SIZE
@ -158,15 +170,15 @@ impl ArchBootSetup for Arm64BootSetup {
true true
} }
fn setup_shared_info(&mut self, _: &mut BootSetup, _: u64) -> Result<()> { async fn setup_shared_info(&mut self, _: &mut BootSetup, _: u64) -> Result<()> {
Ok(()) 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(()) Ok(())
} }
fn meminit( async fn meminit(
&mut self, &mut self,
setup: &mut BootSetup, setup: &mut BootSetup,
total_pages: u64, total_pages: u64,
@ -176,7 +188,7 @@ impl ArchBootSetup for Arm64BootSetup {
let kernel_segment = kernel_segment let kernel_segment = kernel_segment
.as_ref() .as_ref()
.ok_or(Error::MemorySetupFailed("kernel_segment missing"))?; .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 mut ramsize = total_pages << XEN_PAGE_SHIFT;
let bankbase = GUEST_RAM_BANK_BASES; let bankbase = GUEST_RAM_BANK_BASES;
@ -214,7 +226,8 @@ impl ArchBootSetup for Arm64BootSetup {
} }
for i in 0..2 { 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); let bank0end = bankbase[0] + (rambank_size[0] << XEN_PAGE_SHIFT);
@ -227,15 +240,15 @@ impl ArchBootSetup for Arm64BootSetup {
} else { } else {
return Err(Error::MemorySetupFailed("unable to determine modbase")); return Err(Error::MemorySetupFailed("unable to determine modbase"));
}; };
setup.call.claim_pages(setup.domid, 0)?; setup.call.claim_pages(setup.domid, 0).await?;
Ok(()) Ok(())
} }
fn bootlate(&mut self, _: &mut BootSetup, _: &mut BootState) -> Result<()> { async fn bootlate(&mut self, _: &mut BootSetup, _: &mut BootState) -> Result<()> {
Ok(()) 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(); let mut vcpu = VcpuGuestContext::default();
vcpu.user_regs.pc = state.image_info.virt_entry; vcpu.user_regs.pc = state.image_info.virt_entry;
vcpu.user_regs.x0 = 0xffffffff; vcpu.user_regs.x0 = 0xffffffff;
@ -249,11 +262,11 @@ impl ArchBootSetup for Arm64BootSetup {
vcpu.user_regs.cpsr = PSR_GUEST64_INIT; vcpu.user_regs.cpsr = PSR_GUEST64_INIT;
vcpu.flags = 1 << 0; // VGCF_ONLINE vcpu.flags = 1 << 0; // VGCF_ONLINE
trace!("vcpu context: {:?}", vcpu); trace!("vcpu context: {:?}", vcpu);
setup.call.set_vcpu_context(setup.domid, 0, &vcpu)?; setup.call.set_vcpu_context(setup.domid, 0, &vcpu).await?;
Ok(()) Ok(())
} }
fn alloc_p2m_segment( async fn alloc_p2m_segment(
&mut self, &mut self,
_: &mut BootSetup, _: &mut BootSetup,
_: &BootImageInfo, _: &BootImageInfo,
@ -261,7 +274,7 @@ impl ArchBootSetup for Arm64BootSetup {
Ok(None) Ok(None)
} }
fn alloc_page_tables( async fn alloc_page_tables(
&mut self, &mut self,
_: &mut BootSetup, _: &mut BootSetup,
_: &BootImageInfo, _: &BootImageInfo,
@ -269,7 +282,7 @@ impl ArchBootSetup for Arm64BootSetup {
Ok(None) 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(()) Ok(())
} }
} }

View File

@ -4,6 +4,7 @@ use crate::sys::{GrantEntry, XEN_PAGE_SHIFT};
use crate::Error; use crate::Error;
use libc::munmap; use libc::munmap;
use log::debug; use log::debug;
use nix::errno::Errno;
use slice_copy::copy; use slice_copy::copy;
use crate::mem::ARCH_PAGE_SHIFT; use crate::mem::ARCH_PAGE_SHIFT;
@ -83,33 +84,34 @@ impl BootSetup<'_> {
} }
} }
fn initialize_memory( async fn initialize_memory(
&mut self, &mut self,
arch: &mut dyn ArchBootSetup, arch: &mut Box<dyn ArchBootSetup + Send + Sync>,
total_pages: u64, total_pages: u64,
kernel_segment: &Option<DomainSegment>, kernel_segment: &Option<DomainSegment>,
initrd_segment: &Option<DomainSegment>, initrd_segment: &Option<DomainSegment>,
) -> Result<()> { ) -> Result<()> {
self.call.set_address_size(self.domid, 64)?; self.call.set_address_size(self.domid, 64).await?;
arch.meminit(self, total_pages, kernel_segment, initrd_segment)?; arch.meminit(self, total_pages, kernel_segment, initrd_segment)
.await?;
Ok(()) 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 { if image_info.virt_hypercall == XEN_UNSET_ADDR {
return Ok(()); return Ok(());
} }
let pfn = (image_info.virt_hypercall - image_info.virt_base) >> ARCH_PAGE_SHIFT; let pfn = (image_info.virt_hypercall - image_info.virt_base) >> ARCH_PAGE_SHIFT;
let mfn = self.phys.p2m[pfn as usize]; let mfn = self.phys.p2m[pfn as usize];
self.call.hypercall_init(self.domid, mfn)?; self.call.hypercall_init(self.domid, mfn).await?;
Ok(()) Ok(())
} }
pub fn initialize( pub async fn initialize<I: BootImageLoader + Send + Sync>(
&mut self, &mut self,
arch: &mut dyn ArchBootSetup, arch: &mut Box<dyn ArchBootSetup + Send + Sync>,
image_loader: &dyn BootImageLoader, image_loader: &I,
initrd: &[u8], initrd: &[u8],
max_vcpus: u32, max_vcpus: u32,
mem_mb: u64, mem_mb: u64,
@ -117,60 +119,68 @@ impl BootSetup<'_> {
) -> Result<BootState> { ) -> Result<BootState> {
debug!("initialize max_vcpus={:?} mem_mb={:?}", max_vcpus, mem_mb); debug!("initialize max_vcpus={:?} mem_mb={:?}", max_vcpus, mem_mb);
let page_size = arch.page_size();
let image_info = image_loader.parse()?; let image_info = image_loader.parse()?;
debug!("initialize image_info={:?}", image_info); debug!("initialize image_info={:?}", image_info);
let mut kernel_segment: Option<DomainSegment> = None; let mut kernel_segment: Option<DomainSegment> = None;
let mut initrd_segment: Option<DomainSegment> = None; let mut initrd_segment: Option<DomainSegment> = None;
if !image_info.unmapped_initrd { 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() { 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()); 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; self.virt_alloc_end = image_info.virt_base;
if kernel_segment.is_none() { 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; let mut p2m_segment: Option<DomainSegment> = None;
if image_info.virt_p2m_base >= image_info.virt_base if image_info.virt_p2m_base >= image_info.virt_base
|| (image_info.virt_p2m_base & ((1 << arch.page_shift()) - 1)) != 0 || (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 start_info_segment = self.alloc_page(page_size)?;
let xenstore_segment = self.alloc_page(arch)?; let xenstore_segment = self.alloc_page(page_size)?;
let mut consoles: Vec<(u32, DomainSegment)> = Vec::new(); let mut consoles: Vec<(u32, DomainSegment)> = Vec::new();
for _ in 0..console_count { for _ in 0..console_count {
let evtchn = self.call.evtchn_alloc_unbound(self.domid, 0)?; let evtchn = self.call.evtchn_alloc_unbound(self.domid, 0).await?;
let page = self.alloc_page(arch)?; let page = self.alloc_page(page_size)?;
consoles.push((evtchn, page)); consoles.push((evtchn, page));
} }
let page_table_segment = arch.alloc_page_tables(self, &image_info)?; let page_table_segment = arch.alloc_page_tables(self, &image_info).await?;
let boot_stack_segment = self.alloc_page(arch)?; let boot_stack_segment = self.alloc_page(page_size)?;
if self.virt_pgtab_end > 0 { 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 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; segment.vstart = image_info.virt_p2m_base;
p2m_segment = Some(segment); p2m_segment = Some(segment);
} }
} }
if image_info.unmapped_initrd { 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 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 = let kernel_segment =
kernel_segment.ok_or(Error::MemorySetupFailed("kernel_segment missing"))?; kernel_segment.ok_or(Error::MemorySetupFailed("kernel_segment missing"))?;
@ -192,35 +202,37 @@ impl BootSetup<'_> {
Ok(state) Ok(state)
} }
pub fn boot( pub async fn boot(
&mut self, &mut self,
arch: &mut dyn ArchBootSetup, arch: &mut Box<dyn ArchBootSetup + Send + Sync>,
state: &mut BootState, state: &mut BootState,
cmdline: &str, cmdline: &str,
) -> Result<()> { ) -> 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; let shared_info_frame = domain_info.shared_info_frame;
state.shared_info_frame = shared_info_frame; state.shared_info_frame = shared_info_frame;
arch.setup_page_tables(self, state)?; arch.setup_page_tables(self, state).await?;
arch.setup_start_info(self, state, cmdline)?; arch.setup_start_info(self, state, cmdline).await?;
self.setup_hypercall_page(&state.image_info)?; self.setup_hypercall_page(&state.image_info).await?;
arch.bootlate(self, state)?; arch.bootlate(self, state).await?;
arch.setup_shared_info(self, state.shared_info_frame)?; arch.setup_shared_info(self, state.shared_info_frame)
arch.vcpu(self, state)?; .await?;
arch.vcpu(self, state).await?;
self.phys.unmap_all()?; self.phys.unmap_all()?;
self.gnttab_seed(state)?; self.gnttab_seed(state).await?;
Ok(()) Ok(())
} }
fn gnttab_seed(&mut self, state: &mut BootState) -> Result<()> { async fn gnttab_seed(&mut self, state: &mut BootState) -> Result<()> {
let console_gfn = let console_gfn =
self.phys.p2m[state.consoles.first().map(|x| x.1.pfn).unwrap_or(0) as usize]; 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 xenstore_gfn = self.phys.p2m[state.xenstore_segment.pfn as usize];
let addr = self let addr = self
.call .call
.mmap(0, 1 << XEN_PAGE_SHIFT) .mmap(0, 1 << XEN_PAGE_SHIFT)
.await
.ok_or(Error::MmapFailed)?; .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) }; let entries = unsafe { slice::from_raw_parts_mut(addr as *mut GrantEntry, 2) };
entries[0].flags = 1 << 0; entries[0].flags = 1 << 0;
entries[0].domid = 0; entries[0].domid = 0;
@ -231,23 +243,25 @@ impl BootSetup<'_> {
unsafe { unsafe {
let result = munmap(addr as *mut c_void, 1 << XEN_PAGE_SHIFT); let result = munmap(addr as *mut c_void, 1 << XEN_PAGE_SHIFT);
if result != 0 { if result != 0 {
return Err(Error::UnmapFailed); return Err(Error::UnmapFailed(Errno::from_raw(result)));
} }
} }
Ok(()) Ok(())
} }
fn load_kernel_segment( async fn load_kernel_segment<I: BootImageLoader + Send + Sync>(
&mut self, &mut self,
arch: &mut dyn ArchBootSetup, page_size: u64,
image_loader: &dyn BootImageLoader, image_loader: &I,
image_info: &BootImageInfo, image_info: &BootImageInfo,
) -> Result<DomainSegment> { ) -> Result<DomainSegment> {
let kernel_segment = self.alloc_segment( let kernel_segment = self
arch, .alloc_segment(
page_size,
image_info.virt_kstart, image_info.virt_kstart,
image_info.virt_kend - 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_ptr = kernel_segment.addr as *mut u8;
let kernel_segment_slice = let kernel_segment_slice =
unsafe { slice::from_raw_parts_mut(kernel_segment_ptr, kernel_segment.size as usize) }; unsafe { slice::from_raw_parts_mut(kernel_segment_ptr, kernel_segment.size as usize) };
@ -264,18 +278,19 @@ impl BootSetup<'_> {
(1 << bits) - 1 (1 << bits) - 1
} }
pub(crate) fn alloc_segment( pub(crate) async fn alloc_segment(
&mut self, &mut self,
arch: &mut dyn ArchBootSetup, page_size: u64,
start: u64, start: u64,
size: u64, size: u64,
) -> Result<DomainSegment> { ) -> Result<DomainSegment> {
debug!("alloc_segment {:#x} {:#x}", start, size);
if start > 0 { 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 local_page_size: u32 = (1i64 << XEN_PAGE_SHIFT) as u32;
let pages = (size + page_size as u64 - 1) / page_size as u64; let pages = (size + local_page_size as u64 - 1) / local_page_size as u64;
let start = self.virt_alloc_end; let start = self.virt_alloc_end;
let mut segment = DomainSegment { let mut segment = DomainSegment {
@ -288,12 +303,12 @@ impl BootSetup<'_> {
pages, 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; segment.addr = ptr;
let slice = unsafe { 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); slice.fill(0);
segment.vend = self.virt_alloc_end; segment.vend = self.virt_alloc_end;
@ -304,15 +319,15 @@ impl BootSetup<'_> {
Ok(segment) 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 start = self.virt_alloc_end;
let pfn = self.pfn_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); debug!("alloc_page {:#x} (pfn {:#x})", start, pfn);
Ok(DomainSegment { Ok(DomainSegment {
vstart: start, vstart: start,
vend: (start + arch.page_size()) - 1, vend: (start + page_size) - 1,
pfn, pfn,
addr: 0, addr: 0,
size: 0, size: 0,
@ -321,31 +336,29 @@ impl BootSetup<'_> {
}) })
} }
fn alloc_module( async fn alloc_module(&mut self, page_size: u64, buffer: &[u8]) -> Result<DomainSegment> {
&mut self, let segment = self
arch: &mut dyn ArchBootSetup, .alloc_segment(page_size, 0, buffer.len() as u64)
buffer: &[u8], .await?;
) -> Result<DomainSegment> {
let segment = self.alloc_segment(arch, 0, buffer.len() as u64)?;
let slice = unsafe { slice::from_raw_parts_mut(segment.addr as *mut u8, buffer.len()) }; let slice = unsafe { slice::from_raw_parts_mut(segment.addr as *mut u8, buffer.len()) };
copy(slice, buffer); copy(slice, buffer);
Ok(segment) Ok(segment)
} }
fn alloc_padding_pages(&mut self, arch: &mut dyn ArchBootSetup, boundary: u64) -> Result<()> { fn alloc_padding_pages(&mut self, page_size: u64, boundary: u64) -> Result<()> {
if (boundary & (arch.page_size() - 1)) != 0 { if (boundary & (page_size - 1)) != 0 {
return Err(Error::MemorySetupFailed("boundary is incorrect")); return Err(Error::MemorySetupFailed("boundary is incorrect"));
} }
if boundary < self.virt_alloc_end { if boundary < self.virt_alloc_end {
return Err(Error::MemorySetupFailed("boundary is below allocation end")); return Err(Error::MemorySetupFailed("boundary is below allocation end"));
} }
let pages = (boundary - self.virt_alloc_end) / arch.page_size(); let pages = (boundary - self.virt_alloc_end) / page_size;
self.chk_alloc_pages(arch, pages)?; self.chk_alloc_pages(page_size, pages)?;
Ok(()) 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 if pages > self.total_pages
|| self.pfn_alloc_end > self.total_pages || self.pfn_alloc_end > self.total_pages
|| pages > self.total_pages - self.pfn_alloc_end || pages > self.total_pages - self.pfn_alloc_end
@ -354,47 +367,56 @@ impl BootSetup<'_> {
} }
self.pfn_alloc_end += pages; self.pfn_alloc_end += pages;
self.virt_alloc_end += pages * arch.page_size(); self.virt_alloc_end += pages * page_size;
Ok(()) Ok(())
} }
} }
#[async_trait::async_trait]
pub trait ArchBootSetup { pub trait ArchBootSetup {
fn page_size(&mut self) -> u64; fn page_size(&mut self) -> u64;
fn page_shift(&mut self) -> u64; fn page_shift(&mut self) -> u64;
fn needs_early_kernel(&mut self) -> bool; fn needs_early_kernel(&mut self) -> bool;
fn alloc_p2m_segment( async fn alloc_p2m_segment(
&mut self, &mut self,
setup: &mut BootSetup, setup: &mut BootSetup,
image_info: &BootImageInfo, image_info: &BootImageInfo,
) -> Result<Option<DomainSegment>>; ) -> Result<Option<DomainSegment>>;
fn alloc_page_tables( async fn alloc_page_tables(
&mut self, &mut self,
setup: &mut BootSetup, setup: &mut BootSetup,
image_info: &BootImageInfo, image_info: &BootImageInfo,
) -> Result<Option<DomainSegment>>; ) -> 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, &mut self,
setup: &mut BootSetup, setup: &mut BootSetup,
state: &BootState, state: &BootState,
cmdline: &str, cmdline: &str,
) -> Result<()>; ) -> 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, &mut self,
setup: &mut BootSetup, setup: &mut BootSetup,
total_pages: u64, total_pages: u64,
kernel_segment: &Option<DomainSegment>, kernel_segment: &Option<DomainSegment>,
initrd_segment: &Option<DomainSegment>, initrd_segment: &Option<DomainSegment>,
) -> Result<()>; ) -> Result<()>;
fn bootlate(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>; async fn bootlate(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>;
fn vcpu(&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), ElfParseFailed(#[from] elf::ParseError),
#[error("mmap failed")] #[error("mmap failed")]
MmapFailed, MmapFailed,
#[error("munmap failed")] #[error("munmap failed: {0}")]
UnmapFailed, UnmapFailed(nix::errno::Errno),
#[error("memory setup failed: {0}")] #[error("memory setup failed: {0}")]
MemorySetupFailed(&'static str), MemorySetupFailed(&'static str),
#[error("populate physmap failed: wanted={0}, received={1}, input_extents={2}")] #[error("populate physmap failed: wanted={0}, received={1}, input_extents={2}")]

View File

@ -16,7 +16,7 @@ pub mod arm64;
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
use crate::arm64::Arm64BootSetup; use crate::arm64::Arm64BootSetup;
use crate::boot::BootSetup; use crate::boot::{ArchBootSetup, BootSetup};
use crate::elfloader::ElfImageLoader; use crate::elfloader::ElfImageLoader;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use boot::BootState; use boot::BootState;
@ -34,6 +34,7 @@ use xenstore::{
XsPermission, XsdClient, XsdInterface, XS_PERM_NONE, XS_PERM_READ, XS_PERM_READ_WRITE, XsPermission, XsdClient, XsdInterface, XS_PERM_NONE, XS_PERM_READ, XS_PERM_READ_WRITE,
}; };
#[derive(Clone)]
pub struct XenClient { pub struct XenClient {
pub store: XsdClient, pub store: XsdClient,
call: XenCall, call: XenCall,
@ -88,6 +89,7 @@ pub struct DomainConfig<'a> {
pub initrd_path: &'a str, pub initrd_path: &'a str,
pub cmdline: &'a str, pub cmdline: &'a str,
pub disks: Vec<DomainDisk<'a>>, pub disks: Vec<DomainDisk<'a>>,
pub use_console_backend: Option<&'a str>,
pub channels: Vec<DomainChannel>, pub channels: Vec<DomainChannel>,
pub vifs: Vec<DomainNetworkInterface<'a>>, pub vifs: Vec<DomainNetworkInterface<'a>>,
pub filesystems: Vec<DomainFilesystem<'a>>, pub filesystems: Vec<DomainFilesystem<'a>>,
@ -115,7 +117,7 @@ impl XenClient {
Ok(XenClient { store, call }) 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 { let mut domain = CreateDomain {
max_vcpus: config.max_vcpus, max_vcpus: config.max_vcpus,
..Default::default() ..Default::default()
@ -125,7 +127,7 @@ impl XenClient {
domain.flags = XEN_DOMCTL_CDF_HVM_GUEST | XEN_DOMCTL_CDF_HAP; 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 { match self.init(domid, &domain, config).await {
Ok(created) => Ok(created), Ok(created) => Ok(created),
Err(err) => { Err(err) => {
@ -138,7 +140,7 @@ impl XenClient {
} }
async fn init( async fn init(
&mut self, &self,
domid: u32, domid: u32,
domain: &CreateDomain, domain: &CreateDomain,
config: &DomainConfig<'_>, config: &DomainConfig<'_>,
@ -253,8 +255,8 @@ impl XenClient {
tx.commit().await?; tx.commit().await?;
} }
self.call.set_max_vcpus(domid, config.max_vcpus)?; self.call.set_max_vcpus(domid, config.max_vcpus).await?;
self.call.set_max_mem(domid, config.mem_mb * 1024)?; self.call.set_max_mem(domid, config.mem_mb * 1024).await?;
let image_loader = ElfImageLoader::load_file_kernel(config.kernel_path)?; let image_loader = ElfImageLoader::load_file_kernel(config.kernel_path)?;
let xenstore_evtchn: u32; let xenstore_evtchn: u32;
@ -265,19 +267,21 @@ impl XenClient {
{ {
let mut boot = BootSetup::new(&self.call, domid); let mut boot = BootSetup::new(&self.call, domid);
#[cfg(target_arch = "x86_64")] #[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")] #[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)?; let initrd = read(config.initrd_path)?;
state = boot.initialize( state = boot
.initialize(
&mut arch, &mut arch,
&image_loader, &image_loader,
initrd.as_slice(), initrd.as_slice(),
config.max_vcpus, config.max_vcpus,
config.mem_mb, config.mem_mb,
1, 1,
)?; )
boot.boot(&mut arch, &mut state, config.cmdline)?; .await?;
boot.boot(&mut arch, &mut state, config.cmdline).await?;
xenstore_evtchn = state.store_evtchn; xenstore_evtchn = state.store_evtchn;
xenstore_mfn = boot.phys.p2m[state.xenstore_segment.pfn as usize]; xenstore_mfn = boot.phys.p2m[state.xenstore_segment.pfn as usize];
p2m = boot.phys.p2m; p2m = boot.phys.p2m;
@ -346,7 +350,10 @@ impl XenClient {
} }
self.console_device_add( self.console_device_add(
&DomainChannel { &DomainChannel {
typ: "xenconsoled".to_string(), typ: config
.use_console_backend
.unwrap_or("xenconsoled")
.to_string(),
initialized: true, initialized: true,
}, },
&p2m, &p2m,
@ -418,7 +425,8 @@ impl XenClient {
for channel in &config.event_channels { for channel in &config.event_channels {
let id = self let id = self
.call .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); let channel_path = format!("{}/evtchn/{}", dom_path, channel.name);
self.store self.store
.write_string(&format!("{}/name", channel_path), channel.name) .write_string(&format!("{}/name", channel_path), channel.name)
@ -428,12 +436,12 @@ impl XenClient {
.await?; .await?;
} }
self.call.unpause_domain(domid)?; self.call.unpause_domain(domid).await?;
Ok(CreatedDomain { domid, channels }) Ok(CreatedDomain { domid, channels })
} }
async fn disk_device_add( async fn disk_device_add(
&mut self, &self,
dom_path: &str, dom_path: &str,
backend_dom_path: &str, backend_dom_path: &str,
backend_domid: u32, backend_domid: u32,
@ -486,7 +494,7 @@ impl XenClient {
#[allow(clippy::too_many_arguments, clippy::unnecessary_unwrap)] #[allow(clippy::too_many_arguments, clippy::unnecessary_unwrap)]
async fn console_device_add( async fn console_device_add(
&mut self, &self,
channel: &DomainChannel, channel: &DomainChannel,
p2m: &[u64], p2m: &[u64],
state: &BootState, state: &BootState,
@ -553,7 +561,7 @@ impl XenClient {
} }
async fn fs_9p_device_add( async fn fs_9p_device_add(
&mut self, &self,
dom_path: &str, dom_path: &str,
backend_dom_path: &str, backend_dom_path: &str,
backend_domid: u32, backend_domid: u32,
@ -591,7 +599,7 @@ impl XenClient {
} }
async fn vif_device_add( async fn vif_device_add(
&mut self, &self,
dom_path: &str, dom_path: &str,
backend_dom_path: &str, backend_dom_path: &str,
backend_domid: u32, backend_domid: u32,
@ -650,7 +658,7 @@ impl XenClient {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
async fn device_add( async fn device_add(
&mut self, &self,
typ: &str, typ: &str,
id: u64, id: u64,
dom_path: &str, dom_path: &str,
@ -713,15 +721,15 @@ impl XenClient {
Ok(()) 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 { if let Err(err) = self.destroy_store(domid).await {
warn!("failed to destroy store for domain {}: {}", domid, err); warn!("failed to destroy store for domain {}: {}", domid, err);
} }
self.call.destroy_domain(domid)?; self.call.destroy_domain(domid).await?;
Ok(()) 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 dom_path = self.store.get_domain_path(domid).await?;
let vm_path = self.store.read_string(&format!("{}/vm", dom_path)).await?; let vm_path = self.store.read_string(&format!("{}/vm", dom_path)).await?;
if vm_path.is_none() { if vm_path.is_none() {
@ -813,7 +821,7 @@ impl XenClient {
Ok(()) 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 dom_path = self.store.get_domain_path(domid).await?;
let console_tty_path = format!("{}/console/tty", dom_path); let console_tty_path = format!("{}/console/tty", dom_path);
let mut tty: Option<String> = None; 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 crate::Error;
use libc::munmap; use libc::munmap;
use log::debug; use log::debug;
use nix::errno::Errno;
use std::ffi::c_void; use std::ffi::c_void;
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
@ -45,7 +46,7 @@ impl PhysicalPages<'_> {
self.p2m.len() as u64 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 { for page in &self.pages {
if pfn >= page.pfn + page.count { if pfn >= page.pfn + page.count {
continue; continue;
@ -76,10 +77,10 @@ impl PhysicalPages<'_> {
return Err(Error::MemorySetupFailed("page count is zero")); 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]; let mut entries = vec![MmapEntry::default(); count as usize];
for (i, entry) in entries.iter_mut().enumerate() { for (i, entry) in entries.iter_mut().enumerate() {
entry.mfn = self.p2m[pfn as usize + i]; entry.mfn = self.p2m[pfn as usize + i];
@ -98,9 +99,13 @@ impl PhysicalPages<'_> {
let addr = self let addr = self
.call .call
.mmap(0, actual_mmap_len) .mmap(0, actual_mmap_len)
.await
.ok_or(Error::MmapFailed)?; .ok_or(Error::MmapFailed)?;
debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr); 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 { if result != 0 {
return Err(Error::MmapFailed); return Err(Error::MmapFailed);
} }
@ -117,7 +122,7 @@ impl PhysicalPages<'_> {
Ok(addr) 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 num = ((size + XEN_PAGE_SIZE - 1) >> XEN_PAGE_SHIFT) as usize;
let mut pfns = vec![u64::MAX; num]; let mut pfns = vec![u64::MAX; num];
for (i, item) in pfns.iter_mut().enumerate().take(num) { for (i, item) in pfns.iter_mut().enumerate().take(num) {
@ -128,9 +133,13 @@ impl PhysicalPages<'_> {
let addr = self let addr = self
.call .call
.mmap(0, actual_mmap_len) .mmap(0, actual_mmap_len)
.await
.ok_or(Error::MmapFailed)?; .ok_or(Error::MmapFailed)?;
debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr); 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 { if result != 0 {
return Err(Error::MmapFailed); return Err(Error::MmapFailed);
} }
@ -155,7 +164,7 @@ impl PhysicalPages<'_> {
(page.count << ARCH_PAGE_SHIFT) as usize, (page.count << ARCH_PAGE_SHIFT) as usize,
); );
if err != 0 { if err != 0 {
return Err(Error::UnmapFailed); return Err(Error::UnmapFailed(Errno::from_raw(err)));
} }
} }
} }
@ -181,7 +190,7 @@ impl PhysicalPages<'_> {
page.ptr page.ptr
); );
if err != 0 { if err != 0 {
return Err(Error::UnmapFailed); return Err(Error::UnmapFailed(Errno::from_raw(err)));
} }
self.pages.remove(i); self.pages.remove(i);
} }

View File

@ -275,6 +275,7 @@ impl X86BootSetup {
} }
} }
#[async_trait::async_trait]
impl ArchBootSetup for X86BootSetup { impl ArchBootSetup for X86BootSetup {
fn page_size(&mut self) -> u64 { fn page_size(&mut self) -> u64 {
X86_PAGE_SIZE X86_PAGE_SIZE
@ -288,7 +289,7 @@ impl ArchBootSetup for X86BootSetup {
false false
} }
fn alloc_p2m_segment( async fn alloc_p2m_segment(
&mut self, &mut self,
setup: &mut BootSetup, setup: &mut BootSetup,
image_info: &BootImageInfo, image_info: &BootImageInfo,
@ -310,11 +311,13 @@ impl ArchBootSetup for X86BootSetup {
} }
self.table.mappings_count += 1; self.table.mappings_count += 1;
p2m_alloc_size += (pgtables << X86_PAGE_SHIFT) as u64; 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)) Ok(Some(p2m_segment))
} }
fn alloc_page_tables( async fn alloc_page_tables(
&mut self, &mut self,
setup: &mut BootSetup, setup: &mut BootSetup,
image_info: &BootImageInfo, image_info: &BootImageInfo,
@ -341,7 +344,7 @@ impl ArchBootSetup for X86BootSetup {
self.table.mappings_count += 1; self.table.mappings_count += 1;
setup.virt_pgtab_end = try_virt_end + 1; setup.virt_pgtab_end = try_virt_end + 1;
let size = self.table.mappings[m].area.pgtables as u64 * X86_PAGE_SIZE; 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!( debug!(
"alloc_page_tables table={:?} segment={:?}", "alloc_page_tables table={:?} segment={:?}",
self.table, segment self.table, segment
@ -349,7 +352,11 @@ impl ArchBootSetup for X86BootSetup {
Ok(Some(segment)) 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 let p2m_segment = state
.p2m_segment .p2m_segment
.as_ref() .as_ref()
@ -364,7 +371,7 @@ impl ArchBootSetup for X86BootSetup {
let map1 = &self.table.mappings[m1]; let map1 = &self.table.mappings[m1];
let from = map1.levels[l].from; let from = map1.levels[l].from;
let to = map1.levels[l].to; 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 { for m2 in 0usize..self.table.mappings_count {
let map2 = &self.table.mappings[m2]; let map2 = &self.table.mappings[m2];
let lvl = if l > 0 { let lvl = if l > 0 {
@ -407,13 +414,16 @@ impl ArchBootSetup for X86BootSetup {
Ok(()) Ok(())
} }
fn setup_start_info( async fn setup_start_info(
&mut self, &mut self,
setup: &mut BootSetup, setup: &mut BootSetup,
state: &BootState, state: &BootState,
cmdline: &str, cmdline: &str,
) -> Result<()> { ) -> 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 = let byte_slice =
unsafe { slice::from_raw_parts_mut(ptr as *mut u8, X86_PAGE_SIZE as usize) }; unsafe { slice::from_raw_parts_mut(ptr as *mut u8, X86_PAGE_SIZE as usize) };
byte_slice.fill(0); byte_slice.fill(0);
@ -456,11 +466,15 @@ impl ArchBootSetup for X86BootSetup {
Ok(()) 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 let info = setup
.phys .phys
.map_foreign_pages(shared_info_frame, X86_PAGE_SIZE)? .map_foreign_pages(shared_info_frame, X86_PAGE_SIZE)
as *mut SharedInfo; .await? as *mut SharedInfo;
unsafe { unsafe {
let size = size_of::<SharedInfo>(); let size = size_of::<SharedInfo>();
let info_as_buff = slice::from_raw_parts_mut(info as *mut u8, size); let info_as_buff = slice::from_raw_parts_mut(info as *mut u8, size);
@ -473,14 +487,14 @@ impl ArchBootSetup for X86BootSetup {
Ok(()) Ok(())
} }
fn meminit( async fn meminit(
&mut self, &mut self,
setup: &mut BootSetup, setup: &mut BootSetup,
total_pages: u64, total_pages: u64,
_: &Option<DomainSegment>, _: &Option<DomainSegment>,
_: &Option<DomainSegment>, _: &Option<DomainSegment>,
) -> Result<()> { ) -> 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 mut vmemranges: Vec<VmemRange> = Vec::new();
let stub = VmemRange { let stub = VmemRange {
start: 0, start: 0,
@ -530,13 +544,16 @@ impl ArchBootSetup for X86BootSetup {
} }
let extents_init_slice = extents_init.as_slice(); let extents_init_slice = extents_init.as_slice();
let extents = setup.call.populate_physmap( let extents = setup
.call
.populate_physmap(
setup.domid, setup.domid,
count, count,
SUPERPAGE_2MB_SHIFT as u32, SUPERPAGE_2MB_SHIFT as u32,
0, 0,
&extents_init_slice[0usize..count as usize], &extents_init_slice[0usize..count as usize],
)?; )
.await?;
pfn = pfn_base_idx; pfn = pfn_base_idx;
for mfn in extents { for mfn in extents {
@ -558,10 +575,10 @@ impl ArchBootSetup for X86BootSetup {
let p2m_idx = (pfn_base + j) as usize; let p2m_idx = (pfn_base + j) as usize;
let p2m_end_idx = p2m_idx + allocsz as usize; let p2m_end_idx = p2m_idx + allocsz as usize;
let input_extent_starts = &p2m[p2m_idx..p2m_end_idx]; let input_extent_starts = &p2m[p2m_idx..p2m_end_idx];
let result = let result = setup
setup
.call .call
.populate_physmap(setup.domid, allocsz, 0, 0, input_extent_starts)?; .populate_physmap(setup.domid, allocsz, 0, 0, input_extent_starts)
.await?;
if result.len() != allocsz as usize { if result.len() != allocsz as usize {
return Err(Error::PopulatePhysmapFailed( return Err(Error::PopulatePhysmapFailed(
@ -581,11 +598,11 @@ impl ArchBootSetup for X86BootSetup {
} }
setup.phys.load_p2m(p2m); setup.phys.load_p2m(p2m);
setup.call.claim_pages(setup.domid, 0)?; setup.call.claim_pages(setup.domid, 0).await?;
Ok(()) 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 let p2m_segment = state
.p2m_segment .p2m_segment
.as_ref() .as_ref()
@ -600,11 +617,12 @@ impl ArchBootSetup for X86BootSetup {
setup.phys.unmap(p2m_segment.pfn)?; setup.phys.unmap(p2m_segment.pfn)?;
setup setup
.call .call
.mmuext(setup.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)?; .mmuext(setup.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)
.await?;
Ok(()) 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 let page_table_segment = state
.page_table_segment .page_table_segment
.as_ref() .as_ref()
@ -633,7 +651,7 @@ impl ArchBootSetup for X86BootSetup {
vcpu.kernel_ss = vcpu.user_regs.ss as u64; vcpu.kernel_ss = vcpu.user_regs.ss as u64;
vcpu.kernel_sp = vcpu.user_regs.rsp; vcpu.kernel_sp = vcpu.user_regs.rsp;
trace!("vcpu context: {:?}", vcpu); trace!("vcpu context: {:?}", vcpu);
setup.call.set_vcpu_context(setup.domid, 0, &vcpu)?; setup.call.set_vcpu_context(setup.domid, 0, &vcpu).await?;
Ok(()) Ok(())
} }
} }

View File

@ -2,7 +2,7 @@
name = "krata-xenevtchn" name = "krata-xenevtchn"
description = "An implementation of xen evtchn for krata." description = "An implementation of xen evtchn for krata."
license.workspace = true license.workspace = true
version= "0.0.3" version.workspace = true
homepage.workspace = true homepage.workspace = true
repository.workspace = true repository.workspace = true
edition = "2021" edition = "2021"

View File

@ -2,7 +2,7 @@
name = "krata-xengnt" name = "krata-xengnt"
description = "An implementation of xen grant interfaces for krata." description = "An implementation of xen grant interfaces for krata."
license.workspace = true license.workspace = true
version= "0.0.3" version.workspace = true
homepage.workspace = true homepage.workspace = true
repository.workspace = true repository.workspace = true
edition = "2021" edition = "2021"

View File

@ -2,7 +2,7 @@
name = "krata-xenstore" name = "krata-xenstore"
description = "A client that interacts with xenstore for krata." description = "A client that interacts with xenstore for krata."
license.workspace = true license.workspace = true
version= "0.0.3" version.workspace = true
homepage.workspace = true homepage.workspace = true
repository.workspace = true repository.workspace = true
edition = "2021" edition = "2021"

View File

@ -1,7 +1,7 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
ffi::CString, ffi::CString,
io::ErrorKind, io::Read,
os::{ os::{
fd::{AsRawFd, FromRawFd, IntoRawFd}, fd::{AsRawFd, FromRawFd, IntoRawFd},
unix::fs::FileTypeExt, unix::fs::FileTypeExt,
@ -9,11 +9,10 @@ use std::{
sync::Arc, sync::Arc,
}; };
use libc::{fcntl, F_GETFL, F_SETFL, O_NONBLOCK}; use log::{debug, warn};
use log::warn;
use tokio::{ use tokio::{
fs::{metadata, File}, fs::{metadata, File},
io::{AsyncReadExt, AsyncWriteExt}, io::AsyncWriteExt,
net::UnixStream, net::UnixStream,
select, select,
sync::{ sync::{
@ -64,8 +63,8 @@ pub struct XsdSocket {
next_request_id: Arc<Mutex<u32>>, next_request_id: Arc<Mutex<u32>>,
next_watch_id: Arc<Mutex<u32>>, next_watch_id: Arc<Mutex<u32>>,
processor_task: Arc<JoinHandle<()>>, processor_task: Arc<JoinHandle<()>>,
rx_task: Arc<JoinHandle<()>>,
unwatch_sender: Sender<(u32, String)>, unwatch_sender: Sender<(u32, String)>,
_rx_task: Arc<std::thread::JoinHandle<()>>,
} }
impl XsdSocket { impl XsdSocket {
@ -78,15 +77,10 @@ impl XsdSocket {
let file = if socket { let file = if socket {
let stream = UnixStream::connect(path).await?; let stream = UnixStream::connect(path).await?;
let stream = stream.into_std()?; let stream = stream.into_std()?;
stream.set_nonblocking(true)?; stream.set_nonblocking(false)?;
unsafe { File::from_raw_fd(stream.into_raw_fd()) } unsafe { File::from_raw_fd(stream.into_raw_fd()) }
} else { } else {
File::options() File::options().read(true).write(true).open(path).await?
.read(true)
.write(true)
.custom_flags(O_NONBLOCK)
.open(path)
.await?
}; };
XsdSocket::from_handle(file).await XsdSocket::from_handle(file).await
@ -101,7 +95,7 @@ impl XsdSocket {
let (rx_sender, rx_receiver) = channel::<XsdMessage>(10); let (rx_sender, rx_receiver) = channel::<XsdMessage>(10);
let (tx_sender, tx_receiver) = channel::<XsdMessage>(10); let (tx_sender, tx_receiver) = channel::<XsdMessage>(10);
let (unwatch_sender, unwatch_receiver) = channel::<(u32, String)>(1000); 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 { let mut processor = XsdSocketProcessor {
handle, handle,
@ -119,11 +113,13 @@ impl XsdSocket {
} }
}); });
let rx_task = tokio::task::spawn(async move { let rx_task = std::thread::Builder::new()
if let Err(error) = XsdSocketProcessor::process_rx(read, rx_sender).await { .name("xenstore-reader".to_string())
warn!("failed to process xen store responses: {}", error); .spawn(move || {
if let Err(error) = XsdSocketProcessor::process_rx(read, rx_sender) {
debug!("failed to process xen store bus: {}", error);
} }
}); })?;
Ok(XsdSocket { Ok(XsdSocket {
tx_sender, tx_sender,
@ -132,8 +128,8 @@ impl XsdSocket {
next_request_id, next_request_id,
next_watch_id: Arc::new(Mutex::new(0u32)), next_watch_id: Arc::new(Mutex::new(0u32)),
processor_task: Arc::new(processor_task), processor_task: Arc::new(processor_task),
rx_task: Arc::new(rx_task),
unwatch_sender, unwatch_sender,
_rx_task: Arc::new(rx_task),
}) })
} }
@ -201,80 +197,28 @@ struct XsdSocketProcessor {
} }
impl 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 header_buffer: Vec<u8> = vec![0u8; XsdMessageHeader::SIZE];
let mut buffer: Vec<u8> = vec![0u8; XEN_BUS_MAX_PACKET_SIZE - XsdMessageHeader::SIZE]; let mut buffer: Vec<u8> = vec![0u8; XEN_BUS_MAX_PACKET_SIZE - XsdMessageHeader::SIZE];
loop { loop {
select! { let message =
message = XsdSocketProcessor::read_message(&mut header_buffer, &mut buffer, &mut read) => { XsdSocketProcessor::read_message(&mut header_buffer, &mut buffer, &mut read)?;
let message = message?; rx_sender.blocking_send(message)?;
rx_sender.send(message).await?;
},
_ = rx_sender.closed() => {
break;
} }
};
}
Ok(())
} }
fn set_nonblocking(fd: i32, nonblock: bool) -> Result<()> { fn read_message(
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(
header_buffer: &mut [u8], header_buffer: &mut [u8],
buffer: &mut [u8], buffer: &mut [u8],
read: &mut File, read: &mut std::fs::File,
) -> Result<XsdMessage> { ) -> Result<XsdMessage> {
XsdSocketProcessor::set_nonblocking(read.as_raw_fd(), true)?; read.read_exact(header_buffer)?;
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);
}
let header = XsdMessageHeader::decode(header_buffer)?; let header = XsdMessageHeader::decode(header_buffer)?;
if header.len as usize > buffer.len() { if header.len as usize > buffer.len() {
return Err(Error::InvalidBusData); return Err(Error::InvalidBusData);
} }
let payload_buffer = &mut buffer[0..header.len as usize]; let payload_buffer = &mut buffer[0..header.len as usize];
XsdSocketProcessor::set_nonblocking(read.as_raw_fd(), false)?; read.read_exact(payload_buffer)?;
let payload_size = read.read_exact(payload_buffer).await?;
if payload_size != header.len as usize {
return Err(Error::InvalidBusData);
}
Ok(XsdMessage { Ok(XsdMessage {
header, header,
payload: payload_buffer.to_vec(), payload: payload_buffer.to_vec(),
@ -392,10 +336,6 @@ impl XsdMessage {
impl Drop for XsdSocket { impl Drop for XsdSocket {
fn drop(&mut self) { fn drop(&mut self) {
if Arc::strong_count(&self.rx_task) <= 1 {
self.rx_task.abort();
}
if Arc::strong_count(&self.processor_task) <= 1 { if Arc::strong_count(&self.processor_task) <= 1 {
self.processor_task.abort(); 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 let response = self
.socket .socket
.send(0, XSD_GET_DOMAIN_PATH, &[&domid.to_string()]) .send(0, XSD_GET_DOMAIN_PATH, &[&domid.to_string()])
@ -176,7 +176,7 @@ impl XsdClient {
response.parse_string() 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}"); trace!("introduce domain domid={domid} mfn={mfn} evtchn={evtchn}");
let response = self let response = self
.socket .socket

View File

@ -1,7 +1,5 @@
/// Handwritten protocol definitions for XenStore. /// Handwritten protocol definitions for XenStore.
/// Used xen/include/public/io/xs_wire.h as a reference. /// Used xen/include/public/io/xs_wire.h as a reference.
use libc;
use crate::error::Result; use crate::error::Result;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::Cursor; 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 #!/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" 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. # 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_CC_IS_GCC=y
CONFIG_GCC_VERSION=130201 CONFIG_GCC_VERSION=130200
CONFIG_CLANG_VERSION=0 CONFIG_CLANG_VERSION=0
CONFIG_AS_IS_GNU=y CONFIG_AS_IS_GNU=y
CONFIG_AS_VERSION=24000 CONFIG_AS_VERSION=24200
CONFIG_LD_IS_BFD=y CONFIG_LD_IS_BFD=y
CONFIG_LD_VERSION=24000 CONFIG_LD_VERSION=24200
CONFIG_LLD_VERSION=0 CONFIG_LLD_VERSION=0
CONFIG_CC_CAN_LINK=y CONFIG_CC_CAN_LINK=y
CONFIG_CC_CAN_LINK_STATIC=y
CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y
CONFIG_CC_HAS_ASM_GOTO_TIED_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_ASM_INLINE=y
CONFIG_CC_HAS_NO_PROFILE_FN_ATTR=y CONFIG_CC_HAS_NO_PROFILE_FN_ATTR=y
CONFIG_PAHOLE_VERSION=0 CONFIG_PAHOLE_VERSION=124
CONFIG_IRQ_WORK=y CONFIG_IRQ_WORK=y
CONFIG_BUILDTIME_TABLE_SORT=y CONFIG_BUILDTIME_TABLE_SORT=y
CONFIG_THREAD_INFO_IN_TASK=y CONFIG_THREAD_INFO_IN_TASK=y
@ -401,9 +401,7 @@ CONFIG_ARM64_ERRATUM_2067961=y
CONFIG_ARM64_ERRATUM_2441009=y CONFIG_ARM64_ERRATUM_2441009=y
CONFIG_ARM64_ERRATUM_2457168=y CONFIG_ARM64_ERRATUM_2457168=y
CONFIG_ARM64_ERRATUM_2645198=y CONFIG_ARM64_ERRATUM_2645198=y
CONFIG_ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD=y
CONFIG_ARM64_ERRATUM_2966298=y CONFIG_ARM64_ERRATUM_2966298=y
CONFIG_ARM64_ERRATUM_3117295=y
CONFIG_CAVIUM_ERRATUM_22375=y CONFIG_CAVIUM_ERRATUM_22375=y
CONFIG_CAVIUM_ERRATUM_23144=y CONFIG_CAVIUM_ERRATUM_23144=y
CONFIG_CAVIUM_ERRATUM_23154=y CONFIG_CAVIUM_ERRATUM_23154=y
@ -3475,6 +3473,7 @@ CONFIG_BCMA_POSSIBLE=y
# CONFIG_MFD_SM501 is not set # CONFIG_MFD_SM501 is not set
# CONFIG_MFD_SKY81452 is not set # CONFIG_MFD_SKY81452 is not set
# CONFIG_MFD_SYSCON 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_LP3943 is not set
# CONFIG_MFD_TI_LMU is not set # CONFIG_MFD_TI_LMU is not set
# CONFIG_TPS6105X 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_REDUCED is not set
CONFIG_DEBUG_INFO_COMPRESSED_NONE=y CONFIG_DEBUG_INFO_COMPRESSED_NONE=y
# CONFIG_DEBUG_INFO_COMPRESSED_ZLIB is not set # 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_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_GDB_SCRIPTS is not set
CONFIG_FRAME_WARN=1280 CONFIG_FRAME_WARN=1280
# CONFIG_STRIP_ASM_SYMS is not set # CONFIG_STRIP_ASM_SYMS is not set

View File

@ -1,8 +1,8 @@
# #
# Automatically generated file; DO NOT EDIT. # 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_CC_IS_GCC=y
CONFIG_GCC_VERSION=130200 CONFIG_GCC_VERSION=130200
CONFIG_CLANG_VERSION=0 CONFIG_CLANG_VERSION=0
@ -15,6 +15,7 @@ CONFIG_CC_CAN_LINK=y
CONFIG_CC_CAN_LINK_STATIC=y CONFIG_CC_CAN_LINK_STATIC=y
CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y
CONFIG_CC_HAS_ASM_GOTO_TIED_OUTPUT=y CONFIG_CC_HAS_ASM_GOTO_TIED_OUTPUT=y
CONFIG_GCC_ASM_GOTO_OUTPUT_WORKAROUND=y
CONFIG_TOOLS_SUPPORT_RELR=y CONFIG_TOOLS_SUPPORT_RELR=y
CONFIG_CC_HAS_ASM_INLINE=y CONFIG_CC_HAS_ASM_INLINE=y
CONFIG_CC_HAS_NO_PROFILE_FN_ATTR=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_ALWAYS_ON=y
CONFIG_BPF_JIT_DEFAULT_ON=y CONFIG_BPF_JIT_DEFAULT_ON=y
CONFIG_BPF_UNPRIV_DEFAULT_OFF=y CONFIG_BPF_UNPRIV_DEFAULT_OFF=y
CONFIG_USERMODE_DRIVER=y
# CONFIG_BPF_PRELOAD is not set # CONFIG_BPF_PRELOAD is not set
# CONFIG_BPF_LSM is not set # CONFIG_BPF_LSM is not set
# end of BPF subsystem # end of BPF subsystem
@ -188,8 +188,10 @@ CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y
CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y
CONFIG_CC_HAS_INT128=y CONFIG_CC_HAS_INT128=y
CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" 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_CC_NO_ARRAY_BOUNDS=y
CONFIG_GCC_NO_STRINGOP_OVERFLOW=y
CONFIG_CC_NO_STRINGOP_OVERFLOW=y
CONFIG_ARCH_SUPPORTS_INT128=y CONFIG_ARCH_SUPPORTS_INT128=y
CONFIG_CGROUPS=y CONFIG_CGROUPS=y
CONFIG_PAGE_COUNTER=y CONFIG_PAGE_COUNTER=y
@ -268,19 +270,19 @@ CONFIG_AIO=y
CONFIG_IO_URING=y CONFIG_IO_URING=y
CONFIG_ADVISE_SYSCALLS=y CONFIG_ADVISE_SYSCALLS=y
CONFIG_MEMBARRIER=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=y
# CONFIG_KALLSYMS_SELFTEST is not set # CONFIG_KALLSYMS_SELFTEST is not set
CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_ALL=y
CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y
CONFIG_KALLSYMS_BASE_RELATIVE=y CONFIG_KALLSYMS_BASE_RELATIVE=y
CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=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_HAVE_PERF_EVENTS=y
CONFIG_GUEST_PERF_EVENTS=y CONFIG_GUEST_PERF_EVENTS=y
# CONFIG_PC104 is not set
# #
# Kernel Performance Events And Counters # Kernel Performance Events And Counters
@ -380,6 +382,7 @@ CONFIG_GENERIC_CPU=y
CONFIG_X86_INTERNODE_CACHE_SHIFT=6 CONFIG_X86_INTERNODE_CACHE_SHIFT=6
CONFIG_X86_L1_CACHE_SHIFT=6 CONFIG_X86_L1_CACHE_SHIFT=6
CONFIG_X86_TSC=y CONFIG_X86_TSC=y
CONFIG_X86_HAVE_PAE=y
CONFIG_X86_CMPXCHG64=y CONFIG_X86_CMPXCHG64=y
CONFIG_X86_CMOV=y CONFIG_X86_CMOV=y
CONFIG_X86_MINIMUM_CPU_FAMILY=64 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_INTEL_TSX_MODE_AUTO is not set
# CONFIG_X86_SGX is not set # CONFIG_X86_SGX is not set
# CONFIG_X86_USER_SHADOW_STACK is not set # CONFIG_X86_USER_SHADOW_STACK is not set
# CONFIG_INTEL_TDX_HOST is not set
CONFIG_EFI=y CONFIG_EFI=y
CONFIG_EFI_STUB=y CONFIG_EFI_STUB=y
CONFIG_EFI_HANDOVER_PROTOCOL=y CONFIG_EFI_HANDOVER_PROTOCOL=y
@ -520,6 +522,7 @@ CONFIG_CPU_IBRS_ENTRY=y
CONFIG_CPU_SRSO=y CONFIG_CPU_SRSO=y
# CONFIG_SLS is not set # CONFIG_SLS is not set
# CONFIG_GDS_FORCE_MITIGATION is not set # CONFIG_GDS_FORCE_MITIGATION is not set
CONFIG_MITIGATION_RFDS=y
CONFIG_ARCH_HAS_ADD_PAGES=y CONFIG_ARCH_HAS_ADD_PAGES=y
# #
@ -544,6 +547,7 @@ CONFIG_ACPI=y
CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y
CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC=y CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC=y
CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y
CONFIG_ACPI_THERMAL_LIB=y
# CONFIG_ACPI_DEBUGGER is not set # CONFIG_ACPI_DEBUGGER is not set
CONFIG_ACPI_SPCR_TABLE=y CONFIG_ACPI_SPCR_TABLE=y
# CONFIG_ACPI_FPDT is not set # CONFIG_ACPI_FPDT is not set
@ -670,14 +674,13 @@ CONFIG_COMPAT_FOR_U64_ALIGNMENT=y
# end of Binary Emulations # end of Binary Emulations
CONFIG_HAVE_KVM=y CONFIG_HAVE_KVM=y
CONFIG_KVM_COMMON=y
CONFIG_HAVE_KVM_PFNCACHE=y CONFIG_HAVE_KVM_PFNCACHE=y
CONFIG_HAVE_KVM_IRQCHIP=y CONFIG_HAVE_KVM_IRQCHIP=y
CONFIG_HAVE_KVM_IRQFD=y
CONFIG_HAVE_KVM_IRQ_ROUTING=y CONFIG_HAVE_KVM_IRQ_ROUTING=y
CONFIG_HAVE_KVM_DIRTY_RING=y CONFIG_HAVE_KVM_DIRTY_RING=y
CONFIG_HAVE_KVM_DIRTY_RING_TSO=y CONFIG_HAVE_KVM_DIRTY_RING_TSO=y
CONFIG_HAVE_KVM_DIRTY_RING_ACQ_REL=y CONFIG_HAVE_KVM_DIRTY_RING_ACQ_REL=y
CONFIG_HAVE_KVM_EVENTFD=y
CONFIG_KVM_MMIO=y CONFIG_KVM_MMIO=y
CONFIG_KVM_ASYNC_PF=y CONFIG_KVM_ASYNC_PF=y
CONFIG_HAVE_KVM_MSI=y CONFIG_HAVE_KVM_MSI=y
@ -690,13 +693,16 @@ CONFIG_HAVE_KVM_NO_POLL=y
CONFIG_KVM_XFER_TO_GUEST_WORK=y CONFIG_KVM_XFER_TO_GUEST_WORK=y
CONFIG_HAVE_KVM_PM_NOTIFIER=y CONFIG_HAVE_KVM_PM_NOTIFIER=y
CONFIG_KVM_GENERIC_HARDWARE_ENABLING=y CONFIG_KVM_GENERIC_HARDWARE_ENABLING=y
CONFIG_KVM_GENERIC_MMU_NOTIFIER=y
CONFIG_VIRTUALIZATION=y CONFIG_VIRTUALIZATION=y
CONFIG_KVM=m CONFIG_KVM=m
CONFIG_KVM_WERROR=y CONFIG_KVM_WERROR=y
# CONFIG_KVM_SW_PROTECTED_VM is not set
CONFIG_KVM_INTEL=m CONFIG_KVM_INTEL=m
CONFIG_KVM_AMD=m CONFIG_KVM_AMD=m
CONFIG_KVM_AMD_SEV=y CONFIG_KVM_AMD_SEV=y
CONFIG_KVM_SMM=y CONFIG_KVM_SMM=y
CONFIG_KVM_HYPERV=y
# CONFIG_KVM_XEN is not set # CONFIG_KVM_XEN is not set
# CONFIG_KVM_PROVE_MMU is not set # CONFIG_KVM_PROVE_MMU is not set
CONFIG_KVM_MAX_NR_VCPUS=1024 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_ELFCORE_COMPAT=y
CONFIG_ARCH_HAS_PARANOID_L1D_FLUSH=y CONFIG_ARCH_HAS_PARANOID_L1D_FLUSH=y
CONFIG_DYNAMIC_SIGFRAME=y CONFIG_DYNAMIC_SIGFRAME=y
CONFIG_ARCH_HAS_HW_PTE_YOUNG=y
CONFIG_ARCH_HAS_NONLEAF_PMD_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_BSGLIB=y
CONFIG_BLK_DEV_INTEGRITY=y CONFIG_BLK_DEV_INTEGRITY=y
CONFIG_BLK_DEV_INTEGRITY_T10=m CONFIG_BLK_DEV_INTEGRITY_T10=m
CONFIG_BLK_DEV_WRITE_MOUNTED=y
# CONFIG_BLK_DEV_ZONED is not set # CONFIG_BLK_DEV_ZONED is not set
CONFIG_BLK_DEV_THROTTLING=y CONFIG_BLK_DEV_THROTTLING=y
# CONFIG_BLK_DEV_THROTTLING_LOW is not set # CONFIG_BLK_DEV_THROTTLING_LOW is not set
@ -992,6 +1000,7 @@ CONFIG_SWAP=y
CONFIG_ZSWAP=y CONFIG_ZSWAP=y
# CONFIG_ZSWAP_DEFAULT_ON is not set # CONFIG_ZSWAP_DEFAULT_ON is not set
# CONFIG_ZSWAP_EXCLUSIVE_LOADS_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_DEFLATE is not set
CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZO=y CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZO=y
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_842 is not set # CONFIG_ZSWAP_COMPRESSOR_DEFAULT_842 is not set
@ -1010,9 +1019,8 @@ CONFIG_ZSMALLOC=y
CONFIG_ZSMALLOC_CHAIN_SIZE=8 CONFIG_ZSMALLOC_CHAIN_SIZE=8
# #
# SLAB allocator options # Slab allocator options
# #
# CONFIG_SLAB_DEPRECATED is not set
CONFIG_SLUB=y CONFIG_SLUB=y
# CONFIG_SLUB_TINY is not set # CONFIG_SLUB_TINY is not set
CONFIG_SLAB_MERGE_DEFAULT=y CONFIG_SLAB_MERGE_DEFAULT=y
@ -1021,7 +1029,7 @@ CONFIG_SLAB_FREELIST_RANDOM=y
# CONFIG_SLUB_STATS is not set # CONFIG_SLUB_STATS is not set
CONFIG_SLUB_CPU_PARTIAL=y CONFIG_SLUB_CPU_PARTIAL=y
# CONFIG_RANDOM_KMALLOC_CACHES is not set # 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_SHUFFLE_PAGE_ALLOCATOR is not set
# CONFIG_COMPAT_BRK 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=y
CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set # CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set
# CONFIG_TRANSPARENT_HUGEPAGE_NEVER is not set
CONFIG_THP_SWAP=y CONFIG_THP_SWAP=y
# CONFIG_READ_ONLY_THP_FOR_FS is not set # CONFIG_READ_ONLY_THP_FOR_FS is not set
CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y
@ -1093,6 +1102,7 @@ CONFIG_SECRETMEM=y
CONFIG_LRU_GEN=y CONFIG_LRU_GEN=y
CONFIG_LRU_GEN_ENABLED=y CONFIG_LRU_GEN_ENABLED=y
# CONFIG_LRU_GEN_STATS is not set # CONFIG_LRU_GEN_STATS is not set
CONFIG_LRU_GEN_WALKS_MMU=y
CONFIG_ARCH_SUPPORTS_PER_VMA_LOCK=y CONFIG_ARCH_SUPPORTS_PER_VMA_LOCK=y
CONFIG_PER_VMA_LOCK=y CONFIG_PER_VMA_LOCK=y
CONFIG_LOCK_MM_AND_FIND_VMA=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_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_BPFILTER=y
CONFIG_BPFILTER_UMH=m
CONFIG_IP_DCCP=m CONFIG_IP_DCCP=m
CONFIG_INET_DCCP_DIAG=m CONFIG_INET_DCCP_DIAG=m
@ -1789,10 +1797,10 @@ CONFIG_STREAM_PARSER=y
CONFIG_FIB_RULES=y CONFIG_FIB_RULES=y
# CONFIG_WIRELESS is not set # CONFIG_WIRELESS is not set
# CONFIG_RFKILL is not set # CONFIG_RFKILL is not set
CONFIG_NET_9P=m CONFIG_NET_9P=y
CONFIG_NET_9P_FD=m CONFIG_NET_9P_FD=y
CONFIG_NET_9P_VIRTIO=m CONFIG_NET_9P_VIRTIO=y
CONFIG_NET_9P_XEN=m CONFIG_NET_9P_XEN=y
# CONFIG_NET_9P_DEBUG is not set # CONFIG_NET_9P_DEBUG is not set
# CONFIG_CAIF is not set # CONFIG_CAIF is not set
CONFIG_CEPH_LIB=m CONFIG_CEPH_LIB=m
@ -1927,6 +1935,7 @@ CONFIG_ALLOW_DEV_COREDUMP=y
# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set # CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set
# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set # CONFIG_TEST_ASYNC_DRIVER_PROBE is not set
CONFIG_SYS_HYPERVISOR=y CONFIG_SYS_HYPERVISOR=y
CONFIG_GENERIC_CPU_DEVICES=y
CONFIG_GENERIC_CPU_AUTOPROBE=y CONFIG_GENERIC_CPU_AUTOPROBE=y
CONFIG_GENERIC_CPU_VULNERABILITIES=y CONFIG_GENERIC_CPU_VULNERABILITIES=y
CONFIG_REGMAP=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_LZ4HC is not set
CONFIG_ZRAM_DEF_COMP="lz4" CONFIG_ZRAM_DEF_COMP="lz4"
# CONFIG_ZRAM_WRITEBACK is not set # CONFIG_ZRAM_WRITEBACK is not set
# CONFIG_ZRAM_TRACK_ENTRY_ACTIME is not set
# CONFIG_ZRAM_MEMORY_TRACKING is not set # CONFIG_ZRAM_MEMORY_TRACKING is not set
# CONFIG_ZRAM_MULTI_COMP is not set # CONFIG_ZRAM_MULTI_COMP is not set
CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_LOOP=m
@ -2091,6 +2101,7 @@ CONFIG_VMWARE_BALLOON=m
# CONFIG_DW_XDATA_PCIE is not set # CONFIG_DW_XDATA_PCIE is not set
# CONFIG_PCI_ENDPOINT_TEST is not set # CONFIG_PCI_ENDPOINT_TEST is not set
# CONFIG_XILINX_SDFEC is not set # CONFIG_XILINX_SDFEC is not set
# CONFIG_NSM is not set
# CONFIG_C2PORT is not set # CONFIG_C2PORT is not set
# #
@ -2117,8 +2128,6 @@ CONFIG_VMWARE_BALLOON=m
# #
# CONFIG_ALTERA_STAPL is not set # CONFIG_ALTERA_STAPL is not set
# CONFIG_INTEL_MEI 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_VMWARE_VMCI=m
# CONFIG_GENWQE is not set # CONFIG_GENWQE is not set
# CONFIG_ECHO is not set # CONFIG_ECHO is not set
@ -2332,13 +2341,10 @@ CONFIG_MD=y
CONFIG_BLK_DEV_MD=y CONFIG_BLK_DEV_MD=y
# CONFIG_MD_AUTODETECT is not set # CONFIG_MD_AUTODETECT is not set
CONFIG_MD_BITMAP_FILE=y CONFIG_MD_BITMAP_FILE=y
CONFIG_MD_LINEAR=m
CONFIG_MD_RAID0=m CONFIG_MD_RAID0=m
CONFIG_MD_RAID1=m CONFIG_MD_RAID1=m
CONFIG_MD_RAID10=m CONFIG_MD_RAID10=m
CONFIG_MD_RAID456=m CONFIG_MD_RAID456=m
CONFIG_MD_MULTIPATH=m
CONFIG_MD_FAULTY=m
# CONFIG_MD_CLUSTER is not set # CONFIG_MD_CLUSTER is not set
CONFIG_BCACHE=m CONFIG_BCACHE=m
# CONFIG_BCACHE_DEBUG is not set # 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_WIZNET is not set
CONFIG_NET_VENDOR_XILINX=y CONFIG_NET_VENDOR_XILINX=y
# CONFIG_XILINX_EMACLITE is not set # CONFIG_XILINX_EMACLITE is not set
# CONFIG_XILINX_AXI_EMAC is not set
# CONFIG_XILINX_LL_TEMAC is not set # CONFIG_XILINX_LL_TEMAC is not set
# CONFIG_FDDI is not set # CONFIG_FDDI is not set
# CONFIG_HIPPI is not set # CONFIG_HIPPI is not set
@ -2617,6 +2622,7 @@ CONFIG_FIXED_PHY=m
# CONFIG_DP83867_PHY is not set # CONFIG_DP83867_PHY is not set
# CONFIG_DP83869_PHY is not set # CONFIG_DP83869_PHY is not set
# CONFIG_DP83TD510_PHY is not set # CONFIG_DP83TD510_PHY is not set
# CONFIG_DP83TG720_PHY is not set
# CONFIG_VITESSE_PHY is not set # CONFIG_VITESSE_PHY is not set
# CONFIG_XILINX_GMII2RGMII is not set # CONFIG_XILINX_GMII2RGMII is not set
# CONFIG_PSE_CONTROLLER is not set # CONFIG_PSE_CONTROLLER is not set
@ -3075,13 +3081,13 @@ CONFIG_HWMON=m
# CONFIG_SENSORS_DRIVETEMP is not set # CONFIG_SENSORS_DRIVETEMP is not set
# CONFIG_SENSORS_DS620 is not set # CONFIG_SENSORS_DS620 is not set
# CONFIG_SENSORS_DS1621 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_I5K_AMB is not set
# CONFIG_SENSORS_F71805F is not set # CONFIG_SENSORS_F71805F is not set
# CONFIG_SENSORS_F71882FG is not set # CONFIG_SENSORS_F71882FG is not set
# CONFIG_SENSORS_F75375S is not set # CONFIG_SENSORS_F75375S is not set
# CONFIG_SENSORS_FSCHMD is not set # CONFIG_SENSORS_FSCHMD is not set
# CONFIG_SENSORS_FTSTEUTATES is not set # CONFIG_SENSORS_FTSTEUTATES is not set
# CONFIG_SENSORS_GIGABYTE_WATERFORCE is not set
# CONFIG_SENSORS_GL518SM is not set # CONFIG_SENSORS_GL518SM is not set
# CONFIG_SENSORS_GL520SM is not set # CONFIG_SENSORS_GL520SM is not set
# CONFIG_SENSORS_G760A is not set # CONFIG_SENSORS_G760A is not set
@ -3213,6 +3219,7 @@ CONFIG_SENSORS_ACPI_POWER=m
CONFIG_THERMAL=y CONFIG_THERMAL=y
# CONFIG_THERMAL_NETLINK is not set # CONFIG_THERMAL_NETLINK is not set
# CONFIG_THERMAL_STATISTICS is not set # CONFIG_THERMAL_STATISTICS is not set
# CONFIG_THERMAL_DEBUGFS is not set
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
# CONFIG_THERMAL_WRITABLE_TRIPS is not set # CONFIG_THERMAL_WRITABLE_TRIPS is not set
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
@ -3362,7 +3369,6 @@ CONFIG_BCMA_POSSIBLE=y
# CONFIG_MFD_SM501 is not set # CONFIG_MFD_SM501 is not set
# CONFIG_MFD_SKY81452 is not set # CONFIG_MFD_SKY81452 is not set
# CONFIG_MFD_SYSCON 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_LP3943 is not set
# CONFIG_MFD_TI_LMU is not set # CONFIG_MFD_TI_LMU is not set
# CONFIG_TPS6105X 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_AMDGPU is not set
# CONFIG_DRM_NOUVEAU is not set # CONFIG_DRM_NOUVEAU is not set
# CONFIG_DRM_I915 is not set # CONFIG_DRM_I915 is not set
# CONFIG_DRM_XE is not set
# CONFIG_DRM_VGEM is not set # CONFIG_DRM_VGEM is not set
# CONFIG_DRM_VKMS is not set # CONFIG_DRM_VKMS is not set
CONFIG_DRM_VMWGFX=m CONFIG_DRM_VMWGFX=m
@ -3457,7 +3464,6 @@ CONFIG_DRM_PANEL_BRIDGE=y
# CONFIG_DRM_ANALOGIX_ANX78XX is not set # CONFIG_DRM_ANALOGIX_ANX78XX is not set
# end of Display Interface Bridges # end of Display Interface Bridges
# CONFIG_DRM_LOONGSON is not set
# CONFIG_DRM_ETNAVIV is not set # CONFIG_DRM_ETNAVIV is not set
CONFIG_DRM_BOCHS=m CONFIG_DRM_BOCHS=m
CONFIG_DRM_CIRRUS_QEMU=m CONFIG_DRM_CIRRUS_QEMU=m
@ -3469,7 +3475,6 @@ CONFIG_DRM_VBOXVIDEO=m
# CONFIG_DRM_GUD is not set # CONFIG_DRM_GUD is not set
# CONFIG_DRM_SSD130X is not set # CONFIG_DRM_SSD130X is not set
CONFIG_DRM_HYPERV=m CONFIG_DRM_HYPERV=m
# CONFIG_DRM_LEGACY is not set
CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=m CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=m
# #
@ -3489,7 +3494,6 @@ CONFIG_FB=m
# CONFIG_FB_NVIDIA is not set # CONFIG_FB_NVIDIA is not set
# CONFIG_FB_RIVA is not set # CONFIG_FB_RIVA is not set
# CONFIG_FB_I740 is not set # CONFIG_FB_I740 is not set
# CONFIG_FB_LE80578 is not set
# CONFIG_FB_MATROX is not set # CONFIG_FB_MATROX is not set
# CONFIG_FB_RADEON is not set # CONFIG_FB_RADEON is not set
# CONFIG_FB_ATY128 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_COPYAREA=m
CONFIG_FB_SYS_IMAGEBLIT=m CONFIG_FB_SYS_IMAGEBLIT=m
# CONFIG_FB_FOREIGN_ENDIAN is not set # CONFIG_FB_FOREIGN_ENDIAN is not set
CONFIG_FB_SYS_FOPS=m CONFIG_FB_SYSMEM_FOPS=m
CONFIG_FB_DEFERRED_IO=y CONFIG_FB_DEFERRED_IO=y
CONFIG_FB_IOMEM_FOPS=m
CONFIG_FB_SYSMEM_HELPERS=y CONFIG_FB_SYSMEM_HELPERS=y
CONFIG_FB_SYSMEM_HELPERS_DEFERRED=y CONFIG_FB_SYSMEM_HELPERS_DEFERRED=y
# CONFIG_FB_MODE_HELPERS is not set # CONFIG_FB_MODE_HELPERS is not set
@ -4129,6 +4132,7 @@ CONFIG_VIRTIO_PCI_LIB=y
CONFIG_VIRTIO_PCI_LIB_LEGACY=y CONFIG_VIRTIO_PCI_LIB_LEGACY=y
CONFIG_VIRTIO_MENU=y CONFIG_VIRTIO_MENU=y
CONFIG_VIRTIO_PCI=y CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_PCI_ADMIN_LEGACY=y
CONFIG_VIRTIO_PCI_LEGACY=y CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_VDPA=m CONFIG_VIRTIO_VDPA=m
# CONFIG_VIRTIO_PMEM is not set # CONFIG_VIRTIO_PMEM is not set
@ -4308,6 +4312,7 @@ CONFIG_RPMSG_VIRTIO=m
# #
# Qualcomm SoC drivers # Qualcomm SoC drivers
# #
# CONFIG_QCOM_PMIC_PDCHARGER_ULOG is not set
# end of Qualcomm SoC drivers # end of Qualcomm SoC drivers
# CONFIG_SOC_TI is not set # CONFIG_SOC_TI is not set
@ -4382,6 +4387,7 @@ CONFIG_MEMORY=y
# #
# Performance monitor support # Performance monitor support
# #
# CONFIG_DWC_PCIE_PMU is not set
# end of Performance monitor support # end of Performance monitor support
# CONFIG_RAS is not set # CONFIG_RAS is not set
@ -4402,14 +4408,7 @@ CONFIG_DAX=y
# CONFIG_DEV_DAX is not set # CONFIG_DEV_DAX is not set
CONFIG_NVMEM=y CONFIG_NVMEM=y
CONFIG_NVMEM_SYSFS=y CONFIG_NVMEM_SYSFS=y
# CONFIG_NVMEM_LAYOUTS is not set
#
# Layout Types
#
# CONFIG_NVMEM_LAYOUT_SL28_VPD is not set
# CONFIG_NVMEM_LAYOUT_ONIE_TLV is not set
# end of Layout Types
# CONFIG_NVMEM_RMEM is not set # CONFIG_NVMEM_RMEM is not set
# #
@ -4436,6 +4435,7 @@ CONFIG_NVMEM_SYSFS=y
CONFIG_DCACHE_WORD_ACCESS=y CONFIG_DCACHE_WORD_ACCESS=y
# CONFIG_VALIDATE_FS_PARSER is not set # CONFIG_VALIDATE_FS_PARSER is not set
CONFIG_FS_IOMAP=y CONFIG_FS_IOMAP=y
CONFIG_FS_STACK=y
CONFIG_BUFFER_HEAD=y CONFIG_BUFFER_HEAD=y
CONFIG_LEGACY_DIRECT_IO=y CONFIG_LEGACY_DIRECT_IO=y
CONFIG_EXT2_FS=m CONFIG_EXT2_FS=m
@ -4541,9 +4541,9 @@ CONFIG_OVERLAY_FS=y
# #
# Caches # Caches
# #
CONFIG_NETFS_SUPPORT=m CONFIG_NETFS_SUPPORT=y
CONFIG_NETFS_STATS=y CONFIG_NETFS_STATS=y
CONFIG_FSCACHE=m CONFIG_FSCACHE=y
CONFIG_FSCACHE_STATS=y CONFIG_FSCACHE_STATS=y
# CONFIG_FSCACHE_DEBUG is not set # CONFIG_FSCACHE_DEBUG is not set
CONFIG_CACHEFILES=m CONFIG_CACHEFILES=m
@ -4598,9 +4598,9 @@ CONFIG_TMPFS_XATTR=y
CONFIG_TMPFS_INODE64=y CONFIG_TMPFS_INODE64=y
# CONFIG_TMPFS_QUOTA is not set # CONFIG_TMPFS_QUOTA is not set
CONFIG_HUGETLBFS=y CONFIG_HUGETLBFS=y
# CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON is not set
CONFIG_HUGETLB_PAGE=y CONFIG_HUGETLB_PAGE=y
CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP=y CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP=y
# CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON is not set
CONFIG_ARCH_HAS_GIGANTIC_PAGE=y CONFIG_ARCH_HAS_GIGANTIC_PAGE=y
CONFIG_CONFIGFS_FS=y CONFIG_CONFIGFS_FS=y
CONFIG_EFIVAR_FS=y CONFIG_EFIVAR_FS=y
@ -4688,6 +4688,7 @@ CONFIG_NFSD_SCSILAYOUT=y
CONFIG_NFSD_FLEXFILELAYOUT=y CONFIG_NFSD_FLEXFILELAYOUT=y
# CONFIG_NFSD_V4_2_INTER_SSC is not set # CONFIG_NFSD_V4_2_INTER_SSC is not set
# CONFIG_NFSD_V4_SECURITY_LABEL is not set # CONFIG_NFSD_V4_SECURITY_LABEL is not set
# CONFIG_NFSD_LEGACY_CLIENT_TRACKING is not set
CONFIG_GRACE_PERIOD=m CONFIG_GRACE_PERIOD=m
CONFIG_LOCKD=m CONFIG_LOCKD=m
CONFIG_LOCKD_V4=y CONFIG_LOCKD_V4=y
@ -4723,7 +4724,7 @@ CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN=y
CONFIG_SMBFS=m CONFIG_SMBFS=m
# CONFIG_CODA_FS is not set # CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set # CONFIG_AFS_FS is not set
CONFIG_9P_FS=m CONFIG_9P_FS=y
CONFIG_9P_FSCACHE=y CONFIG_9P_FSCACHE=y
CONFIG_9P_FS_POSIX_ACL=y CONFIG_9P_FS_POSIX_ACL=y
# CONFIG_9P_FS_SECURITY is not set # CONFIG_9P_FS_SECURITY is not set
@ -4947,14 +4948,12 @@ CONFIG_CRYPTO_TWOFISH_COMMON=m
CONFIG_CRYPTO_ADIANTUM=m CONFIG_CRYPTO_ADIANTUM=m
CONFIG_CRYPTO_CHACHA20=m CONFIG_CRYPTO_CHACHA20=m
CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_CBC=y
CONFIG_CRYPTO_CFB=m
CONFIG_CRYPTO_CTR=m CONFIG_CRYPTO_CTR=m
CONFIG_CRYPTO_CTS=y CONFIG_CRYPTO_CTS=y
CONFIG_CRYPTO_ECB=y CONFIG_CRYPTO_ECB=y
CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_HCTR2=m
CONFIG_CRYPTO_KEYWRAP=m CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_LRW=m
# CONFIG_CRYPTO_OFB is not set
CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XCTR=m CONFIG_CRYPTO_XCTR=m
CONFIG_CRYPTO_XTS=y 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_C3XXX is not set
# CONFIG_CRYPTO_DEV_QAT_C62X is not set # CONFIG_CRYPTO_DEV_QAT_C62X is not set
CONFIG_CRYPTO_DEV_QAT_4XXX=m CONFIG_CRYPTO_DEV_QAT_4XXX=m
# CONFIG_CRYPTO_DEV_QAT_420XX is not set
CONFIG_CRYPTO_DEV_QAT_DH895xCCVF=m CONFIG_CRYPTO_DEV_QAT_DH895xCCVF=m
# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set # CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set
# CONFIG_CRYPTO_DEV_QAT_C62XVF 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 # Compile-time checks and compiler options
# #
CONFIG_DEBUG_INFO=y 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_NONE is not set
# CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set # CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set
# CONFIG_DEBUG_INFO_DWARF4 is not set # CONFIG_DEBUG_INFO_DWARF4 is not set

41
release-plz.toml Normal file
View File

@ -0,0 +1,41 @@
[workspace]
git_release_enable = false
git_tag_enable = false
changelog_update = false
[[package]]
name = "krata"
git_release_name = "v{{ version }}"
git_tag_name = "v{{ version }}"
git_tag_enable = true
git_release_enable = true
changelog_update = true
changelog_path = "./CHANGELOG.md"
changelog_include = [
"krata-daemon",
"krata-ctl",
"krata-guest",
"krata-network",
"krata-runtime",
"krata-oci",
]
[[package]]
name = "krata-xencall"
semver_check = false
[[package]]
name = "krata-xenclient"
semver_check = false
[[package]]
name = "krata-xenevtchn"
semver_check = false
[[package]]
name = "krata-xengnt"
semver_check = false
[[package]]
name = "krata-xenstore"
semver_check = false

View File

@ -6,7 +6,6 @@ output_log="/var/log/kratad.log"
error_log="/var/log/kratad.err" error_log="/var/log/kratad.err"
depend() { depend() {
use xenconsoled
use xenstored use xenstored
} }

View File

@ -6,7 +6,7 @@ output_log="/var/log/kratanet.log"
error_log="/var/log/kratanet.err" error_log="/var/log/kratanet.err"
depend() { depend() {
use xenstored use kratad
} }
export RUST_LOG=info export RUST_LOG=info

View File

@ -2,6 +2,8 @@
Description=Krata Networking Daemon Description=Krata Networking Daemon
[Service] [Service]
Wants=kratad.service
After=kratad.service
Restart=on-failure Restart=on-failure
Type=simple Type=simple
ExecStart=/usr/libexec/kratanet ExecStart=/usr/libexec/kratanet