mirror of
https://github.com/aljazceru/kata-containers.git
synced 2026-01-19 14:24:26 +01:00
Merge pull request #7513 from fidencio/topic/merge-from-main-Aug-1st
CCv0 | CCv0: Merge from main -- August 1st
This commit is contained in:
4
.github/workflows/PR-wip-checks.yaml
vendored
4
.github/workflows/PR-wip-checks.yaml
vendored
@@ -9,6 +9,10 @@ on:
|
||||
- labeled
|
||||
- unlabeled
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
pr_wip_check:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/add-backport-label.yaml
vendored
4
.github/workflows/add-backport-label.yaml
vendored
@@ -10,6 +10,10 @@ on:
|
||||
- labeled
|
||||
- unlabeled
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
check-issues:
|
||||
if: ${{ github.event.label.name != 'auto-backport' }}
|
||||
|
||||
4
.github/workflows/add-issues-to-project.yaml
vendored
4
.github/workflows/add-issues-to-project.yaml
vendored
@@ -11,6 +11,10 @@ on:
|
||||
- opened
|
||||
- reopened
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
add-new-issues-to-backlog:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/add-pr-sizing-label.yaml
vendored
4
.github/workflows/add-pr-sizing-label.yaml
vendored
@@ -12,6 +12,10 @@ on:
|
||||
- reopened
|
||||
- synchronize
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
add-pr-size-label:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/auto-backport.yaml
vendored
4
.github/workflows/auto-backport.yaml
vendored
@@ -2,6 +2,10 @@ on:
|
||||
pull_request_target:
|
||||
types: ["labeled", "closed"]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
backport:
|
||||
name: Backport PR
|
||||
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
path: kata-artifacts
|
||||
- name: merge-artifacts
|
||||
run: |
|
||||
./tools/packaging/kata-deploy/local-build/kata-deploy-merge-builds.sh kata-artifacts
|
||||
./tools/packaging/kata-deploy/local-build/kata-deploy-merge-builds.sh kata-artifacts versions.yaml
|
||||
- name: store-artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
@@ -2,6 +2,10 @@ name: CI | Build kata-static tarball for arm64
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
stage:
|
||||
required: false
|
||||
type: string
|
||||
default: test
|
||||
tarball-suffix:
|
||||
required: false
|
||||
type: string
|
||||
@@ -29,6 +33,8 @@ jobs:
|
||||
- rootfs-initrd
|
||||
- shim-v2
|
||||
- virtiofsd
|
||||
stage:
|
||||
- ${{ inputs.stage }}
|
||||
steps:
|
||||
- name: Adjust a permission for repo
|
||||
run: |
|
||||
@@ -83,7 +89,7 @@ jobs:
|
||||
path: kata-artifacts
|
||||
- name: merge-artifacts
|
||||
run: |
|
||||
./tools/packaging/kata-deploy/local-build/kata-deploy-merge-builds.sh kata-artifacts
|
||||
./tools/packaging/kata-deploy/local-build/kata-deploy-merge-builds.sh kata-artifacts versions.yaml
|
||||
- name: store-artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
@@ -2,6 +2,10 @@ name: CI | Build kata-static tarball for s390x
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
stage:
|
||||
required: false
|
||||
type: string
|
||||
default: test
|
||||
tarball-suffix:
|
||||
required: false
|
||||
type: string
|
||||
@@ -25,6 +29,8 @@ jobs:
|
||||
- rootfs-initrd
|
||||
- shim-v2
|
||||
- virtiofsd
|
||||
stage:
|
||||
- ${{ inputs.stage }}
|
||||
steps:
|
||||
- name: Adjust a permission for repo
|
||||
run: |
|
||||
@@ -80,7 +86,7 @@ jobs:
|
||||
path: kata-artifacts
|
||||
- name: merge-artifacts
|
||||
run: |
|
||||
./tools/packaging/kata-deploy/local-build/kata-deploy-merge-builds.sh kata-artifacts
|
||||
./tools/packaging/kata-deploy/local-build/kata-deploy-merge-builds.sh kata-artifacts versions.yaml
|
||||
- name: store-artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
5
.github/workflows/cargo-deny-runner.yaml
vendored
5
.github/workflows/cargo-deny-runner.yaml
vendored
@@ -7,6 +7,11 @@ on:
|
||||
- reopened
|
||||
- synchronize
|
||||
paths-ignore: [ '**.md', '**.png', '**.jpg', '**.jpeg', '**.svg', '/docs/**' ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
cargo-deny-runner:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/cc-payload-amd64.yaml
vendored
4
.github/workflows/cc-payload-amd64.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- ovmf
|
||||
- qemu-snp-experimental
|
||||
- qemu-tdx-experimental
|
||||
- cc-sev-rootfs-initrd
|
||||
- rootfs-initrd-sev
|
||||
- cc-tdx-td-shim
|
||||
- tdvf
|
||||
include:
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
- measured_rootfs: yes
|
||||
asset: cc-rootfs-image
|
||||
- measured_rootfs: yes
|
||||
asset: cc-tdx-rootfs-image
|
||||
asset: rootfs-image-tdx
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build ${{ matrix.asset }}
|
||||
|
||||
4
.github/workflows/ci-nightly.yaml
vendored
4
.github/workflows/ci-nightly.yaml
vendored
@@ -4,6 +4,10 @@ on:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
kata-containers-ci-on-push:
|
||||
uses: ./.github/workflows/ci.yaml
|
||||
|
||||
5
.github/workflows/ci-on-push.yaml
vendored
5
.github/workflows/ci-on-push.yaml
vendored
@@ -14,6 +14,11 @@ on:
|
||||
- labeled
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
kata-containers-ci-on-push:
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'ok-to-test') }}
|
||||
|
||||
21
.github/workflows/ci.yaml
vendored
21
.github/workflows/ci.yaml
vendored
@@ -74,3 +74,24 @@ jobs:
|
||||
with:
|
||||
tarball-suffix: -${{ inputs.tag }}
|
||||
commit-hash: ${{ inputs.commit-hash }}
|
||||
|
||||
run-cri-containerd-tests:
|
||||
needs: build-kata-static-tarball-amd64
|
||||
uses: ./.github/workflows/run-cri-containerd-tests.yaml
|
||||
with:
|
||||
tarball-suffix: -${{ inputs.tag }}
|
||||
commit-hash: ${{ inputs.commit-hash }}
|
||||
|
||||
run-nydus-tests:
|
||||
needs: build-kata-static-tarball-amd64
|
||||
uses: ./.github/workflows/run-nydus-tests.yaml
|
||||
with:
|
||||
tarball-suffix: -${{ inputs.tag }}
|
||||
commit-hash: ${{ inputs.commit-hash }}
|
||||
|
||||
run-vfio-tests:
|
||||
needs: build-kata-static-tarball-amd64
|
||||
uses: ./.github/workflows/run-vfio-tests.yaml
|
||||
with:
|
||||
tarball-suffix: -${{ inputs.tag }}
|
||||
commit-hash: ${{ inputs.commit-hash }}
|
||||
|
||||
4
.github/workflows/commit-message-check.yaml
vendored
4
.github/workflows/commit-message-check.yaml
vendored
@@ -6,6 +6,10 @@ on:
|
||||
- reopened
|
||||
- synchronize
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
error_msg: |+
|
||||
See the document below for help on formatting commits for the project.
|
||||
|
||||
5
.github/workflows/darwin-tests.yaml
vendored
5
.github/workflows/darwin-tests.yaml
vendored
@@ -6,6 +6,11 @@ on:
|
||||
- reopened
|
||||
- synchronize
|
||||
paths-ignore: [ '**.md', '**.png', '**.jpg', '**.jpeg', '**.svg', '/docs/**' ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
name: Darwin tests
|
||||
jobs:
|
||||
test:
|
||||
|
||||
36
.github/workflows/kata-runtime-classes-sync.yaml
vendored
Normal file
36
.github/workflows/kata-runtime-classes-sync.yaml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- reopened
|
||||
- synchronize
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
kata-deploy-runtime-classes-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Ensure the split out runtime classes match the all-in-one file
|
||||
run: |
|
||||
pushd tools/packaging/kata-deploy/runtimeclasses/
|
||||
echo "::group::Combine runtime classes"
|
||||
for runtimeClass in `find . -type f \( -name "*.yaml" -and -not -name "kata-runtimeClasses.yaml" \) | sort`; do
|
||||
echo "Adding ${runtimeClass} to the resultingRuntimeClasses.yaml"
|
||||
cat ${runtimeClass} >> resultingRuntimeClasses.yaml;
|
||||
done
|
||||
echo "::endgroup::"
|
||||
echo "::group::Displaying the content of resultingRuntimeClasses.yaml"
|
||||
cat resultingRuntimeClasses.yaml
|
||||
echo "::endgroup::"
|
||||
echo ""
|
||||
echo "::group::Displaying the content of kata-runtimeClasses.yaml"
|
||||
cat kata-runtimeClasses.yaml
|
||||
echo "::endgroup::"
|
||||
echo ""
|
||||
diff resultingRuntimeClasses.yaml kata-runtimeClasses.yaml
|
||||
4
.github/workflows/payload-after-push.yaml
vendored
4
.github/workflows/payload-after-push.yaml
vendored
@@ -5,6 +5,10 @@ on:
|
||||
- main
|
||||
- stable-*
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-assets-amd64:
|
||||
uses: ./.github/workflows/build-kata-static-tarball-amd64.yaml
|
||||
|
||||
19
.github/workflows/release.yaml
vendored
19
.github/workflows/release.yaml
vendored
@@ -4,6 +4,10 @@ on:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+.[0-9]+*'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-and-push-assets-amd64:
|
||||
uses: ./.github/workflows/release-amd64.yaml
|
||||
@@ -117,6 +121,21 @@ jobs:
|
||||
GITHUB_TOKEN=${{ secrets.GIT_UPLOAD_TOKEN }} hub release edit -m "" -a "${tarball}" "${tag}"
|
||||
popd
|
||||
|
||||
upload-versions-yaml:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: upload versions.yaml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GIT_UPLOAD_TOKEN }}
|
||||
run: |
|
||||
tag=$(echo $GITHUB_REF | cut -d/ -f3-)
|
||||
pushd $GITHUB_WORKSPACE
|
||||
versions_file="kata-containers-$tag-versions.yaml"
|
||||
cp versions.yaml ${versions_file}
|
||||
hub release edit -m "" -a "${versions_file}" "${tag}"
|
||||
popd
|
||||
|
||||
upload-cargo-vendored-tarball:
|
||||
needs: upload-multi-arch-static-tarball
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -15,6 +15,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
check-pr-porting-labels:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
42
.github/workflows/run-cri-containerd-tests.yaml
vendored
Normal file
42
.github/workflows/run-cri-containerd-tests.yaml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: CI | Run cri-containerd tests
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
tarball-suffix:
|
||||
required: false
|
||||
type: string
|
||||
commit-hash:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
run-cri-containerd:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
containerd_version: ['lts', 'active']
|
||||
vmm: ['clh', 'qemu']
|
||||
runs-on: garm-ubuntu-2204
|
||||
env:
|
||||
CONTAINERD_VERSION: ${{ matrix.containerd_version }}
|
||||
GOPATH: ${{ github.workspace }}
|
||||
KATA_HYPERVISOR: ${{ matrix.vmm }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.commit-hash }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: bash tests/integration/cri-containerd/gha-run.sh install-dependencies
|
||||
|
||||
- name: get-kata-tarball
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: kata-static-tarball-amd64${{ inputs.tarball-suffix }}
|
||||
path: kata-artifacts
|
||||
|
||||
- name: Install kata
|
||||
run: bash tests/integration/cri-containerd/gha-run.sh install-kata kata-artifacts
|
||||
|
||||
- name: Run cri-containerd tests
|
||||
run: bash tests/integration/cri-containerd/gha-run.sh run
|
||||
22
.github/workflows/run-k8s-tests-on-aks.yaml
vendored
22
.github/workflows/run-k8s-tests-on-aks.yaml
vendored
@@ -40,37 +40,43 @@ jobs:
|
||||
GH_PR_NUMBER: ${{ inputs.pr-number }}
|
||||
KATA_HOST_OS: ${{ matrix.host_os }}
|
||||
KATA_HYPERVISOR: ${{ matrix.vmm }}
|
||||
USING_NFD: "false"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.commit-hash }}
|
||||
|
||||
- name: Download Azure CLI
|
||||
run: bash tests/integration/gha-run.sh install-azure-cli
|
||||
run: bash tests/integration/kubernetes/gha-run.sh install-azure-cli
|
||||
|
||||
- name: Log into the Azure account
|
||||
run: bash tests/integration/gha-run.sh login-azure
|
||||
run: bash tests/integration/kubernetes/gha-run.sh login-azure
|
||||
env:
|
||||
AZ_APPID: ${{ secrets.AZ_APPID }}
|
||||
AZ_PASSWORD: ${{ secrets.AZ_PASSWORD }}
|
||||
AZ_TENANT_ID: ${{ secrets.AZ_TENANT_ID }}
|
||||
|
||||
- name: Create AKS cluster
|
||||
run: bash tests/integration/gha-run.sh create-cluster
|
||||
timeout-minutes: 10
|
||||
run: bash tests/integration/kubernetes/gha-run.sh create-cluster
|
||||
|
||||
- name: Install `bats`
|
||||
run: bash tests/integration/gha-run.sh install-bats
|
||||
run: bash tests/integration/kubernetes/gha-run.sh install-bats
|
||||
|
||||
- name: Install `kubectl`
|
||||
run: bash tests/integration/gha-run.sh install-kubectl
|
||||
run: bash tests/integration/kubernetes/gha-run.sh install-kubectl
|
||||
|
||||
- name: Download credentials for the Kubernetes CLI to use them
|
||||
run: bash tests/integration/gha-run.sh get-cluster-credentials
|
||||
run: bash tests/integration/kubernetes/gha-run.sh get-cluster-credentials
|
||||
|
||||
- name: Deploy Kata
|
||||
timeout-minutes: 10
|
||||
run: bash tests/integration/kubernetes/gha-run.sh deploy-kata-aks
|
||||
|
||||
- name: Run tests
|
||||
timeout-minutes: 60
|
||||
run: bash tests/integration/gha-run.sh run-tests-aks
|
||||
run: bash tests/integration/kubernetes/gha-run.sh run-tests
|
||||
|
||||
- name: Delete AKS cluster
|
||||
if: always()
|
||||
run: bash tests/integration/gha-run.sh delete-cluster
|
||||
run: bash tests/integration/kubernetes/gha-run.sh delete-cluster
|
||||
|
||||
9
.github/workflows/run-k8s-tests-on-sev.yaml
vendored
9
.github/workflows/run-k8s-tests-on-sev.yaml
vendored
@@ -29,15 +29,20 @@ jobs:
|
||||
DOCKER_TAG: ${{ inputs.tag }}
|
||||
KATA_HYPERVISOR: ${{ matrix.vmm }}
|
||||
KUBECONFIG: /home/kata/.kube/config
|
||||
USING_NFD: "false"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.commit-hash }}
|
||||
|
||||
- name: Deploy Kata
|
||||
timeout-minutes: 10
|
||||
run: bash tests/integration/kubernetes/gha-run.sh deploy-kata-sev
|
||||
|
||||
- name: Run tests
|
||||
timeout-minutes: 30
|
||||
run: bash tests/integration/gha-run.sh run-tests-sev
|
||||
run: bash tests/integration/kubernetes/gha-run.sh run-tests
|
||||
|
||||
- name: Delete kata-deploy
|
||||
if: always()
|
||||
run: bash tests/integration/gha-run.sh cleanup-sev
|
||||
run: bash tests/integration/kubernetes/gha-run.sh cleanup-sev
|
||||
|
||||
11
.github/workflows/run-k8s-tests-on-snp.yaml
vendored
11
.github/workflows/run-k8s-tests-on-snp.yaml
vendored
@@ -29,15 +29,20 @@ jobs:
|
||||
DOCKER_TAG: ${{ inputs.tag }}
|
||||
KATA_HYPERVISOR: ${{ matrix.vmm }}
|
||||
KUBECONFIG: /home/kata/.kube/config
|
||||
USING_NFD: "false"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.commit-hash }}
|
||||
|
||||
- name: Deploy Kata
|
||||
timeout-minutes: 10
|
||||
run: bash tests/integration/kubernetes/gha-run.sh deploy-kata-snp
|
||||
|
||||
- name: Run tests
|
||||
timeout-minutes: 30
|
||||
run: bash tests/integration/gha-run.sh run-tests-snp
|
||||
|
||||
run: bash tests/integration/kubernetes/gha-run.sh run-tests
|
||||
|
||||
- name: Delete kata-deploy
|
||||
if: always()
|
||||
run: bash tests/integration/gha-run.sh cleanup-snp
|
||||
run: bash tests/integration/kubernetes/gha-run.sh cleanup-snp
|
||||
|
||||
12
.github/workflows/run-k8s-tests-on-tdx.yaml
vendored
12
.github/workflows/run-k8s-tests-on-tdx.yaml
vendored
@@ -28,16 +28,20 @@ jobs:
|
||||
DOCKER_REPO: ${{ inputs.repo }}
|
||||
DOCKER_TAG: ${{ inputs.tag }}
|
||||
KATA_HYPERVISOR: ${{ matrix.vmm }}
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
USING_NFD: "true"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.commit-hash }}
|
||||
|
||||
- name: Deploy Kata
|
||||
timeout-minutes: 10
|
||||
run: bash tests/integration/kubernetes/gha-run.sh deploy-kata-tdx
|
||||
|
||||
- name: Run tests
|
||||
timeout-minutes: 30
|
||||
run: bash tests/integration/gha-run.sh run-tests-tdx
|
||||
|
||||
run: bash tests/integration/kubernetes/gha-run.sh run-tests
|
||||
|
||||
- name: Delete kata-deploy
|
||||
if: always()
|
||||
run: bash tests/integration/gha-run.sh cleanup-tdx
|
||||
run: bash tests/integration/kubernetes/gha-run.sh cleanup-tdx
|
||||
|
||||
3
.github/workflows/run-metrics.yaml
vendored
3
.github/workflows/run-metrics.yaml
vendored
@@ -46,6 +46,9 @@ jobs:
|
||||
- name: run blogbench test
|
||||
run: bash tests/metrics/gha-run.sh run-test-blogbench
|
||||
|
||||
- name: run tensorflow test
|
||||
run: bash tests/metrics/gha-run.sh run-test-tensorflow
|
||||
|
||||
- name: make metrics tarball ${{ matrix.vmm }}
|
||||
run: bash tests/metrics/gha-run.sh make-tarball-results
|
||||
|
||||
|
||||
42
.github/workflows/run-nydus-tests.yaml
vendored
Normal file
42
.github/workflows/run-nydus-tests.yaml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: CI | Run nydus tests
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
tarball-suffix:
|
||||
required: false
|
||||
type: string
|
||||
commit-hash:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
run-nydus:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
containerd_version: ['lts', 'active']
|
||||
vmm: ['clh', 'qemu', 'dragonball']
|
||||
runs-on: garm-ubuntu-2204
|
||||
env:
|
||||
CONTAINERD_VERSION: ${{ matrix.containerd_version }}
|
||||
GOPATH: ${{ github.workspace }}
|
||||
KATA_HYPERVISOR: ${{ matrix.vmm }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.commit-hash }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: bash tests/integration/nydus/gha-run.sh install-dependencies
|
||||
|
||||
- name: get-kata-tarball
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: kata-static-tarball-amd64${{ inputs.tarball-suffix }}
|
||||
path: kata-artifacts
|
||||
|
||||
- name: Install kata
|
||||
run: bash tests/integration/nydus/gha-run.sh install-kata kata-artifacts
|
||||
|
||||
- name: Run nydus tests
|
||||
run: bash tests/integration/nydus/gha-run.sh run
|
||||
37
.github/workflows/run-vfio-tests.yaml
vendored
Normal file
37
.github/workflows/run-vfio-tests.yaml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: CI | Run vfio tests
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
tarball-suffix:
|
||||
required: false
|
||||
type: string
|
||||
commit-hash:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
run-vfio:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
vmm: ['clh', 'qemu']
|
||||
runs-on: garm-ubuntu-2204
|
||||
env:
|
||||
GOPATH: ${{ github.workspace }}
|
||||
KATA_HYPERVISOR: ${{ matrix.vmm }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.commit-hash }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: bash tests/functional/vfio/gha-run.sh install-dependencies
|
||||
|
||||
- name: get-kata-tarball
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: kata-static-tarball-amd64${{ inputs.tarball-suffix }}
|
||||
path: kata-artifacts
|
||||
|
||||
- name: Run vfio tests
|
||||
run: bash tests/functional/vfio/gha-run.sh run
|
||||
@@ -7,10 +7,14 @@ on:
|
||||
- synchronize
|
||||
paths-ignore: [ '**.md', '**.png', '**.jpg', '**.jpeg', '**.svg', '/docs/**' ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
name: Static checks dragonball
|
||||
jobs:
|
||||
test-dragonball:
|
||||
runs-on: self-hosted
|
||||
runs-on: dragonball
|
||||
env:
|
||||
RUST_BACKTRACE: "1"
|
||||
steps:
|
||||
|
||||
4
.github/workflows/static-checks.yaml
vendored
4
.github/workflows/static-checks.yaml
vendored
@@ -6,6 +6,10 @@ on:
|
||||
- reopened
|
||||
- synchronize
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
name: Static checks
|
||||
jobs:
|
||||
static-checks:
|
||||
|
||||
7
Makefile
7
Makefile
@@ -24,6 +24,10 @@ TOOLS += trace-forwarder
|
||||
|
||||
STANDARD_TARGETS = build check clean install static-checks-build test vendor
|
||||
|
||||
# Variables for the build-and-publish-kata-debug target
|
||||
KATA_DEBUG_REGISTRY ?= ""
|
||||
KATA_DEBUG_TAG ?= ""
|
||||
|
||||
default: all
|
||||
|
||||
include utils.mk
|
||||
@@ -44,6 +48,9 @@ static-checks: static-checks-build
|
||||
docs-url-alive-check:
|
||||
bash ci/docs-url-alive-check.sh
|
||||
|
||||
build-and-publish-kata-debug:
|
||||
bash tools/packaging/kata-debug/kata-debug-build-and-upload-payload.sh ${KATA_DEBUG_REGISTRY} ${KATA_DEBUG_TAG}
|
||||
|
||||
.PHONY: \
|
||||
all \
|
||||
kata-tarball \
|
||||
|
||||
@@ -134,6 +134,7 @@ The table below lists the remaining parts of the project:
|
||||
| [packaging](tools/packaging) | infrastructure | Scripts and metadata for producing packaged binaries<br/>(components, hypervisors, kernel and rootfs). |
|
||||
| [kernel](https://www.kernel.org) | kernel | Linux kernel used by the hypervisor to boot the guest image. Patches are stored [here](tools/packaging/kernel). |
|
||||
| [osbuilder](tools/osbuilder) | infrastructure | Tool to create "mini O/S" rootfs and initrd images and kernel for the hypervisor. |
|
||||
| [kata-debug](tools/packaging/kata-debug/README.md) | infrastructure | Utility tool to gather Kata Containers debug information from Kubernetes clusters. |
|
||||
| [`agent-ctl`](src/tools/agent-ctl) | utility | Tool that provides low-level access for testing the agent. |
|
||||
| [`kata-ctl`](src/tools/kata-ctl) | utility | Tool that provides advanced commands and debug facilities. |
|
||||
| [`log-parser-rs`](src/tools/log-parser-rs) | utility | Tool that aid in analyzing logs from the kata runtime. |
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
[Kubernetes](https://github.com/kubernetes/kubernetes/), or K8s, is a popular open source
|
||||
container orchestration engine. In Kubernetes, a set of containers sharing resources
|
||||
such as networking, storage, mount, PID, etc. is called a
|
||||
[pod](https://kubernetes.io/docs/user-guide/pods/).
|
||||
[pod](https://kubernetes.io/docs/concepts/workloads/pods/).
|
||||
|
||||
A node can have multiple pods, but at a minimum, a node within a Kubernetes cluster
|
||||
only needs to run a container runtime and a container agent (called a
|
||||
[Kubelet](https://kubernetes.io/docs/admin/kubelet/)).
|
||||
[Kubelet](https://kubernetes.io/docs/concepts/overview/components/#kubelet)).
|
||||
|
||||
Kata Containers represents a Kubelet pod as a VM.
|
||||
|
||||
|
||||
2
src/agent/Cargo.lock
generated
2
src/agent/Cargo.lock
generated
@@ -2081,6 +2081,7 @@ dependencies = [
|
||||
"slog",
|
||||
"slog-scope",
|
||||
"slog-stdlog",
|
||||
"slog-term",
|
||||
"tempfile",
|
||||
"test-utils",
|
||||
"thiserror",
|
||||
@@ -2100,6 +2101,7 @@ dependencies = [
|
||||
name = "kata-sys-util"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"cgroups-rs",
|
||||
"chrono",
|
||||
|
||||
@@ -44,6 +44,7 @@ ipnetwork = "0.17.0"
|
||||
logging = { path = "../libs/logging" }
|
||||
slog = "2.5.2"
|
||||
slog-scope = "4.1.2"
|
||||
slog-term = "2.9.0"
|
||||
|
||||
# Redirect ttrpc log calls
|
||||
slog-stdlog = "4.0.0"
|
||||
|
||||
@@ -26,7 +26,7 @@ export VERSION_COMMIT := $(if $(COMMIT),$(VERSION)-$(COMMIT),$(VERSION))
|
||||
EXTRA_RUSTFEATURES :=
|
||||
|
||||
##VAR SECCOMP=yes|no define if agent enables seccomp feature
|
||||
SECCOMP := yes
|
||||
SECCOMP ?= yes
|
||||
|
||||
# Enable seccomp feature of rust build
|
||||
ifeq ($(SECCOMP),yes)
|
||||
|
||||
@@ -1118,6 +1118,7 @@ mod tests {
|
||||
use std::fs::create_dir;
|
||||
use std::fs::create_dir_all;
|
||||
use std::fs::remove_dir_all;
|
||||
use std::fs::remove_file;
|
||||
use std::io;
|
||||
use std::os::unix::fs;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
@@ -1333,14 +1334,9 @@ mod tests {
|
||||
fn test_mknod_dev() {
|
||||
skip_if_not_root!();
|
||||
|
||||
let tempdir = tempdir().unwrap();
|
||||
|
||||
let olddir = unistd::getcwd().unwrap();
|
||||
defer!(let _ = unistd::chdir(&olddir););
|
||||
let _ = unistd::chdir(tempdir.path());
|
||||
|
||||
let path = "/dev/fifo-test";
|
||||
let dev = oci::LinuxDevice {
|
||||
path: "/fifo".to_string(),
|
||||
path: path.to_string(),
|
||||
r#type: "c".to_string(),
|
||||
major: 0,
|
||||
minor: 0,
|
||||
@@ -1348,13 +1344,16 @@ mod tests {
|
||||
uid: Some(unistd::getuid().as_raw()),
|
||||
gid: Some(unistd::getgid().as_raw()),
|
||||
};
|
||||
let path = Path::new("fifo");
|
||||
|
||||
let ret = mknod_dev(&dev, path);
|
||||
let ret = mknod_dev(&dev, Path::new(path));
|
||||
assert!(ret.is_ok(), "Should pass. Got: {:?}", ret);
|
||||
|
||||
let ret = stat::stat(path);
|
||||
assert!(ret.is_ok(), "Should pass. Got: {:?}", ret);
|
||||
|
||||
// clear test device node
|
||||
let ret = remove_file(path);
|
||||
assert!(ret.is_ok(), "Should pass, Got: {:?}", ret);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -161,7 +161,7 @@ impl Process {
|
||||
|
||||
pub fn notify_term_close(&mut self) {
|
||||
let notify = self.term_exit_notifier.clone();
|
||||
notify.notify_one();
|
||||
notify.notify_waiters();
|
||||
}
|
||||
|
||||
pub fn close_stdin(&mut self) {
|
||||
|
||||
@@ -33,7 +33,7 @@ pub fn create_pci_root_bus_path() -> String {
|
||||
|
||||
// check if there is pci bus path for acpi
|
||||
acpi_sysfs_dir.push_str(&acpi_root_bus_path);
|
||||
if let Ok(_) = fs::metadata(&acpi_sysfs_dir) {
|
||||
if fs::metadata(&acpi_sysfs_dir).is_ok() {
|
||||
return acpi_root_bus_path;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ use crate::Sandbox;
|
||||
use crate::{ccw, device::get_virtio_blk_ccw_device_name};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use slog::Logger;
|
||||
|
||||
use tracing::instrument;
|
||||
|
||||
pub const TYPE_ROOTFS: &str = "rootfs";
|
||||
@@ -145,6 +146,11 @@ pub const STORAGE_HANDLER_LIST: &[&str] = &[
|
||||
DRIVER_WATCHABLE_BIND_TYPE,
|
||||
];
|
||||
|
||||
#[instrument]
|
||||
pub fn get_mounts() -> Result<String, std::io::Error> {
|
||||
fs::read_to_string("/proc/mounts")
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub fn baremount(
|
||||
source: &Path,
|
||||
@@ -168,6 +174,31 @@ pub fn baremount(
|
||||
return Err(anyhow!("need mount FS type"));
|
||||
}
|
||||
|
||||
let destination_str = destination.to_string_lossy();
|
||||
let mounts = get_mounts().unwrap_or_else(|_| String::new());
|
||||
let already_mounted = mounts
|
||||
.lines()
|
||||
.map(|line| line.split_whitespace().collect::<Vec<&str>>())
|
||||
.filter(|parts| parts.len() >= 3) // ensure we have at least [source}, destination, and fs_type
|
||||
.any(|parts| {
|
||||
// Check if source, destination and fs_type match any entry in /proc/mounts
|
||||
// minimal check is for destination an fstype since source can have different names like:
|
||||
// udev /dev devtmpfs
|
||||
// dev /dev devtmpfs
|
||||
// depending on which entity is mounting the dev/fs/pseudo-fs
|
||||
parts[1] == destination_str && parts[2] == fs_type
|
||||
});
|
||||
|
||||
if already_mounted {
|
||||
slog_info!(
|
||||
logger,
|
||||
"{:?} is already mounted at {:?}",
|
||||
source,
|
||||
destination
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!(
|
||||
logger,
|
||||
"baremount source={:?}, dest={:?}, fs_type={:?}, options={:?}, flags={:?}",
|
||||
@@ -725,6 +756,14 @@ pub fn recursive_ownership_change(
|
||||
mask |= EXEC_MASK;
|
||||
mask |= MODE_SETGID;
|
||||
}
|
||||
|
||||
// We do not want to change the permission of the underlying file
|
||||
// using symlink. Hence we skip symlinks from recursive ownership
|
||||
// and permission changes.
|
||||
if path.is_symlink() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
nix::unistd::chown(path, uid, gid)?;
|
||||
|
||||
if gid.is_some() {
|
||||
@@ -1102,6 +1141,7 @@ fn parse_options(option_list: Vec<String>) -> HashMap<String, String> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use protocols::agent::FSGroup;
|
||||
use slog::Drain;
|
||||
use std::fs::File;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
@@ -1112,6 +1152,31 @@ mod tests {
|
||||
skip_if_not_root, skip_loop_by_user, skip_loop_if_not_root, skip_loop_if_root,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_already_baremounted() {
|
||||
let plain = slog_term::PlainSyncDecorator::new(std::io::stdout());
|
||||
let logger = Logger::root(slog_term::FullFormat::new(plain).build().fuse(), o!());
|
||||
|
||||
let test_cases = [
|
||||
("dev", "/dev", "devtmpfs"),
|
||||
("udev", "/dev", "devtmpfs"),
|
||||
("proc", "/proc", "proc"),
|
||||
("sysfs", "/sys", "sysfs"),
|
||||
];
|
||||
|
||||
for &(source, destination, fs_type) in &test_cases {
|
||||
let source = Path::new(source);
|
||||
let destination = Path::new(destination);
|
||||
let flags = MsFlags::MS_RDONLY;
|
||||
let options = "mode=755";
|
||||
println!(
|
||||
"testing if already mounted baremount({:?} {:?} {:?})",
|
||||
source, destination, fs_type
|
||||
);
|
||||
assert!(baremount(source, destination, fs_type, flags, options, &logger).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mount() {
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -665,15 +665,16 @@ impl AgentService {
|
||||
let cid = req.container_id;
|
||||
let eid = req.exec_id;
|
||||
|
||||
let mut term_exit_notifier = Arc::new(tokio::sync::Notify::new());
|
||||
let term_exit_notifier;
|
||||
let reader = {
|
||||
let s = self.sandbox.clone();
|
||||
let mut sandbox = s.lock().await;
|
||||
|
||||
let p = sandbox.find_container_process(cid.as_str(), eid.as_str())?;
|
||||
|
||||
term_exit_notifier = p.term_exit_notifier.clone();
|
||||
|
||||
if p.term_master.is_some() {
|
||||
term_exit_notifier = p.term_exit_notifier.clone();
|
||||
p.get_reader(StreamType::TermMaster)
|
||||
} else if stdout {
|
||||
if p.parent_stdout.is_some() {
|
||||
@@ -693,9 +694,12 @@ impl AgentService {
|
||||
let reader = reader.ok_or_else(|| anyhow!("cannot get stream reader"))?;
|
||||
|
||||
tokio::select! {
|
||||
_ = term_exit_notifier.notified() => {
|
||||
Err(anyhow!("eof"))
|
||||
}
|
||||
// Poll the futures in the order they appear from top to bottom
|
||||
// it is very important to avoid data loss. If there is still
|
||||
// data in the buffer and read_stream branch will return
|
||||
// Poll::Ready so that the term_exit_notifier will never polled
|
||||
// before all data were read.
|
||||
biased;
|
||||
v = read_stream(reader, req.len as usize) => {
|
||||
let vector = v?;
|
||||
let mut resp = ReadStreamResponse::new();
|
||||
@@ -703,6 +707,9 @@ impl AgentService {
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
_ = term_exit_notifier.notified() => {
|
||||
Err(anyhow!("eof"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -435,7 +435,7 @@ fn online_resources(logger: &Logger, path: &str, pattern: &str, num: i32) -> Res
|
||||
}
|
||||
|
||||
// max wait for all CPUs to online will use 50 * 100 = 5 seconds.
|
||||
const ONLINE_CPUMEM_WATI_MILLIS: u64 = 50;
|
||||
const ONLINE_CPUMEM_WAIT_MILLIS: u64 = 50;
|
||||
const ONLINE_CPUMEM_MAX_RETRIES: i32 = 100;
|
||||
|
||||
#[instrument]
|
||||
@@ -465,7 +465,7 @@ fn online_cpus(logger: &Logger, num: i32) -> Result<i32> {
|
||||
);
|
||||
return Ok(num);
|
||||
}
|
||||
thread::sleep(time::Duration::from_millis(ONLINE_CPUMEM_WATI_MILLIS));
|
||||
thread::sleep(time::Duration::from_millis(ONLINE_CPUMEM_WAIT_MILLIS));
|
||||
}
|
||||
|
||||
Err(anyhow!(
|
||||
|
||||
@@ -57,7 +57,7 @@ async fn handle_sigchild(logger: Logger, sandbox: Arc<Mutex<Sandbox>>) -> Result
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut p = process.unwrap();
|
||||
let p = process.unwrap();
|
||||
|
||||
let ret: i32 = match wait_status {
|
||||
WaitStatus::Exited(_, c) => c,
|
||||
|
||||
290
src/dragonball/Cargo.lock
generated
290
src/dragonball/Cargo.lock
generated
@@ -210,8 +210,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "dbs-address-space"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95e20d28a9cd13bf00d0ecd1bd073d242242b04f0acb663d7adfc659f8879322"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"lazy_static",
|
||||
@@ -225,8 +223,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "dbs-allocator"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "543711b94b4bc1437d2ebb45f856452e96a45a67ab39f8dcf8c887c2a3701004"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
@@ -234,8 +230,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "dbs-arch"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "194c844946cd1d13f7a9eb29b84afbc5354578eee2b06fea96226bc3872e7424"
|
||||
dependencies = [
|
||||
"kvm-bindings",
|
||||
"kvm-ioctls",
|
||||
@@ -249,8 +243,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "dbs-boot"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5466a92f75aa928a9103dcb2088f6d1638ef9da8945fad7389a73864dfa0182c"
|
||||
dependencies = [
|
||||
"dbs-arch",
|
||||
"kvm-bindings",
|
||||
@@ -265,8 +257,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "dbs-device"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14ecea44b4bc861c0c2ccb51868bea781286dc70e40ae46b54d4511e690a654a"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
@@ -274,8 +264,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "dbs-interrupt"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1eb2c5bb9f8f123ace33b1b2e8d53dd2d87331ee770ad1f82e56c3382c6bed6d"
|
||||
dependencies = [
|
||||
"dbs-arch",
|
||||
"dbs-device",
|
||||
@@ -288,11 +276,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "dbs-legacy-devices"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4d089ac1c4d186c8133be59de09462e9793f7add10017c5b040318a3a7f431f"
|
||||
dependencies = [
|
||||
"dbs-device",
|
||||
"dbs-utils",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
"vm-superio",
|
||||
@@ -302,8 +289,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "dbs-upcall"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea3a78128fd0be8b8b10257675c262b378dc5d00b1e18157736a6c27e45ce4fb"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dbs-utils",
|
||||
@@ -316,8 +301,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "dbs-utils"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cb6ff873451b76e22789af7fbe1d0478c42c717f817e66908be7a3a2288068c"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"event-manager",
|
||||
@@ -332,8 +315,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "dbs-virtio-devices"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d671cc3e5f98b84ef6b6bed007d28f72f16d3aea8eb38e2d42b00b2973c1d8"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"caps",
|
||||
@@ -349,9 +330,10 @@ dependencies = [
|
||||
"log",
|
||||
"nix 0.24.3",
|
||||
"nydus-api",
|
||||
"nydus-blobfs",
|
||||
"nydus-rafs",
|
||||
"nydus-storage",
|
||||
"rlimit",
|
||||
"sendfd",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
@@ -498,10 +480,25 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuse-backend-rs"
|
||||
version = "0.10.2"
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08af89cb80a7c5693bd63a2b1ee7ac31a307670977c18fda036b3aa94be8c47f"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "fuse-backend-rs"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc24820b14267bec37fa87f5c2a32b5f1c5405b8c60cc3aa77afd481bd2628a6"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"bitflags",
|
||||
@@ -518,95 +515,6 @@ dependencies = [
|
||||
"vmm-sys-util 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.6"
|
||||
@@ -891,82 +799,45 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nydus-api"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1899def1a22ed32b1d60de4e444f525c4023a208ee0d1136a65399cff82837ce"
|
||||
checksum = "33a6ca41dd10813e3d29397550fbb0f15ad149381f312e04659d39e0adcf2002"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"libc",
|
||||
"log",
|
||||
"nydus-error",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nydus-blobfs"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784cf6e1319da7a94734987dcc71d2940f74231256922431a505c832fc778dd3"
|
||||
dependencies = [
|
||||
"fuse-backend-rs",
|
||||
"libc",
|
||||
"log",
|
||||
"nydus-api",
|
||||
"nydus-error",
|
||||
"nydus-rafs",
|
||||
"nydus-storage",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"vm-memory",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nydus-error"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae2ec1efd1589377dbefca6b1047294c71b2fbab164d93319f97b20faae92001"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"httpdate",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nydus-rafs"
|
||||
version = "0.2.2"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0ace6945daa16842e72e9fe7647e2b8715856f50f07350cce82bd68db1ed02c"
|
||||
checksum = "ed21e44a99472850d2afc4fb07427ed46d4e6a8b1cce28b42bd689319e45076d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"bitflags",
|
||||
"blake3",
|
||||
"fuse-backend-rs",
|
||||
"futures",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"lz4-sys",
|
||||
"nix 0.24.3",
|
||||
"nydus-api",
|
||||
"nydus-error",
|
||||
"nydus-storage",
|
||||
"nydus-utils",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"spmc",
|
||||
"vm-memory",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nydus-storage"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08bc5ea9054fca2ec8b19dcce25ea600679b7fbf035aad86cfe4a659002c88b"
|
||||
checksum = "9591fbee1875895bf1f765656695d0be6887fe65372fbf4924b8b3959bd61375"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"bitflags",
|
||||
@@ -978,7 +849,6 @@ dependencies = [
|
||||
"log",
|
||||
"nix 0.24.3",
|
||||
"nydus-api",
|
||||
"nydus-error",
|
||||
"nydus-utils",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -989,12 +859,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nydus-utils"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1e681d7207a1ec500323d5ca39ebb7e381fc4f14db5ff0c532c18ff1226a81f"
|
||||
checksum = "fe8b9269e3a370682f272a1b2cac4bdaf6d6657f3f6966560c4fedab36548362"
|
||||
dependencies = [
|
||||
"blake3",
|
||||
"flate2",
|
||||
"httpdate",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
@@ -1002,7 +873,8 @@ dependencies = [
|
||||
"lz4",
|
||||
"lz4-sys",
|
||||
"nix 0.24.3",
|
||||
"nydus-error",
|
||||
"nydus-api",
|
||||
"openssl",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
@@ -1025,6 +897,54 @@ version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.26.0+1.1.1u"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
@@ -1054,12 +974,6 @@ version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.26"
|
||||
@@ -1068,18 +982,18 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1166,6 +1080,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sendfd"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "604b71b8fc267e13bb3023a2c901126c8f349393666a6d98ac1ae5729b701798"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.156"
|
||||
@@ -1183,7 +1106,7 @@ checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1275,12 +1198,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spmc"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02a8428da277a8e3a15271d79943e80ccc2ef254e78813a166a08d65e4c3ece5"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
@@ -1298,6 +1215,17 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "take_mut"
|
||||
version = "0.2.2"
|
||||
@@ -1350,7 +1278,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1434,7 +1362,7 @@ checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1480,7 +1408,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -12,16 +12,16 @@ edition = "2018"
|
||||
[dependencies]
|
||||
arc-swap = "1.5.0"
|
||||
bytes = "1.1.0"
|
||||
dbs-address-space = "0.3.0"
|
||||
dbs-allocator = "0.1.0"
|
||||
dbs-arch = "0.2.0"
|
||||
dbs-boot = "0.4.0"
|
||||
dbs-device = "0.2.0"
|
||||
dbs-interrupt = { version = "0.2.0", features = ["kvm-irq"] }
|
||||
dbs-legacy-devices = "0.1.0"
|
||||
dbs-upcall = { version = "0.3.0", optional = true }
|
||||
dbs-utils = "0.2.0"
|
||||
dbs-virtio-devices = { version = "0.3.1", optional = true, features = ["virtio-mmio"] }
|
||||
dbs-address-space = { path = "./src/dbs_address_space" }
|
||||
dbs-allocator = { path = "./src/dbs_allocator" }
|
||||
dbs-arch = { path = "./src/dbs_arch" }
|
||||
dbs-boot = { path = "./src/dbs_boot" }
|
||||
dbs-device = { path = "./src/dbs_device" }
|
||||
dbs-interrupt = { path = "./src/dbs_interrupt", features = ["kvm-irq"] }
|
||||
dbs-legacy-devices = { path = "./src/dbs_legacy_devices" }
|
||||
dbs-upcall = { path = "./src/dbs_upcall" , optional = true }
|
||||
dbs-utils = { path = "./src/dbs_utils" }
|
||||
dbs-virtio-devices = { path = "./src/dbs_virtio_devices", optional = true, features = ["virtio-mmio"] }
|
||||
kvm-bindings = "0.6.0"
|
||||
kvm-ioctls = "0.12.0"
|
||||
lazy_static = "1.2"
|
||||
|
||||
@@ -39,12 +39,15 @@ clean:
|
||||
|
||||
test:
|
||||
ifdef SUPPORT_VIRTUALIZATION
|
||||
cargo test --all-features --target $(TRIPLE) -- --nocapture
|
||||
RUST_BACKTRACE=1 cargo test --all-features --target $(TRIPLE) -- --nocapture --test-threads=1
|
||||
else
|
||||
@echo "INFO: skip testing dragonball, it need virtualization support."
|
||||
exit 0
|
||||
endif
|
||||
|
||||
coverage:
|
||||
RUST_BACKTRACE=1 cargo llvm-cov --all-features --target $(TRIPLE) -- --nocapture --test-threads=1
|
||||
|
||||
endif # ifeq ($(ARCH), s390x)
|
||||
|
||||
.DEFAULT_GOAL := default
|
||||
|
||||
@@ -16,10 +16,22 @@ and configuration process.
|
||||
|
||||
# Documentation
|
||||
|
||||
Device: [Device Document](docs/device.md)
|
||||
vCPU: [vCPU Document](docs/vcpu.md)
|
||||
API: [API Document](docs/api.md)
|
||||
`Upcall`: [`Upcall` Document](docs/upcall.md)
|
||||
- Device: [Device Document](docs/device.md)
|
||||
- vCPU: [vCPU Document](docs/vcpu.md)
|
||||
- API: [API Document](docs/api.md)
|
||||
- `Upcall`: [`Upcall` Document](docs/upcall.md)
|
||||
- `dbs_acpi`: [`dbs_acpi` Document](src/dbs_acpi/README.md)
|
||||
- `dbs_address_space`: [`dbs_address_space` Document](src/dbs_address_space/README.md)
|
||||
- `dbs_allocator`: [`dbs_allocator` Document](src/dbs_allocator/README.md)
|
||||
- `dbs_arch`: [`dbs_arch` Document](src/dbs_arch/README.md)
|
||||
- `dbs_boot`: [`dbs_boot` Document](src/dbs_boot/README.md)
|
||||
- `dbs_device`: [`dbs_device` Document](src/dbs_device/README.md)
|
||||
- `dbs_interrupt`: [`dbs_interrput` Document](src/dbs_interrupt/README.md)
|
||||
- `dbs_legacy_devices`: [`dbs_legacy_devices` Document](src/dbs_legacy_devices/README.md)
|
||||
- `dbs_tdx`: [`dbs_tdx` Document](src/dbs_tdx/README.md)
|
||||
- `dbs_upcall`: [`dbs_upcall` Document](src/dbs_upcall/README.md)
|
||||
- `dbs_utils`: [`dbs_utils` Document](src/dbs_utils/README.md)
|
||||
- `dbs_virtio_devices`: [`dbs_virtio_devices` Document](src/dbs_virtio_devices/README.md)
|
||||
|
||||
Currently, the documents are still actively adding.
|
||||
You could see the [official documentation](docs/) page for more details.
|
||||
|
||||
14
src/dragonball/src/dbs_acpi/Cargo.toml
Normal file
14
src/dragonball/src/dbs_acpi/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "dbs-acpi"
|
||||
version = "0.1.0"
|
||||
authors = ["Alibaba Dragonball Team"]
|
||||
description = "acpi definitions for virtual machines."
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
homepage = "https://github.com/openanolis/dragonball-sandbox"
|
||||
repository = "https://github.com/openanolis/dragonball-sandbox"
|
||||
keywords = ["dragonball", "acpi", "vmm", "secure-sandbox"]
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
vm-memory = "0.9.0"
|
||||
11
src/dragonball/src/dbs_acpi/README.md
Normal file
11
src/dragonball/src/dbs_acpi/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# dbs-acpi
|
||||
|
||||
`dbs-acpi` provides ACPI data structures for VMM to emulate ACPI behavior.
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
Part of the code is derived from the [Cloud Hypervisor](https://github.com/cloud-hypervisor/cloud-hypervisor) project.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||
29
src/dragonball/src/dbs_acpi/src/lib.rs
Normal file
29
src/dragonball/src/dbs_acpi/src/lib.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2019 Intel Corporation
|
||||
// Copyright (c) 2023 Alibaba Cloud
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
pub mod rsdp;
|
||||
pub mod sdt;
|
||||
|
||||
fn generate_checksum(data: &[u8]) -> u8 {
|
||||
(255 - data.iter().fold(0u8, |acc, x| acc.wrapping_add(*x))).wrapping_add(1)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_generate_checksum() {
|
||||
let mut buf = [0x00; 8];
|
||||
let sum = generate_checksum(&buf);
|
||||
assert_eq!(sum, 0);
|
||||
buf[0] = 0xff;
|
||||
let sum = generate_checksum(&buf);
|
||||
assert_eq!(sum, 1);
|
||||
buf[0] = 0xaa;
|
||||
buf[1] = 0xcc;
|
||||
buf[4] = generate_checksum(&buf);
|
||||
let sum = buf.iter().fold(0u8, |s, v| s.wrapping_add(*v));
|
||||
assert_eq!(sum, 0);
|
||||
}
|
||||
}
|
||||
60
src/dragonball/src/dbs_acpi/src/rsdp.rs
Normal file
60
src/dragonball/src/dbs_acpi/src/rsdp.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2019 Intel Corporation
|
||||
// Copyright (c) 2023 Alibaba Cloud
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// RSDP (Root System Description Pointer) is a data structure used in the ACPI programming interface.
|
||||
use vm_memory::ByteValued;
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct Rsdp {
|
||||
pub signature: [u8; 8],
|
||||
pub checksum: u8,
|
||||
pub oem_id: [u8; 6],
|
||||
pub revision: u8,
|
||||
_rsdt_addr: u32,
|
||||
pub length: u32,
|
||||
pub xsdt_addr: u64,
|
||||
pub extended_checksum: u8,
|
||||
_reserved: [u8; 3],
|
||||
}
|
||||
|
||||
// SAFETY: Rsdp only contains a series of integers
|
||||
unsafe impl ByteValued for Rsdp {}
|
||||
|
||||
impl Rsdp {
|
||||
pub fn new(xsdt_addr: u64) -> Self {
|
||||
let mut rsdp = Rsdp {
|
||||
signature: *b"RSD PTR ",
|
||||
checksum: 0,
|
||||
oem_id: *b"ALICLD",
|
||||
revision: 1,
|
||||
_rsdt_addr: 0,
|
||||
length: std::mem::size_of::<Rsdp>() as u32,
|
||||
xsdt_addr,
|
||||
extended_checksum: 0,
|
||||
_reserved: [0; 3],
|
||||
};
|
||||
rsdp.checksum = super::generate_checksum(&rsdp.as_slice()[0..19]);
|
||||
rsdp.extended_checksum = super::generate_checksum(rsdp.as_slice());
|
||||
rsdp
|
||||
}
|
||||
|
||||
pub fn len() -> usize {
|
||||
std::mem::size_of::<Rsdp>()
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Rsdp;
|
||||
use vm_memory::bytes::ByteValued;
|
||||
#[test]
|
||||
fn test_rsdp() {
|
||||
let rsdp = Rsdp::new(0xa0000);
|
||||
let sum = rsdp
|
||||
.as_slice()
|
||||
.iter()
|
||||
.fold(0u8, |acc, x| acc.wrapping_add(*x));
|
||||
assert_eq!(sum, 0);
|
||||
}
|
||||
}
|
||||
137
src/dragonball/src/dbs_acpi/src/sdt.rs
Normal file
137
src/dragonball/src/dbs_acpi/src/sdt.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright (c) 2019 Intel Corporation
|
||||
// Copyright (c) 2023 Alibaba Cloud
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#[repr(packed)]
|
||||
pub struct GenericAddress {
|
||||
pub address_space_id: u8,
|
||||
pub register_bit_width: u8,
|
||||
pub register_bit_offset: u8,
|
||||
pub access_size: u8,
|
||||
pub address: u64,
|
||||
}
|
||||
|
||||
impl GenericAddress {
|
||||
pub fn io_port_address<T>(address: u16) -> Self {
|
||||
GenericAddress {
|
||||
address_space_id: 1,
|
||||
register_bit_width: 8 * std::mem::size_of::<T>() as u8,
|
||||
register_bit_offset: 0,
|
||||
access_size: std::mem::size_of::<T>() as u8,
|
||||
address: u64::from(address),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mmio_address<T>(address: u64) -> Self {
|
||||
GenericAddress {
|
||||
address_space_id: 0,
|
||||
register_bit_width: 8 * std::mem::size_of::<T>() as u8,
|
||||
register_bit_offset: 0,
|
||||
access_size: std::mem::size_of::<T>() as u8,
|
||||
address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Sdt {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
impl Sdt {
|
||||
pub fn new(signature: [u8; 4], length: u32, revision: u8) -> Self {
|
||||
assert!(length >= 36);
|
||||
const OEM_ID: [u8; 6] = *b"ALICLD";
|
||||
const OEM_TABLE: [u8; 8] = *b"RUND ";
|
||||
const CREATOR_ID: [u8; 4] = *b"ALIC";
|
||||
let mut data = Vec::with_capacity(length as usize);
|
||||
data.extend_from_slice(&signature);
|
||||
data.extend_from_slice(&length.to_le_bytes());
|
||||
data.push(revision);
|
||||
data.push(0); // checksum
|
||||
data.extend_from_slice(&OEM_ID); // oem id u32
|
||||
data.extend_from_slice(&OEM_TABLE); // oem table
|
||||
data.extend_from_slice(&1u32.to_le_bytes()); // oem revision u32
|
||||
data.extend_from_slice(&CREATOR_ID); // creator id u32
|
||||
data.extend_from_slice(&1u32.to_le_bytes()); // creator revison u32
|
||||
assert_eq!(data.len(), 36);
|
||||
data.resize(length as usize, 0);
|
||||
let mut sdt = Sdt { data };
|
||||
sdt.update_checksum();
|
||||
sdt
|
||||
}
|
||||
|
||||
pub fn update_checksum(&mut self) {
|
||||
self.data[9] = 0;
|
||||
let checksum = super::generate_checksum(self.data.as_slice());
|
||||
self.data[9] = checksum
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
self.data.as_slice()
|
||||
}
|
||||
|
||||
pub fn append<T>(&mut self, value: T) {
|
||||
let orig_length = self.data.len();
|
||||
let new_length = orig_length + std::mem::size_of::<T>();
|
||||
self.data.resize(new_length, 0);
|
||||
self.write_u32(4, new_length as u32);
|
||||
self.write(orig_length, value);
|
||||
}
|
||||
|
||||
pub fn append_slice(&mut self, data: &[u8]) {
|
||||
let orig_length = self.data.len();
|
||||
let new_length = orig_length + data.len();
|
||||
self.write_u32(4, new_length as u32);
|
||||
self.data.extend_from_slice(data);
|
||||
self.update_checksum();
|
||||
}
|
||||
|
||||
/// Write a value at the given offset
|
||||
pub fn write<T>(&mut self, offset: usize, value: T) {
|
||||
assert!((offset + (std::mem::size_of::<T>() - 1)) < self.data.len());
|
||||
unsafe {
|
||||
*(((self.data.as_mut_ptr() as usize) + offset) as *mut T) = value;
|
||||
}
|
||||
self.update_checksum();
|
||||
}
|
||||
|
||||
pub fn write_u8(&mut self, offset: usize, val: u8) {
|
||||
self.write(offset, val);
|
||||
}
|
||||
|
||||
pub fn write_u16(&mut self, offset: usize, val: u16) {
|
||||
self.write(offset, val);
|
||||
}
|
||||
|
||||
pub fn write_u32(&mut self, offset: usize, val: u32) {
|
||||
self.write(offset, val);
|
||||
}
|
||||
|
||||
pub fn write_u64(&mut self, offset: usize, val: u64) {
|
||||
self.write(offset, val);
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Sdt;
|
||||
#[test]
|
||||
fn test_sdt() {
|
||||
let mut sdt = Sdt::new(*b"TEST", 40, 1);
|
||||
let sum: u8 = sdt
|
||||
.as_slice()
|
||||
.iter()
|
||||
.fold(0u8, |acc, x| acc.wrapping_add(*x));
|
||||
assert_eq!(sum, 0);
|
||||
sdt.write_u32(36, 0x12345678);
|
||||
let sum: u8 = sdt
|
||||
.as_slice()
|
||||
.iter()
|
||||
.fold(0u8, |acc, x| acc.wrapping_add(*x));
|
||||
assert_eq!(sum, 0);
|
||||
}
|
||||
}
|
||||
20
src/dragonball/src/dbs_address_space/Cargo.toml
Normal file
20
src/dragonball/src/dbs_address_space/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "dbs-address-space"
|
||||
version = "0.3.0"
|
||||
authors = ["Alibaba Dragonball Team"]
|
||||
description = "address space manager for virtual machines."
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
homepage = "https://github.com/openanolis/dragonball-sandbox"
|
||||
repository = "https://github.com/openanolis/dragonball-sandbox"
|
||||
keywords = ["dragonball", "address", "vmm", "secure-sandbox"]
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
arc-swap = ">=0.4.8"
|
||||
libc = "0.2.39"
|
||||
nix = "0.23.1"
|
||||
lazy_static = "1"
|
||||
thiserror = "1"
|
||||
vmm-sys-util = "0.11.0"
|
||||
vm-memory = { version = "0.9", features = ["backend-mmap", "backend-atomic"] }
|
||||
1
src/dragonball/src/dbs_address_space/LICENSE
Symbolic link
1
src/dragonball/src/dbs_address_space/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../LICENSE
|
||||
80
src/dragonball/src/dbs_address_space/README.md
Normal file
80
src/dragonball/src/dbs_address_space/README.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# dbs-address-space
|
||||
|
||||
## Design
|
||||
|
||||
The `dbs-address-space` crate is an address space manager for virtual machines, which manages memory and MMIO resources resident in the guest physical address space.
|
||||
|
||||
Main components are:
|
||||
- `AddressSpaceRegion`: Struct to maintain configuration information about a guest address region.
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AddressSpaceRegion {
|
||||
/// Type of address space regions.
|
||||
pub ty: AddressSpaceRegionType,
|
||||
/// Base address of the region in virtual machine's physical address space.
|
||||
pub base: GuestAddress,
|
||||
/// Size of the address space region.
|
||||
pub size: GuestUsize,
|
||||
/// Host NUMA node ids assigned to this region.
|
||||
pub host_numa_node_id: Option<u32>,
|
||||
|
||||
/// File/offset tuple to back the memory allocation.
|
||||
file_offset: Option<FileOffset>,
|
||||
/// Mmap permission flags.
|
||||
perm_flags: i32,
|
||||
/// Hugepage madvise hint.
|
||||
///
|
||||
/// It needs 'advise' or 'always' policy in host shmem config.
|
||||
is_hugepage: bool,
|
||||
/// Hotplug hint.
|
||||
is_hotplug: bool,
|
||||
/// Anonymous memory hint.
|
||||
///
|
||||
/// It should be true for regions with the MADV_DONTFORK flag enabled.
|
||||
is_anon: bool,
|
||||
}
|
||||
```
|
||||
- `AddressSpaceBase`: Base implementation to manage guest physical address space, without support of region hotplug.
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct AddressSpaceBase {
|
||||
regions: Vec<Arc<AddressSpaceRegion>>,
|
||||
layout: AddressSpaceLayout,
|
||||
}
|
||||
```
|
||||
- `AddressSpaceBase`: An address space implementation with region hotplug capability.
|
||||
```rust
|
||||
/// The `AddressSpace` is a wrapper over [AddressSpaceBase] to support hotplug of
|
||||
/// address space regions.
|
||||
#[derive(Clone)]
|
||||
pub struct AddressSpace {
|
||||
state: Arc<ArcSwap<AddressSpaceBase>>,
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
```rust
|
||||
// 1. create several memory regions
|
||||
let reg = Arc::new(
|
||||
AddressSpaceRegion::create_default_memory_region(
|
||||
GuestAddress(0x100000),
|
||||
0x100000,
|
||||
None,
|
||||
"shmem",
|
||||
"",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
let regions = vec![reg];
|
||||
// 2. create layout (depending on archs)
|
||||
let layout = AddressSpaceLayout::new(GUEST_PHYS_END, GUEST_MEM_START, GUEST_MEM_END);
|
||||
// 3. create address space from regions and layout
|
||||
let address_space = AddressSpace::from_regions(regions, layout.clone());
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0.
|
||||
830
src/dragonball/src/dbs_address_space/src/address_space.rs
Normal file
830
src/dragonball/src/dbs_address_space/src/address_space.rs
Normal file
@@ -0,0 +1,830 @@
|
||||
// Copyright (C) 2021 Alibaba Cloud. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Physical address space manager for virtual machines.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use vm_memory::{GuestAddress, GuestMemoryMmap};
|
||||
|
||||
use crate::{AddressSpaceError, AddressSpaceLayout, AddressSpaceRegion, AddressSpaceRegionType};
|
||||
|
||||
/// Base implementation to manage guest physical address space, without support of region hotplug.
|
||||
#[derive(Clone)]
|
||||
pub struct AddressSpaceBase {
|
||||
regions: Vec<Arc<AddressSpaceRegion>>,
|
||||
layout: AddressSpaceLayout,
|
||||
}
|
||||
|
||||
impl AddressSpaceBase {
|
||||
/// Create an instance of `AddressSpaceBase` from an `AddressSpaceRegion` array.
|
||||
///
|
||||
/// To achieve better performance by using binary search algorithm, the `regions` vector
|
||||
/// will gotten sorted by guest physical address.
|
||||
///
|
||||
/// Note, panicking if some regions intersects with each other.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `regions` - prepared regions to managed by the address space instance.
|
||||
/// * `layout` - prepared address space layout configuration.
|
||||
pub fn from_regions(
|
||||
mut regions: Vec<Arc<AddressSpaceRegion>>,
|
||||
layout: AddressSpaceLayout,
|
||||
) -> Self {
|
||||
regions.sort_unstable_by_key(|v| v.base);
|
||||
for region in regions.iter() {
|
||||
if !layout.is_region_valid(region) {
|
||||
panic!(
|
||||
"Invalid region {:?} for address space layout {:?}",
|
||||
region, layout
|
||||
);
|
||||
}
|
||||
}
|
||||
for idx in 1..regions.len() {
|
||||
if regions[idx].intersect_with(®ions[idx - 1]) {
|
||||
panic!("address space regions intersect with each other");
|
||||
}
|
||||
}
|
||||
AddressSpaceBase { regions, layout }
|
||||
}
|
||||
|
||||
/// Insert a new address space region into the address space.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `region` - the new region to be inserted.
|
||||
pub fn insert_region(
|
||||
&mut self,
|
||||
region: Arc<AddressSpaceRegion>,
|
||||
) -> Result<(), AddressSpaceError> {
|
||||
if !self.layout.is_region_valid(®ion) {
|
||||
return Err(AddressSpaceError::InvalidAddressRange(
|
||||
region.start_addr().0,
|
||||
region.len(),
|
||||
));
|
||||
}
|
||||
for idx in 0..self.regions.len() {
|
||||
if self.regions[idx].intersect_with(®ion) {
|
||||
return Err(AddressSpaceError::InvalidAddressRange(
|
||||
region.start_addr().0,
|
||||
region.len(),
|
||||
));
|
||||
}
|
||||
}
|
||||
self.regions.push(region);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Enumerate all regions in the address space.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `cb` - the callback function to apply to each region.
|
||||
pub fn walk_regions<F>(&self, mut cb: F) -> Result<(), AddressSpaceError>
|
||||
where
|
||||
F: FnMut(&Arc<AddressSpaceRegion>) -> Result<(), AddressSpaceError>,
|
||||
{
|
||||
for reg in self.regions.iter() {
|
||||
cb(reg)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get address space layout associated with the address space.
|
||||
pub fn layout(&self) -> AddressSpaceLayout {
|
||||
self.layout.clone()
|
||||
}
|
||||
|
||||
/// Get maximum of guest physical address in the address space.
|
||||
pub fn last_addr(&self) -> GuestAddress {
|
||||
let mut last_addr = GuestAddress(self.layout.mem_start);
|
||||
for reg in self.regions.iter() {
|
||||
if reg.ty != AddressSpaceRegionType::DAXMemory && reg.last_addr() > last_addr {
|
||||
last_addr = reg.last_addr();
|
||||
}
|
||||
}
|
||||
last_addr
|
||||
}
|
||||
|
||||
/// Check whether the guest physical address `guest_addr` belongs to a DAX memory region.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `guest_addr` - the guest physical address to inquire
|
||||
pub fn is_dax_region(&self, guest_addr: GuestAddress) -> bool {
|
||||
for reg in self.regions.iter() {
|
||||
// Safe because we have validate the region when creating the address space object.
|
||||
if reg.region_type() == AddressSpaceRegionType::DAXMemory
|
||||
&& reg.start_addr() <= guest_addr
|
||||
&& reg.start_addr().0 + reg.len() > guest_addr.0
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Get protection flags of memory region that guest physical address `guest_addr` belongs to.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `guest_addr` - the guest physical address to inquire
|
||||
pub fn prot_flags(&self, guest_addr: GuestAddress) -> Result<i32, AddressSpaceError> {
|
||||
for reg in self.regions.iter() {
|
||||
if reg.start_addr() <= guest_addr && reg.start_addr().0 + reg.len() > guest_addr.0 {
|
||||
return Ok(reg.prot_flags());
|
||||
}
|
||||
}
|
||||
|
||||
Err(AddressSpaceError::InvalidRegionType)
|
||||
}
|
||||
|
||||
/// Get optional NUMA node id associated with guest physical address `gpa`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `gpa` - guest physical address to query.
|
||||
pub fn numa_node_id(&self, gpa: u64) -> Option<u32> {
|
||||
for reg in self.regions.iter() {
|
||||
if gpa >= reg.base.0 && gpa < (reg.base.0 + reg.size) {
|
||||
return reg.host_numa_node_id;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// An address space implementation with region hotplug capability.
|
||||
///
|
||||
/// The `AddressSpace` is a wrapper over [AddressSpaceBase] to support hotplug of
|
||||
/// address space regions.
|
||||
#[derive(Clone)]
|
||||
pub struct AddressSpace {
|
||||
state: Arc<ArcSwap<AddressSpaceBase>>,
|
||||
}
|
||||
|
||||
impl AddressSpace {
|
||||
/// Convert a [GuestMemoryMmap] object into `GuestMemoryAtomic<GuestMemoryMmap>`.
|
||||
pub fn convert_into_vm_as(
|
||||
gm: GuestMemoryMmap,
|
||||
) -> vm_memory::atomic::GuestMemoryAtomic<GuestMemoryMmap> {
|
||||
vm_memory::atomic::GuestMemoryAtomic::from(Arc::new(gm))
|
||||
}
|
||||
|
||||
/// Create an instance of `AddressSpace` from an `AddressSpaceRegion` array.
|
||||
///
|
||||
/// To achieve better performance by using binary search algorithm, the `regions` vector
|
||||
/// will gotten sorted by guest physical address.
|
||||
///
|
||||
/// Note, panicking if some regions intersects with each other.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `regions` - prepared regions to managed by the address space instance.
|
||||
/// * `layout` - prepared address space layout configuration.
|
||||
pub fn from_regions(regions: Vec<Arc<AddressSpaceRegion>>, layout: AddressSpaceLayout) -> Self {
|
||||
let base = AddressSpaceBase::from_regions(regions, layout);
|
||||
|
||||
AddressSpace {
|
||||
state: Arc::new(ArcSwap::new(Arc::new(base))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a new address space region into the address space.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `region` - the new region to be inserted.
|
||||
pub fn insert_region(
|
||||
&mut self,
|
||||
region: Arc<AddressSpaceRegion>,
|
||||
) -> Result<(), AddressSpaceError> {
|
||||
let curr = self.state.load().regions.clone();
|
||||
let layout = self.state.load().layout.clone();
|
||||
let mut base = AddressSpaceBase::from_regions(curr, layout);
|
||||
base.insert_region(region)?;
|
||||
let _old = self.state.swap(Arc::new(base));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Enumerate all regions in the address space.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `cb` - the callback function to apply to each region.
|
||||
pub fn walk_regions<F>(&self, cb: F) -> Result<(), AddressSpaceError>
|
||||
where
|
||||
F: FnMut(&Arc<AddressSpaceRegion>) -> Result<(), AddressSpaceError>,
|
||||
{
|
||||
self.state.load().walk_regions(cb)
|
||||
}
|
||||
|
||||
/// Get address space layout associated with the address space.
|
||||
pub fn layout(&self) -> AddressSpaceLayout {
|
||||
self.state.load().layout()
|
||||
}
|
||||
|
||||
/// Get maximum of guest physical address in the address space.
|
||||
pub fn last_addr(&self) -> GuestAddress {
|
||||
self.state.load().last_addr()
|
||||
}
|
||||
|
||||
/// Check whether the guest physical address `guest_addr` belongs to a DAX memory region.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `guest_addr` - the guest physical address to inquire
|
||||
pub fn is_dax_region(&self, guest_addr: GuestAddress) -> bool {
|
||||
self.state.load().is_dax_region(guest_addr)
|
||||
}
|
||||
|
||||
/// Get protection flags of memory region that guest physical address `guest_addr` belongs to.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `guest_addr` - the guest physical address to inquire
|
||||
pub fn prot_flags(&self, guest_addr: GuestAddress) -> Result<i32, AddressSpaceError> {
|
||||
self.state.load().prot_flags(guest_addr)
|
||||
}
|
||||
|
||||
/// Get optional NUMA node id associated with guest physical address `gpa`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `gpa` - guest physical address to query.
|
||||
pub fn numa_node_id(&self, gpa: u64) -> Option<u32> {
|
||||
self.state.load().numa_node_id(gpa)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::io::Write;
|
||||
use vm_memory::GuestUsize;
|
||||
use vmm_sys_util::tempfile::TempFile;
|
||||
|
||||
// define macros for unit test
|
||||
const GUEST_PHYS_END: u64 = (1 << 46) - 1;
|
||||
const GUEST_MEM_START: u64 = 0;
|
||||
const GUEST_MEM_END: u64 = GUEST_PHYS_END >> 1;
|
||||
const GUEST_DEVICE_START: u64 = GUEST_MEM_END + 1;
|
||||
|
||||
#[test]
|
||||
fn test_address_space_base_from_regions() {
|
||||
let mut file = TempFile::new().unwrap().into_file();
|
||||
let sample_buf = &[1, 2, 3, 4, 5];
|
||||
assert!(file.write_all(sample_buf).is_ok());
|
||||
file.set_len(0x10000).unwrap();
|
||||
|
||||
let reg = Arc::new(
|
||||
AddressSpaceRegion::create_device_region(GuestAddress(GUEST_DEVICE_START), 0x1000)
|
||||
.unwrap(),
|
||||
);
|
||||
let regions = vec![reg];
|
||||
let layout = AddressSpaceLayout::new(GUEST_PHYS_END, GUEST_MEM_START, GUEST_MEM_END);
|
||||
let address_space = AddressSpaceBase::from_regions(regions, layout.clone());
|
||||
assert_eq!(address_space.layout(), layout);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Invalid region")]
|
||||
fn test_address_space_base_from_regions_when_region_invalid() {
|
||||
let reg = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x1000,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let regions = vec![reg];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x200, 0x1800);
|
||||
let _address_space = AddressSpaceBase::from_regions(regions, layout);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "address space regions intersect with each other")]
|
||||
fn test_address_space_base_from_regions_when_region_intersected() {
|
||||
let reg1 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let reg2 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x200),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let regions = vec![reg1, reg2];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
|
||||
let _address_space = AddressSpaceBase::from_regions(regions, layout);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_base_insert_region() {
|
||||
let reg1 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let reg2 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x300),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let regions = vec![reg1];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x100, 0x1800);
|
||||
let mut address_space = AddressSpaceBase::from_regions(regions, layout);
|
||||
|
||||
// Normal case.
|
||||
address_space.insert_region(reg2).unwrap();
|
||||
assert!(!address_space.regions[1].intersect_with(&address_space.regions[0]));
|
||||
|
||||
// Error invalid address range case when region invaled.
|
||||
let invalid_reg = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x0),
|
||||
0x100,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{:?}",
|
||||
address_space.insert_region(invalid_reg).err().unwrap()
|
||||
),
|
||||
format!("InvalidAddressRange({:?}, {:?})", 0x0, 0x100)
|
||||
);
|
||||
|
||||
// Error Error invalid address range case when region to be inserted will intersect
|
||||
// exsisting regions.
|
||||
let intersected_reg = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x400),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{:?}",
|
||||
address_space.insert_region(intersected_reg).err().unwrap()
|
||||
),
|
||||
format!("InvalidAddressRange({:?}, {:?})", 0x400, 0x200)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_base_walk_regions() {
|
||||
let reg1 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let reg2 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x300),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let regions = vec![reg1, reg2];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
|
||||
let address_space = AddressSpaceBase::from_regions(regions, layout);
|
||||
|
||||
// The argument of walk_regions is a function which takes a &Arc<AddressSpaceRegion>
|
||||
// and returns result. This function will be applied to all regions.
|
||||
fn do_not_have_hotplug(region: &Arc<AddressSpaceRegion>) -> Result<(), AddressSpaceError> {
|
||||
if region.is_hotplug() {
|
||||
Err(AddressSpaceError::InvalidRegionType) // The Error type is dictated to AddressSpaceError.
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
assert!(matches!(
|
||||
address_space.walk_regions(do_not_have_hotplug).unwrap(),
|
||||
()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_base_last_addr() {
|
||||
let reg1 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let reg2 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x300),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let regions = vec![reg1, reg2];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
|
||||
let address_space = AddressSpaceBase::from_regions(regions, layout);
|
||||
|
||||
assert_eq!(address_space.last_addr(), GuestAddress(0x500 - 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_base_is_dax_region() {
|
||||
let page_size = 4096;
|
||||
let address_space_region = vec![
|
||||
Arc::new(AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(page_size),
|
||||
page_size as GuestUsize,
|
||||
)),
|
||||
Arc::new(AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(page_size * 2),
|
||||
page_size as GuestUsize,
|
||||
)),
|
||||
Arc::new(AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DAXMemory,
|
||||
GuestAddress(GUEST_DEVICE_START),
|
||||
page_size as GuestUsize,
|
||||
)),
|
||||
];
|
||||
let layout = AddressSpaceLayout::new(GUEST_PHYS_END, GUEST_MEM_START, GUEST_MEM_END);
|
||||
let address_space = AddressSpaceBase::from_regions(address_space_region, layout);
|
||||
|
||||
assert!(!address_space.is_dax_region(GuestAddress(page_size)));
|
||||
assert!(!address_space.is_dax_region(GuestAddress(page_size * 2)));
|
||||
assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START)));
|
||||
assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + 1)));
|
||||
assert!(!address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + page_size)));
|
||||
assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + page_size - 1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_base_prot_flags() {
|
||||
let reg1 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x200,
|
||||
Some(0),
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let reg2 = Arc::new(AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x300),
|
||||
0x300,
|
||||
));
|
||||
let regions = vec![reg1, reg2];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
|
||||
let address_space = AddressSpaceBase::from_regions(regions, layout);
|
||||
|
||||
// Normal case, reg1.
|
||||
assert_eq!(address_space.prot_flags(GuestAddress(0x200)).unwrap(), 0);
|
||||
// Normal case, reg2.
|
||||
assert_eq!(
|
||||
address_space.prot_flags(GuestAddress(0x500)).unwrap(),
|
||||
libc::PROT_READ | libc::PROT_WRITE
|
||||
);
|
||||
// Inquire gpa where no region is set.
|
||||
assert!(matches!(
|
||||
address_space.prot_flags(GuestAddress(0x600)),
|
||||
Err(AddressSpaceError::InvalidRegionType)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_base_numa_node_id() {
|
||||
let reg1 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x200,
|
||||
Some(0),
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let reg2 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x300),
|
||||
0x300,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let regions = vec![reg1, reg2];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
|
||||
let address_space = AddressSpaceBase::from_regions(regions, layout);
|
||||
|
||||
// Normal case.
|
||||
assert_eq!(address_space.numa_node_id(0x200).unwrap(), 0);
|
||||
// Inquire region with None as its numa node id.
|
||||
assert_eq!(address_space.numa_node_id(0x400), None);
|
||||
// Inquire gpa where no region is set.
|
||||
assert_eq!(address_space.numa_node_id(0x600), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_convert_into_vm_as() {
|
||||
// ! Further and detailed test is needed here.
|
||||
let gmm = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0x0), 0x400)]).unwrap();
|
||||
let _vm = AddressSpace::convert_into_vm_as(gmm);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_insert_region() {
|
||||
let reg1 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let reg2 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x300),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let regions = vec![reg1];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x100, 0x1800);
|
||||
let mut address_space = AddressSpace::from_regions(regions, layout);
|
||||
|
||||
// Normal case.
|
||||
assert!(matches!(address_space.insert_region(reg2).unwrap(), ()));
|
||||
|
||||
// Error invalid address range case when region invaled.
|
||||
let invalid_reg = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x0),
|
||||
0x100,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{:?}",
|
||||
address_space.insert_region(invalid_reg).err().unwrap()
|
||||
),
|
||||
format!("InvalidAddressRange({:?}, {:?})", 0x0, 0x100)
|
||||
);
|
||||
|
||||
// Error Error invalid address range case when region to be inserted will intersect
|
||||
// exsisting regions.
|
||||
let intersected_reg = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x400),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{:?}",
|
||||
address_space.insert_region(intersected_reg).err().unwrap()
|
||||
),
|
||||
format!("InvalidAddressRange({:?}, {:?})", 0x400, 0x200)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_walk_regions() {
|
||||
let reg1 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let reg2 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x300),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let regions = vec![reg1, reg2];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
|
||||
let address_space = AddressSpace::from_regions(regions, layout);
|
||||
|
||||
fn access_all_hotplug_flag(
|
||||
region: &Arc<AddressSpaceRegion>,
|
||||
) -> Result<(), AddressSpaceError> {
|
||||
region.is_hotplug();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert!(matches!(
|
||||
address_space.walk_regions(access_all_hotplug_flag).unwrap(),
|
||||
()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_layout() {
|
||||
let reg = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x1000,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let regions = vec![reg];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
|
||||
let address_space = AddressSpace::from_regions(regions, layout.clone());
|
||||
|
||||
assert_eq!(layout, address_space.layout());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_last_addr() {
|
||||
let reg1 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let reg2 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x300),
|
||||
0x200,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let regions = vec![reg1, reg2];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
|
||||
let address_space = AddressSpace::from_regions(regions, layout);
|
||||
|
||||
assert_eq!(address_space.last_addr(), GuestAddress(0x500 - 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_is_dax_region() {
|
||||
let page_size = 4096;
|
||||
let address_space_region = vec![
|
||||
Arc::new(AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(page_size),
|
||||
page_size as GuestUsize,
|
||||
)),
|
||||
Arc::new(AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(page_size * 2),
|
||||
page_size as GuestUsize,
|
||||
)),
|
||||
Arc::new(AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DAXMemory,
|
||||
GuestAddress(GUEST_DEVICE_START),
|
||||
page_size as GuestUsize,
|
||||
)),
|
||||
];
|
||||
let layout = AddressSpaceLayout::new(GUEST_PHYS_END, GUEST_MEM_START, GUEST_MEM_END);
|
||||
let address_space = AddressSpace::from_regions(address_space_region, layout);
|
||||
|
||||
assert!(!address_space.is_dax_region(GuestAddress(page_size)));
|
||||
assert!(!address_space.is_dax_region(GuestAddress(page_size * 2)));
|
||||
assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START)));
|
||||
assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + 1)));
|
||||
assert!(!address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + page_size)));
|
||||
assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + page_size - 1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_prot_flags() {
|
||||
let reg1 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x200,
|
||||
Some(0),
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let reg2 = Arc::new(AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x300),
|
||||
0x300,
|
||||
));
|
||||
let regions = vec![reg1, reg2];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
|
||||
let address_space = AddressSpace::from_regions(regions, layout);
|
||||
|
||||
// Normal case, reg1.
|
||||
assert_eq!(address_space.prot_flags(GuestAddress(0x200)).unwrap(), 0);
|
||||
// Normal case, reg2.
|
||||
assert_eq!(
|
||||
address_space.prot_flags(GuestAddress(0x500)).unwrap(),
|
||||
libc::PROT_READ | libc::PROT_WRITE
|
||||
);
|
||||
// Inquire gpa where no region is set.
|
||||
assert!(matches!(
|
||||
address_space.prot_flags(GuestAddress(0x600)),
|
||||
Err(AddressSpaceError::InvalidRegionType)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_numa_node_id() {
|
||||
let reg1 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x100),
|
||||
0x200,
|
||||
Some(0),
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let reg2 = Arc::new(AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x300),
|
||||
0x300,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
let regions = vec![reg1, reg2];
|
||||
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
|
||||
let address_space = AddressSpace::from_regions(regions, layout);
|
||||
|
||||
// Normal case.
|
||||
assert_eq!(address_space.numa_node_id(0x200).unwrap(), 0);
|
||||
// Inquire region with None as its numa node id.
|
||||
assert_eq!(address_space.numa_node_id(0x400), None);
|
||||
// Inquire gpa where no region is set.
|
||||
assert_eq!(address_space.numa_node_id(0x600), None);
|
||||
}
|
||||
}
|
||||
154
src/dragonball/src/dbs_address_space/src/layout.rs
Normal file
154
src/dragonball/src/dbs_address_space/src/layout.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright (C) 2021 Alibaba Cloud. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::{AddressSpaceRegion, AddressSpaceRegionType};
|
||||
|
||||
// Max retry times for reading /proc
|
||||
const PROC_READ_RETRY: u64 = 5;
|
||||
|
||||
lazy_static! {
|
||||
/// Upper bound of host memory.
|
||||
pub static ref USABLE_END: u64 = {
|
||||
for _ in 0..PROC_READ_RETRY {
|
||||
if let Ok(buf) = std::fs::read("/proc/meminfo") {
|
||||
let content = String::from_utf8_lossy(&buf);
|
||||
for line in content.lines() {
|
||||
if line.starts_with("MemTotal:") {
|
||||
if let Some(end) = line.find(" kB") {
|
||||
if let Ok(size) = line[9..end].trim().parse::<u64>() {
|
||||
return (size << 10) - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("Exceed max retry times. Cannot get total mem size from /proc/meminfo");
|
||||
};
|
||||
}
|
||||
|
||||
/// Address space layout configuration.
|
||||
///
|
||||
/// The layout configuration must guarantee that `mem_start` <= `mem_end` <= `phys_end`.
|
||||
/// Non-memory region should be arranged into the range [mem_end, phys_end).
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AddressSpaceLayout {
|
||||
/// end of guest physical address
|
||||
pub phys_end: u64,
|
||||
/// start of guest memory address
|
||||
pub mem_start: u64,
|
||||
/// end of guest memory address
|
||||
pub mem_end: u64,
|
||||
/// end of usable memory address
|
||||
pub usable_end: u64,
|
||||
}
|
||||
|
||||
impl AddressSpaceLayout {
|
||||
/// Create a new instance of `AddressSpaceLayout`.
|
||||
pub fn new(phys_end: u64, mem_start: u64, mem_end: u64) -> Self {
|
||||
AddressSpaceLayout {
|
||||
phys_end,
|
||||
mem_start,
|
||||
mem_end,
|
||||
usable_end: *USABLE_END,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether an region is valid with the constraints of the layout.
|
||||
pub fn is_region_valid(&self, region: &AddressSpaceRegion) -> bool {
|
||||
let region_end = match region.base.0.checked_add(region.size) {
|
||||
None => return false,
|
||||
Some(v) => v,
|
||||
};
|
||||
|
||||
match region.ty {
|
||||
AddressSpaceRegionType::DefaultMemory => {
|
||||
if region.base.0 < self.mem_start || region_end > self.mem_end {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
AddressSpaceRegionType::DeviceMemory | AddressSpaceRegionType::DAXMemory => {
|
||||
if region.base.0 < self.mem_end || region_end > self.phys_end {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
#[test]
|
||||
fn test_is_region_valid() {
|
||||
let layout = AddressSpaceLayout::new(0x1_0000_0000, 0x1000_0000, 0x2000_0000);
|
||||
|
||||
let region = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x0),
|
||||
0x1_0000,
|
||||
);
|
||||
assert!(!layout.is_region_valid(®ion));
|
||||
let region = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x2000_0000),
|
||||
0x1_0000,
|
||||
);
|
||||
assert!(!layout.is_region_valid(®ion));
|
||||
let region = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x1_0000),
|
||||
0x2000_0000,
|
||||
);
|
||||
assert!(!layout.is_region_valid(®ion));
|
||||
let region = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(u64::MAX),
|
||||
0x1_0000_0000,
|
||||
);
|
||||
assert!(!layout.is_region_valid(®ion));
|
||||
let region = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x1000_0000),
|
||||
0x1_0000,
|
||||
);
|
||||
assert!(layout.is_region_valid(®ion));
|
||||
|
||||
let region = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DeviceMemory,
|
||||
GuestAddress(0x1000_0000),
|
||||
0x1_0000,
|
||||
);
|
||||
assert!(!layout.is_region_valid(®ion));
|
||||
let region = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DeviceMemory,
|
||||
GuestAddress(0x1_0000_0000),
|
||||
0x1_0000,
|
||||
);
|
||||
assert!(!layout.is_region_valid(®ion));
|
||||
let region = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DeviceMemory,
|
||||
GuestAddress(0x1_0000),
|
||||
0x1_0000_0000,
|
||||
);
|
||||
assert!(!layout.is_region_valid(®ion));
|
||||
let region = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DeviceMemory,
|
||||
GuestAddress(u64::MAX),
|
||||
0x1_0000_0000,
|
||||
);
|
||||
assert!(!layout.is_region_valid(®ion));
|
||||
let region = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DeviceMemory,
|
||||
GuestAddress(0x8000_0000),
|
||||
0x1_0000,
|
||||
);
|
||||
assert!(layout.is_region_valid(®ion));
|
||||
}
|
||||
}
|
||||
87
src/dragonball/src/dbs_address_space/src/lib.rs
Normal file
87
src/dragonball/src/dbs_address_space/src/lib.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (C) 2021 Alibaba Cloud. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
//! Traits and Structs to manage guest physical address space for virtual machines.
|
||||
//!
|
||||
//! The [vm-memory](https://crates.io/crates/vm-memory) implements mechanisms to manage and access
|
||||
//! guest memory resident in guest physical address space. In addition to guest memory, there may
|
||||
//! be other type of devices resident in the same guest physical address space.
|
||||
//!
|
||||
//! The `dbs-address-space` crate provides traits and structs to manage the guest physical address
|
||||
//! space for virtual machines, and mechanisms to coordinate all the devices resident in the
|
||||
//! guest physical address space.
|
||||
|
||||
use vm_memory::GuestUsize;
|
||||
|
||||
mod address_space;
|
||||
pub use self::address_space::{AddressSpace, AddressSpaceBase};
|
||||
|
||||
mod layout;
|
||||
pub use layout::{AddressSpaceLayout, USABLE_END};
|
||||
|
||||
mod memory;
|
||||
pub use memory::{GuestMemoryHybrid, GuestMemoryManager, GuestRegionHybrid, GuestRegionRaw};
|
||||
|
||||
mod numa;
|
||||
pub use self::numa::{NumaIdTable, NumaNode, NumaNodeInfo, MPOL_MF_MOVE, MPOL_PREFERRED};
|
||||
|
||||
mod region;
|
||||
pub use region::{AddressSpaceRegion, AddressSpaceRegionType};
|
||||
|
||||
/// Errors associated with virtual machine address space management.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum AddressSpaceError {
|
||||
/// Invalid address space region type.
|
||||
#[error("invalid address space region type")]
|
||||
InvalidRegionType,
|
||||
|
||||
/// Invalid address range.
|
||||
#[error("invalid address space region (0x{0:x}, 0x{1:x})")]
|
||||
InvalidAddressRange(u64, GuestUsize),
|
||||
|
||||
/// Invalid guest memory source type.
|
||||
#[error("invalid memory source type {0}")]
|
||||
InvalidMemorySourceType(String),
|
||||
|
||||
/// Failed to create memfd to map anonymous memory.
|
||||
#[error("can not create memfd to map anonymous memory")]
|
||||
CreateMemFd(#[source] nix::Error),
|
||||
|
||||
/// Failed to open memory file.
|
||||
#[error("can not open memory file")]
|
||||
OpenFile(#[source] std::io::Error),
|
||||
|
||||
/// Failed to create directory.
|
||||
#[error("can not create directory")]
|
||||
CreateDir(#[source] std::io::Error),
|
||||
|
||||
/// Failed to set size for memory file.
|
||||
#[error("can not set size for memory file")]
|
||||
SetFileSize(#[source] std::io::Error),
|
||||
|
||||
/// Failed to unlink memory file.
|
||||
#[error("can not unlink memory file")]
|
||||
UnlinkFile(#[source] nix::Error),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_error_code() {
|
||||
let e = AddressSpaceError::InvalidRegionType;
|
||||
|
||||
assert_eq!(format!("{e}"), "invalid address space region type");
|
||||
assert_eq!(format!("{e:?}"), "InvalidRegionType");
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{}",
|
||||
AddressSpaceError::InvalidMemorySourceType("test".to_string())
|
||||
),
|
||||
"invalid memory source type test"
|
||||
);
|
||||
}
|
||||
}
|
||||
1105
src/dragonball/src/dbs_address_space/src/memory/hybrid.rs
Normal file
1105
src/dragonball/src/dbs_address_space/src/memory/hybrid.rs
Normal file
File diff suppressed because it is too large
Load Diff
193
src/dragonball/src/dbs_address_space/src/memory/mod.rs
Normal file
193
src/dragonball/src/dbs_address_space/src/memory/mod.rs
Normal file
@@ -0,0 +1,193 @@
|
||||
// Copyright (C) 2022 Alibaba Cloud. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Structs to manage guest memory for virtual machines.
|
||||
//!
|
||||
//! The `vm-memory` crate only provides traits and structs to access normal guest memory,
|
||||
//! it doesn't support special guest memory like virtio-fs/virtio-pmem DAX window etc.
|
||||
//! So this crate provides `GuestMemoryManager` over `vm-memory` to provide uniform abstraction
|
||||
//! for all guest memory.
|
||||
//!
|
||||
//! It also provides interfaces to coordinate guest memory hotplug events.
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use vm_memory::{GuestAddressSpace, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap};
|
||||
|
||||
mod raw_region;
|
||||
pub use raw_region::GuestRegionRaw;
|
||||
|
||||
mod hybrid;
|
||||
pub use hybrid::{GuestMemoryHybrid, GuestRegionHybrid};
|
||||
|
||||
/// Type of source to allocate memory for virtual machines.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum MemorySourceType {
|
||||
/// File on HugeTlbFs.
|
||||
FileOnHugeTlbFs,
|
||||
/// mmap() without flag `MAP_HUGETLB`.
|
||||
MmapAnonymous,
|
||||
/// mmap() with flag `MAP_HUGETLB`.
|
||||
MmapAnonymousHugeTlbFs,
|
||||
/// memfd() without flag `MFD_HUGETLB`.
|
||||
MemFdShared,
|
||||
/// memfd() with flag `MFD_HUGETLB`.
|
||||
MemFdOnHugeTlbFs,
|
||||
}
|
||||
|
||||
impl MemorySourceType {
|
||||
/// Check whether the memory source is huge page.
|
||||
pub fn is_hugepage(&self) -> bool {
|
||||
*self == Self::FileOnHugeTlbFs
|
||||
|| *self == Self::MmapAnonymousHugeTlbFs
|
||||
|| *self == Self::MemFdOnHugeTlbFs
|
||||
}
|
||||
|
||||
/// Check whether the memory source is anonymous memory.
|
||||
pub fn is_mmap_anonymous(&self) -> bool {
|
||||
*self == Self::MmapAnonymous || *self == Self::MmapAnonymousHugeTlbFs
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for MemorySourceType {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"hugetlbfs" => Ok(MemorySourceType::FileOnHugeTlbFs),
|
||||
"memfd" => Ok(MemorySourceType::MemFdShared),
|
||||
"shmem" => Ok(MemorySourceType::MemFdShared),
|
||||
"hugememfd" => Ok(MemorySourceType::MemFdOnHugeTlbFs),
|
||||
"hugeshmem" => Ok(MemorySourceType::MemFdOnHugeTlbFs),
|
||||
"anon" => Ok(MemorySourceType::MmapAnonymous),
|
||||
"mmap" => Ok(MemorySourceType::MmapAnonymous),
|
||||
"hugeanon" => Ok(MemorySourceType::MmapAnonymousHugeTlbFs),
|
||||
"hugemmap" => Ok(MemorySourceType::MmapAnonymousHugeTlbFs),
|
||||
_ => Err(format!("unknown memory source type {s}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct GuestMemoryHotplugManager {}
|
||||
|
||||
/// The `GuestMemoryManager` manages all guest memory for virtual machines.
|
||||
///
|
||||
/// The `GuestMemoryManager` fulfills several different responsibilities.
|
||||
/// - First, it manages different types of guest memory, such as normal guest memory, virtio-fs
|
||||
/// DAX window and virtio-pmem DAX window etc. Different clients may want to access different
|
||||
/// types of memory. So the manager maintains two GuestMemory objects, one contains all guest
|
||||
/// memory, the other contains only normal guest memory.
|
||||
/// - Second, it coordinates memory/DAX window hotplug events, so clients may register hooks
|
||||
/// to receive hotplug notifications.
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GuestMemoryManager {
|
||||
default: GuestMemoryAtomic<GuestMemoryHybrid>,
|
||||
/// GuestMemory object hosts all guest memory.
|
||||
hybrid: GuestMemoryAtomic<GuestMemoryHybrid>,
|
||||
/// GuestMemory object for vIOMMU.
|
||||
iommu: GuestMemoryAtomic<GuestMemoryHybrid>,
|
||||
/// GuestMemory object hosts normal guest memory.
|
||||
normal: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
hotplug: Arc<GuestMemoryHotplugManager>,
|
||||
}
|
||||
|
||||
impl GuestMemoryManager {
|
||||
/// Create a new instance of `GuestMemoryManager`.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Get a reference to the normal `GuestMemory` object.
|
||||
pub fn get_normal_guest_memory(&self) -> &GuestMemoryAtomic<GuestMemoryMmap> {
|
||||
&self.normal
|
||||
}
|
||||
|
||||
/// Try to downcast the `GuestAddressSpace` object to a `GuestMemoryManager` object.
|
||||
pub fn to_manager<AS: GuestAddressSpace>(_m: &AS) -> Option<&Self> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GuestMemoryManager {
|
||||
fn default() -> Self {
|
||||
let hybrid = GuestMemoryAtomic::new(GuestMemoryHybrid::new());
|
||||
let iommu = GuestMemoryAtomic::new(GuestMemoryHybrid::new());
|
||||
let normal = GuestMemoryAtomic::new(GuestMemoryMmap::new());
|
||||
// By default, it provides to the `GuestMemoryHybrid` object containing all guest memory.
|
||||
let default = hybrid.clone();
|
||||
|
||||
GuestMemoryManager {
|
||||
default,
|
||||
hybrid,
|
||||
iommu,
|
||||
normal,
|
||||
hotplug: Arc::new(GuestMemoryHotplugManager::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GuestAddressSpace for GuestMemoryManager {
|
||||
type M = GuestMemoryHybrid;
|
||||
type T = GuestMemoryLoadGuard<GuestMemoryHybrid>;
|
||||
|
||||
fn memory(&self) -> Self::T {
|
||||
self.default.memory()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_memory_source_type() {
|
||||
assert_eq!(
|
||||
MemorySourceType::from_str("hugetlbfs").unwrap(),
|
||||
MemorySourceType::FileOnHugeTlbFs
|
||||
);
|
||||
assert_eq!(
|
||||
MemorySourceType::from_str("memfd").unwrap(),
|
||||
MemorySourceType::MemFdShared
|
||||
);
|
||||
assert_eq!(
|
||||
MemorySourceType::from_str("shmem").unwrap(),
|
||||
MemorySourceType::MemFdShared
|
||||
);
|
||||
assert_eq!(
|
||||
MemorySourceType::from_str("hugememfd").unwrap(),
|
||||
MemorySourceType::MemFdOnHugeTlbFs
|
||||
);
|
||||
assert_eq!(
|
||||
MemorySourceType::from_str("hugeshmem").unwrap(),
|
||||
MemorySourceType::MemFdOnHugeTlbFs
|
||||
);
|
||||
assert_eq!(
|
||||
MemorySourceType::from_str("anon").unwrap(),
|
||||
MemorySourceType::MmapAnonymous
|
||||
);
|
||||
assert_eq!(
|
||||
MemorySourceType::from_str("mmap").unwrap(),
|
||||
MemorySourceType::MmapAnonymous
|
||||
);
|
||||
assert_eq!(
|
||||
MemorySourceType::from_str("hugeanon").unwrap(),
|
||||
MemorySourceType::MmapAnonymousHugeTlbFs
|
||||
);
|
||||
assert_eq!(
|
||||
MemorySourceType::from_str("hugemmap").unwrap(),
|
||||
MemorySourceType::MmapAnonymousHugeTlbFs
|
||||
);
|
||||
assert!(MemorySourceType::from_str("test").is_err());
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_to_manager() {
|
||||
let manager = GuestMemoryManager::new();
|
||||
let mgr = GuestMemoryManager::to_manager(&manager).unwrap();
|
||||
|
||||
assert_eq!(&manager as *const _, mgr as *const _);
|
||||
}
|
||||
}
|
||||
990
src/dragonball/src/dbs_address_space/src/memory/raw_region.rs
Normal file
990
src/dragonball/src/dbs_address_space/src/memory/raw_region.rs
Normal file
@@ -0,0 +1,990 @@
|
||||
// Copyright (C) 2022 Alibaba Cloud. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::io::{Read, Write};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use vm_memory::bitmap::{Bitmap, BS};
|
||||
use vm_memory::mmap::NewBitmap;
|
||||
use vm_memory::volatile_memory::compute_offset;
|
||||
use vm_memory::{
|
||||
guest_memory, volatile_memory, Address, AtomicAccess, Bytes, FileOffset, GuestAddress,
|
||||
GuestMemoryRegion, GuestUsize, MemoryRegionAddress, VolatileSlice,
|
||||
};
|
||||
|
||||
/// Guest memory region for virtio-fs DAX window.
|
||||
#[derive(Debug)]
|
||||
pub struct GuestRegionRaw<B = ()> {
|
||||
guest_base: GuestAddress,
|
||||
addr: *mut u8,
|
||||
size: usize,
|
||||
bitmap: B,
|
||||
}
|
||||
|
||||
impl<B: NewBitmap> GuestRegionRaw<B> {
|
||||
/// Create a `GuestRegionRaw` object from raw pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// Caller needs to ensure `addr` and `size` are valid with static lifetime.
|
||||
pub unsafe fn new(guest_base: GuestAddress, addr: *mut u8, size: usize) -> Self {
|
||||
let bitmap = B::with_len(size);
|
||||
|
||||
GuestRegionRaw {
|
||||
guest_base,
|
||||
addr,
|
||||
size,
|
||||
bitmap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bitmap> Bytes<MemoryRegionAddress> for GuestRegionRaw<B> {
|
||||
type E = guest_memory::Error;
|
||||
|
||||
fn write(&self, buf: &[u8], addr: MemoryRegionAddress) -> guest_memory::Result<usize> {
|
||||
let maddr = addr.raw_value() as usize;
|
||||
self.as_volatile_slice()
|
||||
.unwrap()
|
||||
.write(buf, maddr)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8], addr: MemoryRegionAddress) -> guest_memory::Result<usize> {
|
||||
let maddr = addr.raw_value() as usize;
|
||||
self.as_volatile_slice()
|
||||
.unwrap()
|
||||
.read(buf, maddr)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn write_slice(&self, buf: &[u8], addr: MemoryRegionAddress) -> guest_memory::Result<()> {
|
||||
let maddr = addr.raw_value() as usize;
|
||||
self.as_volatile_slice()
|
||||
.unwrap()
|
||||
.write_slice(buf, maddr)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn read_slice(&self, buf: &mut [u8], addr: MemoryRegionAddress) -> guest_memory::Result<()> {
|
||||
let maddr = addr.raw_value() as usize;
|
||||
self.as_volatile_slice()
|
||||
.unwrap()
|
||||
.read_slice(buf, maddr)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn read_from<F>(
|
||||
&self,
|
||||
addr: MemoryRegionAddress,
|
||||
src: &mut F,
|
||||
count: usize,
|
||||
) -> guest_memory::Result<usize>
|
||||
where
|
||||
F: Read,
|
||||
{
|
||||
let maddr = addr.raw_value() as usize;
|
||||
self.as_volatile_slice()
|
||||
.unwrap()
|
||||
.read_from::<F>(maddr, src, count)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn read_exact_from<F>(
|
||||
&self,
|
||||
addr: MemoryRegionAddress,
|
||||
src: &mut F,
|
||||
count: usize,
|
||||
) -> guest_memory::Result<()>
|
||||
where
|
||||
F: Read,
|
||||
{
|
||||
let maddr = addr.raw_value() as usize;
|
||||
self.as_volatile_slice()
|
||||
.unwrap()
|
||||
.read_exact_from::<F>(maddr, src, count)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn write_to<F>(
|
||||
&self,
|
||||
addr: MemoryRegionAddress,
|
||||
dst: &mut F,
|
||||
count: usize,
|
||||
) -> guest_memory::Result<usize>
|
||||
where
|
||||
F: Write,
|
||||
{
|
||||
let maddr = addr.raw_value() as usize;
|
||||
self.as_volatile_slice()
|
||||
.unwrap()
|
||||
.write_to::<F>(maddr, dst, count)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn write_all_to<F>(
|
||||
&self,
|
||||
addr: MemoryRegionAddress,
|
||||
dst: &mut F,
|
||||
count: usize,
|
||||
) -> guest_memory::Result<()>
|
||||
where
|
||||
F: Write,
|
||||
{
|
||||
let maddr = addr.raw_value() as usize;
|
||||
self.as_volatile_slice()
|
||||
.unwrap()
|
||||
.write_all_to::<F>(maddr, dst, count)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn store<T: AtomicAccess>(
|
||||
&self,
|
||||
val: T,
|
||||
addr: MemoryRegionAddress,
|
||||
order: Ordering,
|
||||
) -> guest_memory::Result<()> {
|
||||
self.as_volatile_slice().and_then(|s| {
|
||||
s.store(val, addr.raw_value() as usize, order)
|
||||
.map_err(Into::into)
|
||||
})
|
||||
}
|
||||
|
||||
fn load<T: AtomicAccess>(
|
||||
&self,
|
||||
addr: MemoryRegionAddress,
|
||||
order: Ordering,
|
||||
) -> guest_memory::Result<T> {
|
||||
self.as_volatile_slice()
|
||||
.and_then(|s| s.load(addr.raw_value() as usize, order).map_err(Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bitmap> GuestMemoryRegion for GuestRegionRaw<B> {
|
||||
type B = B;
|
||||
|
||||
fn len(&self) -> GuestUsize {
|
||||
self.size as GuestUsize
|
||||
}
|
||||
|
||||
fn start_addr(&self) -> GuestAddress {
|
||||
self.guest_base
|
||||
}
|
||||
|
||||
fn bitmap(&self) -> &Self::B {
|
||||
&self.bitmap
|
||||
}
|
||||
|
||||
fn get_host_address(&self, addr: MemoryRegionAddress) -> guest_memory::Result<*mut u8> {
|
||||
// Not sure why wrapping_offset is not unsafe. Anyway this
|
||||
// is safe because we've just range-checked addr using check_address.
|
||||
self.check_address(addr)
|
||||
.ok_or(guest_memory::Error::InvalidBackendAddress)
|
||||
.map(|addr| self.addr.wrapping_offset(addr.raw_value() as isize))
|
||||
}
|
||||
|
||||
fn file_offset(&self) -> Option<&FileOffset> {
|
||||
None
|
||||
}
|
||||
|
||||
unsafe fn as_slice(&self) -> Option<&[u8]> {
|
||||
// This is safe because we mapped the area at addr ourselves, so this slice will not
|
||||
// overflow. However, it is possible to alias.
|
||||
Some(std::slice::from_raw_parts(self.addr, self.size))
|
||||
}
|
||||
|
||||
unsafe fn as_mut_slice(&self) -> Option<&mut [u8]> {
|
||||
// This is safe because we mapped the area at addr ourselves, so this slice will not
|
||||
// overflow. However, it is possible to alias.
|
||||
Some(std::slice::from_raw_parts_mut(self.addr, self.size))
|
||||
}
|
||||
|
||||
fn get_slice(
|
||||
&self,
|
||||
offset: MemoryRegionAddress,
|
||||
count: usize,
|
||||
) -> guest_memory::Result<VolatileSlice<BS<B>>> {
|
||||
let offset = offset.raw_value() as usize;
|
||||
let end = compute_offset(offset, count)?;
|
||||
if end > self.size {
|
||||
return Err(volatile_memory::Error::OutOfBounds { addr: end }.into());
|
||||
}
|
||||
|
||||
// Safe because we checked that offset + count was within our range and we only ever hand
|
||||
// out volatile accessors.
|
||||
Ok(unsafe {
|
||||
VolatileSlice::with_bitmap(
|
||||
(self.addr as usize + offset) as *mut _,
|
||||
count,
|
||||
self.bitmap.slice_at(offset),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn is_hugetlbfs(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate vmm_sys_util;
|
||||
|
||||
use super::*;
|
||||
use crate::{GuestMemoryHybrid, GuestRegionHybrid};
|
||||
use std::sync::Arc;
|
||||
use vm_memory::{GuestAddressSpace, GuestMemory, VolatileMemory};
|
||||
|
||||
/*
|
||||
use crate::bitmap::tests::test_guest_memory_and_region;
|
||||
use crate::bitmap::AtomicBitmap;
|
||||
use crate::GuestAddressSpace;
|
||||
|
||||
use std::fs::File;
|
||||
use std::mem;
|
||||
use std::path::Path;
|
||||
use vmm_sys_util::tempfile::TempFile;
|
||||
|
||||
type GuestMemoryMmap = super::GuestMemoryMmap<()>;
|
||||
type GuestRegionMmap = super::GuestRegionMmap<()>;
|
||||
type MmapRegion = super::MmapRegion<()>;
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn test_region_raw_new() {
|
||||
let mut buf = [0u8; 1024];
|
||||
let m =
|
||||
unsafe { GuestRegionRaw::<()>::new(GuestAddress(0x10_0000), &mut buf as *mut _, 1024) };
|
||||
|
||||
assert_eq!(m.start_addr(), GuestAddress(0x10_0000));
|
||||
assert_eq!(m.len(), 1024);
|
||||
}
|
||||
|
||||
/*
|
||||
fn check_guest_memory_mmap(
|
||||
maybe_guest_mem: Result<GuestMemoryMmap, Error>,
|
||||
expected_regions_summary: &[(GuestAddress, usize)],
|
||||
) {
|
||||
assert!(maybe_guest_mem.is_ok());
|
||||
|
||||
let guest_mem = maybe_guest_mem.unwrap();
|
||||
assert_eq!(guest_mem.num_regions(), expected_regions_summary.len());
|
||||
let maybe_last_mem_reg = expected_regions_summary.last();
|
||||
if let Some((region_addr, region_size)) = maybe_last_mem_reg {
|
||||
let mut last_addr = region_addr.unchecked_add(*region_size as u64);
|
||||
if last_addr.raw_value() != 0 {
|
||||
last_addr = last_addr.unchecked_sub(1);
|
||||
}
|
||||
assert_eq!(guest_mem.last_addr(), last_addr);
|
||||
}
|
||||
for ((region_addr, region_size), mmap) in expected_regions_summary
|
||||
.iter()
|
||||
.zip(guest_mem.regions.iter())
|
||||
{
|
||||
assert_eq!(region_addr, &mmap.guest_base);
|
||||
assert_eq!(region_size, &mmap.mapping.size());
|
||||
|
||||
assert!(guest_mem.find_region(*region_addr).is_some());
|
||||
}
|
||||
}
|
||||
|
||||
fn new_guest_memory_mmap(
|
||||
regions_summary: &[(GuestAddress, usize)],
|
||||
) -> Result<GuestMemoryMmap, Error> {
|
||||
GuestMemoryMmap::from_ranges(regions_summary)
|
||||
}
|
||||
|
||||
fn new_guest_memory_mmap_from_regions(
|
||||
regions_summary: &[(GuestAddress, usize)],
|
||||
) -> Result<GuestMemoryMmap, Error> {
|
||||
GuestMemoryMmap::from_regions(
|
||||
regions_summary
|
||||
.iter()
|
||||
.map(|(region_addr, region_size)| {
|
||||
GuestRegionMmap::new(MmapRegion::new(*region_size).unwrap(), *region_addr)
|
||||
.unwrap()
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn new_guest_memory_mmap_from_arc_regions(
|
||||
regions_summary: &[(GuestAddress, usize)],
|
||||
) -> Result<GuestMemoryMmap, Error> {
|
||||
GuestMemoryMmap::from_arc_regions(
|
||||
regions_summary
|
||||
.iter()
|
||||
.map(|(region_addr, region_size)| {
|
||||
Arc::new(
|
||||
GuestRegionMmap::new(MmapRegion::new(*region_size).unwrap(), *region_addr)
|
||||
.unwrap(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn new_guest_memory_mmap_with_files(
|
||||
regions_summary: &[(GuestAddress, usize)],
|
||||
) -> Result<GuestMemoryMmap, Error> {
|
||||
let regions: Vec<(GuestAddress, usize, Option<FileOffset>)> = regions_summary
|
||||
.iter()
|
||||
.map(|(region_addr, region_size)| {
|
||||
let f = TempFile::new().unwrap().into_file();
|
||||
f.set_len(*region_size as u64).unwrap();
|
||||
|
||||
(*region_addr, *region_size, Some(FileOffset::new(f, 0)))
|
||||
})
|
||||
.collect();
|
||||
|
||||
GuestMemoryMmap::from_ranges_with_files(®ions)
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn slice_addr() {
|
||||
let mut buf = [0u8; 1024];
|
||||
let m =
|
||||
unsafe { GuestRegionRaw::<()>::new(GuestAddress(0x10_0000), &mut buf as *mut _, 1024) };
|
||||
|
||||
let s = m.get_slice(MemoryRegionAddress(2), 3).unwrap();
|
||||
assert_eq!(s.as_ptr(), &mut buf[2] as *mut _);
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn test_address_in_range() {
|
||||
let f1 = TempFile::new().unwrap().into_file();
|
||||
f1.set_len(0x400).unwrap();
|
||||
let f2 = TempFile::new().unwrap().into_file();
|
||||
f2.set_len(0x400).unwrap();
|
||||
|
||||
let start_addr1 = GuestAddress(0x0);
|
||||
let start_addr2 = GuestAddress(0x800);
|
||||
let guest_mem =
|
||||
GuestMemoryMmap::from_ranges(&[(start_addr1, 0x400), (start_addr2, 0x400)]).unwrap();
|
||||
let guest_mem_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[
|
||||
(start_addr1, 0x400, Some(FileOffset::new(f1, 0))),
|
||||
(start_addr2, 0x400, Some(FileOffset::new(f2, 0))),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let guest_mem_list = vec![guest_mem, guest_mem_backed_by_file];
|
||||
for guest_mem in guest_mem_list.iter() {
|
||||
assert!(guest_mem.address_in_range(GuestAddress(0x200)));
|
||||
assert!(!guest_mem.address_in_range(GuestAddress(0x600)));
|
||||
assert!(guest_mem.address_in_range(GuestAddress(0xa00)));
|
||||
assert!(!guest_mem.address_in_range(GuestAddress(0xc00)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_address() {
|
||||
let f1 = TempFile::new().unwrap().into_file();
|
||||
f1.set_len(0x400).unwrap();
|
||||
let f2 = TempFile::new().unwrap().into_file();
|
||||
f2.set_len(0x400).unwrap();
|
||||
|
||||
let start_addr1 = GuestAddress(0x0);
|
||||
let start_addr2 = GuestAddress(0x800);
|
||||
let guest_mem =
|
||||
GuestMemoryMmap::from_ranges(&[(start_addr1, 0x400), (start_addr2, 0x400)]).unwrap();
|
||||
let guest_mem_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[
|
||||
(start_addr1, 0x400, Some(FileOffset::new(f1, 0))),
|
||||
(start_addr2, 0x400, Some(FileOffset::new(f2, 0))),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let guest_mem_list = vec![guest_mem, guest_mem_backed_by_file];
|
||||
for guest_mem in guest_mem_list.iter() {
|
||||
assert_eq!(
|
||||
guest_mem.check_address(GuestAddress(0x200)),
|
||||
Some(GuestAddress(0x200))
|
||||
);
|
||||
assert_eq!(guest_mem.check_address(GuestAddress(0x600)), None);
|
||||
assert_eq!(
|
||||
guest_mem.check_address(GuestAddress(0xa00)),
|
||||
Some(GuestAddress(0xa00))
|
||||
);
|
||||
assert_eq!(guest_mem.check_address(GuestAddress(0xc00)), None);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_region_addr() {
|
||||
let f1 = TempFile::new().unwrap().into_file();
|
||||
f1.set_len(0x400).unwrap();
|
||||
let f2 = TempFile::new().unwrap().into_file();
|
||||
f2.set_len(0x400).unwrap();
|
||||
|
||||
let start_addr1 = GuestAddress(0x0);
|
||||
let start_addr2 = GuestAddress(0x800);
|
||||
let guest_mem =
|
||||
GuestMemoryMmap::from_ranges(&[(start_addr1, 0x400), (start_addr2, 0x400)]).unwrap();
|
||||
let guest_mem_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[
|
||||
(start_addr1, 0x400, Some(FileOffset::new(f1, 0))),
|
||||
(start_addr2, 0x400, Some(FileOffset::new(f2, 0))),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let guest_mem_list = vec![guest_mem, guest_mem_backed_by_file];
|
||||
for guest_mem in guest_mem_list.iter() {
|
||||
assert!(guest_mem.to_region_addr(GuestAddress(0x600)).is_none());
|
||||
let (r0, addr0) = guest_mem.to_region_addr(GuestAddress(0x800)).unwrap();
|
||||
let (r1, addr1) = guest_mem.to_region_addr(GuestAddress(0xa00)).unwrap();
|
||||
assert!(r0.as_ptr() == r1.as_ptr());
|
||||
assert_eq!(addr0, MemoryRegionAddress(0));
|
||||
assert_eq!(addr1, MemoryRegionAddress(0x200));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_host_address() {
|
||||
let f1 = TempFile::new().unwrap().into_file();
|
||||
f1.set_len(0x400).unwrap();
|
||||
let f2 = TempFile::new().unwrap().into_file();
|
||||
f2.set_len(0x400).unwrap();
|
||||
|
||||
let start_addr1 = GuestAddress(0x0);
|
||||
let start_addr2 = GuestAddress(0x800);
|
||||
let guest_mem =
|
||||
GuestMemoryMmap::from_ranges(&[(start_addr1, 0x400), (start_addr2, 0x400)]).unwrap();
|
||||
let guest_mem_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[
|
||||
(start_addr1, 0x400, Some(FileOffset::new(f1, 0))),
|
||||
(start_addr2, 0x400, Some(FileOffset::new(f2, 0))),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let guest_mem_list = vec![guest_mem, guest_mem_backed_by_file];
|
||||
for guest_mem in guest_mem_list.iter() {
|
||||
assert!(guest_mem.get_host_address(GuestAddress(0x600)).is_err());
|
||||
let ptr0 = guest_mem.get_host_address(GuestAddress(0x800)).unwrap();
|
||||
let ptr1 = guest_mem.get_host_address(GuestAddress(0xa00)).unwrap();
|
||||
assert_eq!(
|
||||
ptr0,
|
||||
guest_mem.find_region(GuestAddress(0x800)).unwrap().as_ptr()
|
||||
);
|
||||
assert_eq!(unsafe { ptr0.offset(0x200) }, ptr1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deref() {
|
||||
let f = TempFile::new().unwrap().into_file();
|
||||
f.set_len(0x400).unwrap();
|
||||
|
||||
let start_addr = GuestAddress(0x0);
|
||||
let guest_mem = GuestMemoryMmap::from_ranges(&[(start_addr, 0x400)]).unwrap();
|
||||
let guest_mem_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[(
|
||||
start_addr,
|
||||
0x400,
|
||||
Some(FileOffset::new(f, 0)),
|
||||
)])
|
||||
.unwrap();
|
||||
|
||||
let guest_mem_list = vec![guest_mem, guest_mem_backed_by_file];
|
||||
for guest_mem in guest_mem_list.iter() {
|
||||
let sample_buf = &[1, 2, 3, 4, 5];
|
||||
|
||||
assert_eq!(guest_mem.write(sample_buf, start_addr).unwrap(), 5);
|
||||
let slice = guest_mem
|
||||
.find_region(GuestAddress(0))
|
||||
.unwrap()
|
||||
.as_volatile_slice()
|
||||
.unwrap();
|
||||
|
||||
let buf = &mut [0, 0, 0, 0, 0];
|
||||
assert_eq!(slice.read(buf, 0).unwrap(), 5);
|
||||
assert_eq!(buf, sample_buf);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_u64() {
|
||||
let f1 = TempFile::new().unwrap().into_file();
|
||||
f1.set_len(0x1000).unwrap();
|
||||
let f2 = TempFile::new().unwrap().into_file();
|
||||
f2.set_len(0x1000).unwrap();
|
||||
|
||||
let start_addr1 = GuestAddress(0x0);
|
||||
let start_addr2 = GuestAddress(0x1000);
|
||||
let bad_addr = GuestAddress(0x2001);
|
||||
let bad_addr2 = GuestAddress(0x1ffc);
|
||||
let max_addr = GuestAddress(0x2000);
|
||||
|
||||
let gm =
|
||||
GuestMemoryMmap::from_ranges(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap();
|
||||
let gm_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[
|
||||
(start_addr1, 0x1000, Some(FileOffset::new(f1, 0))),
|
||||
(start_addr2, 0x1000, Some(FileOffset::new(f2, 0))),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let gm_list = vec![gm, gm_backed_by_file];
|
||||
for gm in gm_list.iter() {
|
||||
let val1: u64 = 0xaa55_aa55_aa55_aa55;
|
||||
let val2: u64 = 0x55aa_55aa_55aa_55aa;
|
||||
assert_eq!(
|
||||
format!("{:?}", gm.write_obj(val1, bad_addr).err().unwrap()),
|
||||
format!("InvalidGuestAddress({:?})", bad_addr,)
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", gm.write_obj(val1, bad_addr2).err().unwrap()),
|
||||
format!(
|
||||
"PartialBuffer {{ expected: {:?}, completed: {:?} }}",
|
||||
mem::size_of::<u64>(),
|
||||
max_addr.checked_offset_from(bad_addr2).unwrap()
|
||||
)
|
||||
);
|
||||
|
||||
gm.write_obj(val1, GuestAddress(0x500)).unwrap();
|
||||
gm.write_obj(val2, GuestAddress(0x1000 + 32)).unwrap();
|
||||
let num1: u64 = gm.read_obj(GuestAddress(0x500)).unwrap();
|
||||
let num2: u64 = gm.read_obj(GuestAddress(0x1000 + 32)).unwrap();
|
||||
assert_eq!(val1, num1);
|
||||
assert_eq!(val2, num2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_and_read() {
|
||||
let f = TempFile::new().unwrap().into_file();
|
||||
f.set_len(0x400).unwrap();
|
||||
|
||||
let mut start_addr = GuestAddress(0x1000);
|
||||
let gm = GuestMemoryMmap::from_ranges(&[(start_addr, 0x400)]).unwrap();
|
||||
let gm_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[(
|
||||
start_addr,
|
||||
0x400,
|
||||
Some(FileOffset::new(f, 0)),
|
||||
)])
|
||||
.unwrap();
|
||||
|
||||
let gm_list = vec![gm, gm_backed_by_file];
|
||||
for gm in gm_list.iter() {
|
||||
let sample_buf = &[1, 2, 3, 4, 5];
|
||||
|
||||
assert_eq!(gm.write(sample_buf, start_addr).unwrap(), 5);
|
||||
|
||||
let buf = &mut [0u8; 5];
|
||||
assert_eq!(gm.read(buf, start_addr).unwrap(), 5);
|
||||
assert_eq!(buf, sample_buf);
|
||||
|
||||
start_addr = GuestAddress(0x13ff);
|
||||
assert_eq!(gm.write(sample_buf, start_addr).unwrap(), 1);
|
||||
assert_eq!(gm.read(buf, start_addr).unwrap(), 1);
|
||||
assert_eq!(buf[0], sample_buf[0]);
|
||||
start_addr = GuestAddress(0x1000);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_to_and_write_from_mem() {
|
||||
let f = TempFile::new().unwrap().into_file();
|
||||
f.set_len(0x400).unwrap();
|
||||
|
||||
let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0x1000), 0x400)]).unwrap();
|
||||
let gm_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[(
|
||||
GuestAddress(0x1000),
|
||||
0x400,
|
||||
Some(FileOffset::new(f, 0)),
|
||||
)])
|
||||
.unwrap();
|
||||
|
||||
let gm_list = vec![gm, gm_backed_by_file];
|
||||
for gm in gm_list.iter() {
|
||||
let addr = GuestAddress(0x1010);
|
||||
let mut file = if cfg!(unix) {
|
||||
File::open(Path::new("/dev/zero")).unwrap()
|
||||
} else {
|
||||
File::open(Path::new("c:\\Windows\\system32\\ntoskrnl.exe")).unwrap()
|
||||
};
|
||||
gm.write_obj(!0u32, addr).unwrap();
|
||||
gm.read_exact_from(addr, &mut file, mem::size_of::<u32>())
|
||||
.unwrap();
|
||||
let value: u32 = gm.read_obj(addr).unwrap();
|
||||
if cfg!(unix) {
|
||||
assert_eq!(value, 0);
|
||||
} else {
|
||||
assert_eq!(value, 0x0090_5a4d);
|
||||
}
|
||||
|
||||
let mut sink = Vec::new();
|
||||
gm.write_all_to(addr, &mut sink, mem::size_of::<u32>())
|
||||
.unwrap();
|
||||
if cfg!(unix) {
|
||||
assert_eq!(sink, vec![0; mem::size_of::<u32>()]);
|
||||
} else {
|
||||
assert_eq!(sink, vec![0x4d, 0x5a, 0x90, 0x00]);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_vec_with_regions() {
|
||||
let region_size = 0x400;
|
||||
let regions = vec![
|
||||
(GuestAddress(0x0), region_size),
|
||||
(GuestAddress(0x1000), region_size),
|
||||
];
|
||||
let mut iterated_regions = Vec::new();
|
||||
let gm = GuestMemoryMmap::from_ranges(®ions).unwrap();
|
||||
|
||||
for region in gm.iter() {
|
||||
assert_eq!(region.len(), region_size as GuestUsize);
|
||||
}
|
||||
|
||||
for region in gm.iter() {
|
||||
iterated_regions.push((region.start_addr(), region.len() as usize));
|
||||
}
|
||||
assert_eq!(regions, iterated_regions);
|
||||
|
||||
assert!(regions
|
||||
.iter()
|
||||
.map(|x| (x.0, x.1))
|
||||
.eq(iterated_regions.iter().copied()));
|
||||
|
||||
assert_eq!(gm.regions[0].guest_base, regions[0].0);
|
||||
assert_eq!(gm.regions[1].guest_base, regions[1].0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory() {
|
||||
let region_size = 0x400;
|
||||
let regions = vec![
|
||||
(GuestAddress(0x0), region_size),
|
||||
(GuestAddress(0x1000), region_size),
|
||||
];
|
||||
let mut iterated_regions = Vec::new();
|
||||
let gm = Arc::new(GuestMemoryMmap::from_ranges(®ions).unwrap());
|
||||
let mem = gm.memory();
|
||||
|
||||
for region in mem.iter() {
|
||||
assert_eq!(region.len(), region_size as GuestUsize);
|
||||
}
|
||||
|
||||
for region in mem.iter() {
|
||||
iterated_regions.push((region.start_addr(), region.len() as usize));
|
||||
}
|
||||
assert_eq!(regions, iterated_regions);
|
||||
|
||||
assert!(regions
|
||||
.iter()
|
||||
.map(|x| (x.0, x.1))
|
||||
.eq(iterated_regions.iter().copied()));
|
||||
|
||||
assert_eq!(gm.regions[0].guest_base, regions[0].0);
|
||||
assert_eq!(gm.regions[1].guest_base, regions[1].0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_access_cross_boundary() {
|
||||
let f1 = TempFile::new().unwrap().into_file();
|
||||
f1.set_len(0x1000).unwrap();
|
||||
let f2 = TempFile::new().unwrap().into_file();
|
||||
f2.set_len(0x1000).unwrap();
|
||||
|
||||
let start_addr1 = GuestAddress(0x0);
|
||||
let start_addr2 = GuestAddress(0x1000);
|
||||
let gm =
|
||||
GuestMemoryMmap::from_ranges(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap();
|
||||
let gm_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[
|
||||
(start_addr1, 0x1000, Some(FileOffset::new(f1, 0))),
|
||||
(start_addr2, 0x1000, Some(FileOffset::new(f2, 0))),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let gm_list = vec![gm, gm_backed_by_file];
|
||||
for gm in gm_list.iter() {
|
||||
let sample_buf = &[1, 2, 3, 4, 5];
|
||||
assert_eq!(gm.write(sample_buf, GuestAddress(0xffc)).unwrap(), 5);
|
||||
let buf = &mut [0u8; 5];
|
||||
assert_eq!(gm.read(buf, GuestAddress(0xffc)).unwrap(), 5);
|
||||
assert_eq!(buf, sample_buf);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_retrieve_fd_backing_memory_region() {
|
||||
let f = TempFile::new().unwrap().into_file();
|
||||
f.set_len(0x400).unwrap();
|
||||
|
||||
let start_addr = GuestAddress(0x0);
|
||||
let gm = GuestMemoryMmap::from_ranges(&[(start_addr, 0x400)]).unwrap();
|
||||
assert!(gm.find_region(start_addr).is_some());
|
||||
let region = gm.find_region(start_addr).unwrap();
|
||||
assert!(region.file_offset().is_none());
|
||||
|
||||
let gm = GuestMemoryMmap::from_ranges_with_files(&[(
|
||||
start_addr,
|
||||
0x400,
|
||||
Some(FileOffset::new(f, 0)),
|
||||
)])
|
||||
.unwrap();
|
||||
assert!(gm.find_region(start_addr).is_some());
|
||||
let region = gm.find_region(start_addr).unwrap();
|
||||
assert!(region.file_offset().is_some());
|
||||
}
|
||||
|
||||
// Windows needs a dedicated test where it will retrieve the allocation
|
||||
// granularity to determine a proper offset (other than 0) that can be
|
||||
// used for the backing file. Refer to Microsoft docs here:
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-mapviewoffile
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_retrieve_offset_from_fd_backing_memory_region() {
|
||||
let f = TempFile::new().unwrap().into_file();
|
||||
f.set_len(0x1400).unwrap();
|
||||
// Needs to be aligned on 4k, otherwise mmap will fail.
|
||||
let offset = 0x1000;
|
||||
|
||||
let start_addr = GuestAddress(0x0);
|
||||
let gm = GuestMemoryMmap::from_ranges(&[(start_addr, 0x400)]).unwrap();
|
||||
assert!(gm.find_region(start_addr).is_some());
|
||||
let region = gm.find_region(start_addr).unwrap();
|
||||
assert!(region.file_offset().is_none());
|
||||
|
||||
let gm = GuestMemoryMmap::from_ranges_with_files(&[(
|
||||
start_addr,
|
||||
0x400,
|
||||
Some(FileOffset::new(f, offset)),
|
||||
)])
|
||||
.unwrap();
|
||||
assert!(gm.find_region(start_addr).is_some());
|
||||
let region = gm.find_region(start_addr).unwrap();
|
||||
assert!(region.file_offset().is_some());
|
||||
assert_eq!(region.file_offset().unwrap().start(), offset);
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn test_mmap_insert_region() {
|
||||
let start_addr1 = GuestAddress(0);
|
||||
let start_addr2 = GuestAddress(0x10_0000);
|
||||
|
||||
let guest_mem = GuestMemoryHybrid::<()>::new();
|
||||
let mut raw_buf = [0u8; 0x1000];
|
||||
let raw_ptr = &mut raw_buf as *mut u8;
|
||||
let reg = unsafe { GuestRegionRaw::<()>::new(start_addr1, raw_ptr, 0x1000) };
|
||||
let guest_mem = guest_mem
|
||||
.insert_region(Arc::new(GuestRegionHybrid::from_raw_region(reg)))
|
||||
.unwrap();
|
||||
let reg = unsafe { GuestRegionRaw::<()>::new(start_addr2, raw_ptr, 0x1000) };
|
||||
let gm = &guest_mem
|
||||
.insert_region(Arc::new(GuestRegionHybrid::from_raw_region(reg)))
|
||||
.unwrap();
|
||||
let mem_orig = gm.memory();
|
||||
assert_eq!(mem_orig.num_regions(), 2);
|
||||
|
||||
let reg = unsafe { GuestRegionRaw::new(GuestAddress(0x8000), raw_ptr, 0x1000) };
|
||||
let mmap = Arc::new(GuestRegionHybrid::from_raw_region(reg));
|
||||
let gm = gm.insert_region(mmap).unwrap();
|
||||
let reg = unsafe { GuestRegionRaw::new(GuestAddress(0x4000), raw_ptr, 0x1000) };
|
||||
let mmap = Arc::new(GuestRegionHybrid::from_raw_region(reg));
|
||||
let gm = gm.insert_region(mmap).unwrap();
|
||||
let reg = unsafe { GuestRegionRaw::new(GuestAddress(0xc000), raw_ptr, 0x1000) };
|
||||
let mmap = Arc::new(GuestRegionHybrid::from_raw_region(reg));
|
||||
let gm = gm.insert_region(mmap).unwrap();
|
||||
let reg = unsafe { GuestRegionRaw::new(GuestAddress(0xc000), raw_ptr, 0x1000) };
|
||||
let mmap = Arc::new(GuestRegionHybrid::from_raw_region(reg));
|
||||
gm.insert_region(mmap).unwrap_err();
|
||||
|
||||
assert_eq!(mem_orig.num_regions(), 2);
|
||||
assert_eq!(gm.num_regions(), 5);
|
||||
|
||||
assert_eq!(gm.regions[0].start_addr(), GuestAddress(0x0000));
|
||||
assert_eq!(gm.regions[1].start_addr(), GuestAddress(0x4000));
|
||||
assert_eq!(gm.regions[2].start_addr(), GuestAddress(0x8000));
|
||||
assert_eq!(gm.regions[3].start_addr(), GuestAddress(0xc000));
|
||||
assert_eq!(gm.regions[4].start_addr(), GuestAddress(0x10_0000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mmap_remove_region() {
|
||||
let start_addr1 = GuestAddress(0);
|
||||
let start_addr2 = GuestAddress(0x10_0000);
|
||||
|
||||
let guest_mem = GuestMemoryHybrid::<()>::new();
|
||||
let mut raw_buf = [0u8; 0x1000];
|
||||
let reg = unsafe { GuestRegionRaw::<()>::new(start_addr1, &mut raw_buf as *mut _, 0x1000) };
|
||||
let guest_mem = guest_mem
|
||||
.insert_region(Arc::new(GuestRegionHybrid::from_raw_region(reg)))
|
||||
.unwrap();
|
||||
let reg = unsafe { GuestRegionRaw::<()>::new(start_addr2, &mut raw_buf as *mut _, 0x1000) };
|
||||
let gm = &guest_mem
|
||||
.insert_region(Arc::new(GuestRegionHybrid::from_raw_region(reg)))
|
||||
.unwrap();
|
||||
let mem_orig = gm.memory();
|
||||
assert_eq!(mem_orig.num_regions(), 2);
|
||||
|
||||
gm.remove_region(GuestAddress(0), 128).unwrap_err();
|
||||
gm.remove_region(GuestAddress(0x4000), 128).unwrap_err();
|
||||
let (gm, region) = gm.remove_region(GuestAddress(0x10_0000), 0x1000).unwrap();
|
||||
|
||||
assert_eq!(mem_orig.num_regions(), 2);
|
||||
assert_eq!(gm.num_regions(), 1);
|
||||
|
||||
assert_eq!(gm.regions[0].start_addr(), GuestAddress(0x0000));
|
||||
assert_eq!(region.start_addr(), GuestAddress(0x10_0000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_guest_memory_mmap_get_slice() {
|
||||
let start_addr1 = GuestAddress(0);
|
||||
let mut raw_buf = [0u8; 0x400];
|
||||
let region =
|
||||
unsafe { GuestRegionRaw::<()>::new(start_addr1, &mut raw_buf as *mut _, 0x400) };
|
||||
|
||||
// Normal case.
|
||||
let slice_addr = MemoryRegionAddress(0x100);
|
||||
let slice_size = 0x200;
|
||||
let slice = region.get_slice(slice_addr, slice_size).unwrap();
|
||||
assert_eq!(slice.len(), slice_size);
|
||||
|
||||
// Empty slice.
|
||||
let slice_addr = MemoryRegionAddress(0x200);
|
||||
let slice_size = 0x0;
|
||||
let slice = region.get_slice(slice_addr, slice_size).unwrap();
|
||||
assert!(slice.is_empty());
|
||||
|
||||
// Error case when slice_size is beyond the boundary.
|
||||
let slice_addr = MemoryRegionAddress(0x300);
|
||||
let slice_size = 0x200;
|
||||
assert!(region.get_slice(slice_addr, slice_size).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_guest_memory_mmap_as_volatile_slice() {
|
||||
let start_addr1 = GuestAddress(0);
|
||||
let mut raw_buf = [0u8; 0x400];
|
||||
let region =
|
||||
unsafe { GuestRegionRaw::<()>::new(start_addr1, &mut raw_buf as *mut _, 0x400) };
|
||||
let region_size = 0x400;
|
||||
|
||||
// Test slice length.
|
||||
let slice = region.as_volatile_slice().unwrap();
|
||||
assert_eq!(slice.len(), region_size);
|
||||
|
||||
// Test slice data.
|
||||
let v = 0x1234_5678u32;
|
||||
let r = slice.get_ref::<u32>(0x200).unwrap();
|
||||
r.store(v);
|
||||
assert_eq!(r.load(), v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_guest_memory_get_slice() {
|
||||
let start_addr1 = GuestAddress(0);
|
||||
let start_addr2 = GuestAddress(0x800);
|
||||
|
||||
let guest_mem = GuestMemoryHybrid::<()>::new();
|
||||
let mut raw_buf = [0u8; 0x400];
|
||||
let reg = unsafe { GuestRegionRaw::<()>::new(start_addr1, &mut raw_buf as *mut _, 0x400) };
|
||||
let guest_mem = guest_mem
|
||||
.insert_region(Arc::new(GuestRegionHybrid::from_raw_region(reg)))
|
||||
.unwrap();
|
||||
let reg = unsafe { GuestRegionRaw::<()>::new(start_addr2, &mut raw_buf as *mut _, 0x400) };
|
||||
let guest_mem = guest_mem
|
||||
.insert_region(Arc::new(GuestRegionHybrid::from_raw_region(reg)))
|
||||
.unwrap();
|
||||
|
||||
// Normal cases.
|
||||
let slice_size = 0x200;
|
||||
let slice = guest_mem
|
||||
.get_slice(GuestAddress(0x100), slice_size)
|
||||
.unwrap();
|
||||
assert_eq!(slice.len(), slice_size);
|
||||
|
||||
let slice_size = 0x400;
|
||||
let slice = guest_mem
|
||||
.get_slice(GuestAddress(0x800), slice_size)
|
||||
.unwrap();
|
||||
assert_eq!(slice.len(), slice_size);
|
||||
|
||||
// Empty slice.
|
||||
assert!(guest_mem
|
||||
.get_slice(GuestAddress(0x900), 0)
|
||||
.unwrap()
|
||||
.is_empty());
|
||||
|
||||
// Error cases, wrong size or base address.
|
||||
assert!(guest_mem.get_slice(GuestAddress(0), 0x500).is_err());
|
||||
assert!(guest_mem.get_slice(GuestAddress(0x600), 0x100).is_err());
|
||||
assert!(guest_mem.get_slice(GuestAddress(0xc00), 0x100).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_checked_offset() {
|
||||
let start_addr1 = GuestAddress(0);
|
||||
let start_addr2 = GuestAddress(0x800);
|
||||
let start_addr3 = GuestAddress(0xc00);
|
||||
|
||||
let guest_mem = GuestMemoryHybrid::<()>::new();
|
||||
let mut raw_buf = [0u8; 0x400];
|
||||
let reg = unsafe { GuestRegionRaw::<()>::new(start_addr1, &mut raw_buf as *mut _, 0x400) };
|
||||
let guest_mem = guest_mem
|
||||
.insert_region(Arc::new(GuestRegionHybrid::from_raw_region(reg)))
|
||||
.unwrap();
|
||||
let reg = unsafe { GuestRegionRaw::<()>::new(start_addr2, &mut raw_buf as *mut _, 0x400) };
|
||||
let guest_mem = guest_mem
|
||||
.insert_region(Arc::new(GuestRegionHybrid::from_raw_region(reg)))
|
||||
.unwrap();
|
||||
let reg = unsafe { GuestRegionRaw::<()>::new(start_addr3, &mut raw_buf as *mut _, 0x400) };
|
||||
let guest_mem = guest_mem
|
||||
.insert_region(Arc::new(GuestRegionHybrid::from_raw_region(reg)))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
guest_mem.checked_offset(start_addr1, 0x200),
|
||||
Some(GuestAddress(0x200))
|
||||
);
|
||||
assert_eq!(
|
||||
guest_mem.checked_offset(start_addr1, 0xa00),
|
||||
Some(GuestAddress(0xa00))
|
||||
);
|
||||
assert_eq!(
|
||||
guest_mem.checked_offset(start_addr2, 0x7ff),
|
||||
Some(GuestAddress(0xfff))
|
||||
);
|
||||
assert_eq!(guest_mem.checked_offset(start_addr2, 0xc00), None);
|
||||
assert_eq!(guest_mem.checked_offset(start_addr1, std::usize::MAX), None);
|
||||
|
||||
assert_eq!(guest_mem.checked_offset(start_addr1, 0x400), None);
|
||||
assert_eq!(
|
||||
guest_mem.checked_offset(start_addr1, 0x400 - 1),
|
||||
Some(GuestAddress(0x400 - 1))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_range() {
|
||||
let start_addr1 = GuestAddress(0);
|
||||
let start_addr2 = GuestAddress(0x800);
|
||||
let start_addr3 = GuestAddress(0xc00);
|
||||
|
||||
let guest_mem = GuestMemoryHybrid::<()>::new();
|
||||
let mut raw_buf = [0u8; 0x400];
|
||||
let reg = unsafe { GuestRegionRaw::<()>::new(start_addr1, &mut raw_buf as *mut _, 0x400) };
|
||||
let guest_mem = guest_mem
|
||||
.insert_region(Arc::new(GuestRegionHybrid::from_raw_region(reg)))
|
||||
.unwrap();
|
||||
let reg = unsafe { GuestRegionRaw::<()>::new(start_addr2, &mut raw_buf as *mut _, 0x400) };
|
||||
let guest_mem = guest_mem
|
||||
.insert_region(Arc::new(GuestRegionHybrid::from_raw_region(reg)))
|
||||
.unwrap();
|
||||
let reg = unsafe { GuestRegionRaw::<()>::new(start_addr3, &mut raw_buf as *mut _, 0x400) };
|
||||
let guest_mem = guest_mem
|
||||
.insert_region(Arc::new(GuestRegionHybrid::from_raw_region(reg)))
|
||||
.unwrap();
|
||||
|
||||
assert!(guest_mem.check_range(start_addr1, 0x0));
|
||||
assert!(guest_mem.check_range(start_addr1, 0x200));
|
||||
assert!(guest_mem.check_range(start_addr1, 0x400));
|
||||
assert!(!guest_mem.check_range(start_addr1, 0xa00));
|
||||
assert!(guest_mem.check_range(start_addr2, 0x7ff));
|
||||
assert!(guest_mem.check_range(start_addr2, 0x800));
|
||||
assert!(!guest_mem.check_range(start_addr2, 0x801));
|
||||
assert!(!guest_mem.check_range(start_addr2, 0xc00));
|
||||
assert!(!guest_mem.check_range(start_addr1, usize::MAX));
|
||||
}
|
||||
}
|
||||
85
src/dragonball/src/dbs_address_space/src/numa.rs
Normal file
85
src/dragonball/src/dbs_address_space/src/numa.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright (C) 2021 Alibaba Cloud. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Types for NUMA information.
|
||||
|
||||
use vm_memory::{GuestAddress, GuestUsize};
|
||||
|
||||
/// Strategy of mbind() and don't lead to OOM.
|
||||
pub const MPOL_PREFERRED: u32 = 1;
|
||||
|
||||
/// Strategy of mbind()
|
||||
pub const MPOL_MF_MOVE: u32 = 2;
|
||||
|
||||
/// Type for recording numa ids of different devices
|
||||
pub struct NumaIdTable {
|
||||
/// vectors of numa id for each memory region
|
||||
pub memory: Vec<u32>,
|
||||
/// vectors of numa id for each cpu
|
||||
pub cpu: Vec<u32>,
|
||||
}
|
||||
|
||||
/// Record numa node memory information.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct NumaNodeInfo {
|
||||
/// Base address of the region in guest physical address space.
|
||||
pub base: GuestAddress,
|
||||
/// Size of the address region.
|
||||
pub size: GuestUsize,
|
||||
}
|
||||
|
||||
/// Record all region's info of a numa node.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct NumaNode {
|
||||
region_infos: Vec<NumaNodeInfo>,
|
||||
vcpu_ids: Vec<u32>,
|
||||
}
|
||||
|
||||
impl NumaNode {
|
||||
/// get reference of region_infos in numa node.
|
||||
pub fn region_infos(&self) -> &Vec<NumaNodeInfo> {
|
||||
&self.region_infos
|
||||
}
|
||||
|
||||
/// get vcpu ids belonging to a numa node.
|
||||
pub fn vcpu_ids(&self) -> &Vec<u32> {
|
||||
&self.vcpu_ids
|
||||
}
|
||||
|
||||
/// add a new numa region info into this numa node.
|
||||
pub fn add_info(&mut self, info: &NumaNodeInfo) {
|
||||
self.region_infos.push(*info);
|
||||
}
|
||||
|
||||
/// add a group of vcpu ids belong to this numa node
|
||||
pub fn add_vcpu_ids(&mut self, vcpu_ids: &[u32]) {
|
||||
self.vcpu_ids.extend(vcpu_ids)
|
||||
}
|
||||
|
||||
/// create a new numa node struct
|
||||
pub fn new() -> NumaNode {
|
||||
NumaNode {
|
||||
region_infos: Vec::new(),
|
||||
vcpu_ids: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_create_numa_node() {
|
||||
let mut numa_node = NumaNode::new();
|
||||
let info = NumaNodeInfo {
|
||||
base: GuestAddress(0),
|
||||
size: 1024,
|
||||
};
|
||||
numa_node.add_info(&info);
|
||||
assert_eq!(*numa_node.region_infos(), vec![info]);
|
||||
let vcpu_ids = vec![0, 1, 2, 3];
|
||||
numa_node.add_vcpu_ids(&vcpu_ids);
|
||||
assert_eq!(*numa_node.vcpu_ids(), vcpu_ids);
|
||||
}
|
||||
}
|
||||
564
src/dragonball/src/dbs_address_space/src/region.rs
Normal file
564
src/dragonball/src/dbs_address_space/src/region.rs
Normal file
@@ -0,0 +1,564 @@
|
||||
// Copyright (C) 2021 Alibaba Cloud. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::os::unix::io::FromRawFd;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use nix::sys::memfd;
|
||||
use vm_memory::{Address, FileOffset, GuestAddress, GuestUsize};
|
||||
|
||||
use crate::memory::MemorySourceType;
|
||||
use crate::memory::MemorySourceType::MemFdShared;
|
||||
use crate::AddressSpaceError;
|
||||
|
||||
/// Type of address space regions.
|
||||
///
|
||||
/// On physical machines, physical memory may have different properties, such as
|
||||
/// volatile vs non-volatile, read-only vs read-write, non-executable vs executable etc.
|
||||
/// On virtual machines, the concept of memory property may be extended to support better
|
||||
/// cooperation between the hypervisor and the guest kernel. Here address space region type means
|
||||
/// what the region will be used for by the guest OS, and different permissions and policies may
|
||||
/// be applied to different address space regions.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum AddressSpaceRegionType {
|
||||
/// Normal memory accessible by CPUs and IO devices.
|
||||
DefaultMemory,
|
||||
/// MMIO address region for Devices.
|
||||
DeviceMemory,
|
||||
/// DAX address region for virtio-fs/virtio-pmem.
|
||||
DAXMemory,
|
||||
}
|
||||
|
||||
/// Struct to maintain configuration information about a guest address region.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AddressSpaceRegion {
|
||||
/// Type of address space regions.
|
||||
pub ty: AddressSpaceRegionType,
|
||||
/// Base address of the region in virtual machine's physical address space.
|
||||
pub base: GuestAddress,
|
||||
/// Size of the address space region.
|
||||
pub size: GuestUsize,
|
||||
/// Host NUMA node ids assigned to this region.
|
||||
pub host_numa_node_id: Option<u32>,
|
||||
|
||||
/// File/offset tuple to back the memory allocation.
|
||||
file_offset: Option<FileOffset>,
|
||||
/// Mmap permission flags.
|
||||
perm_flags: i32,
|
||||
/// Mmap protection flags.
|
||||
prot_flags: i32,
|
||||
/// Hugepage madvise hint.
|
||||
///
|
||||
/// It needs 'advise' or 'always' policy in host shmem config.
|
||||
is_hugepage: bool,
|
||||
/// Hotplug hint.
|
||||
is_hotplug: bool,
|
||||
/// Anonymous memory hint.
|
||||
///
|
||||
/// It should be true for regions with the MADV_DONTFORK flag enabled.
|
||||
is_anon: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
impl AddressSpaceRegion {
|
||||
/// Create an address space region with default configuration.
|
||||
pub fn new(ty: AddressSpaceRegionType, base: GuestAddress, size: GuestUsize) -> Self {
|
||||
AddressSpaceRegion {
|
||||
ty,
|
||||
base,
|
||||
size,
|
||||
host_numa_node_id: None,
|
||||
file_offset: None,
|
||||
perm_flags: libc::MAP_SHARED,
|
||||
prot_flags: libc::PROT_READ | libc::PROT_WRITE,
|
||||
is_hugepage: false,
|
||||
is_hotplug: false,
|
||||
is_anon: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an address space region with all configurable information.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `ty` - Type of the address region
|
||||
/// * `base` - Base address in VM to map content
|
||||
/// * `size` - Length of content to map
|
||||
/// * `numa_node_id` - Optional NUMA node id to allocate memory from
|
||||
/// * `file_offset` - Optional file descriptor and offset to map content from
|
||||
/// * `perm_flags` - mmap permission flags
|
||||
/// * `prot_flags` - mmap protection flags
|
||||
/// * `is_hotplug` - Whether it's a region for hotplug.
|
||||
pub fn build(
|
||||
ty: AddressSpaceRegionType,
|
||||
base: GuestAddress,
|
||||
size: GuestUsize,
|
||||
host_numa_node_id: Option<u32>,
|
||||
file_offset: Option<FileOffset>,
|
||||
perm_flags: i32,
|
||||
prot_flags: i32,
|
||||
is_hotplug: bool,
|
||||
) -> Self {
|
||||
let mut region = Self::new(ty, base, size);
|
||||
|
||||
region.set_host_numa_node_id(host_numa_node_id);
|
||||
region.set_file_offset(file_offset);
|
||||
region.set_perm_flags(perm_flags);
|
||||
region.set_prot_flags(prot_flags);
|
||||
if is_hotplug {
|
||||
region.set_hotplug();
|
||||
}
|
||||
|
||||
region
|
||||
}
|
||||
|
||||
/// Create an address space region to map memory into the virtual machine.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base` - Base address in VM to map content
|
||||
/// * `size` - Length of content to map
|
||||
/// * `numa_node_id` - Optional NUMA node id to allocate memory from
|
||||
/// * `mem_type` - Memory mapping from, 'shmem' or 'hugetlbfs'
|
||||
/// * `mem_file_path` - Memory file path
|
||||
/// * `mem_prealloc` - Whether to enable pre-allocation of guest memory
|
||||
/// * `is_hotplug` - Whether it's a region for hotplug.
|
||||
pub fn create_default_memory_region(
|
||||
base: GuestAddress,
|
||||
size: GuestUsize,
|
||||
numa_node_id: Option<u32>,
|
||||
mem_type: &str,
|
||||
mem_file_path: &str,
|
||||
mem_prealloc: bool,
|
||||
is_hotplug: bool,
|
||||
) -> Result<AddressSpaceRegion, AddressSpaceError> {
|
||||
Self::create_memory_region(
|
||||
base,
|
||||
size,
|
||||
numa_node_id,
|
||||
mem_type,
|
||||
mem_file_path,
|
||||
mem_prealloc,
|
||||
libc::PROT_READ | libc::PROT_WRITE,
|
||||
is_hotplug,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create an address space region to map memory from memfd/hugetlbfs into the virtual machine.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base` - Base address in VM to map content
|
||||
/// * `size` - Length of content to map
|
||||
/// * `numa_node_id` - Optional NUMA node id to allocate memory from
|
||||
/// * `mem_type` - Memory mapping from, 'shmem' or 'hugetlbfs'
|
||||
/// * `mem_file_path` - Memory file path
|
||||
/// * `mem_prealloc` - Whether to enable pre-allocation of guest memory
|
||||
/// * `is_hotplug` - Whether it's a region for hotplug.
|
||||
/// * `prot_flags` - mmap protection flags
|
||||
pub fn create_memory_region(
|
||||
base: GuestAddress,
|
||||
size: GuestUsize,
|
||||
numa_node_id: Option<u32>,
|
||||
mem_type: &str,
|
||||
mem_file_path: &str,
|
||||
mem_prealloc: bool,
|
||||
prot_flags: i32,
|
||||
is_hotplug: bool,
|
||||
) -> Result<AddressSpaceRegion, AddressSpaceError> {
|
||||
let perm_flags = if mem_prealloc {
|
||||
libc::MAP_SHARED | libc::MAP_POPULATE
|
||||
} else {
|
||||
libc::MAP_SHARED
|
||||
};
|
||||
let source_type = MemorySourceType::from_str(mem_type)
|
||||
.map_err(|_e| AddressSpaceError::InvalidMemorySourceType(mem_type.to_string()))?;
|
||||
let mut reg = match source_type {
|
||||
MemorySourceType::MemFdShared | MemorySourceType::MemFdOnHugeTlbFs => {
|
||||
let fn_str = if source_type == MemFdShared {
|
||||
CString::new("shmem").expect("CString::new('shmem') failed")
|
||||
} else {
|
||||
CString::new("hugeshmem").expect("CString::new('hugeshmem') failed")
|
||||
};
|
||||
let filename = fn_str.as_c_str();
|
||||
let fd = memfd::memfd_create(filename, memfd::MemFdCreateFlag::empty())
|
||||
.map_err(AddressSpaceError::CreateMemFd)?;
|
||||
// Safe because we have just created the fd.
|
||||
let file: File = unsafe { File::from_raw_fd(fd) };
|
||||
file.set_len(size).map_err(AddressSpaceError::SetFileSize)?;
|
||||
Self::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
base,
|
||||
size,
|
||||
numa_node_id,
|
||||
Some(FileOffset::new(file, 0)),
|
||||
perm_flags,
|
||||
prot_flags,
|
||||
is_hotplug,
|
||||
)
|
||||
}
|
||||
MemorySourceType::MmapAnonymous | MemorySourceType::MmapAnonymousHugeTlbFs => {
|
||||
let mut perm_flags = libc::MAP_PRIVATE | libc::MAP_ANONYMOUS;
|
||||
if mem_prealloc {
|
||||
perm_flags |= libc::MAP_POPULATE
|
||||
}
|
||||
Self::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
base,
|
||||
size,
|
||||
numa_node_id,
|
||||
None,
|
||||
perm_flags,
|
||||
prot_flags,
|
||||
is_hotplug,
|
||||
)
|
||||
}
|
||||
MemorySourceType::FileOnHugeTlbFs => {
|
||||
let path = Path::new(mem_file_path);
|
||||
if let Some(parent_dir) = path.parent() {
|
||||
// Ensure that the parent directory is existed for the mem file path.
|
||||
std::fs::create_dir_all(parent_dir).map_err(AddressSpaceError::CreateDir)?;
|
||||
}
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(mem_file_path)
|
||||
.map_err(AddressSpaceError::OpenFile)?;
|
||||
nix::unistd::unlink(mem_file_path).map_err(AddressSpaceError::UnlinkFile)?;
|
||||
file.set_len(size).map_err(AddressSpaceError::SetFileSize)?;
|
||||
let file_offset = FileOffset::new(file, 0);
|
||||
Self::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
base,
|
||||
size,
|
||||
numa_node_id,
|
||||
Some(file_offset),
|
||||
perm_flags,
|
||||
prot_flags,
|
||||
is_hotplug,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
if source_type.is_hugepage() {
|
||||
reg.set_hugepage();
|
||||
}
|
||||
if source_type.is_mmap_anonymous() {
|
||||
reg.set_anonpage();
|
||||
}
|
||||
|
||||
Ok(reg)
|
||||
}
|
||||
|
||||
/// Create an address region for device MMIO.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base` - Base address in VM to map content
|
||||
/// * `size` - Length of content to map
|
||||
pub fn create_device_region(
|
||||
base: GuestAddress,
|
||||
size: GuestUsize,
|
||||
) -> Result<AddressSpaceRegion, AddressSpaceError> {
|
||||
Ok(Self::build(
|
||||
AddressSpaceRegionType::DeviceMemory,
|
||||
base,
|
||||
size,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
))
|
||||
}
|
||||
|
||||
/// Get type of the address space region.
|
||||
pub fn region_type(&self) -> AddressSpaceRegionType {
|
||||
self.ty
|
||||
}
|
||||
|
||||
/// Get size of region.
|
||||
pub fn len(&self) -> GuestUsize {
|
||||
self.size
|
||||
}
|
||||
|
||||
/// Get the inclusive start physical address of the region.
|
||||
pub fn start_addr(&self) -> GuestAddress {
|
||||
self.base
|
||||
}
|
||||
|
||||
/// Get the inclusive end physical address of the region.
|
||||
pub fn last_addr(&self) -> GuestAddress {
|
||||
debug_assert!(self.size > 0 && self.base.checked_add(self.size).is_some());
|
||||
GuestAddress(self.base.raw_value() + self.size - 1)
|
||||
}
|
||||
|
||||
/// Get mmap permission flags of the address space region.
|
||||
pub fn perm_flags(&self) -> i32 {
|
||||
self.perm_flags
|
||||
}
|
||||
|
||||
/// Set mmap permission flags for the address space region.
|
||||
pub fn set_perm_flags(&mut self, perm_flags: i32) {
|
||||
self.perm_flags = perm_flags;
|
||||
}
|
||||
|
||||
/// Get mmap protection flags of the address space region.
|
||||
pub fn prot_flags(&self) -> i32 {
|
||||
self.prot_flags
|
||||
}
|
||||
|
||||
/// Set mmap protection flags for the address space region.
|
||||
pub fn set_prot_flags(&mut self, prot_flags: i32) {
|
||||
self.prot_flags = prot_flags;
|
||||
}
|
||||
|
||||
/// Get host_numa_node_id flags
|
||||
pub fn host_numa_node_id(&self) -> Option<u32> {
|
||||
self.host_numa_node_id
|
||||
}
|
||||
|
||||
/// Set associated NUMA node ID to allocate memory from for this region.
|
||||
pub fn set_host_numa_node_id(&mut self, host_numa_node_id: Option<u32>) {
|
||||
self.host_numa_node_id = host_numa_node_id;
|
||||
}
|
||||
|
||||
/// Check whether the address space region is backed by a memory file.
|
||||
pub fn has_file(&self) -> bool {
|
||||
self.file_offset.is_some()
|
||||
}
|
||||
|
||||
/// Get optional file associated with the region.
|
||||
pub fn file_offset(&self) -> Option<&FileOffset> {
|
||||
self.file_offset.as_ref()
|
||||
}
|
||||
|
||||
/// Set associated file/offset pair for the region.
|
||||
pub fn set_file_offset(&mut self, file_offset: Option<FileOffset>) {
|
||||
self.file_offset = file_offset;
|
||||
}
|
||||
|
||||
/// Set the hotplug hint.
|
||||
pub fn set_hotplug(&mut self) {
|
||||
self.is_hotplug = true
|
||||
}
|
||||
|
||||
/// Get the hotplug hint.
|
||||
pub fn is_hotplug(&self) -> bool {
|
||||
self.is_hotplug
|
||||
}
|
||||
|
||||
/// Set hugepage hint for `madvise()`, only takes effect when the memory type is `shmem`.
|
||||
pub fn set_hugepage(&mut self) {
|
||||
self.is_hugepage = true
|
||||
}
|
||||
|
||||
/// Get the hugepage hint.
|
||||
pub fn is_hugepage(&self) -> bool {
|
||||
self.is_hugepage
|
||||
}
|
||||
|
||||
/// Set the anonymous memory hint.
|
||||
pub fn set_anonpage(&mut self) {
|
||||
self.is_anon = true
|
||||
}
|
||||
|
||||
/// Get the anonymous memory hint.
|
||||
pub fn is_anonpage(&self) -> bool {
|
||||
self.is_anon
|
||||
}
|
||||
|
||||
/// Check whether the address space region is valid.
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.size > 0 && self.base.checked_add(self.size).is_some()
|
||||
}
|
||||
|
||||
/// Check whether the address space region intersects with another one.
|
||||
pub fn intersect_with(&self, other: &AddressSpaceRegion) -> bool {
|
||||
// Treat invalid address region as intersecting always
|
||||
let end1 = match self.base.checked_add(self.size) {
|
||||
Some(addr) => addr,
|
||||
None => return true,
|
||||
};
|
||||
let end2 = match other.base.checked_add(other.size) {
|
||||
Some(addr) => addr,
|
||||
None => return true,
|
||||
};
|
||||
|
||||
!(end1 <= other.base || self.base >= end2)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::io::Write;
|
||||
use vmm_sys_util::tempfile::TempFile;
|
||||
|
||||
#[test]
|
||||
fn test_address_space_region_valid() {
|
||||
let reg1 = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0xFFFFFFFFFFFFF000),
|
||||
0x2000,
|
||||
);
|
||||
assert!(!reg1.is_valid());
|
||||
let reg1 = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0xFFFFFFFFFFFFF000),
|
||||
0x1000,
|
||||
);
|
||||
assert!(!reg1.is_valid());
|
||||
let reg1 = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DeviceMemory,
|
||||
GuestAddress(0xFFFFFFFFFFFFE000),
|
||||
0x1000,
|
||||
);
|
||||
assert!(reg1.is_valid());
|
||||
assert_eq!(reg1.start_addr(), GuestAddress(0xFFFFFFFFFFFFE000));
|
||||
assert_eq!(reg1.len(), 0x1000);
|
||||
assert!(!reg1.has_file());
|
||||
assert!(reg1.file_offset().is_none());
|
||||
assert_eq!(reg1.perm_flags(), libc::MAP_SHARED);
|
||||
assert_eq!(reg1.prot_flags(), libc::PROT_READ | libc::PROT_WRITE);
|
||||
assert_eq!(reg1.region_type(), AddressSpaceRegionType::DeviceMemory);
|
||||
|
||||
let tmp_file = TempFile::new().unwrap();
|
||||
let mut f = tmp_file.into_file();
|
||||
let sample_buf = &[1, 2, 3, 4, 5];
|
||||
assert!(f.write_all(sample_buf).is_ok());
|
||||
let reg2 = AddressSpaceRegion::build(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x1000),
|
||||
0x1000,
|
||||
None,
|
||||
Some(FileOffset::new(f, 0x0)),
|
||||
0x5a,
|
||||
0x5a,
|
||||
false,
|
||||
);
|
||||
assert_eq!(reg2.region_type(), AddressSpaceRegionType::DefaultMemory);
|
||||
assert!(reg2.is_valid());
|
||||
assert_eq!(reg2.start_addr(), GuestAddress(0x1000));
|
||||
assert_eq!(reg2.len(), 0x1000);
|
||||
assert!(reg2.has_file());
|
||||
assert!(reg2.file_offset().is_some());
|
||||
assert_eq!(reg2.perm_flags(), 0x5a);
|
||||
assert_eq!(reg2.prot_flags(), 0x5a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_space_region_intersect() {
|
||||
let reg1 = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x1000),
|
||||
0x1000,
|
||||
);
|
||||
let reg2 = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x2000),
|
||||
0x1000,
|
||||
);
|
||||
let reg3 = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x1000),
|
||||
0x1001,
|
||||
);
|
||||
let reg4 = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0x1100),
|
||||
0x100,
|
||||
);
|
||||
let reg5 = AddressSpaceRegion::new(
|
||||
AddressSpaceRegionType::DefaultMemory,
|
||||
GuestAddress(0xFFFFFFFFFFFFF000),
|
||||
0x2000,
|
||||
);
|
||||
|
||||
assert!(!reg1.intersect_with(®2));
|
||||
assert!(!reg2.intersect_with(®1));
|
||||
|
||||
// intersect with self
|
||||
assert!(reg1.intersect_with(®1));
|
||||
|
||||
// intersect with others
|
||||
assert!(reg3.intersect_with(®2));
|
||||
assert!(reg2.intersect_with(®3));
|
||||
assert!(reg1.intersect_with(®4));
|
||||
assert!(reg4.intersect_with(®1));
|
||||
assert!(reg1.intersect_with(®5));
|
||||
assert!(reg5.intersect_with(®1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_device_region() {
|
||||
let reg = AddressSpaceRegion::create_device_region(GuestAddress(0x10000), 0x1000).unwrap();
|
||||
assert_eq!(reg.region_type(), AddressSpaceRegionType::DeviceMemory);
|
||||
assert_eq!(reg.start_addr(), GuestAddress(0x10000));
|
||||
assert_eq!(reg.len(), 0x1000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_default_memory_region() {
|
||||
AddressSpaceRegion::create_default_memory_region(
|
||||
GuestAddress(0x100000),
|
||||
0x100000,
|
||||
None,
|
||||
"invalid",
|
||||
"invalid",
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
let reg = AddressSpaceRegion::create_default_memory_region(
|
||||
GuestAddress(0x100000),
|
||||
0x100000,
|
||||
None,
|
||||
"shmem",
|
||||
"",
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(reg.region_type(), AddressSpaceRegionType::DefaultMemory);
|
||||
assert_eq!(reg.start_addr(), GuestAddress(0x100000));
|
||||
assert_eq!(reg.last_addr(), GuestAddress(0x1fffff));
|
||||
assert_eq!(reg.len(), 0x100000);
|
||||
assert!(reg.file_offset().is_some());
|
||||
|
||||
let reg = AddressSpaceRegion::create_default_memory_region(
|
||||
GuestAddress(0x100000),
|
||||
0x100000,
|
||||
None,
|
||||
"hugeshmem",
|
||||
"",
|
||||
true,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(reg.region_type(), AddressSpaceRegionType::DefaultMemory);
|
||||
assert_eq!(reg.start_addr(), GuestAddress(0x100000));
|
||||
assert_eq!(reg.last_addr(), GuestAddress(0x1fffff));
|
||||
assert_eq!(reg.len(), 0x100000);
|
||||
assert!(reg.file_offset().is_some());
|
||||
|
||||
let reg = AddressSpaceRegion::create_default_memory_region(
|
||||
GuestAddress(0x100000),
|
||||
0x100000,
|
||||
None,
|
||||
"mmap",
|
||||
"",
|
||||
true,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(reg.region_type(), AddressSpaceRegionType::DefaultMemory);
|
||||
assert_eq!(reg.start_addr(), GuestAddress(0x100000));
|
||||
assert_eq!(reg.last_addr(), GuestAddress(0x1fffff));
|
||||
assert_eq!(reg.len(), 0x100000);
|
||||
assert!(reg.file_offset().is_none());
|
||||
|
||||
// TODO: test hugetlbfs
|
||||
}
|
||||
}
|
||||
14
src/dragonball/src/dbs_allocator/Cargo.toml
Normal file
14
src/dragonball/src/dbs_allocator/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "dbs-allocator"
|
||||
version = "0.1.1"
|
||||
authors = ["Liu Jiang <gerry@linux.alibaba.com>"]
|
||||
description = "a resource allocator for virtual machine manager"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
homepage = "https://github.com/openanolis/dragonball-sandbox"
|
||||
repository = "https://github.com/openanolis/dragonball-sandbox"
|
||||
keywords = ["dragonball"]
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1.0"
|
||||
1
src/dragonball/src/dbs_allocator/LICENSE
Symbolic link
1
src/dragonball/src/dbs_allocator/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../LICENSE
|
||||
106
src/dragonball/src/dbs_allocator/README.md
Normal file
106
src/dragonball/src/dbs_allocator/README.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# dbs-allocator
|
||||
|
||||
## Design
|
||||
|
||||
The resource manager in the `Dragonball Sandbox` needs to manage and allocate different kinds of resource for the
|
||||
sandbox (virtual machine), such as memory-mapped I/O address space, port I/O address space, legacy IRQ numbers,
|
||||
MSI/MSI-X vectors, device instance id, etc. The `dbs-allocator` crate is designed to help the resource manager
|
||||
to track and allocate these types of resources.
|
||||
|
||||
Main components are:
|
||||
- *Constraints*: struct to declare constraints for resource allocation.
|
||||
```rust
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Constraint {
|
||||
/// Size of resource to allocate.
|
||||
pub size: u64,
|
||||
/// Lower boundary for resource allocation.
|
||||
pub min: u64,
|
||||
/// Upper boundary for resource allocation.
|
||||
pub max: u64,
|
||||
/// Alignment for allocated resource.
|
||||
pub align: u64,
|
||||
/// Policy for resource allocation.
|
||||
pub policy: AllocPolicy,
|
||||
}
|
||||
```
|
||||
- `IntervalTree`: An interval tree implementation specialized for VMM resource management.
|
||||
```rust
|
||||
pub struct IntervalTree<T> {
|
||||
pub(crate) root: Option<Node<T>>,
|
||||
}
|
||||
|
||||
pub fn allocate(&mut self, constraint: &Constraint) -> Option<Range>
|
||||
pub fn free(&mut self, key: &Range) -> Option<T>
|
||||
pub fn insert(&mut self, key: Range, data: Option<T>) -> Self
|
||||
pub fn update(&mut self, key: &Range, data: T) -> Option<T>
|
||||
pub fn delete(&mut self, key: &Range) -> Option<T>
|
||||
pub fn get(&self, key: &Range) -> Option<NodeState<&T>>
|
||||
```
|
||||
|
||||
## Usage
|
||||
The concept of Interval Tree may seem complicated, but using dbs-allocator to do resource allocation and release is simple and straightforward.
|
||||
You can following these steps to allocate your VMM resource.
|
||||
```rust
|
||||
// 1. To start with, we should create an interval tree for some specific resouces and give maximum address/id range as root node. The range here could be address range, id range, etc.
|
||||
|
||||
let mut resources_pool = IntervalTree::new();
|
||||
resources_pool.insert(Range::new(MIN_RANGE, MAX_RANGE), None);
|
||||
|
||||
// 2. Next, create a constraint with the size for your resource, you could also assign the maximum, minimum and alignment for the constraint. Then we could use the constraint to allocate the resource in the range we previously decided. Interval Tree will give you the appropriate range.
|
||||
let mut constraint = Constraint::new(SIZE);
|
||||
let mut resources_range = self.resources_pool.allocate(&constraint);
|
||||
|
||||
// 3. Then we could use the resource range to let other crates like vm-pci / vm-device to create and maintain the device
|
||||
let mut device = Device::create(resources_range, ..)
|
||||
```
|
||||
|
||||
## Example
|
||||
We will show examples for allocating an unused PCI device ID from the PCI device ID pool and allocating memory address using dbs-allocator
|
||||
```rust
|
||||
use dbs_allocator::{Constraint, IntervalTree, Range};
|
||||
|
||||
// Init a dbs-allocator IntervalTree
|
||||
let mut pci_device_pool = IntervalTree::new();
|
||||
|
||||
// Init PCI device id pool with the range 0 to 255
|
||||
pci_device_pool.insert(Range::new(0x0u8, 0xffu8), None);
|
||||
|
||||
// Construct a constraint with size 1 and alignment 1 to ask for an ID.
|
||||
let mut constraint = Constraint::new(1u64).align(1u64);
|
||||
|
||||
// Get an ID from the pci_device_pool
|
||||
let mut id = pci_device_pool.allocate(&constraint).map(|e| e.min as u8);
|
||||
|
||||
// Pass the ID generated from dbs-allocator to vm-pci specified functions to create pci devices
|
||||
let mut pci_device = PciDevice::new(id as u8, ..);
|
||||
|
||||
```
|
||||
|
||||
```rust
|
||||
use dbs_allocator::{Constraint, IntervalTree, Range};
|
||||
|
||||
// Init a dbs-allocator IntervalTree
|
||||
let mut mem_pool = IntervalTree::new();
|
||||
|
||||
// Init memory address from GUEST_MEM_START to GUEST_MEM_END
|
||||
mem_pool.insert(Range::new(GUEST_MEM_START, GUEST_MEM_END), None);
|
||||
|
||||
// Construct a constraint with size, maximum addr and minimum address of memory region to ask for an memory allocation range.
|
||||
let constraint = Constraint::new(region.len())
|
||||
.min(region.start_addr().raw_value())
|
||||
.max(region.last_addr().raw_value());
|
||||
|
||||
// Get the memory allocation range from the pci_device_pool
|
||||
let mem_range = mem_pool.allocate(&constraint).unwrap();
|
||||
|
||||
// Update the mem_range in IntervalTree with memory region info
|
||||
mem_pool.update(&mem_range, region);
|
||||
|
||||
// After allocation, we can use the memory range to do mapping and other memory related work.
|
||||
...
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0.
|
||||
1297
src/dragonball/src/dbs_allocator/src/interval_tree.rs
Normal file
1297
src/dragonball/src/dbs_allocator/src/interval_tree.rs
Normal file
File diff suppressed because it is too large
Load Diff
164
src/dragonball/src/dbs_allocator/src/lib.rs
Normal file
164
src/dragonball/src/dbs_allocator/src/lib.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright (C) 2019, 2022 Alibaba Cloud. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Data structures and algorithms to support resource allocation and management.
|
||||
//!
|
||||
//! The `dbs-allocator` crate provides data structures and algorithms to manage and allocate
|
||||
//! integer identifiable resources. The resource manager in virtual machine monitor (VMM) may
|
||||
//! manage and allocate resources for virtual machines by using:
|
||||
//! - [Constraint]: Struct to declare constraints for resource allocation.
|
||||
//! - [IntervalTree]: An interval tree implementation specialized for VMM resource management.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
pub mod interval_tree;
|
||||
pub use interval_tree::{IntervalTree, NodeState, Range};
|
||||
|
||||
/// Error codes for resource allocation operations.
|
||||
#[derive(thiserror::Error, Debug, Eq, PartialEq)]
|
||||
pub enum Error {
|
||||
/// Invalid boundary for resource allocation.
|
||||
#[error("invalid boundary constraint: min ({0}), max ({1})")]
|
||||
InvalidBoundary(u64, u64),
|
||||
}
|
||||
|
||||
/// Specialized version of [`std::result::Result`] for resource allocation operations.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Resource allocation policies.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum AllocPolicy {
|
||||
/// Default resource allocation policy.
|
||||
Default,
|
||||
/// Return the first available resource matching the allocation constraints.
|
||||
FirstMatch,
|
||||
}
|
||||
|
||||
/// Struct to declare resource allocation constraints.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Constraint {
|
||||
/// Size of resource to allocate.
|
||||
pub size: u64,
|
||||
/// Lower boundary for resource allocation.
|
||||
pub min: u64,
|
||||
/// Upper boundary for resource allocation.
|
||||
pub max: u64,
|
||||
/// Alignment for allocated resource.
|
||||
pub align: u64,
|
||||
/// Policy for resource allocation.
|
||||
pub policy: AllocPolicy,
|
||||
}
|
||||
|
||||
impl Constraint {
|
||||
/// Create a new instance of [`Constraint`] with default settings.
|
||||
pub fn new<T>(size: T) -> Self
|
||||
where
|
||||
u64: From<T>,
|
||||
{
|
||||
Constraint {
|
||||
size: u64::from(size),
|
||||
min: 0,
|
||||
max: u64::MAX,
|
||||
align: 1,
|
||||
policy: AllocPolicy::Default,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the lower boundary constraint for resource allocation.
|
||||
pub fn min<T>(mut self, min: T) -> Self
|
||||
where
|
||||
u64: From<T>,
|
||||
{
|
||||
self.min = u64::from(min);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the upper boundary constraint for resource allocation.
|
||||
pub fn max<T>(mut self, max: T) -> Self
|
||||
where
|
||||
u64: From<T>,
|
||||
{
|
||||
self.max = u64::from(max);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the alignment constraint for allocated resource.
|
||||
pub fn align<T>(mut self, align: T) -> Self
|
||||
where
|
||||
u64: From<T>,
|
||||
{
|
||||
self.align = u64::from(align);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the resource allocation policy.
|
||||
pub fn policy(mut self, policy: AllocPolicy) -> Self {
|
||||
self.policy = policy;
|
||||
self
|
||||
}
|
||||
|
||||
/// Validate the resource allocation constraints.
|
||||
pub fn validate(&self) -> Result<()> {
|
||||
if self.max < self.min {
|
||||
return Err(Error::InvalidBoundary(self.min, self.max));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_set_min() {
|
||||
let constraint = Constraint::new(2_u64).min(1_u64);
|
||||
assert_eq!(constraint.min, 1_u64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_max() {
|
||||
let constraint = Constraint::new(2_u64).max(100_u64);
|
||||
assert_eq!(constraint.max, 100_u64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_align() {
|
||||
let constraint = Constraint::new(2_u64).align(8_u64);
|
||||
assert_eq!(constraint.align, 8_u64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_policy() {
|
||||
let mut constraint = Constraint::new(2_u64).policy(AllocPolicy::FirstMatch);
|
||||
assert_eq!(constraint.policy, AllocPolicy::FirstMatch);
|
||||
constraint = constraint.policy(AllocPolicy::Default);
|
||||
assert_eq!(constraint.policy, AllocPolicy::Default);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_consistently_change_constraint() {
|
||||
let constraint = Constraint::new(2_u64)
|
||||
.min(1_u64)
|
||||
.max(100_u64)
|
||||
.align(8_u64)
|
||||
.policy(AllocPolicy::FirstMatch);
|
||||
assert_eq!(constraint.min, 1_u64);
|
||||
assert_eq!(constraint.max, 100_u64);
|
||||
assert_eq!(constraint.align, 8_u64);
|
||||
assert_eq!(constraint.policy, AllocPolicy::FirstMatch);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_invalid_boundary() {
|
||||
// Normal case.
|
||||
let constraint = Constraint::new(2_u64).max(1000_u64).min(999_u64);
|
||||
assert!(constraint.validate().is_ok());
|
||||
|
||||
// Error case.
|
||||
let constraint = Constraint::new(2_u64).max(999_u64).min(1000_u64);
|
||||
assert_eq!(
|
||||
constraint.validate(),
|
||||
Err(Error::InvalidBoundary(1000u64, 999u64))
|
||||
);
|
||||
}
|
||||
}
|
||||
26
src/dragonball/src/dbs_arch/Cargo.toml
Normal file
26
src/dragonball/src/dbs_arch/Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "dbs-arch"
|
||||
version = "0.2.3"
|
||||
authors = ["Alibaba Dragonball Team"]
|
||||
license = "Apache-2.0 AND BSD-3-Clause"
|
||||
edition = "2018"
|
||||
description = "A collection of CPU architecture specific constants and utilities."
|
||||
homepage = "https://github.com/openanolis/dragonball-sandbox"
|
||||
repository = "https://github.com/openanolis/dragonball-sandbox"
|
||||
keywords = ["dragonball", "secure-sandbox", "arch", "ARM64", "x86"]
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
memoffset = "0.6"
|
||||
kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] }
|
||||
kvm-ioctls = "0.12.0"
|
||||
thiserror = "1"
|
||||
vm-memory = { version = "0.9" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
libc = ">=0.2.39"
|
||||
|
||||
[dev-dependencies]
|
||||
vm-memory = { version = "0.9", features = ["backend-mmap"] }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
1
src/dragonball/src/dbs_arch/LICENSE
Symbolic link
1
src/dragonball/src/dbs_arch/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../LICENSE
|
||||
29
src/dragonball/src/dbs_arch/README.md
Normal file
29
src/dragonball/src/dbs_arch/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# dbs-arch
|
||||
|
||||
## Design
|
||||
|
||||
The `dbs-arch` crate is a collection of CPU architecture specific constants and utilities to hide CPU architecture details away from the Dragonball Sandbox or other VMMs.
|
||||
Also, we have provided x86_64 CPUID support in this crate, for more details you could look at [this document](docs/x86_64_cpuid.md)
|
||||
|
||||
## Supported Architectures
|
||||
|
||||
- AMD64 (x86_64)
|
||||
- ARM64 (aarch64)
|
||||
|
||||
## Submodule List
|
||||
|
||||
This repository contains the following submodules:
|
||||
| Name | Arch| Description |
|
||||
| --- | --- | --- |
|
||||
| [x86_64::cpuid](src/x86_64/cpuid/) | x86_64 |Facilities to process CPUID information. |
|
||||
| [x86_64::msr](src/x86_64/msr.rs) | x86_64 | Constants and functions for Model Specific Registers |
|
||||
| [aarch64::gic](src/aarch64/gic) | aarch64 | Structures to manage GICv2/GICv3/ITS devices for ARM64 |
|
||||
| [aarch64::regs](src/aarch64/regs.rs) | aarch64 | Constants and functions to configure and manage CPU registers |
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
Part of the code is derived from the [Firecracker](https://github.com/firecracker-microvm/firecracker) project.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0.
|
||||
1
src/dragonball/src/dbs_arch/THIRD-PARTY
Symbolic link
1
src/dragonball/src/dbs_arch/THIRD-PARTY
Symbolic link
@@ -0,0 +1 @@
|
||||
../../THIRD-PARTY
|
||||
68
src/dragonball/src/dbs_arch/docs/x86_64_cpuid.md
Normal file
68
src/dragonball/src/dbs_arch/docs/x86_64_cpuid.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# CPUID
|
||||
|
||||
## Design
|
||||
|
||||
CPUID is designed as the CPUID filter for Intel and AMD CPU Identification. Through CPUID configuration, we could set CPU topology, Cache topology, PMU status and other features for the VMs.
|
||||
|
||||
CPUID is developed based on the Firecracker CPUID code while we add other extensions such as CPU Topology and VPMU features.
|
||||
|
||||
## Usage
|
||||
To use CPUID, you should first use KVM_GET_CPUID2 ioctl to get the original CPUID then use process_cpuid() provided by the db-arch to filter CPUID with the information you want and suitable for VM conditions.
|
||||
|
||||
Currently, we support following specifications that db-arch could use to filter CPUID:
|
||||
```rust
|
||||
pub struct VmSpec {
|
||||
/// The vendor id of the CPU
|
||||
cpu_vendor_id: [u8; 12],
|
||||
/// The id of the current logical cpu in the range [0..cpu_count].
|
||||
cpu_id: u8,
|
||||
/// The total number of logical cpus (includes cpus that could be hotplugged).
|
||||
cpu_count: u8,
|
||||
/// The desired brand string for the guest.
|
||||
brand_string: BrandString,
|
||||
/// threads per core for cpu topology information
|
||||
threads_per_core: u8,
|
||||
/// cores per die for cpu topology information
|
||||
cores_per_die: u8,
|
||||
/// dies per socket for cpu topology information
|
||||
dies_per_socket: u8,
|
||||
/// if vpmu feature is Disabled, it means vpmu feature is off (by default)
|
||||
/// if vpmu feature is LimitedlyEnabled, it means minimal vpmu counters are supported (cycles and instructions)
|
||||
/// if vpmu feature is FullyEnabled, it means all vpmu counters are supported
|
||||
vpmu_feature: VpmuFeatureLevel,
|
||||
}
|
||||
```
|
||||
|
||||
## Example
|
||||
We will show examples for filtering CPUID.
|
||||
First, you need to use KVM_GET_CPUID2 ioctl to get the original CPUID, this part is not included in the db-cpuid.
|
||||
|
||||
```rust
|
||||
// an example for getting the cpuid in the vmm.
|
||||
let mut cpuid = CpuId::new(num_entries).map_err(|_| errno::Error::new(libc::ENOMEM))?;
|
||||
let ret = unsafe {ioctl_with_mut_ptr(self, KVM_GET_CPUID2(), cpuid.as_mut_fam_struct_ptr())};
|
||||
if ret != 0 {
|
||||
return Err(errno::Error::last());
|
||||
}
|
||||
```
|
||||
|
||||
Then we could create the `VmSpec` to describe the VM specification we want and use process_cpuid() to filter CPUID.
|
||||
|
||||
```rust
|
||||
let cpuid_vm_spec = VmSpec::new(
|
||||
self.id,
|
||||
vcpu_config.max_all_vcpu_count as u8,
|
||||
vcpu_config.threads_per_core,
|
||||
vcpu_config.cores_per_die,
|
||||
vcpu_config.dies_per_socket,
|
||||
vcpu_config.vpmu_feature,
|
||||
)
|
||||
.map_err(VcpuError::CpuId)?;
|
||||
process_cpuid(&mut self.cpuid, &cpuid_vm_spec).map_err(|e| {
|
||||
METRICS.vcpu.process_cpuid.inc();
|
||||
error!("Failure in configuring CPUID for vcpu {}: {:?}", self.id, e);
|
||||
VcpuError::CpuId(e)
|
||||
})?;
|
||||
```
|
||||
|
||||
After the CPUID is filtered, we could use it to set the guest's CPUID.
|
||||
110
src/dragonball/src/dbs_arch/src/aarch64/gic/gicv2.rs
Normal file
110
src/dragonball/src/dbs_arch/src/aarch64/gic/gicv2.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use kvm_ioctls::DeviceFd;
|
||||
|
||||
use super::{GICDevice, Result};
|
||||
|
||||
/// Represent a GIC v2 device
|
||||
pub struct GICv2 {
|
||||
/// The file descriptor for the KVM device
|
||||
fd: DeviceFd,
|
||||
|
||||
/// GIC device properties, to be used for setting up the fdt entry
|
||||
properties: [u64; 4],
|
||||
|
||||
/// Number of CPUs handled by the device
|
||||
vcpu_count: u64,
|
||||
}
|
||||
|
||||
impl GICv2 {
|
||||
// Unfortunately bindgen omits defines that are based on other defines.
|
||||
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
|
||||
const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000;
|
||||
const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000;
|
||||
|
||||
// Device trees specific constants
|
||||
const ARCH_GIC_V2_MAINT_IRQ: u32 = 8;
|
||||
|
||||
/// Get the address of the GICv2 distributor.
|
||||
const fn get_dist_addr() -> u64 {
|
||||
crate::aarch64::gic::GIC_REG_END_ADDRESS - GICv2::KVM_VGIC_V2_DIST_SIZE
|
||||
}
|
||||
|
||||
/// Get the size of the GIC_v2 distributor.
|
||||
const fn get_dist_size() -> u64 {
|
||||
GICv2::KVM_VGIC_V2_DIST_SIZE
|
||||
}
|
||||
|
||||
/// Get the address of the GIC_v2 CPU.
|
||||
const fn get_cpu_addr() -> u64 {
|
||||
GICv2::get_dist_addr() - GICv2::KVM_VGIC_V2_CPU_SIZE
|
||||
}
|
||||
|
||||
/// Get the size of the GIC_v2 CPU.
|
||||
const fn get_cpu_size() -> u64 {
|
||||
GICv2::KVM_VGIC_V2_CPU_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
impl GICDevice for GICv2 {
|
||||
fn device_fd(&self) -> &DeviceFd {
|
||||
&self.fd
|
||||
}
|
||||
|
||||
fn device_properties(&self) -> &[u64] {
|
||||
&self.properties
|
||||
}
|
||||
|
||||
fn vcpu_count(&self) -> u64 {
|
||||
self.vcpu_count
|
||||
}
|
||||
|
||||
fn fdt_compatibility(&self) -> &str {
|
||||
"arm,gic-400"
|
||||
}
|
||||
|
||||
fn fdt_maint_irq(&self) -> u32 {
|
||||
GICv2::ARCH_GIC_V2_MAINT_IRQ
|
||||
}
|
||||
|
||||
fn version() -> u32 {
|
||||
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2
|
||||
}
|
||||
|
||||
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice> {
|
||||
Box::new(GICv2 {
|
||||
fd,
|
||||
properties: [
|
||||
GICv2::get_dist_addr(),
|
||||
GICv2::get_dist_size(),
|
||||
GICv2::get_cpu_addr(),
|
||||
GICv2::get_cpu_size(),
|
||||
],
|
||||
vcpu_count,
|
||||
})
|
||||
}
|
||||
|
||||
fn init_device_attributes(gic_device: &dyn GICDevice) -> Result<()> {
|
||||
/* Setting up the distributor attribute.
|
||||
We are placing the GIC below 1GB so we need to substract the size of the distributor. */
|
||||
Self::set_device_attribute(
|
||||
gic_device.device_fd(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST),
|
||||
&GICv2::get_dist_addr() as *const u64 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
/* Setting up the CPU attribute. */
|
||||
Self::set_device_attribute(
|
||||
gic_device.device_fd(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU),
|
||||
&GICv2::get_cpu_addr() as *const u64 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
136
src/dragonball/src/dbs_arch/src/aarch64/gic/gicv3.rs
Normal file
136
src/dragonball/src/dbs_arch/src/aarch64/gic/gicv3.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright 2022 Alibaba Cloud. All Rights Reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::boxed::Box;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use kvm_ioctls::{DeviceFd, VmFd};
|
||||
|
||||
use super::its::ItsType::{PciMsiIts, PlatformMsiIts};
|
||||
use super::its::{ItsType, ITS};
|
||||
use super::{GICDevice, Result};
|
||||
|
||||
/// GICv3 instance
|
||||
pub struct GICv3 {
|
||||
/// The file descriptor for the KVM device
|
||||
fd: DeviceFd,
|
||||
|
||||
/// GIC device properties, to be used for setting up the fdt entry
|
||||
properties: [u64; 4],
|
||||
|
||||
/// Number of CPUs handled by the device
|
||||
vcpu_count: u64,
|
||||
|
||||
/// ITS instance of this gic control
|
||||
its: HashMap<ItsType, ITS>,
|
||||
}
|
||||
|
||||
impl GICv3 {
|
||||
// Unfortunately bindgen omits defines that are based on other defines.
|
||||
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
|
||||
const SZ_64K: u64 = 0x0001_0000;
|
||||
const KVM_VGIC_V3_DIST_SIZE: u64 = GICv3::SZ_64K;
|
||||
const KVM_VGIC_V3_REDIST_SIZE: u64 = (2 * GICv3::SZ_64K);
|
||||
|
||||
// Device trees specific constants
|
||||
const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
|
||||
|
||||
/// Get the address of the GIC distributor.
|
||||
fn get_dist_addr() -> u64 {
|
||||
crate::aarch64::gic::GIC_REG_END_ADDRESS - GICv3::KVM_VGIC_V3_DIST_SIZE
|
||||
}
|
||||
|
||||
/// Get the size of the GIC distributor.
|
||||
fn get_dist_size() -> u64 {
|
||||
GICv3::KVM_VGIC_V3_DIST_SIZE
|
||||
}
|
||||
|
||||
/// Get the address of the GIC redistributors.
|
||||
pub fn get_redists_addr(vcpu_count: u64) -> u64 {
|
||||
GICv3::get_dist_addr() - GICv3::get_redists_size(vcpu_count)
|
||||
}
|
||||
|
||||
/// Get the size of the GIC redistributors.
|
||||
fn get_redists_size(vcpu_count: u64) -> u64 {
|
||||
vcpu_count * GICv3::KVM_VGIC_V3_REDIST_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
impl GICDevice for GICv3 {
|
||||
fn device_fd(&self) -> &DeviceFd {
|
||||
&self.fd
|
||||
}
|
||||
|
||||
fn device_properties(&self) -> &[u64] {
|
||||
&self.properties
|
||||
}
|
||||
|
||||
fn vcpu_count(&self) -> u64 {
|
||||
self.vcpu_count
|
||||
}
|
||||
|
||||
fn fdt_compatibility(&self) -> &str {
|
||||
"arm,gic-v3"
|
||||
}
|
||||
|
||||
fn fdt_maint_irq(&self) -> u32 {
|
||||
GICv3::ARCH_GIC_V3_MAINT_IRQ
|
||||
}
|
||||
|
||||
fn get_its_reg_range(&self, its_type: &ItsType) -> Option<[u64; 2]> {
|
||||
self.its.get(its_type).map(|its| its.get_reg_range())
|
||||
}
|
||||
|
||||
fn attach_its(&mut self, vm: &VmFd) -> Result<()> {
|
||||
let its = ITS::new(vm, self, PlatformMsiIts)?;
|
||||
self.its.insert(PlatformMsiIts, its);
|
||||
let its = ITS::new(vm, self, PciMsiIts)?;
|
||||
self.its.insert(PciMsiIts, its);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn version() -> u32 {
|
||||
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
|
||||
}
|
||||
|
||||
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice> {
|
||||
Box::new(GICv3 {
|
||||
fd,
|
||||
properties: [
|
||||
GICv3::get_dist_addr(),
|
||||
GICv3::get_dist_size(),
|
||||
GICv3::get_redists_addr(vcpu_count),
|
||||
GICv3::get_redists_size(vcpu_count),
|
||||
],
|
||||
vcpu_count,
|
||||
its: HashMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn init_device_attributes(gic_device: &dyn GICDevice) -> Result<()> {
|
||||
/* Setting up the distributor attribute.
|
||||
We are placing the GIC below 1GB so we need to substract the size of the distributor.
|
||||
*/
|
||||
Self::set_device_attribute(
|
||||
gic_device.device_fd(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST.into(),
|
||||
&GICv3::get_dist_addr() as *const u64 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
/* Setting up the redistributors' attribute.
|
||||
We are calculating here the start of the redistributors address. We have one per CPU.
|
||||
*/
|
||||
Self::set_device_attribute(
|
||||
gic_device.device_fd(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST.into(),
|
||||
&GICv3::get_redists_addr(gic_device.vcpu_count()) as *const u64 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
81
src/dragonball/src/dbs_arch/src/aarch64/gic/its.rs
Normal file
81
src/dragonball/src/dbs_arch/src/aarch64/gic/its.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright 2022 Alibaba Cloud. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use kvm_ioctls::{DeviceFd, VmFd};
|
||||
|
||||
use super::gicv3::GICv3;
|
||||
use super::{Error, GICDevice, Result};
|
||||
|
||||
// ITS register range
|
||||
const REG_RANGE_LEN: u64 = 0x20000;
|
||||
|
||||
/// ITS type
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
pub enum ItsType {
|
||||
/// platform msi its
|
||||
PlatformMsiIts,
|
||||
/// pci msi its
|
||||
PciMsiIts,
|
||||
}
|
||||
|
||||
/// Only GIC-V3 can use ITS
|
||||
pub struct ITS {
|
||||
/// The file descriptor for the KVM device
|
||||
fd: DeviceFd,
|
||||
reg_range: [u64; 2],
|
||||
}
|
||||
|
||||
impl ITS {
|
||||
/// Create an ITS device
|
||||
pub fn new(vm: &VmFd, gic_ctl: &GICv3, its_type: ItsType) -> Result<ITS> {
|
||||
let fd = ITS::create_device_fd(vm)?;
|
||||
// Define the mmio space of platform msi its after the mmio space of pci msi its
|
||||
let offset = match its_type {
|
||||
ItsType::PlatformMsiIts => REG_RANGE_LEN,
|
||||
ItsType::PciMsiIts => REG_RANGE_LEN * 2,
|
||||
};
|
||||
let vcpu_count = gic_ctl.vcpu_count();
|
||||
// No document has been found to accurately describe the storage location and
|
||||
// length of the ITS register. Currently, we store the ITS register in front of
|
||||
// the redistributor register. And temporarily refer to the "arm, gic-v3-its"
|
||||
// kernel document to set the ITS register length to 0x20000.In addition,
|
||||
// reg_range is a two-tuple, representing the register base address and the
|
||||
// length of the register address space.
|
||||
let reg_range: [u64; 2] = [GICv3::get_redists_addr(vcpu_count) - offset, REG_RANGE_LEN];
|
||||
let its = ITS { fd, reg_range };
|
||||
let reg_base_addr = its.get_reg_range_base_addr();
|
||||
its.set_attribute(reg_base_addr)?;
|
||||
Ok(its)
|
||||
}
|
||||
|
||||
fn create_device_fd(vm: &VmFd) -> Result<DeviceFd> {
|
||||
let mut its_device = kvm_bindings::kvm_create_device {
|
||||
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS,
|
||||
fd: 0,
|
||||
flags: 0,
|
||||
};
|
||||
vm.create_device(&mut its_device).map_err(Error::CreateITS)
|
||||
}
|
||||
|
||||
fn set_attribute(&self, reg_base_addr: u64) -> Result<()> {
|
||||
let attribute = kvm_bindings::kvm_device_attr {
|
||||
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
attr: u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE),
|
||||
addr: ®_base_addr as *const u64 as u64,
|
||||
flags: 0,
|
||||
};
|
||||
self.fd
|
||||
.set_device_attr(&attribute)
|
||||
.map_err(Error::SetITSAttribute)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_reg_range_base_addr(&self) -> u64 {
|
||||
self.reg_range[0]
|
||||
}
|
||||
|
||||
/// Get its reg range
|
||||
pub fn get_reg_range(&self) -> [u64; 2] {
|
||||
self.reg_range
|
||||
}
|
||||
}
|
||||
218
src/dragonball/src/dbs_arch/src/aarch64/gic/mod.rs
Normal file
218
src/dragonball/src/dbs_arch/src/aarch64/gic/mod.rs
Normal file
@@ -0,0 +1,218 @@
|
||||
// Copyright 2022 Alibaba Cloud. All Rights Reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// Export gicv2 interface
|
||||
pub mod gicv2;
|
||||
/// Export gicv3 interface
|
||||
pub mod gicv3;
|
||||
/// Export ITS interface
|
||||
pub mod its;
|
||||
|
||||
use std::{boxed::Box, result};
|
||||
|
||||
use kvm_ioctls::{DeviceFd, VmFd};
|
||||
|
||||
use gicv2::GICv2;
|
||||
use gicv3::GICv3;
|
||||
|
||||
// As per virt/kvm/arm/vgic/vgic-kvm-device.c we need
|
||||
// the number of interrupts our GIC will support to be:
|
||||
// * bigger than 32
|
||||
// * less than 1023 and
|
||||
// * a multiple of 32.
|
||||
// We are setting up our interrupt controller to support a maximum of 128 interrupts.
|
||||
|
||||
/// First usable interrupt on aarch64.
|
||||
pub const IRQ_BASE: u32 = 32;
|
||||
|
||||
/// Last usable interrupt on aarch64.
|
||||
pub const IRQ_MAX: u32 = 159;
|
||||
|
||||
/// Define the gic register end address.
|
||||
pub const GIC_REG_END_ADDRESS: u64 = 1 << 30; // 1GB
|
||||
|
||||
/// Errors thrown while setting up the GIC.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
||||
CreateGIC(kvm_ioctls::Error),
|
||||
/// Error while setting device attributes for the GIC.
|
||||
SetDeviceAttribute(kvm_ioctls::Error),
|
||||
/// The number of vCPUs in the GicState doesn't match the number of vCPUs on the system
|
||||
InconsistentVcpuCount,
|
||||
/// The VgicSysRegsState is invalid
|
||||
InvalidVgicSysRegState,
|
||||
/// ERROR while create ITS fail
|
||||
CreateITS(kvm_ioctls::Error),
|
||||
/// ERROR while set ITS attr fail
|
||||
SetITSAttribute(kvm_ioctls::Error),
|
||||
}
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// Function that flushes `RDIST` pending tables into guest RAM.
|
||||
///
|
||||
/// The tables get flushed to guest RAM whenever the VM gets stopped.
|
||||
pub fn save_pending_tables(fd: &DeviceFd) -> Result<()> {
|
||||
let init_gic_attr = kvm_bindings::kvm_device_attr {
|
||||
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES),
|
||||
addr: 0,
|
||||
flags: 0,
|
||||
};
|
||||
fd.set_device_attr(&init_gic_attr)
|
||||
.map_err(Error::SetDeviceAttribute)
|
||||
}
|
||||
|
||||
/// Trait for GIC devices.
|
||||
pub trait GICDevice: Send {
|
||||
/// Returns the file descriptor of the GIC device
|
||||
fn device_fd(&self) -> &DeviceFd;
|
||||
|
||||
/// Returns an array with GIC device properties
|
||||
fn device_properties(&self) -> &[u64];
|
||||
|
||||
/// Returns the number of vCPUs this GIC handles
|
||||
fn vcpu_count(&self) -> u64;
|
||||
|
||||
/// Returns the fdt compatibility property of the device
|
||||
fn fdt_compatibility(&self) -> &str;
|
||||
|
||||
/// Returns the maint_irq fdt property of the device
|
||||
fn fdt_maint_irq(&self) -> u32;
|
||||
|
||||
/// Get ITS reg range
|
||||
fn get_its_reg_range(&self, _its_type: &its::ItsType) -> Option<[u64; 2]> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Only gic-v3 has its
|
||||
fn attach_its(&mut self, _vm: &VmFd) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the GIC version of the device
|
||||
fn version() -> u32
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Create the GIC device object
|
||||
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Setup the device-specific attributes
|
||||
fn init_device_attributes(gic_device: &dyn GICDevice) -> Result<()>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Initialize a GIC device
|
||||
fn init_device(vm: &VmFd) -> Result<DeviceFd>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut gic_device = kvm_bindings::kvm_create_device {
|
||||
type_: Self::version(),
|
||||
fd: 0,
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
vm.create_device(&mut gic_device).map_err(Error::CreateGIC)
|
||||
}
|
||||
|
||||
/// Set a GIC device attribute
|
||||
fn set_device_attribute(
|
||||
fd: &DeviceFd,
|
||||
group: u32,
|
||||
attr: u64,
|
||||
addr: u64,
|
||||
flags: u32,
|
||||
) -> Result<()>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let attr = kvm_bindings::kvm_device_attr {
|
||||
group,
|
||||
attr,
|
||||
addr,
|
||||
flags,
|
||||
};
|
||||
fd.set_device_attr(&attr)
|
||||
.map_err(Error::SetDeviceAttribute)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Finalize the setup of a GIC device
|
||||
fn finalize_device(gic_device: &dyn GICDevice) -> Result<()>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
/* We need to tell the kernel how many irqs to support with this vgic.
|
||||
* See the `layout` module for details.
|
||||
*/
|
||||
let nr_irqs: u32 = IRQ_MAX - IRQ_BASE + 1;
|
||||
let nr_irqs_ptr = &nr_irqs as *const u32;
|
||||
Self::set_device_attribute(
|
||||
gic_device.device_fd(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
||||
0,
|
||||
nr_irqs_ptr as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
/* Finalize the GIC.
|
||||
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
|
||||
*/
|
||||
Self::set_device_attribute(
|
||||
gic_device.device_fd(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
|
||||
0,
|
||||
0,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
/// Method to initialize the GIC device
|
||||
fn new(vm: &VmFd, vcpu_count: u64) -> Result<Box<dyn GICDevice>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let vgic_fd = Self::init_device(vm)?;
|
||||
|
||||
let mut device = Self::create_device(vgic_fd, vcpu_count);
|
||||
|
||||
device.attach_its(vm)?;
|
||||
|
||||
Self::init_device_attributes(device.as_ref())?;
|
||||
|
||||
Self::finalize_device(device.as_ref())?;
|
||||
|
||||
Ok(device)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a GIC device.
|
||||
///
|
||||
/// It will try to create by default a GICv3 device. If that fails it will try
|
||||
/// to fall-back to a GICv2 device.
|
||||
pub fn create_gic(vm: &VmFd, vcpu_count: u64) -> Result<Box<dyn GICDevice>> {
|
||||
GICv3::new(vm, vcpu_count).or_else(|_| GICv2::new(vm, vcpu_count))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use kvm_ioctls::Kvm;
|
||||
|
||||
#[test]
|
||||
fn test_create_gic() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
assert!(create_gic(&vm, 1).is_ok());
|
||||
}
|
||||
}
|
||||
139
src/dragonball/src/dbs_arch/src/aarch64/mod.rs
Normal file
139
src/dragonball/src/dbs_arch/src/aarch64/mod.rs
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright 2021 Alibaba Cloud. All Rights Reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! CPU architecture specific constants, structures and utilities for the `aarch64` architecture.
|
||||
|
||||
/// Module for the global interrupt controller configuration.
|
||||
pub mod gic;
|
||||
/// Module for PMU virtualization.
|
||||
pub mod pmu;
|
||||
/// Logic for configuring aarch64 registers.
|
||||
pub mod regs;
|
||||
|
||||
use std::{fmt, result};
|
||||
|
||||
const MMIO_DEVICE_LEGACY_IRQ_NUMBER: usize = 1;
|
||||
|
||||
/// Error for ARM64 architecture information
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// MMIO device information error
|
||||
MMIODeviceInfoError,
|
||||
/// Invalid arguments
|
||||
InvalidArguments,
|
||||
}
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// Types of devices that can get attached to this platform.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
|
||||
pub enum DeviceType {
|
||||
/// Device Type: Virtio.
|
||||
Virtio(u32),
|
||||
/// Device Type: Serial.
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
Serial,
|
||||
/// Device Type: RTC.
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
RTC,
|
||||
}
|
||||
|
||||
impl fmt::Display for DeviceType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for devices to be added to the Flattened Device Tree.
|
||||
pub trait DeviceInfoForFDT {
|
||||
/// Returns the address where this device will be loaded.
|
||||
fn addr(&self) -> u64;
|
||||
/// Returns the amount of memory that needs to be reserved for this device.
|
||||
fn length(&self) -> u64;
|
||||
/// Returns the associated interrupt for this device.
|
||||
fn irq(&self) -> Result<u32>;
|
||||
/// Get device id
|
||||
fn get_device_id(&self) -> Option<u32>;
|
||||
}
|
||||
|
||||
/// MMIO device info used for FDT generating.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MMIODeviceInfo {
|
||||
/// MMIO address base
|
||||
pub base: u64,
|
||||
/// MMIO address size
|
||||
pub size: u64,
|
||||
/// Device irq
|
||||
pub irqs: Vec<u32>,
|
||||
/// Only virtio devices that support platform msi have device id
|
||||
pub device_id: Option<u32>,
|
||||
}
|
||||
|
||||
impl MMIODeviceInfo {
|
||||
/// Create mmio device info.
|
||||
pub fn new(base: u64, size: u64, irqs: Vec<u32>, device_id: Option<u32>) -> Self {
|
||||
MMIODeviceInfo {
|
||||
base,
|
||||
size,
|
||||
irqs,
|
||||
device_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceInfoForFDT for MMIODeviceInfo {
|
||||
fn addr(&self) -> u64 {
|
||||
self.base
|
||||
}
|
||||
|
||||
fn length(&self) -> u64 {
|
||||
self.size
|
||||
}
|
||||
|
||||
fn irq(&self) -> Result<u32> {
|
||||
// Currently mmio devices have only one legacy irq.
|
||||
if self.irqs.len() != MMIO_DEVICE_LEGACY_IRQ_NUMBER {
|
||||
return Err(Error::MMIODeviceInfoError);
|
||||
}
|
||||
let irq = self.irqs[0];
|
||||
if !(gic::IRQ_BASE..=gic::IRQ_MAX).contains(&irq) {
|
||||
return Err(Error::MMIODeviceInfoError);
|
||||
}
|
||||
|
||||
Ok(irq)
|
||||
}
|
||||
|
||||
fn get_device_id(&self) -> Option<u32> {
|
||||
self.device_id
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_mmo_device_info() {
|
||||
let info = MMIODeviceInfo::new(0x1000, 0x2000, vec![gic::IRQ_BASE], Some(5));
|
||||
assert_eq!(info.addr(), 0x1000);
|
||||
assert_eq!(info.length(), 0x2000);
|
||||
assert_eq!(info.irq().unwrap(), gic::IRQ_BASE);
|
||||
assert_eq!(info.get_device_id(), Some(5));
|
||||
|
||||
let info = MMIODeviceInfo::new(0x1000, 0x2000, vec![gic::IRQ_BASE], None);
|
||||
assert_eq!(info.get_device_id(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mmo_device_info_get_irq() {
|
||||
let info = MMIODeviceInfo::new(0x1000, 0x1000, vec![], None);
|
||||
assert!(info.irq().is_err());
|
||||
let info = MMIODeviceInfo::new(0x1000, 0x1000, vec![1, 2], None);
|
||||
assert!(info.irq().is_err());
|
||||
let info = MMIODeviceInfo::new(0x1000, 0x1000, vec![gic::IRQ_BASE - 1], None);
|
||||
assert!(info.irq().is_err());
|
||||
let info = MMIODeviceInfo::new(0x1000, 0x1000, vec![gic::IRQ_MAX + 1], None);
|
||||
assert!(info.irq().is_err());
|
||||
}
|
||||
}
|
||||
172
src/dragonball/src/dbs_arch/src/aarch64/pmu.rs
Normal file
172
src/dragonball/src/dbs_arch/src/aarch64/pmu.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright (C) 2022 Alibaba Cloud. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Constants and utilities for aarch64 PMU virtualization.
|
||||
|
||||
use kvm_bindings::{
|
||||
kvm_device_attr, KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT, KVM_ARM_VCPU_PMU_V3_IRQ,
|
||||
};
|
||||
use kvm_ioctls::{Error as KvmError, VcpuFd, VmFd};
|
||||
use thiserror::Error;
|
||||
|
||||
/// PPI base number on aarch64.
|
||||
pub const PPI_BASE: u32 = 16;
|
||||
/// Pmu ppi number
|
||||
pub const VIRTUAL_PMU_IRQ: u32 = 7;
|
||||
|
||||
/// Errors thrown while setting up the PMU.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PmuError {
|
||||
/// Error while check kvm pmu capability
|
||||
#[error("Check kvm pmu capability failed: {0}")]
|
||||
CheckKvmPmuCap(#[source] KvmError),
|
||||
/// Error while check pmu irq.
|
||||
#[error("Check pmu irq error: {0}")]
|
||||
HasPmuIrq(#[source] KvmError),
|
||||
/// Error while check pmu init.
|
||||
#[error("Check pmu init error: {0}")]
|
||||
HasPmuInit(#[source] KvmError),
|
||||
/// Error while set pmu irq.
|
||||
#[error("Set pmu irq error: {0}")]
|
||||
SetPmuIrq(#[source] KvmError),
|
||||
/// Error while set pmu init.
|
||||
#[error("Set pmu init error: {0}")]
|
||||
SetPmuInit(#[source] KvmError),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, PmuError>;
|
||||
|
||||
/// Tests whether a cpu supports KVM_ARM_VCPU_PMU_V3_IRQ attribute.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `vcpu` - The VCPU file descriptor
|
||||
fn has_pmu_irq(vcpu: &VcpuFd) -> Result<()> {
|
||||
let irq = (VIRTUAL_PMU_IRQ + PPI_BASE) as u64;
|
||||
let attribute = kvm_device_attr {
|
||||
group: KVM_ARM_VCPU_PMU_V3_CTRL,
|
||||
attr: u64::from(KVM_ARM_VCPU_PMU_V3_IRQ),
|
||||
addr: &irq as *const u64 as u64,
|
||||
flags: 0,
|
||||
};
|
||||
vcpu.has_device_attr(&attribute)
|
||||
.map_err(PmuError::HasPmuIrq)
|
||||
}
|
||||
|
||||
/// Tests whether a cpu supports KVM_ARM_VCPU_PMU_V3_INIT attribute.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `vcpu` - The VCPU file descriptor
|
||||
fn has_pmu_init(vcpu: &VcpuFd) -> Result<()> {
|
||||
let attribute = kvm_device_attr {
|
||||
group: KVM_ARM_VCPU_PMU_V3_CTRL,
|
||||
attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT),
|
||||
addr: 0,
|
||||
flags: 0,
|
||||
};
|
||||
vcpu.has_device_attr(&attribute)
|
||||
.map_err(PmuError::HasPmuInit)
|
||||
}
|
||||
|
||||
/// Set KVM_ARM_VCPU_PMU_V3_IRQ for a specific vcpu.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `vcpu` - The VCPU file descriptor
|
||||
fn set_pmu_irq(vcpu: &VcpuFd) -> Result<()> {
|
||||
let irq = (VIRTUAL_PMU_IRQ + PPI_BASE) as u64;
|
||||
let attribute = kvm_device_attr {
|
||||
group: KVM_ARM_VCPU_PMU_V3_CTRL,
|
||||
attr: u64::from(KVM_ARM_VCPU_PMU_V3_IRQ),
|
||||
addr: &irq as *const u64 as u64,
|
||||
flags: 0,
|
||||
};
|
||||
vcpu.set_device_attr(&attribute)
|
||||
.map_err(PmuError::SetPmuIrq)
|
||||
}
|
||||
|
||||
/// Set KVM_ARM_VCPU_PMU_V3_INIT for a specific vcpu.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `vcpu` - The VCPU file descriptor
|
||||
fn set_pmu_init(vcpu: &VcpuFd) -> Result<()> {
|
||||
let attribute = kvm_device_attr {
|
||||
group: KVM_ARM_VCPU_PMU_V3_CTRL,
|
||||
attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT),
|
||||
addr: 0,
|
||||
flags: 0,
|
||||
};
|
||||
vcpu.set_device_attr(&attribute)
|
||||
.map_err(PmuError::SetPmuInit)
|
||||
}
|
||||
|
||||
/// Check kvm pmu capability
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `vm` - The VM file descriptor
|
||||
fn check_kvm_pmu_cap(_vm: &VmFd) -> Result<()> {
|
||||
// TODO: check KVM_CAP_ARM_PMU_V3 capability before setting PMU
|
||||
// Cap for KVM_CAP_ARM_PMU_V3 isn't supported in kvm-ioctls upstream, so
|
||||
// leave a todo here for supporting this check in the future.
|
||||
// Interface: vm.check_extension(kvm_ioctls::Cap)
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check pmu feature
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `vcpu` - The VCPU file descriptor
|
||||
fn check_pmu_feature(vcpu: &VcpuFd) -> Result<()> {
|
||||
has_pmu_irq(vcpu)?;
|
||||
has_pmu_init(vcpu)
|
||||
}
|
||||
|
||||
/// Set pmu feature
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `vcpu` - The VCPU file descriptor
|
||||
fn set_pmu_feature(vcpu: &VcpuFd) -> Result<()> {
|
||||
set_pmu_irq(vcpu)?;
|
||||
set_pmu_init(vcpu)
|
||||
}
|
||||
|
||||
/// Initialize PMU in for vcpu
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `vm` - The VM file descriptor
|
||||
/// * `vcpu` - The VCPU file descriptor
|
||||
pub fn initialize_pmu(vm: &VmFd, vcpu: &VcpuFd) -> Result<()> {
|
||||
check_kvm_pmu_cap(vm)?;
|
||||
check_pmu_feature(vcpu)?;
|
||||
set_pmu_feature(vcpu)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use kvm_bindings::{kvm_vcpu_init, KVM_ARM_VCPU_PMU_V3, KVM_ARM_VCPU_PSCI_0_2};
|
||||
use kvm_ioctls::Kvm;
|
||||
|
||||
use super::*;
|
||||
use crate::gic::create_gic;
|
||||
|
||||
#[test]
|
||||
fn test_create_pmu() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
|
||||
assert!(create_gic(&vm, 1).is_ok());
|
||||
assert!(initialize_pmu(&vm, &vcpu).is_err());
|
||||
|
||||
if check_kvm_pmu_cap(&vm).is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut kvi: kvm_vcpu_init = kvm_vcpu_init::default();
|
||||
vm.get_preferred_target(&mut kvi)
|
||||
.expect("Cannot get preferred target");
|
||||
kvi.features[0] = 1 << KVM_ARM_VCPU_PSCI_0_2 | 1 << KVM_ARM_VCPU_PMU_V3;
|
||||
|
||||
assert!(vcpu.vcpu_init(&kvi).is_ok());
|
||||
assert!(initialize_pmu(&vm, &vcpu).is_ok());
|
||||
}
|
||||
}
|
||||
200
src/dragonball/src/dbs_arch/src/aarch64/regs.rs
Normal file
200
src/dragonball/src/dbs_arch/src/aarch64/regs.rs
Normal file
@@ -0,0 +1,200 @@
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
//! Constants and utilities for aarch64 CPU generic, system and model specific registers.
|
||||
|
||||
use std::{mem, result};
|
||||
|
||||
use kvm_bindings::*;
|
||||
use kvm_ioctls::VcpuFd;
|
||||
use memoffset::offset_of;
|
||||
use vmm_sys_util;
|
||||
|
||||
/// Errors thrown while setting aarch64 registers.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Failed to get core register (PC, PSTATE or general purpose ones).
|
||||
GetCoreRegister(kvm_ioctls::Error),
|
||||
/// Failed to set core register (PC, PSTATE or general purpose ones).
|
||||
SetCoreRegister(kvm_ioctls::Error),
|
||||
/// Failed to get a system register.
|
||||
GetSysRegister(kvm_ioctls::Error),
|
||||
/// Failed to get the register list.
|
||||
GetRegList(kvm_ioctls::Error),
|
||||
/// Failed to get a system register.
|
||||
SetRegister(kvm_ioctls::Error),
|
||||
/// Failed to init fam reglist
|
||||
FamRegister(vmm_sys_util::fam::Error),
|
||||
}
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
// PSR (Processor State Register) bits.
|
||||
// Taken from arch/arm64/include/uapi/asm/ptrace.h.
|
||||
const PSR_MODE_EL1h: u64 = 0x0000_0005;
|
||||
const PSR_F_BIT: u64 = 0x0000_0040;
|
||||
const PSR_I_BIT: u64 = 0x0000_0080;
|
||||
const PSR_A_BIT: u64 = 0x0000_0100;
|
||||
const PSR_D_BIT: u64 = 0x0000_0200;
|
||||
// Taken from arch/arm64/kvm/inject_fault.c.
|
||||
const PSTATE_FAULT_BITS_64: u64 = PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT;
|
||||
|
||||
// Following are macros that help with getting the ID of a aarch64 core register.
|
||||
// The core register are represented by the user_pt_regs structure. Look for it in
|
||||
// arch/arm64/include/uapi/asm/ptrace.h.
|
||||
|
||||
macro_rules! arm64_core_reg {
|
||||
($reg: tt) => {
|
||||
// As per `kvm_arm_copy_reg_indices`, the id of a core register can be obtained like this:
|
||||
// `const u64 core_reg = KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | i`, where i is obtained with:
|
||||
// `for (i = 0; i < sizeof(struct kvm_regs) / sizeof(__u32); i++) {`
|
||||
// We are using here `user_pt_regs` since this structure contains the core register and it is at
|
||||
// the start of `kvm_regs`.
|
||||
// struct kvm_regs {
|
||||
// struct user_pt_regs regs; /* sp = sp_el0 */
|
||||
//
|
||||
// __u64 sp_el1;
|
||||
// __u64 elr_el1;
|
||||
//
|
||||
// __u64 spsr[KVM_NR_SPSR];
|
||||
//
|
||||
// struct user_fpsimd_state fp_regs;
|
||||
//};
|
||||
// struct user_pt_regs {
|
||||
// __u64 regs[31];
|
||||
// __u64 sp;
|
||||
// __u64 pc;
|
||||
// __u64 pstate;
|
||||
//};
|
||||
// In our implementation we need: pc, pstate and user_pt_regs->regs[0].
|
||||
KVM_REG_ARM64 as u64
|
||||
| KVM_REG_SIZE_U64 as u64
|
||||
| u64::from(KVM_REG_ARM_CORE)
|
||||
| ((offset_of!(user_pt_regs, $reg) / mem::size_of::<u32>()) as u64)
|
||||
};
|
||||
}
|
||||
|
||||
// This macro computes the ID of a specific ARM64 system register similar to how
|
||||
// the kernel C macro does.
|
||||
// https://elixir.bootlin.com/linux/v4.20.17/source/arch/arm64/include/uapi/asm/kvm.h#L203
|
||||
macro_rules! arm64_sys_reg {
|
||||
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
|
||||
const $name: u64 = KVM_REG_ARM64 as u64
|
||||
| KVM_REG_SIZE_U64 as u64
|
||||
| KVM_REG_ARM64_SYSREG as u64
|
||||
| ((($op0 as u64) << KVM_REG_ARM64_SYSREG_OP0_SHIFT)
|
||||
& KVM_REG_ARM64_SYSREG_OP0_MASK as u64)
|
||||
| ((($op1 as u64) << KVM_REG_ARM64_SYSREG_OP1_SHIFT)
|
||||
& KVM_REG_ARM64_SYSREG_OP1_MASK as u64)
|
||||
| ((($crn as u64) << KVM_REG_ARM64_SYSREG_CRN_SHIFT)
|
||||
& KVM_REG_ARM64_SYSREG_CRN_MASK as u64)
|
||||
| ((($crm as u64) << KVM_REG_ARM64_SYSREG_CRM_SHIFT)
|
||||
& KVM_REG_ARM64_SYSREG_CRM_MASK as u64)
|
||||
| ((($op2 as u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT)
|
||||
& KVM_REG_ARM64_SYSREG_OP2_MASK as u64);
|
||||
};
|
||||
}
|
||||
|
||||
// Constant imported from the Linux kernel:
|
||||
// https://elixir.bootlin.com/linux/v4.20.17/source/arch/arm64/include/asm/sysreg.h#L135
|
||||
arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
|
||||
|
||||
/// Configure core registers for a given CPU.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
/// * `cpu_id` - Index of current vcpu.
|
||||
/// * `boot_ip` - Starting instruction pointer.
|
||||
/// * `mem` - Reserved DRAM for current VM.
|
||||
pub fn setup_regs(vcpu: &VcpuFd, cpu_id: u8, boot_ip: u64, fdt_address: u64) -> Result<()> {
|
||||
// Get the register index of the PSTATE (Processor State) register.
|
||||
vcpu.set_one_reg(arm64_core_reg!(pstate), PSTATE_FAULT_BITS_64 as u128)
|
||||
.map_err(Error::SetCoreRegister)?;
|
||||
|
||||
// Other vCPUs are powered off initially awaiting PSCI wakeup.
|
||||
if cpu_id == 0 {
|
||||
// Setting the PC (Processor Counter) to the current program address (kernel address).
|
||||
vcpu.set_one_reg(arm64_core_reg!(pc), boot_ip as u128)
|
||||
.map_err(Error::SetCoreRegister)?;
|
||||
|
||||
// Last mandatory thing to set -> the address pointing to the FDT (also called DTB).
|
||||
// "The device tree blob (dtb) must be placed on an 8-byte boundary and must
|
||||
// not exceed 2 megabytes in size." -> https://www.kernel.org/doc/Documentation/arm64/booting.txt.
|
||||
// We are choosing to place it the end of DRAM. See `get_fdt_addr`.
|
||||
vcpu.set_one_reg(arm64_core_reg!(regs), fdt_address as u128)
|
||||
.map_err(Error::SetCoreRegister)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Specifies whether a particular register is a system register or not.
|
||||
/// The kernel splits the registers on aarch64 in core registers and system registers.
|
||||
/// So, below we get the system registers by checking that they are not core registers.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `regid` - The index of the register we are checking.
|
||||
pub fn is_system_register(regid: u64) -> bool {
|
||||
if (regid & KVM_REG_ARM_COPROC_MASK as u64) == KVM_REG_ARM_CORE as u64 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let size = regid & KVM_REG_SIZE_MASK;
|
||||
if size != KVM_REG_SIZE_U32 && size != KVM_REG_SIZE_U64 {
|
||||
panic!("Unexpected register size for system register {}", size);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Read the MPIDR - Multiprocessor Affinity Register.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
pub fn read_mpidr(vcpu: &VcpuFd) -> Result<u64> {
|
||||
vcpu.get_one_reg(MPIDR_EL1)
|
||||
.map(|value| value as u64)
|
||||
.map_err(Error::GetSysRegister)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use kvm_ioctls::Kvm;
|
||||
|
||||
#[test]
|
||||
fn test_setup_regs() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
match setup_regs(&vcpu, 0, 0x0, crate::gic::GIC_REG_END_ADDRESS).unwrap_err() {
|
||||
Error::SetCoreRegister(ref e) => assert_eq!(e.errno(), libc::ENOEXEC),
|
||||
_ => panic!("Expected to receive Error::SetCoreRegister"),
|
||||
}
|
||||
let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default();
|
||||
vm.get_preferred_target(&mut kvi).unwrap();
|
||||
vcpu.vcpu_init(&kvi).unwrap();
|
||||
|
||||
assert!(setup_regs(&vcpu, 0, 0x0, crate::gic::GIC_REG_END_ADDRESS).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_mpidr() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default();
|
||||
vm.get_preferred_target(&mut kvi).unwrap();
|
||||
|
||||
// Must fail when vcpu is not initialized yet.
|
||||
assert!(read_mpidr(&vcpu).is_err());
|
||||
|
||||
vcpu.vcpu_init(&kvi).unwrap();
|
||||
assert_eq!(read_mpidr(&vcpu).unwrap(), 0x80000000);
|
||||
}
|
||||
}
|
||||
67
src/dragonball/src/dbs_arch/src/lib.rs
Normal file
67
src/dragonball/src/dbs_arch/src/lib.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2021-2022 Alibaba Cloud. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
//! CPU architecture specific constants, structures and utilities.
|
||||
//!
|
||||
//! This crate provides CPU architecture specific constants, structures and utilities to abstract
|
||||
//! away CPU architecture specific details from the Dragonball Secure Sandbox or other VMMs.
|
||||
//!
|
||||
//! # Supported CPU Architectures
|
||||
//! - **x86_64**: x86_64 (also known as x64, x86-64, AMD64, and Intel 64) is a 64-bit
|
||||
//! version of the x86 instruction set.
|
||||
//! - **ARM64**: AArch64 or ARM64 is the 64-bit extension of the ARM architecture.
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod x86_64;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use x86_64::*;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
mod aarch64;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub use aarch64::*;
|
||||
|
||||
/// Enum indicating vpmu feature level
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
pub enum VpmuFeatureLevel {
|
||||
/// Disabled means vpmu feature is off (by default)
|
||||
Disabled,
|
||||
/// LimitedlyEnabled means minimal vpmu counters are supported( only cycles and instructions )
|
||||
/// For aarch64, LimitedlyEnabled isn't supported currently. The ability will be implemented in the future.
|
||||
LimitedlyEnabled,
|
||||
/// FullyEnabled means all vpmu counters are supported
|
||||
FullyEnabled,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_debug_trait() {
|
||||
let level = VpmuFeatureLevel::Disabled;
|
||||
assert_eq!(format!("{level:#?}"), "Disabled");
|
||||
|
||||
let level = VpmuFeatureLevel::LimitedlyEnabled;
|
||||
assert_eq!(format!("{level:#?}"), "LimitedlyEnabled");
|
||||
|
||||
let level = VpmuFeatureLevel::FullyEnabled;
|
||||
assert_eq!(format!("{level:#?}"), "FullyEnabled");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq_trait() {
|
||||
let level = VpmuFeatureLevel::Disabled;
|
||||
assert!(level == VpmuFeatureLevel::Disabled);
|
||||
assert!(level != VpmuFeatureLevel::LimitedlyEnabled);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy_trait() {
|
||||
let level1 = VpmuFeatureLevel::Disabled;
|
||||
let level2 = level1;
|
||||
assert_eq!(level1, level2);
|
||||
}
|
||||
}
|
||||
599
src/dragonball/src/dbs_arch/src/x86_64/cpuid/bit_helper.rs
Normal file
599
src/dragonball/src/dbs_arch/src/x86_64/cpuid/bit_helper.rs
Normal file
@@ -0,0 +1,599 @@
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Helper to manipulate CPUID register content.
|
||||
|
||||
#![macro_use]
|
||||
|
||||
/// Structure representing a range of bits in a number.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// use dbs_arch::cpuid::bit_helper::*;
|
||||
///
|
||||
/// let range = BitRange {
|
||||
/// msb_index: 7,
|
||||
/// lsb_index: 3,
|
||||
/// };
|
||||
/// ```
|
||||
/// The BitRange specified above will represent the following part of the number 72:
|
||||
/// +-------------------------------------+---+---+---+---+---+---+---+---+---+---+
|
||||
/// | Base 2 Representation of the number | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
|
||||
/// +-------------------------------------+---+---+---+---+---+---+---+---+---+---+
|
||||
/// | bits indexes | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
/// +-------------------------------------+---+---+---+---+---+---+---+---+---+---+
|
||||
/// | BitRange | | | * | * | * | * | * | | | |
|
||||
/// +-------------------------------------+---+---+---+---+---+---+---+---+---+---+
|
||||
pub struct BitRange {
|
||||
/// most significant bit index
|
||||
pub msb_index: u32,
|
||||
/// least significant bit index
|
||||
pub lsb_index: u32,
|
||||
}
|
||||
|
||||
/// Trait containing helper methods for [`BitRange`](struct.BitRange.html)
|
||||
///
|
||||
/// The methods are needed for:
|
||||
/// - checking if the `BitRange` is valid for a type `T`
|
||||
/// - creating masks for a type `T`
|
||||
pub trait BitRangeExt<T> {
|
||||
/// Returns a value of type `T` that has all the bits in the specified bit range set to 1.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// use dbs_arch::cpuid::bit_helper::*;
|
||||
///
|
||||
/// let range = BitRange {
|
||||
/// msb_index: 7,
|
||||
/// lsb_index: 3,
|
||||
/// };
|
||||
/// println!("binary value: {:b}", range.get_mask());
|
||||
/// ```
|
||||
/// The code above will print:
|
||||
/// ```bash
|
||||
/// binary value: 11111000
|
||||
/// ```
|
||||
fn get_mask(&self) -> T;
|
||||
|
||||
/// Checks if the current BitRange is valid for type `T`.
|
||||
fn is_valid(&self) -> bool;
|
||||
|
||||
/// Asserts if `self.is_valid()` returns true.
|
||||
fn check(&self) {
|
||||
assert!(self.is_valid(), "Invalid BitRange");
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_U32_BIT_INDEX: u32 = 31;
|
||||
|
||||
impl BitRangeExt<u32> for BitRange {
|
||||
fn get_mask(&self) -> u32 {
|
||||
self.check();
|
||||
|
||||
((((1_u64) << (self.msb_index - self.lsb_index + 1)) - 1) << self.lsb_index) as u32
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> bool {
|
||||
self.msb_index >= self.lsb_index && self.msb_index <= MAX_U32_BIT_INDEX
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! bit_range {
|
||||
($msb_index:expr, $lsb_index:expr) => {
|
||||
BitRange {
|
||||
msb_index: $msb_index,
|
||||
lsb_index: $lsb_index,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Trait containing helper methods for bit operations.
|
||||
pub trait BitHelper {
|
||||
/// Reads the value of the bit at position `pos`
|
||||
fn read_bit(&self, pos: u32) -> bool;
|
||||
|
||||
/// Changes the value of the bit at position `pos` to `val`
|
||||
fn write_bit(&mut self, pos: u32, val: bool) -> &mut Self;
|
||||
|
||||
/// Reads the value stored within the specified range of bits
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// use dbs_arch::cpuid::bit_helper::*;
|
||||
///
|
||||
/// let val: u32 = 0b000010001000;
|
||||
/// let range = BitRange {
|
||||
/// msb_index: 7,
|
||||
/// lsb_index: 3,
|
||||
/// };
|
||||
/// println!("binary value: {:b}", val.read_bits_in_range(&range));
|
||||
/// ```
|
||||
/// The code above will print:
|
||||
/// ```bash
|
||||
/// binary value: 10001
|
||||
/// ```
|
||||
fn read_bits_in_range(&self, bit_range: &BitRange) -> Self;
|
||||
|
||||
/// Stores a value within the specified range of bits
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// use dbs_arch::cpuid::bit_helper::*;
|
||||
///
|
||||
/// let mut val: u32 = 0;
|
||||
/// let range = BitRange {
|
||||
/// msb_index: 7,
|
||||
/// lsb_index: 3,
|
||||
/// };
|
||||
/// val.write_bits_in_range(&range, 0b10001 as u32);
|
||||
/// println!("binary value: {:b}", val);
|
||||
/// ```
|
||||
/// The code above will print:
|
||||
/// ```bash
|
||||
/// binary value: 10001000
|
||||
/// ```
|
||||
fn write_bits_in_range(&mut self, bit_range: &BitRange, val: Self) -> &mut Self;
|
||||
}
|
||||
|
||||
impl BitHelper for u32 {
|
||||
fn read_bit(&self, pos: u32) -> bool {
|
||||
assert!(pos <= MAX_U32_BIT_INDEX, "Invalid pos");
|
||||
|
||||
(*self & (1 << pos)) > 0
|
||||
}
|
||||
|
||||
fn write_bit(&mut self, pos: u32, val: bool) -> &mut Self {
|
||||
assert!(pos <= MAX_U32_BIT_INDEX, "Invalid pos");
|
||||
|
||||
*self &= !(1 << pos);
|
||||
*self |= (val as u32) << pos;
|
||||
self
|
||||
}
|
||||
|
||||
fn read_bits_in_range(&self, range: &BitRange) -> Self {
|
||||
range.check();
|
||||
|
||||
(self & range.get_mask()) >> range.lsb_index
|
||||
}
|
||||
|
||||
fn write_bits_in_range(&mut self, range: &BitRange, val: Self) -> &mut Self {
|
||||
range.check();
|
||||
let mask = range.get_mask();
|
||||
let max_val = mask >> range.lsb_index;
|
||||
assert!(val <= max_val, "Invalid val");
|
||||
|
||||
*self &= !mask;
|
||||
*self |= val << range.lsb_index;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::cpuid::bit_helper::*;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_invalid_msb_index() {
|
||||
let range = BitRange {
|
||||
msb_index: 32,
|
||||
lsb_index: 2,
|
||||
};
|
||||
range.check();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_invalid_range() {
|
||||
let range = BitRange {
|
||||
msb_index: 10,
|
||||
lsb_index: 15,
|
||||
};
|
||||
range.check();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_invalid_write_bit() {
|
||||
// Set bit to 1
|
||||
let mut val: u32 = 0;
|
||||
val.write_bit(32, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_write_bit() {
|
||||
// Set bit to 1
|
||||
let mut val: u32 = 0;
|
||||
val.write_bit(5, true);
|
||||
assert!(val == 1 << 5);
|
||||
|
||||
// Set bit to 0
|
||||
val = 1 << 5;
|
||||
val.write_bit(5, false);
|
||||
assert!(val == 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_invalid_read_bit() {
|
||||
// Set bit to 1
|
||||
let val: u32 = 0;
|
||||
val.read_bit(32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_read_bit() {
|
||||
// Set bit to 1
|
||||
let val: u32 = 0b10_0000;
|
||||
assert!(val.read_bit(5));
|
||||
assert!(!val.read_bit(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chained_write_bit() {
|
||||
let mut val: u32 = 1 << 12;
|
||||
|
||||
val.write_bit(5, true)
|
||||
.write_bit(10, true)
|
||||
.write_bit(15, true)
|
||||
.write_bit(12, false);
|
||||
assert!(val == 1 << 5 | 1 << 10 | 1 << 15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_u32_mask_for_range() {
|
||||
// Test a couple of successive ranges
|
||||
assert!(
|
||||
BitRange {
|
||||
msb_index: 3,
|
||||
lsb_index: 2
|
||||
}
|
||||
.get_mask()
|
||||
== 0b1100
|
||||
);
|
||||
assert!(
|
||||
BitRange {
|
||||
msb_index: 4,
|
||||
lsb_index: 2
|
||||
}
|
||||
.get_mask()
|
||||
== 0b11100
|
||||
);
|
||||
assert!(
|
||||
BitRange {
|
||||
msb_index: 5,
|
||||
lsb_index: 2
|
||||
}
|
||||
.get_mask()
|
||||
== 0b11_1100
|
||||
);
|
||||
assert!(
|
||||
BitRange {
|
||||
msb_index: 6,
|
||||
lsb_index: 2
|
||||
}
|
||||
.get_mask()
|
||||
== 0b111_1100
|
||||
);
|
||||
assert!(
|
||||
BitRange {
|
||||
msb_index: 7,
|
||||
lsb_index: 2
|
||||
}
|
||||
.get_mask()
|
||||
== 0b1111_1100
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_invalid_read_bits() {
|
||||
let val: u32 = 30;
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 32,
|
||||
lsb_index: 2,
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_bits() {
|
||||
let val: u32 = 0b1000_0000_0000_0000_0011_0101_0001_0000;
|
||||
|
||||
// Test a couple of successive ranges
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 3,
|
||||
lsb_index: 2
|
||||
}) == 0b00
|
||||
);
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 4,
|
||||
lsb_index: 2
|
||||
}) == 0b100
|
||||
);
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 5,
|
||||
lsb_index: 2
|
||||
}) == 0b0100
|
||||
);
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 6,
|
||||
lsb_index: 2
|
||||
}) == 0b00100
|
||||
);
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 7,
|
||||
lsb_index: 2
|
||||
}) == 0b00_0100
|
||||
);
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 8,
|
||||
lsb_index: 2
|
||||
}) == 0b100_0100
|
||||
);
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 9,
|
||||
lsb_index: 2
|
||||
}) == 0b0100_0100
|
||||
);
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 10,
|
||||
lsb_index: 2
|
||||
}) == 0b1_0100_0100
|
||||
);
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 11,
|
||||
lsb_index: 2
|
||||
}) == 0b01_0100_0100
|
||||
);
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 12,
|
||||
lsb_index: 2
|
||||
}) == 0b101_0100_0100
|
||||
);
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 13,
|
||||
lsb_index: 2
|
||||
}) == 0b1101_0100_0100
|
||||
);
|
||||
|
||||
// Test max left and max right
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 31,
|
||||
lsb_index: 15
|
||||
}) == 0b1_0000_0000_0000_0000
|
||||
);
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 14,
|
||||
lsb_index: 0
|
||||
}) == 0b011_0101_0001_0000
|
||||
);
|
||||
assert!(
|
||||
val.read_bits_in_range(&BitRange {
|
||||
msb_index: 31,
|
||||
lsb_index: 0
|
||||
}) == 0b1000_0000_0000_0000_0011_0101_0001_0000
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_invalid_write_bits() {
|
||||
let mut val: u32 = 0;
|
||||
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 32,
|
||||
lsb_index: 2,
|
||||
},
|
||||
0b100,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_overflow_write_bits() {
|
||||
let mut val: u32 = 0;
|
||||
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 3,
|
||||
lsb_index: 2,
|
||||
},
|
||||
0b100,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_write_bits() {
|
||||
let mut val: u32 = 0;
|
||||
|
||||
// Test a couple of successive ranges
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 3,
|
||||
lsb_index: 2
|
||||
},
|
||||
0b00
|
||||
) == &0b0000
|
||||
);
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 4,
|
||||
lsb_index: 2
|
||||
},
|
||||
0b100
|
||||
) == &0b10000
|
||||
);
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 5,
|
||||
lsb_index: 2
|
||||
},
|
||||
0b0100
|
||||
) == &0b01_0000
|
||||
);
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 6,
|
||||
lsb_index: 2
|
||||
},
|
||||
0b0_0100
|
||||
) == &0b001_0000
|
||||
);
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 7,
|
||||
lsb_index: 2
|
||||
},
|
||||
0b00_0100
|
||||
) == &0b0001_0000
|
||||
);
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 8,
|
||||
lsb_index: 2
|
||||
},
|
||||
0b100_0100
|
||||
) == &0b1_0001_0000
|
||||
);
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 9,
|
||||
lsb_index: 2
|
||||
},
|
||||
0b0100_0100
|
||||
) == &0b01_0001_0000
|
||||
);
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 10,
|
||||
lsb_index: 2
|
||||
},
|
||||
0b1_0100_0100
|
||||
) == &0b101_0001_0000
|
||||
);
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 11,
|
||||
lsb_index: 2
|
||||
},
|
||||
0b01_0100_0100
|
||||
) == &0b0101_0001_0000
|
||||
);
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 12,
|
||||
lsb_index: 2
|
||||
},
|
||||
0b101_0100_0100
|
||||
) == &0b1_0101_0001_0000
|
||||
);
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 13,
|
||||
lsb_index: 2
|
||||
},
|
||||
0b1101_0100_0100
|
||||
) == &0b11_0101_0001_0000
|
||||
);
|
||||
|
||||
// Test max left and max right
|
||||
val = 0;
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 31,
|
||||
lsb_index: 15
|
||||
},
|
||||
0b1_0000_0000_0000_0000
|
||||
) == &0b1000_0000_0000_0000_0000_0000_0000_0000
|
||||
);
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 14,
|
||||
lsb_index: 0
|
||||
},
|
||||
0b011_0101_0001_0000
|
||||
) == &0b1000_0000_0000_0000_0011_0101_0001_0000
|
||||
);
|
||||
assert!(
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 31,
|
||||
lsb_index: 0
|
||||
},
|
||||
0b1000_0000_0000_0000_0011_0101_0001_0000
|
||||
) == &0b1000_0000_0000_0000_0011_0101_0001_0000
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chained_write_bits() {
|
||||
let mut val: u32 = 0;
|
||||
|
||||
// Test a couple of ranges
|
||||
val.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 4,
|
||||
lsb_index: 2,
|
||||
},
|
||||
0b100,
|
||||
)
|
||||
.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 12,
|
||||
lsb_index: 10,
|
||||
},
|
||||
0b110,
|
||||
)
|
||||
.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 24,
|
||||
lsb_index: 20,
|
||||
},
|
||||
0b10101,
|
||||
)
|
||||
.write_bits_in_range(
|
||||
&BitRange {
|
||||
msb_index: 31,
|
||||
lsb_index: 28,
|
||||
},
|
||||
0b1011,
|
||||
);
|
||||
|
||||
assert!(val == 0b1011_0001_0101_0000_0001_1000_0001_0000);
|
||||
}
|
||||
}
|
||||
462
src/dragonball/src/dbs_arch/src/x86_64/cpuid/brand_string.rs
Normal file
462
src/dragonball/src/dbs_arch/src/x86_64/cpuid/brand_string.rs
Normal file
@@ -0,0 +1,462 @@
|
||||
// Copyright 2021 Alibaba Cloud. All Rights Reserved.
|
||||
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::arch::x86_64::__cpuid as host_cpuid;
|
||||
use std::slice;
|
||||
|
||||
use crate::cpuid::common::{VENDOR_ID_AMD, VENDOR_ID_INTEL};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub enum Error {
|
||||
NotSupported,
|
||||
Overflow(String),
|
||||
}
|
||||
|
||||
/// Register designations used to get/set specific register values within the brand string buffer.
|
||||
pub enum Reg {
|
||||
Eax = 0,
|
||||
Ebx = 1,
|
||||
Ecx = 2,
|
||||
Edx = 3,
|
||||
}
|
||||
|
||||
const BRAND_STRING_INTEL: &[u8] = b"Intel(R) Xeon(R) Processor";
|
||||
const BRAND_STRING_AMD: &[u8] = b"AMD EPYC";
|
||||
|
||||
/// A CPUID brand string wrapper, providing some efficient manipulation primitives.
|
||||
///
|
||||
/// This is achieved by bypassing the `O(n)` indexing, heap allocation, and the unicode checks
|
||||
/// done by `std::string::String`.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct BrandString {
|
||||
/// Flattened buffer, holding an array of 32-bit register values.
|
||||
///
|
||||
/// It has the following layout:
|
||||
/// reg_buf[0] = leaf_0x80000002.Eax
|
||||
/// reg_buf[1] = leaf_0x80000002.Ebx
|
||||
/// reg_buf[2] = leaf_0x80000002.Ecx
|
||||
/// reg_buf[3] = leaf_0x80000002.Edx
|
||||
/// reg_buf[4] = leaf_0x80000003.Eax
|
||||
/// ...
|
||||
/// reg_buf[10] = leaf_0x80000004.Ecx
|
||||
/// reg_buf[11] = leaf_0x80000004.Edx
|
||||
/// When seen as a byte-array, this buffer holds the ASCII-encoded CPU brand string.
|
||||
reg_buf: [u32; BrandString::REG_BUF_SIZE],
|
||||
|
||||
/// Actual string length, in bytes.
|
||||
///
|
||||
/// E.g. For "Intel CPU", this would be `strlen("Intel CPU") == 9`.
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl BrandString {
|
||||
/// Register buffer size (in number of registers).
|
||||
///
|
||||
/// There are 3 leaves (0x800000002 through 0x80000004), each with 4 regs (Eax, Ebx, Ecx, Edx).
|
||||
const REG_BUF_SIZE: usize = 3 * 4;
|
||||
|
||||
/// Max Brand string length, in bytes (also in chars, since it is ASCII-encoded).
|
||||
///
|
||||
/// The string is NULL-terminated, so the max string length is actually one byte
|
||||
/// less than the buffer size in bytes
|
||||
const MAX_LEN: usize = Self::REG_BUF_SIZE * 4 - 1;
|
||||
|
||||
/// Creates an empty brand string (0-initialized)
|
||||
fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Generates the emulated brand string.
|
||||
///
|
||||
/// For Intel CPUs, the brand string we expose will be:
|
||||
/// "Intel(R) Xeon(R) Processor @ {host freq}"
|
||||
/// where {host freq} is the CPU frequency, as present in the
|
||||
/// host brand string (e.g. 4.01GHz).
|
||||
///
|
||||
/// For AMD CPUs, the brand string we expose will be AMD EPYC.
|
||||
///
|
||||
/// For other CPUs, we'll just expose an empty string.
|
||||
///
|
||||
/// This is safe because we know BRAND_STRING_INTEL and BRAND_STRING_AMD to hold valid data
|
||||
/// (allowed length and holding only valid ASCII chars).
|
||||
pub fn from_vendor_id(vendor_id: &[u8; 12]) -> Result<BrandString, Error> {
|
||||
let brand = match vendor_id {
|
||||
VENDOR_ID_INTEL => {
|
||||
let mut this = BrandString::from_bytes_unchecked(BRAND_STRING_INTEL);
|
||||
if let Ok(host_bstr) = BrandString::from_host_cpuid() {
|
||||
if let Some(freq) = host_bstr.find_freq() {
|
||||
this.push_bytes(b" @ ")?;
|
||||
this.push_bytes(freq)?;
|
||||
}
|
||||
}
|
||||
this
|
||||
}
|
||||
VENDOR_ID_AMD => BrandString::from_bytes_unchecked(BRAND_STRING_AMD),
|
||||
_ => BrandString::from_bytes_unchecked(b""),
|
||||
};
|
||||
|
||||
Ok(brand)
|
||||
}
|
||||
|
||||
/// Creates a brand string, initialized from the CPUID leaves 0x80000002 through 0x80000004
|
||||
/// of the host CPU.
|
||||
fn from_host_cpuid() -> Result<Self, Error> {
|
||||
let mut this = Self::new();
|
||||
let mut cpuid_regs = unsafe { host_cpuid(0x8000_0000) };
|
||||
|
||||
if cpuid_regs.eax < 0x8000_0004 {
|
||||
// Brand string not supported by the host CPU
|
||||
return Err(Error::NotSupported);
|
||||
}
|
||||
|
||||
for leaf in 0x8000_0002..=0x8000_0004 {
|
||||
cpuid_regs = unsafe { host_cpuid(leaf) };
|
||||
this.set_reg_for_leaf(leaf, Reg::Eax, cpuid_regs.eax);
|
||||
this.set_reg_for_leaf(leaf, Reg::Ebx, cpuid_regs.ebx);
|
||||
this.set_reg_for_leaf(leaf, Reg::Ecx, cpuid_regs.ecx);
|
||||
this.set_reg_for_leaf(leaf, Reg::Edx, cpuid_regs.edx);
|
||||
}
|
||||
|
||||
let mut len = Self::MAX_LEN;
|
||||
{
|
||||
let this_bytes = this.as_bytes();
|
||||
while this_bytes[len - 1] == 0 && len > 0 {
|
||||
len -= 1;
|
||||
}
|
||||
}
|
||||
this.len = len;
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
/// Creates a (custom) brand string, initialized from `src`.
|
||||
///
|
||||
/// No checks are performed on the length of `src` or its contents (`src` should be an
|
||||
/// ASCII-encoded string).
|
||||
#[inline]
|
||||
fn from_bytes_unchecked(src: &[u8]) -> Self {
|
||||
let mut this = Self::new();
|
||||
this.len = src.len();
|
||||
this.as_bytes_mut()[..src.len()].copy_from_slice(src);
|
||||
this
|
||||
}
|
||||
|
||||
/// Returns the given register value for the given CPUID leaf.
|
||||
///
|
||||
/// `leaf` must be between 0x80000002 and 0x80000004.
|
||||
#[inline]
|
||||
pub fn get_reg_for_leaf(&self, leaf: u32, reg: Reg) -> u32 {
|
||||
if (0x80000002u32..=0x80000004).contains(&leaf) {
|
||||
// It's ok not to validate parameters here, leaf and reg should
|
||||
// both be compile-time constants. If there's something wrong with them,
|
||||
// that's a programming error and we should panic anyway.
|
||||
self.reg_buf[(leaf - 0x8000_0002) as usize * 4 + reg as usize]
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the value for the given leaf/register pair.
|
||||
///
|
||||
/// `leaf` must be between 0x80000002 and 0x80000004.
|
||||
#[inline]
|
||||
fn set_reg_for_leaf(&mut self, leaf: u32, reg: Reg, val: u32) {
|
||||
// It's ok not to validate parameters here, leaf and reg should
|
||||
// both be compile-time constants. If there's something wrong with them,
|
||||
// that's a programming error and we should panic anyway.
|
||||
self.reg_buf[(leaf - 0x8000_0002) as usize * 4 + reg as usize] = val;
|
||||
}
|
||||
|
||||
/// Gets an immutable `u8` slice view into the brand string buffer.
|
||||
#[inline]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
// This is actually safe, because self.reg_buf has a fixed, known size,
|
||||
// and also there's no risk of misalignment, since we're downgrading
|
||||
// alignment constraints from dword to byte.
|
||||
unsafe { slice::from_raw_parts(self.reg_buf.as_ptr() as *const u8, Self::REG_BUF_SIZE * 4) }
|
||||
}
|
||||
|
||||
/// Gets a mutable `u8` slice view into the brand string buffer.
|
||||
#[inline]
|
||||
fn as_bytes_mut(&mut self) -> &mut [u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(self.reg_buf.as_mut_ptr() as *mut u8, Self::REG_BUF_SIZE * 4)
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts whether or not there is enough room to append `src` to the brand string.
|
||||
fn check_push(&mut self, src: &[u8]) -> bool {
|
||||
src.len() <= Self::MAX_LEN - self.len
|
||||
}
|
||||
|
||||
/// Appends `src` to the brand string if there is enough room to append it.
|
||||
fn push_bytes(&mut self, src: &[u8]) -> Result<(), Error> {
|
||||
if !self.check_push(src) {
|
||||
// No room to push all of src.
|
||||
return Err(Error::Overflow(
|
||||
"Appending to the brand string failed.".to_string(),
|
||||
));
|
||||
}
|
||||
let start = self.len;
|
||||
let count = src.len();
|
||||
self.len += count;
|
||||
self.as_bytes_mut()[start..(start + count)].copy_from_slice(src);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Searches the brand string for the CPU frequency data it may contain (e.g. 4.01GHz),
|
||||
/// and, if found, returns it as an `u8` slice.
|
||||
///
|
||||
/// Basically, we're implementing a search for this regex: "([0-9]+\.[0-9]+[MGT]Hz)".
|
||||
fn find_freq(&self) -> Option<&[u8]> {
|
||||
// The algorithm for matching the regular expression above is based
|
||||
// on a Moore machine, and 'stage' represents the current state of
|
||||
// the machine.
|
||||
enum Stages {
|
||||
/// Initial state, looking for a digit.
|
||||
Initial,
|
||||
/// Found integer part of the frequency.
|
||||
FoundFreqIntPart,
|
||||
/// Found the decimal point.
|
||||
FoundFreqDecimalPoint,
|
||||
/// Found the decimal part.
|
||||
FoundFreqDecimalPart,
|
||||
/// Found the unit size.
|
||||
FoundFreqUnitSize,
|
||||
/// Found the H in 'Hz'.
|
||||
FoundH,
|
||||
}
|
||||
|
||||
let mut freq_start = 0;
|
||||
let mut decimal_start = 0;
|
||||
|
||||
let mut stage = Stages::Initial;
|
||||
|
||||
for (i, &ch) in self.as_bytes().iter().enumerate() {
|
||||
match stage {
|
||||
Stages::Initial => {
|
||||
// Looking for one or more digits.
|
||||
if ch.is_ascii_digit() {
|
||||
freq_start = i;
|
||||
stage = Stages::FoundFreqIntPart;
|
||||
}
|
||||
}
|
||||
Stages::FoundFreqIntPart => {
|
||||
// Looking for a decimal point.
|
||||
if !ch.is_ascii_digit() {
|
||||
if ch == b'.' {
|
||||
stage = Stages::FoundFreqDecimalPoint;
|
||||
} else {
|
||||
stage = Stages::Initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
Stages::FoundFreqDecimalPoint => {
|
||||
// Looking for the decimal part.
|
||||
if ch.is_ascii_digit() {
|
||||
stage = Stages::FoundFreqDecimalPart;
|
||||
decimal_start = i;
|
||||
} else {
|
||||
stage = Stages::Initial;
|
||||
}
|
||||
}
|
||||
Stages::FoundFreqDecimalPart => {
|
||||
// Looking for the unit of measure.
|
||||
if !ch.is_ascii_digit() {
|
||||
if ch == b'.' {
|
||||
stage = Stages::FoundFreqDecimalPoint;
|
||||
freq_start = decimal_start;
|
||||
} else if ch == b'M' || ch == b'G' || ch == b'T' {
|
||||
stage = Stages::FoundFreqUnitSize;
|
||||
} else {
|
||||
stage = Stages::Initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
Stages::FoundFreqUnitSize => {
|
||||
// Looking for the 'H' in 'Hz'.
|
||||
if ch == b'H' {
|
||||
stage = Stages::FoundH;
|
||||
} else if ch.is_ascii_digit() {
|
||||
stage = Stages::FoundFreqIntPart;
|
||||
freq_start = i;
|
||||
} else {
|
||||
stage = Stages::Initial;
|
||||
}
|
||||
}
|
||||
Stages::FoundH => {
|
||||
// Looking for the 'z' in 'Hz'.
|
||||
// If found, we stop the search and return the slice.
|
||||
if ch == b'z' {
|
||||
let freq_end = i + 1;
|
||||
return Some(&self.as_bytes()[freq_start..freq_end]);
|
||||
} else if ch.is_ascii_digit() {
|
||||
stage = Stages::FoundFreqIntPart;
|
||||
freq_start = i;
|
||||
} else {
|
||||
stage = Stages::Initial;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::iter::repeat;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_brand_string() {
|
||||
#[inline]
|
||||
fn pack_u32(src: &[u8]) -> u32 {
|
||||
assert!(src.len() >= 4);
|
||||
u32::from(src[0])
|
||||
| (u32::from(src[1]) << 8)
|
||||
| (u32::from(src[2]) << 16)
|
||||
| (u32::from(src[3]) << 24)
|
||||
}
|
||||
|
||||
const TEST_STR: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
let mut bstr = BrandString::from_bytes_unchecked(TEST_STR);
|
||||
|
||||
// Test the immutable bitwise casts
|
||||
//
|
||||
{
|
||||
for i in 0_usize..=1_usize {
|
||||
let eax_offs = (4 * 4) * i;
|
||||
let ebx_offs = (4 * 4) * i + 4;
|
||||
let ecx_offs = (4 * 4) * i + 8;
|
||||
let edx_offs = (4 * 4) * i + 12;
|
||||
assert_eq!(
|
||||
bstr.get_reg_for_leaf(0x8000_0002 + i as u32, Reg::Eax),
|
||||
pack_u32(&TEST_STR[eax_offs..(eax_offs + 4)])
|
||||
);
|
||||
assert_eq!(
|
||||
bstr.get_reg_for_leaf(0x8000_0002 + i as u32, Reg::Ebx),
|
||||
pack_u32(&TEST_STR[ebx_offs..(ebx_offs + 4)])
|
||||
);
|
||||
assert_eq!(
|
||||
bstr.get_reg_for_leaf(0x8000_0002 + i as u32, Reg::Ecx),
|
||||
pack_u32(&TEST_STR[ecx_offs..(ecx_offs + 4)])
|
||||
);
|
||||
assert_eq!(
|
||||
bstr.get_reg_for_leaf(0x8000_0002 + i as u32, Reg::Edx),
|
||||
pack_u32(&TEST_STR[edx_offs..(edx_offs + 4)])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(bstr.get_reg_for_leaf(0x8000_0005, Reg::Eax), 0);
|
||||
|
||||
// Test find_freq() failure path
|
||||
//
|
||||
assert!(bstr.find_freq().is_none());
|
||||
|
||||
// Test mutable bitwise casting and finding the frequency substring
|
||||
//
|
||||
bstr.set_reg_for_leaf(0x8000_0003, Reg::Ebx, pack_u32(b"5.20"));
|
||||
bstr.set_reg_for_leaf(0x8000_0003, Reg::Ecx, pack_u32(b"GHz "));
|
||||
assert_eq!(bstr.find_freq().unwrap(), b"5.20GHz");
|
||||
|
||||
let _overflow: [u8; 50] = [b'a'; 50];
|
||||
|
||||
// Test BrandString::check_push()
|
||||
//
|
||||
bstr = BrandString::new();
|
||||
assert!(bstr.check_push(b"Hello"));
|
||||
bstr.push_bytes(b"Hello").unwrap();
|
||||
assert!(bstr.check_push(b", world!"));
|
||||
bstr.push_bytes(b", world!").unwrap();
|
||||
|
||||
assert!(!bstr.check_push(&_overflow));
|
||||
|
||||
// Test BrandString::push_bytes()
|
||||
//
|
||||
let actual_len = bstr.as_bytes().len();
|
||||
let mut old_bytes: Vec<u8> = repeat(0).take(actual_len).collect();
|
||||
old_bytes.copy_from_slice(bstr.as_bytes());
|
||||
assert_eq!(
|
||||
bstr.push_bytes(&_overflow),
|
||||
Err(Error::Overflow(
|
||||
"Appending to the brand string failed.".to_string()
|
||||
))
|
||||
);
|
||||
assert!(bstr.as_bytes().to_vec() == old_bytes);
|
||||
|
||||
// Test BrandString::from_host_cpuid() and get_reg_for_leaf()
|
||||
//
|
||||
match BrandString::from_host_cpuid() {
|
||||
Ok(bstr) => {
|
||||
for leaf in 0x8000_0002..=0x8000_0004_u32 {
|
||||
let host_regs = unsafe { host_cpuid(leaf) };
|
||||
assert_eq!(bstr.get_reg_for_leaf(leaf, Reg::Eax), host_regs.eax);
|
||||
assert_eq!(bstr.get_reg_for_leaf(leaf, Reg::Ebx), host_regs.ebx);
|
||||
assert_eq!(bstr.get_reg_for_leaf(leaf, Reg::Ecx), host_regs.ecx);
|
||||
assert_eq!(bstr.get_reg_for_leaf(leaf, Reg::Edx), host_regs.edx);
|
||||
}
|
||||
}
|
||||
Err(Error::NotSupported) => {
|
||||
// from_host_cpuid() should only fail if the host CPU doesn't support
|
||||
// CPUID leaves up to 0x80000004, so let's make sure that's what happened.
|
||||
let host_regs = unsafe { host_cpuid(0x8000_0000) };
|
||||
assert!(host_regs.eax < 0x8000_0004);
|
||||
}
|
||||
_ => panic!("This function should not return another type of error"),
|
||||
}
|
||||
|
||||
// Test BrandString::from_vendor_id()
|
||||
let bstr = BrandString::from_vendor_id(VENDOR_ID_INTEL).unwrap();
|
||||
assert!(bstr.as_bytes().starts_with(BRAND_STRING_INTEL));
|
||||
let bstr = BrandString::from_vendor_id(VENDOR_ID_AMD).unwrap();
|
||||
assert!(bstr.as_bytes().starts_with(BRAND_STRING_AMD));
|
||||
let bstr = BrandString::from_vendor_id(b"............").unwrap();
|
||||
assert!(bstr.as_bytes() == vec![b'\0'; 48].as_slice());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_freq_fails() {
|
||||
let bstr_thz = BrandString::from_bytes_unchecked(b"5.20THz");
|
||||
assert_eq!(bstr_thz.find_freq().unwrap(), b"5.20THz");
|
||||
|
||||
let bstr_unused_end = BrandString::from_bytes_unchecked(b"AAA5.20MHzXz");
|
||||
assert_eq!(bstr_unused_end.find_freq().unwrap(), b"5.20MHz");
|
||||
|
||||
let bstr_faulty_unit = BrandString::from_bytes_unchecked(b"5.20BHz ");
|
||||
assert!(bstr_faulty_unit.find_freq().is_none());
|
||||
|
||||
let short_bstr = BrandString::from_bytes_unchecked(b"z");
|
||||
assert!(short_bstr.find_freq().is_none());
|
||||
|
||||
let skip_from_unit = BrandString::from_bytes_unchecked(b"Mz");
|
||||
assert!(skip_from_unit.find_freq().is_none());
|
||||
|
||||
let short_bstr = BrandString::from_bytes_unchecked(b"Hz");
|
||||
assert!(short_bstr.find_freq().is_none());
|
||||
|
||||
let short_bstr = BrandString::from_bytes_unchecked(b"GHz");
|
||||
assert!(short_bstr.find_freq().is_none());
|
||||
|
||||
let multiple_points_bstr = BrandString::from_bytes_unchecked(b"50.5.20GHz");
|
||||
assert_eq!(multiple_points_bstr.find_freq().unwrap(), b"5.20GHz");
|
||||
|
||||
let no_decimal_bstr = BrandString::from_bytes_unchecked(b"5GHz");
|
||||
assert!(no_decimal_bstr.find_freq().is_none());
|
||||
|
||||
let interrupted_bstr = BrandString::from_bytes_unchecked(b"500.00M5.20GHz");
|
||||
assert_eq!(interrupted_bstr.find_freq().unwrap(), b"5.20GHz");
|
||||
|
||||
let split_bstr = BrandString::from_bytes_unchecked(b"5.30AMHz");
|
||||
assert!(split_bstr.find_freq().is_none());
|
||||
|
||||
let long_bstr = BrandString::from_bytes_unchecked(b"1.12bc5.30MaHz2.4.25THz");
|
||||
assert_eq!(long_bstr.find_freq().unwrap(), b"4.25THz");
|
||||
|
||||
let found_h_bstr = BrandString::from_bytes_unchecked(b"1.A5.2MH3.20GHx4.30GHz");
|
||||
assert_eq!(found_h_bstr.find_freq().unwrap(), b"4.30GHz");
|
||||
}
|
||||
}
|
||||
105
src/dragonball/src/dbs_arch/src/x86_64/cpuid/common.rs
Normal file
105
src/dragonball/src/dbs_arch/src/x86_64/cpuid/common.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::arch::x86_64::{CpuidResult, __cpuid_count, __get_cpuid_max};
|
||||
|
||||
use super::cpu_leaf::*;
|
||||
|
||||
pub(crate) const VENDOR_ID_INTEL: &[u8; 12] = b"GenuineIntel";
|
||||
pub(crate) const VENDOR_ID_AMD: &[u8; 12] = b"AuthenticAMD";
|
||||
pub(crate) const VENDOR_ID_HYGON: &[u8; 12] = b"HygonGenuine";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Error {
|
||||
InvalidParameters(String),
|
||||
NotSupported,
|
||||
}
|
||||
|
||||
/// Get CPUID value for (`function`, `count`).
|
||||
pub fn get_cpuid(function: u32, count: u32) -> Result<CpuidResult, Error> {
|
||||
#[cfg(target_env = "sgx")]
|
||||
{
|
||||
return Err(Error::NotSupported);
|
||||
}
|
||||
|
||||
// TODO: replace with validation based on `has_cpuid()` when it becomes stable:
|
||||
// https://doc.rust-lang.org/core/arch/x86/fn.has_cpuid.html
|
||||
// this is safe because the host supports the `cpuid` instruction
|
||||
let max_function = unsafe { __get_cpuid_max(function & leaf_0x80000000::LEAF_NUM).0 };
|
||||
if function > max_function {
|
||||
return Err(Error::InvalidParameters(format!(
|
||||
"Function not supported: 0x{function:x}",
|
||||
)));
|
||||
}
|
||||
|
||||
// this is safe because the host supports the `cpuid` instruction
|
||||
let entry = unsafe { __cpuid_count(function, count) };
|
||||
if entry.eax == 0 && entry.ebx == 0 && entry.ecx == 0 && entry.edx == 0 {
|
||||
return Err(Error::InvalidParameters(format!("Invalid count: {count}")));
|
||||
}
|
||||
|
||||
Ok(entry)
|
||||
}
|
||||
|
||||
/// Extracts the CPU vendor id from leaf 0x0.
|
||||
pub fn get_vendor_id() -> Result<[u8; 12], Error> {
|
||||
let vendor_entry = get_cpuid(0, 0)?;
|
||||
let bytes: [u8; 12] =
|
||||
unsafe { std::mem::transmute([vendor_entry.ebx, vendor_entry.edx, vendor_entry.ecx]) };
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
pub fn get_topoext_fn() -> u32 {
|
||||
let vendor_id = get_vendor_id();
|
||||
assert!(vendor_id.is_ok());
|
||||
let function = match &vendor_id.ok().unwrap() {
|
||||
VENDOR_ID_INTEL => leaf_0x4::LEAF_NUM,
|
||||
VENDOR_ID_AMD => leaf_0x8000001d::LEAF_NUM,
|
||||
_ => 0,
|
||||
};
|
||||
assert!(function != 0);
|
||||
|
||||
function
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cpu_id() {
|
||||
// get_cpu_id should work correctly here
|
||||
let topoext_fn = get_topoext_fn();
|
||||
|
||||
// check that get_cpuid works for valid parameters
|
||||
match get_cpuid(topoext_fn, 0) {
|
||||
Ok(topoext_entry) => {
|
||||
assert!(topoext_entry.eax != 0);
|
||||
}
|
||||
_ => panic!("Wrong behavior"),
|
||||
}
|
||||
|
||||
// check that get_cpuid returns correct error for invalid `function`
|
||||
match get_cpuid(0x9000_0000, 0) {
|
||||
Err(Error::InvalidParameters(s)) => {
|
||||
assert!(s == "Function not supported: 0x90000000");
|
||||
}
|
||||
_ => panic!("Wrong behavior"),
|
||||
}
|
||||
|
||||
// check that get_cpuid returns correct error for invalid `count`
|
||||
match get_cpuid(topoext_fn, 100) {
|
||||
Err(Error::InvalidParameters(s)) => {
|
||||
assert!(s == "Invalid count: 100");
|
||||
}
|
||||
_ => panic!("Wrong behavior"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_vendor_id() {
|
||||
let vendor_id = get_vendor_id().unwrap();
|
||||
assert!(matches!(&vendor_id, VENDOR_ID_INTEL | VENDOR_ID_AMD));
|
||||
}
|
||||
}
|
||||
439
src/dragonball/src/dbs_arch/src/x86_64/cpuid/cpu_leaf.rs
Normal file
439
src/dragonball/src/dbs_arch/src/x86_64/cpuid/cpu_leaf.rs
Normal file
@@ -0,0 +1,439 @@
|
||||
// Copyright 2021 Alibaba Cloud. All Rights Reserved.
|
||||
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
//! CPUID leaf registers constant values.
|
||||
|
||||
#![allow(unused)]
|
||||
pub mod leaf_0x0 {
|
||||
pub const LEAF_NUM: u32 = 0x0;
|
||||
}
|
||||
|
||||
pub mod leaf_0x1 {
|
||||
pub const LEAF_NUM: u32 = 0x1;
|
||||
|
||||
pub mod eax {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
pub const EXTENDED_FAMILY_ID_BITRANGE: BitRange = bit_range!(27, 20);
|
||||
pub const EXTENDED_PROCESSOR_MODEL_BITRANGE: BitRange = bit_range!(19, 16);
|
||||
pub const PROCESSOR_TYPE_BITRANGE: BitRange = bit_range!(13, 12);
|
||||
pub const PROCESSOR_FAMILY_BITRANGE: BitRange = bit_range!(11, 8);
|
||||
pub const PROCESSOR_MODEL_BITRANGE: BitRange = bit_range!(7, 4);
|
||||
pub const STEPPING_BITRANGE: BitRange = bit_range!(3, 0);
|
||||
}
|
||||
|
||||
pub mod ebx {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
// The bit-range containing the (fixed) default APIC ID.
|
||||
pub const APICID_BITRANGE: BitRange = bit_range!(31, 24);
|
||||
// The bit-range containing the logical processor count.
|
||||
pub const CPU_COUNT_BITRANGE: BitRange = bit_range!(23, 16);
|
||||
// The bit-range containing the number of bytes flushed when executing CLFLUSH.
|
||||
pub const CLFLUSH_SIZE_BITRANGE: BitRange = bit_range!(15, 8);
|
||||
}
|
||||
|
||||
pub mod ecx {
|
||||
// DTES64 = 64-bit debug store
|
||||
pub const DTES64_BITINDEX: u32 = 2;
|
||||
// MONITOR = Monitor/MWAIT
|
||||
pub const MONITOR_BITINDEX: u32 = 3;
|
||||
// CPL Qualified Debug Store
|
||||
pub const DS_CPL_SHIFT: u32 = 4;
|
||||
// 5 = VMX (Virtual Machine Extensions)
|
||||
// 6 = SMX (Safer Mode Extensions)
|
||||
// 7 = EIST (Enhanced Intel SpeedStep® technology)
|
||||
// TM2 = Thermal Monitor 2
|
||||
pub const TM2_BITINDEX: u32 = 8;
|
||||
// CNXT_ID = L1 Context ID (L1 data cache can be set to adaptive/shared mode)
|
||||
pub const CNXT_ID_BITINDEX: u32 = 10;
|
||||
// SDBG (cpu supports IA32_DEBUG_INTERFACE MSR for silicon debug)
|
||||
pub const SDBG_BITINDEX: u32 = 11;
|
||||
pub const FMA_BITINDEX: u32 = 12;
|
||||
// XTPR_UPDATE = xTPR Update Control
|
||||
pub const XTPR_UPDATE_BITINDEX: u32 = 14;
|
||||
// PDCM = Perfmon and Debug Capability
|
||||
pub const PDCM_BITINDEX: u32 = 15;
|
||||
// 18 = DCA Direct Cache Access (prefetch data from a memory mapped device)
|
||||
pub const MOVBE_BITINDEX: u32 = 22;
|
||||
pub const TSC_DEADLINE_TIMER_BITINDEX: u32 = 24;
|
||||
pub const OSXSAVE_BITINDEX: u32 = 27;
|
||||
// Cpu is running on a hypervisor.
|
||||
pub const HYPERVISOR_BITINDEX: u32 = 31;
|
||||
}
|
||||
|
||||
pub mod edx {
|
||||
pub const PSN_BITINDEX: u32 = 18; // Processor Serial Number
|
||||
pub const DS_BITINDEX: u32 = 21; // Debug Store.
|
||||
pub const ACPI_BITINDEX: u32 = 22; // Thermal Monitor and Software Controlled Clock Facilities.
|
||||
pub const SS_BITINDEX: u32 = 27; // Self Snoop
|
||||
pub const HTT_BITINDEX: u32 = 28; // Max APIC IDs reserved field is valid
|
||||
pub const TM_BITINDEX: u32 = 29; // Thermal Monitor.
|
||||
pub const PBE_BITINDEX: u32 = 31; // Pending Break Enable.
|
||||
}
|
||||
}
|
||||
|
||||
pub mod leaf_cache_parameters {
|
||||
pub mod eax {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
pub const CACHE_LEVEL_BITRANGE: BitRange = bit_range!(7, 5);
|
||||
pub const MAX_CPUS_PER_CORE_BITRANGE: BitRange = bit_range!(25, 14);
|
||||
}
|
||||
}
|
||||
|
||||
// Deterministic Cache Parameters Leaf
|
||||
pub mod leaf_0x4 {
|
||||
pub const LEAF_NUM: u32 = 0x4;
|
||||
|
||||
pub mod eax {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
// inherit eax from leaf_cache_parameters
|
||||
pub use crate::cpuid::cpu_leaf::leaf_cache_parameters::eax::*;
|
||||
|
||||
pub const MAX_CORES_PER_PACKAGE_BITRANGE: BitRange = bit_range!(31, 26);
|
||||
}
|
||||
}
|
||||
|
||||
// Thermal and Power Management Leaf
|
||||
#[allow(dead_code)]
|
||||
pub mod leaf_0x6 {
|
||||
pub const LEAF_NUM: u32 = 0x6;
|
||||
|
||||
pub mod eax {
|
||||
pub const TURBO_BOOST_BITINDEX: u32 = 1;
|
||||
}
|
||||
|
||||
pub mod ecx {
|
||||
// "Energy Performance Bias" bit.
|
||||
pub const EPB_BITINDEX: u32 = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Structured Extended Feature Flags Enumeration Leaf
|
||||
pub mod leaf_0x7 {
|
||||
pub const LEAF_NUM: u32 = 0x7;
|
||||
|
||||
pub mod index0 {
|
||||
pub mod ebx {
|
||||
// 1 = TSC_ADJUST
|
||||
pub const SGX_BITINDEX: u32 = 2;
|
||||
pub const BMI1_BITINDEX: u32 = 3;
|
||||
pub const HLE_BITINDEX: u32 = 4;
|
||||
pub const AVX2_BITINDEX: u32 = 5;
|
||||
// FPU Data Pointer updated only on x87 exceptions if 1.
|
||||
pub const FPDP_BITINDEX: u32 = 6;
|
||||
// 7 = SMEP (Supervisor-Mode Execution Prevention if 1)
|
||||
pub const BMI2_BITINDEX: u32 = 8;
|
||||
// 9 = Enhanced REP MOVSB/STOSB if 1
|
||||
// 10 = INVPCID
|
||||
pub const INVPCID_BITINDEX: u32 = 10;
|
||||
pub const RTM_BITINDEX: u32 = 11;
|
||||
// Intel® Resource Director Technology (Intel® RDT) Monitoring
|
||||
pub const RDT_M_BITINDEX: u32 = 12;
|
||||
// 13 = Deprecates FPU CS and FPU DS values if 1
|
||||
// Memory Protection Extensions
|
||||
pub const MPX_BITINDEX: u32 = 14;
|
||||
// RDT = Intel® Resource Director Technology
|
||||
pub const RDT_A_BITINDEX: u32 = 15;
|
||||
// AVX-512 Foundation instructions
|
||||
pub const AVX512F_BITINDEX: u32 = 16;
|
||||
// AVX-512 Doubleword and Quadword Instructions
|
||||
pub const AVX512DQ_BITINDEX: u32 = 17;
|
||||
pub const RDSEED_BITINDEX: u32 = 18;
|
||||
pub const ADX_BITINDEX: u32 = 19;
|
||||
// 20 = SMAP (Supervisor-Mode Access Prevention)
|
||||
// AVX512IFMA = AVX-512 Integer Fused Multiply-Add Instructions
|
||||
pub const AVX512IFMA_BITINDEX: u32 = 21;
|
||||
// 21 = PCOMMIT intruction
|
||||
// 22 reserved
|
||||
// CLFLUSHOPT (flushing multiple cache lines in parallel within a single logical processor)
|
||||
pub const CLFLUSHOPT_BITINDEX: u32 = 23;
|
||||
// CLWB = Cache Line Write Back
|
||||
pub const CLWB_BITINDEX: u32 = 24;
|
||||
// PT = Intel Processor Trace
|
||||
pub const PT_BITINDEX: u32 = 25;
|
||||
// AVX512PF = AVX512 Prefetch Instructions
|
||||
pub const AVX512PF_BITINDEX: u32 = 26;
|
||||
// AVX512ER = AVX-512 Exponential and Reciprocal Instructions
|
||||
pub const AVX512ER_BITINDEX: u32 = 27;
|
||||
// AVX512CD = AVX-512 Conflict Detection Instructions
|
||||
pub const AVX512CD_BITINDEX: u32 = 28;
|
||||
// Intel Secure Hash Algorithm Extensions
|
||||
pub const SHA_BITINDEX: u32 = 29;
|
||||
// AVX-512 Byte and Word Instructions
|
||||
pub const AVX512BW_BITINDEX: u32 = 30;
|
||||
// AVX-512 Vector Length Extensions
|
||||
pub const AVX512VL_BITINDEX: u32 = 31;
|
||||
}
|
||||
|
||||
pub mod ecx {
|
||||
// 0 = PREFETCHWT1 (move data closer to the processor in anticipation of future use)
|
||||
// AVX512_VBMI = AVX-512 Vector Byte Manipulation Instructions
|
||||
pub const AVX512_VBMI_BITINDEX: u32 = 1;
|
||||
// 2 = UMIP (User Mode Instruction Prevention)
|
||||
// PKU = Protection Keys for user-mode pages
|
||||
pub const PKU_BITINDEX: u32 = 3;
|
||||
// OSPKE = If 1, OS has set CR4.PKE to enable protection keys
|
||||
pub const OSPKE_BITINDEX: u32 = 4;
|
||||
// 5 = WAITPKG
|
||||
// 7-6 reserved
|
||||
// 8 = GFNI
|
||||
// 13-09 reserved
|
||||
// AVX512_VPOPCNTDQ = Vector population count instruction (Intel® Xeon Phi™ only.)
|
||||
pub const AVX512_VPOPCNTDQ_BITINDEX: u32 = 14;
|
||||
// 21 - 17 = The value of MAWAU used by the BNDLDX and BNDSTX instructions in 64-bit mode.
|
||||
// Read Processor ID
|
||||
pub const RDPID_BITINDEX: u32 = 22;
|
||||
// 23 - 29 reserved
|
||||
// SGX_LC = SGX Launch Configuration
|
||||
pub const SGX_LC_BITINDEX: u32 = 30;
|
||||
// 31 reserved
|
||||
}
|
||||
|
||||
pub mod edx {
|
||||
// AVX-512 4-register Neural Network Instructions
|
||||
pub const AVX512_4VNNIW_BITINDEX: u32 = 2;
|
||||
// AVX-512 4-register Multiply Accumulation Single precision
|
||||
pub const AVX512_4FMAPS_BITINDEX: u32 = 3;
|
||||
pub const ARCH_CAPABILITIES_BITINDEX: u32 = 29;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Architecture Performance Monitor Features
|
||||
pub mod leaf_0xa {
|
||||
pub const LEAF_NUM: u32 = 0xa;
|
||||
|
||||
pub mod eax {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
pub const PMC_VERSION_ID: BitRange = bit_range!(7, 0);
|
||||
pub const BIT_LEN_PMEVENT: BitRange = bit_range!(31, 24);
|
||||
}
|
||||
|
||||
pub mod ebx {
|
||||
pub const CORE_CYCLES_BITINDEX: u32 = 0;
|
||||
pub const INST_RETIRED_BITINDEX: u32 = 1;
|
||||
pub const REF_CYCLES_BITINDEX: u32 = 2;
|
||||
pub const LLC_REF_BITINDEX: u32 = 3;
|
||||
pub const LLC_MISSES_BITINDEX: u32 = 4;
|
||||
pub const BR_INST_RETIRED_BITINDEX: u32 = 5;
|
||||
pub const BR_MIS_RETIRED_BITINDEX: u32 = 6;
|
||||
}
|
||||
}
|
||||
|
||||
// Extended Topology Leaf
|
||||
pub mod leaf_0xb {
|
||||
pub const LEAF_NUM: u32 = 0xb;
|
||||
|
||||
pub const LEVEL_TYPE_THREAD: u32 = 1;
|
||||
pub const LEVEL_TYPE_CORE: u32 = 2;
|
||||
|
||||
pub mod eax {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
// The bit-range containing the number of bits to shift right the APIC ID in order to get
|
||||
// the next level APIC ID
|
||||
pub const APICID_BITRANGE: BitRange = bit_range!(4, 0);
|
||||
}
|
||||
|
||||
pub mod ebx {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
// The bit-range containing the number of factory-configured logical processors
|
||||
// at the current cache level
|
||||
pub const NUM_LOGICAL_PROCESSORS_BITRANGE: BitRange = bit_range!(15, 0);
|
||||
}
|
||||
|
||||
pub mod ecx {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
pub const LEVEL_TYPE_BITRANGE: BitRange = bit_range!(15, 8);
|
||||
pub const LEVEL_NUMBER_BITRANGE: BitRange = bit_range!(7, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Processor Extended State Enumeration Sub-leaves
|
||||
pub mod leaf_0xd {
|
||||
pub const LEAF_NUM: u32 = 0xd;
|
||||
|
||||
pub mod index0 {
|
||||
pub mod eax {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
pub const MPX_STATE_BITRANGE: BitRange = bit_range!(4, 3);
|
||||
pub const AVX512_STATE_BITRANGE: BitRange = bit_range!(7, 5);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod index1 {
|
||||
pub mod eax {
|
||||
pub const XSAVEC_SHIFT: u32 = 1;
|
||||
pub const XGETBV_SHIFT: u32 = 2;
|
||||
pub const XSAVES_SHIFT: u32 = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// V2 Extended Topology Enumeration Leaf
|
||||
pub mod leaf_0x1f {
|
||||
pub const LEAF_NUM: u32 = 0x1f;
|
||||
|
||||
pub const LEVEL_TYPE_THREAD: u32 = 1;
|
||||
pub const LEVEL_TYPE_CORE: u32 = 2;
|
||||
pub const LEVEL_TYPE_DIE: u32 = 5;
|
||||
|
||||
pub mod eax {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
// The bit-range containing the number of bits to shift right the APIC ID in order to get
|
||||
// the next level APIC ID
|
||||
pub const APICID_BITRANGE: BitRange = bit_range!(4, 0);
|
||||
}
|
||||
|
||||
pub mod ebx {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
// The bit-range containing the number of factory-configured logical processors
|
||||
// at the current cache level
|
||||
pub const NUM_LOGICAL_PROCESSORS_BITRANGE: BitRange = bit_range!(15, 0);
|
||||
}
|
||||
|
||||
pub mod ecx {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
pub const LEVEL_TYPE_BITRANGE: BitRange = bit_range!(15, 8);
|
||||
pub const LEVEL_NUMBER_BITRANGE: BitRange = bit_range!(7, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// KVM CPUID bits
|
||||
/// A guest running on a kvm host, can check some of its features using cpuid. This is not always guaranteed to work,
|
||||
/// since userspace can mask-out some, or even all KVM-related cpuid features before launching a guest.
|
||||
/// More information: https://docs.kernel.org/virt/kvm/x86/cpuid.html
|
||||
pub mod leaf_0x4000_0001 {
|
||||
pub const LEAF_NUM: u32 = 0x4000_0001;
|
||||
pub mod eax {
|
||||
/// kvmclock available at msrs 0x11 and 0x12
|
||||
pub const KVM_FEATURE_CLOCKSOURCE_BITINDEX: u32 = 0;
|
||||
/// not necessary to perform delays on PIO operations
|
||||
pub const KVM_FEATURE_NOP_IO_DELAY_BITINDEX: u32 = 1;
|
||||
/// deprecated
|
||||
pub const KVM_FEATURE_MMU_OP_BITINDEX: u32 = 2;
|
||||
/// kvmclock available at msrs 0x4b564d00 and 0x4b564d01
|
||||
pub const KVM_FEATURE_CLOCKSOURCE2_BITINDEX: u32 = 3;
|
||||
/// async pf can be enabled by writing to msr 0x4b564d02
|
||||
pub const KVM_FEATURE_ASYNC_PF_BITINDEX: u32 = 4;
|
||||
/// steal time can be enabled by writing to msr 0x4b564d03
|
||||
pub const KVM_FEATURE_STEAL_TIME_BITINDEX: u32 = 5;
|
||||
/// paravirtualized end of interrupt handler can be enabled by writing to msr 0x4b564d04
|
||||
pub const KVM_FEATURE_PV_EOI_BITINDEX: u32 = 6;
|
||||
/// guest checks this feature bit before enabling paravirtualized spinlock support
|
||||
pub const KVM_FEATURE_PV_UNHALT_BITINDEX: u32 = 7;
|
||||
/// guest checks this feature bit before enabling paravirtualized tlb flush
|
||||
pub const KVM_FEATURE_PV_TLB_FLUSH_BITINDEX: u32 = 9;
|
||||
/// paravirtualized async PF VM EXIT can be enabled by setting bit 2 when writing to msr 0x4b564d02
|
||||
pub const KVM_FEATURE_ASYNC_PF_VMEXIT_BITINDEX: u32 = 10;
|
||||
/// guest checks this feature bit before enabling paravirtualized send IPIs
|
||||
pub const KVM_FEATURE_PV_SEND_IPI_BITINDEX: u32 = 11;
|
||||
/// host-side polling on HLT can be disabled by writing to msr 0x4b564d05.
|
||||
pub const KVM_FEATURE_POLL_CONTROL_BITINDEX: u32 = 12;
|
||||
/// guest checks this feature bit before using paravirtualized sched yield.
|
||||
pub const KVM_FEATURE_PV_SCHED_YIELD_BITINDEX: u32 = 13;
|
||||
/// guest checks this feature bit before using the second async pf control msr 0x4b564d06 and async pf acknowledgment msr 0x4b564d07.
|
||||
pub const KVM_FEATURE_ASYNC_PF_INT_BITINDEX: u32 = 14;
|
||||
/// guest checks this feature bit before using extended destination ID bits in MSI address bits 11-5.
|
||||
pub const KVM_FEATURE_MSI_EXT_DEST_ID_BITINDEX: u32 = 15;
|
||||
/// guest checks this feature bit before using the map gpa range hypercall to notify the page state change
|
||||
pub const KVM_FEATURE_HC_MAP_GPA_RANGE_BITINDEX: u32 = 16;
|
||||
/// guest checks this feature bit before using MSR_KVM_MIGRATION_CONTROL
|
||||
pub const KVM_FEATURE_MIGRATION_CONTROL_BITINDEX: u32 = 17;
|
||||
/// host will warn if no guest-side per-cpu warps are expected in kvmclock
|
||||
pub const KVM_FEATURE_CLOCKSOURCE_STABLE_BITINDEX: u32 = 24;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod leaf_0x80000000 {
|
||||
pub const LEAF_NUM: u32 = 0x8000_0000;
|
||||
|
||||
pub mod eax {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
pub const LARGEST_EXTENDED_FN_BITRANGE: BitRange = bit_range!(31, 0);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod leaf_0x80000001 {
|
||||
pub const LEAF_NUM: u32 = 0x8000_0001;
|
||||
|
||||
pub mod ecx {
|
||||
pub const TOPOEXT_INDEX: u32 = 22;
|
||||
pub const PREFETCH_BITINDEX: u32 = 8; // 3DNow! PREFETCH/PREFETCHW instructions
|
||||
pub const LZCNT_BITINDEX: u32 = 5; // advanced bit manipulation
|
||||
}
|
||||
|
||||
pub mod edx {
|
||||
pub const PDPE1GB_BITINDEX: u32 = 26; // 1-GByte pages are available if 1.
|
||||
}
|
||||
}
|
||||
|
||||
pub mod leaf_0x80000008 {
|
||||
pub const LEAF_NUM: u32 = 0x8000_0008;
|
||||
|
||||
pub mod ecx {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
// The number of bits in the initial ApicId value that indicate thread ID within a package
|
||||
// Possible values:
|
||||
// 0-3 -> Reserved
|
||||
// 4 -> 1 Die, up to 16 threads
|
||||
// 5 -> 2 Die, up to 32 threads
|
||||
// 6 -> 3,4 Die, up to 64 threads
|
||||
pub const THREAD_ID_SIZE_BITRANGE: BitRange = bit_range!(15, 12);
|
||||
// The number of threads in the package - 1
|
||||
pub const NUM_THREADS_BITRANGE: BitRange = bit_range!(7, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Extended Cache Topology Leaf
|
||||
pub mod leaf_0x8000001d {
|
||||
pub const LEAF_NUM: u32 = 0x8000_001d;
|
||||
|
||||
// inherit eax from leaf_cache_parameters
|
||||
pub use crate::cpuid::cpu_leaf::leaf_cache_parameters::eax;
|
||||
}
|
||||
|
||||
// Extended APIC ID Leaf
|
||||
pub mod leaf_0x8000001e {
|
||||
pub const LEAF_NUM: u32 = 0x8000_001e;
|
||||
|
||||
pub mod eax {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
pub const EXTENDED_APIC_ID_BITRANGE: BitRange = bit_range!(31, 0);
|
||||
}
|
||||
|
||||
pub mod ebx {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
// The number of threads per core - 1
|
||||
pub const THREADS_PER_CORE_BITRANGE: BitRange = bit_range!(15, 8);
|
||||
pub const CORE_ID_BITRANGE: BitRange = bit_range!(7, 0);
|
||||
}
|
||||
|
||||
pub mod ecx {
|
||||
use crate::cpuid::bit_helper::BitRange;
|
||||
|
||||
// The number of nodes per processor. Possible values:
|
||||
// 0 -> 1 node per processor
|
||||
// 1 -> 2 nodes per processor
|
||||
// 2 -> Reserved
|
||||
// 3 -> 4 nodes per processor
|
||||
pub const NODES_PER_PROCESSOR_BITRANGE: BitRange = bit_range!(10, 8);
|
||||
pub const NODE_ID_BITRANGE: BitRange = bit_range!(7, 0);
|
||||
}
|
||||
}
|
||||
76
src/dragonball/src/dbs_arch/src/x86_64/cpuid/mod.rs
Normal file
76
src/dragonball/src/dbs_arch/src/x86_64/cpuid/mod.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2021 Alibaba Cloud. All Rights Reserved.
|
||||
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
//! Utilities for configuring the CPUID (CPU identification) for the guest microVM.
|
||||
|
||||
pub mod bit_helper;
|
||||
pub mod cpu_leaf;
|
||||
|
||||
mod brand_string;
|
||||
mod common;
|
||||
mod transformer;
|
||||
|
||||
pub use transformer::{Error, VmSpec};
|
||||
|
||||
pub use crate::VpmuFeatureLevel;
|
||||
|
||||
type CpuId = kvm_bindings::CpuId;
|
||||
type CpuIdEntry = kvm_bindings::kvm_cpuid_entry2;
|
||||
|
||||
/// Setup CPUID entries for the given vCPU.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `kvm_cpuid` - KVM related structure holding the relevant CPUID info.
|
||||
/// * `vm_spec` - The specifications of the VM.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// use dbs_arch::cpuid::{process_cpuid, VmSpec, VpmuFeatureLevel};
|
||||
/// use kvm_bindings::{CpuId, KVM_MAX_CPUID_ENTRIES};
|
||||
/// use kvm_ioctls::Kvm;
|
||||
///
|
||||
/// let kvm = Kvm::new().unwrap();
|
||||
/// let mut kvm_cpuid: CpuId = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap();
|
||||
///
|
||||
/// let vm_spec = VmSpec::new(0, 1, 1, 1, 1, VpmuFeatureLevel::Disabled).unwrap();
|
||||
///
|
||||
/// process_cpuid(&mut kvm_cpuid, &vm_spec).unwrap();
|
||||
///
|
||||
/// // Get expected `kvm_cpuid` entries.
|
||||
/// let entries = kvm_cpuid.as_mut_slice();
|
||||
/// ```
|
||||
pub fn process_cpuid(kvm_cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
use transformer::CpuidTransformer;
|
||||
|
||||
match vm_spec.cpu_vendor_id() {
|
||||
self::common::VENDOR_ID_INTEL => {
|
||||
self::transformer::intel::IntelCpuidTransformer::new().process_cpuid(kvm_cpuid, vm_spec)
|
||||
}
|
||||
self::common::VENDOR_ID_AMD => {
|
||||
self::transformer::amd::AmdCpuidTransformer::new().process_cpuid(kvm_cpuid, vm_spec)
|
||||
}
|
||||
self::common::VENDOR_ID_HYGON => {
|
||||
self::transformer::amd::AmdCpuidTransformer::new().process_cpuid(kvm_cpuid, vm_spec)
|
||||
}
|
||||
_ => Err(Error::CpuNotSupported),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_cpuid() {
|
||||
let mut cpuid = CpuId::new(0).unwrap();
|
||||
let vm_spec = VmSpec::new(0, 2, 1, 1, 1, VpmuFeatureLevel::Disabled).unwrap();
|
||||
|
||||
process_cpuid(&mut cpuid, &vm_spec).unwrap();
|
||||
}
|
||||
}
|
||||
412
src/dragonball/src/dbs_arch/src/x86_64/cpuid/transformer/amd.rs
Normal file
412
src/dragonball/src/dbs_arch/src/x86_64/cpuid/transformer/amd.rs
Normal file
@@ -0,0 +1,412 @@
|
||||
// Copyright 2021 Alibaba Cloud. All Rights Reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use kvm_bindings::KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
|
||||
|
||||
use super::super::bit_helper::BitHelper;
|
||||
use super::super::cpu_leaf;
|
||||
use super::*;
|
||||
|
||||
// Largest extended function. It has to be larger than 0x8000001d (Extended Cache Topology).
|
||||
const LARGEST_EXTENDED_FN: u32 = 0x8000_001f;
|
||||
// This value allows at most 256 logical threads within a package. But we currently only support
|
||||
// less than or equal to 254vcpus.
|
||||
// See also the documentation for leaf_0x80000008::ecx::THREAD_ID_SIZE_BITRANGE
|
||||
const THREAD_ID_MAX_SIZE: u32 = 8;
|
||||
// This value means there is 1 node per processor.
|
||||
// See also the documentation for leaf_0x8000001e::ecx::NODES_PER_PROCESSOR_BITRANGE.
|
||||
const NODES_PER_PROCESSOR: u32 = 0;
|
||||
|
||||
fn update_structured_extended_entry(
|
||||
entry: &mut CpuIdEntry,
|
||||
_vm_spec: &VmSpec,
|
||||
) -> Result<(), Error> {
|
||||
use cpu_leaf::leaf_0x7::index0::*;
|
||||
|
||||
// according to the EPYC PPR, only the leaf 0x7 with index 0 contains the
|
||||
// structured extended feature identifiers
|
||||
if entry.index == 0 {
|
||||
// KVM sets this bit no matter what but this feature is not supported by hardware
|
||||
entry.edx.write_bit(edx::ARCH_CAPABILITIES_BITINDEX, false);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_largest_extended_fn_entry(
|
||||
entry: &mut CpuIdEntry,
|
||||
_vm_spec: &VmSpec,
|
||||
) -> Result<(), Error> {
|
||||
use cpu_leaf::leaf_0x80000000::*;
|
||||
|
||||
// KVM sets the largest extended function to 0x80000000. Change it to 0x8000001f
|
||||
// Since we also use the leaf 0x8000001d (Extended Cache Topology).
|
||||
entry
|
||||
.eax
|
||||
.write_bits_in_range(&eax::LARGEST_EXTENDED_FN_BITRANGE, LARGEST_EXTENDED_FN);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_extended_feature_info_entry(
|
||||
entry: &mut CpuIdEntry,
|
||||
_vm_spec: &VmSpec,
|
||||
) -> Result<(), Error> {
|
||||
use crate::cpuid::cpu_leaf::leaf_0x80000001::*;
|
||||
|
||||
// set the Topology Extension bit since we use the Extended Cache Topology leaf
|
||||
entry.ecx.write_bit(ecx::TOPOEXT_INDEX, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_amd_features_entry(entry: &mut CpuIdEntry, vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
use cpu_leaf::leaf_0x80000008::*;
|
||||
|
||||
// We don't support more then 254 threads right now.
|
||||
entry
|
||||
.ecx
|
||||
.write_bits_in_range(&ecx::THREAD_ID_SIZE_BITRANGE, THREAD_ID_MAX_SIZE)
|
||||
.write_bits_in_range(&ecx::NUM_THREADS_BITRANGE, u32::from(vm_spec.cpu_count - 1));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_extended_cache_topology_entry(
|
||||
entry: &mut CpuIdEntry,
|
||||
vm_spec: &VmSpec,
|
||||
) -> Result<(), Error> {
|
||||
entry.flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
|
||||
|
||||
common::update_cache_parameters_entry(entry, vm_spec)
|
||||
}
|
||||
|
||||
fn update_extended_apic_id_entry(entry: &mut CpuIdEntry, vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
use crate::cpuid::cpu_leaf::leaf_0x8000001e::*;
|
||||
|
||||
let mut core_id = u32::from(vm_spec.cpu_id);
|
||||
// When hyper-threading is enabled each pair of 2 consecutive logical CPUs
|
||||
// will have the same core id since they represent 2 threads in the same core.
|
||||
// For Example:
|
||||
// logical CPU 0 -> core id: 0
|
||||
// logical CPU 1 -> core id: 0
|
||||
// logical CPU 2 -> core id: 1
|
||||
// logical CPU 3 -> core id: 1
|
||||
if vm_spec.threads_per_core == 2 {
|
||||
core_id /= 2;
|
||||
}
|
||||
|
||||
entry
|
||||
.eax
|
||||
// the Extended APIC ID is the id of the current logical CPU
|
||||
.write_bits_in_range(&eax::EXTENDED_APIC_ID_BITRANGE, u32::from(vm_spec.cpu_id));
|
||||
|
||||
entry
|
||||
.ebx
|
||||
.write_bits_in_range(&ebx::CORE_ID_BITRANGE, core_id)
|
||||
.write_bits_in_range(
|
||||
&ebx::THREADS_PER_CORE_BITRANGE,
|
||||
u32::from(vm_spec.threads_per_core - 1),
|
||||
);
|
||||
|
||||
entry
|
||||
.ecx
|
||||
.write_bits_in_range(&ecx::NODES_PER_PROCESSOR_BITRANGE, NODES_PER_PROCESSOR)
|
||||
// Put all the cpus in the same node.
|
||||
.write_bits_in_range(&ecx::NODE_ID_BITRANGE, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AmdCpuidTransformer {}
|
||||
|
||||
impl AmdCpuidTransformer {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuidTransformer for AmdCpuidTransformer {
|
||||
fn process_cpuid(&self, cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
use cpu_leaf::*;
|
||||
|
||||
common::use_host_cpuid_function(cpuid, leaf_0x0::LEAF_NUM, false)?;
|
||||
common::use_host_cpuid_function(cpuid, leaf_0x8000001d::LEAF_NUM, false)?;
|
||||
common::use_host_cpuid_function(cpuid, leaf_0x8000001d::LEAF_NUM, true)?;
|
||||
self.process_entries(cpuid, vm_spec)
|
||||
}
|
||||
|
||||
fn entry_transformer_fn(&self, entry: &mut CpuIdEntry) -> Option<EntryTransformerFn> {
|
||||
use cpu_leaf::*;
|
||||
|
||||
match entry.function {
|
||||
leaf_0x1::LEAF_NUM => Some(common::update_feature_info_entry),
|
||||
leaf_0x7::LEAF_NUM => Some(update_structured_extended_entry),
|
||||
leaf_0xb::LEAF_NUM => Some(common::update_extended_topology_entry),
|
||||
leaf_0x1f::LEAF_NUM => Some(common::update_extended_topology_v2_entry),
|
||||
leaf_0x80000000::LEAF_NUM => Some(update_largest_extended_fn_entry),
|
||||
leaf_0x80000001::LEAF_NUM => Some(update_extended_feature_info_entry),
|
||||
leaf_0x80000008::LEAF_NUM => Some(update_amd_features_entry),
|
||||
leaf_0x8000001d::LEAF_NUM => Some(update_extended_cache_topology_entry),
|
||||
leaf_0x8000001e::LEAF_NUM => Some(update_extended_apic_id_entry),
|
||||
0x8000_0002..=0x8000_0004 => Some(common::update_brand_string_entry),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_transformer_construct() {
|
||||
use cpu_leaf::leaf_0x7::index0::*;
|
||||
|
||||
let transformer = AmdCpuidTransformer::new();
|
||||
|
||||
let vm_spec =
|
||||
VmSpec::new(0, 1, 1, 1, 1, VpmuFeatureLevel::Disabled).expect("Error creating vm_spec");
|
||||
let mut cpuid = CpuId::from_entries(&[CpuIdEntry {
|
||||
function: cpu_leaf::leaf_0x7::LEAF_NUM,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: 0,
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: *(0_u32).write_bit(edx::ARCH_CAPABILITIES_BITINDEX, true),
|
||||
padding: [0, 0, 0],
|
||||
}])
|
||||
.unwrap();
|
||||
|
||||
transformer.process_cpuid(&mut cpuid, &vm_spec).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_structured_extended_entry() {
|
||||
use cpu_leaf::leaf_0x7::index0::*;
|
||||
|
||||
// Check that if index == 0 the entry is processed
|
||||
let vm_spec =
|
||||
VmSpec::new(0, 1, 1, 1, 1, VpmuFeatureLevel::Disabled).expect("Error creating vm_spec");
|
||||
let entry = &mut CpuIdEntry {
|
||||
function: cpu_leaf::leaf_0x7::LEAF_NUM,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: 0,
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: *(0_u32).write_bit(edx::ARCH_CAPABILITIES_BITINDEX, true),
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
assert!(update_structured_extended_entry(entry, &vm_spec).is_ok());
|
||||
assert!(!entry.edx.read_bit(edx::ARCH_CAPABILITIES_BITINDEX));
|
||||
|
||||
// Check that if index != 0 the entry is not processed
|
||||
entry.index = 1;
|
||||
entry.edx.write_bit(edx::ARCH_CAPABILITIES_BITINDEX, true);
|
||||
assert!(update_structured_extended_entry(entry, &vm_spec).is_ok());
|
||||
assert!(entry.edx.read_bit(edx::ARCH_CAPABILITIES_BITINDEX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_largest_extended_fn_entry() {
|
||||
use crate::cpuid::cpu_leaf::leaf_0x80000000::*;
|
||||
|
||||
let vm_spec =
|
||||
VmSpec::new(0, 1, 1, 1, 1, VpmuFeatureLevel::Disabled).expect("Error creating vm_spec");
|
||||
let entry = &mut CpuIdEntry {
|
||||
function: LEAF_NUM,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: 0,
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_largest_extended_fn_entry(entry, &vm_spec).is_ok());
|
||||
|
||||
assert_eq!(
|
||||
entry
|
||||
.eax
|
||||
.read_bits_in_range(&eax::LARGEST_EXTENDED_FN_BITRANGE),
|
||||
LARGEST_EXTENDED_FN
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_extended_feature_info_entry() {
|
||||
use crate::cpuid::cpu_leaf::leaf_0x80000001::*;
|
||||
|
||||
let vm_spec =
|
||||
VmSpec::new(0, 1, 1, 1, 1, VpmuFeatureLevel::Disabled).expect("Error creating vm_spec");
|
||||
let entry = &mut CpuIdEntry {
|
||||
function: LEAF_NUM,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: 0,
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_extended_feature_info_entry(entry, &vm_spec).is_ok());
|
||||
|
||||
assert!(entry.ecx.read_bit(ecx::TOPOEXT_INDEX));
|
||||
}
|
||||
|
||||
fn check_update_amd_features_entry(
|
||||
cpu_count: u8,
|
||||
threads_per_core: u8,
|
||||
cores_per_die: u8,
|
||||
dies_per_socket: u8,
|
||||
) {
|
||||
use crate::cpuid::cpu_leaf::leaf_0x80000008::*;
|
||||
|
||||
let vm_spec = VmSpec::new(
|
||||
0,
|
||||
cpu_count,
|
||||
threads_per_core,
|
||||
cores_per_die,
|
||||
dies_per_socket,
|
||||
VpmuFeatureLevel::Disabled,
|
||||
)
|
||||
.expect("Error creating vm_spec");
|
||||
let entry = &mut CpuIdEntry {
|
||||
function: LEAF_NUM,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: 0,
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_amd_features_entry(entry, &vm_spec).is_ok());
|
||||
|
||||
assert_eq!(
|
||||
entry.ecx.read_bits_in_range(&ecx::NUM_THREADS_BITRANGE),
|
||||
u32::from(cpu_count - 1)
|
||||
);
|
||||
assert_eq!(
|
||||
entry.ecx.read_bits_in_range(&ecx::THREAD_ID_SIZE_BITRANGE),
|
||||
THREAD_ID_MAX_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
fn check_update_extended_apic_id_entry(
|
||||
cpu_id: u8,
|
||||
cpu_count: u8,
|
||||
expected_core_id: u32,
|
||||
expected_threads_per_core: u32,
|
||||
threads_per_core: u8,
|
||||
cores_per_die: u8,
|
||||
dies_per_socket: u8,
|
||||
) {
|
||||
use crate::cpuid::cpu_leaf::leaf_0x8000001e::*;
|
||||
|
||||
let vm_spec = VmSpec::new(
|
||||
cpu_id,
|
||||
cpu_count,
|
||||
threads_per_core,
|
||||
cores_per_die,
|
||||
dies_per_socket,
|
||||
VpmuFeatureLevel::Disabled,
|
||||
)
|
||||
.expect("Error creating vm_spec");
|
||||
let entry = &mut CpuIdEntry {
|
||||
function: LEAF_NUM,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: 0,
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_extended_apic_id_entry(entry, &vm_spec).is_ok());
|
||||
|
||||
assert_eq!(
|
||||
entry
|
||||
.eax
|
||||
.read_bits_in_range(&eax::EXTENDED_APIC_ID_BITRANGE),
|
||||
u32::from(cpu_id)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
entry.ebx.read_bits_in_range(&ebx::CORE_ID_BITRANGE),
|
||||
expected_core_id
|
||||
);
|
||||
assert_eq!(
|
||||
entry
|
||||
.ebx
|
||||
.read_bits_in_range(&ebx::THREADS_PER_CORE_BITRANGE),
|
||||
expected_threads_per_core
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
entry
|
||||
.ecx
|
||||
.read_bits_in_range(&ecx::NODES_PER_PROCESSOR_BITRANGE),
|
||||
NODES_PER_PROCESSOR
|
||||
);
|
||||
assert_eq!(entry.ecx.read_bits_in_range(&ecx::NODE_ID_BITRANGE), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_extended_cache_topology_entry() {
|
||||
let vm_spec =
|
||||
VmSpec::new(0, 1, 1, 1, 1, VpmuFeatureLevel::Disabled).expect("Error creating vm_spec");
|
||||
let entry = &mut CpuIdEntry {
|
||||
function: cpu_leaf::leaf_0x8000001d::LEAF_NUM,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: 0,
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_extended_cache_topology_entry(entry, &vm_spec).is_ok());
|
||||
|
||||
assert_eq!(entry.flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_1vcpu_ht_off() {
|
||||
check_update_amd_features_entry(1, 1, 1, 1);
|
||||
|
||||
check_update_extended_apic_id_entry(0, 1, 0, 0, 1, 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_1vcpu_ht_on() {
|
||||
check_update_amd_features_entry(1, 2, 1, 1);
|
||||
|
||||
check_update_extended_apic_id_entry(0, 1, 0, 1, 2, 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2vcpu_ht_off() {
|
||||
check_update_amd_features_entry(2, 1, 2, 1);
|
||||
|
||||
check_update_extended_apic_id_entry(0, 2, 0, 0, 1, 2, 1);
|
||||
check_update_extended_apic_id_entry(1, 2, 1, 0, 1, 2, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2vcpu_ht_on() {
|
||||
check_update_amd_features_entry(2, 2, 2, 1);
|
||||
|
||||
check_update_extended_apic_id_entry(0, 2, 0, 1, 2, 2, 1);
|
||||
check_update_extended_apic_id_entry(1, 2, 0, 1, 2, 2, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,628 @@
|
||||
// Copyright 2019 Alibaba Cloud. All Rights Reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::super::bit_helper::BitHelper;
|
||||
use super::super::common::get_cpuid;
|
||||
use super::super::cpu_leaf;
|
||||
use super::*;
|
||||
|
||||
// constants for setting the fields of kvm_cpuid2 structures
|
||||
// CPUID bits in ebx, ecx, and edx.
|
||||
const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size.
|
||||
|
||||
/// Prepare content for CPUID standard level 0000_0001h: get processor type/family/model/stepping
|
||||
/// and feature flags
|
||||
pub fn update_feature_info_entry(entry: &mut CpuIdEntry, vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
use cpu_leaf::leaf_0x1::*;
|
||||
|
||||
// ECX bit 31 (HV): hypervisor present (and intercepting this bit, to advertise its presence)
|
||||
// ECX bit 24 (TSCD): local APIC supports one-shot operation using TSC deadline value
|
||||
entry
|
||||
.ecx
|
||||
.write_bit(ecx::TSC_DEADLINE_TIMER_BITINDEX, true)
|
||||
.write_bit(ecx::HYPERVISOR_BITINDEX, true);
|
||||
|
||||
// EBX bit 8-15: The CLFLUSH (8-byte) chunk count
|
||||
// EBX bit 16-23: The logical processor count
|
||||
// EBX bit 24-31: The (fixed) default APIC ID
|
||||
entry
|
||||
.ebx
|
||||
.write_bits_in_range(&ebx::APICID_BITRANGE, u32::from(vm_spec.cpu_id))
|
||||
.write_bits_in_range(&ebx::CLFLUSH_SIZE_BITRANGE, EBX_CLFLUSH_CACHELINE)
|
||||
.write_bits_in_range(
|
||||
&ebx::CPU_COUNT_BITRANGE,
|
||||
u32::from(vm_spec.threads_per_core * vm_spec.cores_per_die * vm_spec.dies_per_socket),
|
||||
);
|
||||
|
||||
// EDX bit 28: Hyper-Threading Technology, PAUSE. A value of 1 for HTT indicates the value in
|
||||
// CPUID.1.Ebx[23:16] (the Maximum number of addressable IDs for logical processors in this
|
||||
// package) is valid for the package
|
||||
entry
|
||||
.edx
|
||||
.write_bit(edx::HTT_BITINDEX, vm_spec.cpu_count > 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prepare content for CPUID standard level 0000_000Bh: get topology enumeration information.
|
||||
pub fn update_extended_topology_entry(
|
||||
entry: &mut CpuIdEntry,
|
||||
vm_spec: &VmSpec,
|
||||
) -> Result<(), Error> {
|
||||
use cpu_leaf::leaf_0xb::*;
|
||||
let thread_width = 8 - (vm_spec.threads_per_core - 1).leading_zeros();
|
||||
let core_width = (8 - (vm_spec.cores_per_die - 1).leading_zeros()) + thread_width;
|
||||
|
||||
// EAX bit 0-4: number of bits to shift x2APIC ID right to get unique topology ID of
|
||||
// next level type all logical processors with same next level ID share current level
|
||||
// EBX bit 0-15: number of enabled logical processors at this level
|
||||
// ECX bit 0-8: level number (same as input)
|
||||
// ECX bit 8-15: level type (00h=invalid, 01h=SMT, 02h=core, 03h...FFh=reserved)
|
||||
// EDX bits 0-31 contain x2APIC ID of current logical processor
|
||||
entry.eax = 0_u32;
|
||||
entry.ebx = 0_u32;
|
||||
entry.ecx = 0_u32;
|
||||
entry.edx = u32::from(vm_spec.cpu_id);
|
||||
|
||||
match entry.index {
|
||||
// Thread Level Topology; index = 0
|
||||
0 => {
|
||||
// To get the next level APIC ID, shift right with at most 1 because we have
|
||||
// maximum 2 hyperthreads per core that can be represented by 1 bit.
|
||||
entry
|
||||
.eax
|
||||
.write_bits_in_range(&eax::APICID_BITRANGE, thread_width);
|
||||
// When cpu_count == 1 or HT is disabled, there is 1 logical core at this level
|
||||
// Otherwise there are 2
|
||||
entry.ebx.write_bits_in_range(
|
||||
&ebx::NUM_LOGICAL_PROCESSORS_BITRANGE,
|
||||
vm_spec.threads_per_core as u32,
|
||||
);
|
||||
entry
|
||||
.ecx
|
||||
.write_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE, LEVEL_TYPE_THREAD);
|
||||
}
|
||||
|
||||
// Core Level Processor Topology; index = 1
|
||||
1 => {
|
||||
entry
|
||||
.eax
|
||||
.write_bits_in_range(&eax::APICID_BITRANGE, core_width);
|
||||
entry.ebx.write_bits_in_range(
|
||||
&ebx::NUM_LOGICAL_PROCESSORS_BITRANGE,
|
||||
u32::from(vm_spec.threads_per_core * vm_spec.cores_per_die),
|
||||
);
|
||||
entry
|
||||
.ecx
|
||||
.write_bits_in_range(&ecx::LEVEL_NUMBER_BITRANGE, entry.index);
|
||||
entry
|
||||
.ecx
|
||||
.write_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE, LEVEL_TYPE_CORE);
|
||||
}
|
||||
// Core Level Processor Topology; index >=2
|
||||
// No other levels available; This should already be set to correctly,
|
||||
// and it is added here as a "re-enforcement" in case we run on
|
||||
// different hardware
|
||||
level => {
|
||||
entry.ecx = level;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prepare content for Intel V2 Extended Topology Enumeration Leaf.
|
||||
///
|
||||
/// Leaf_0x1f is a superset of leaf_0xb. It gives extra information like die_per_socket.
|
||||
/// When CPUID executes with EAX set to 1FH, the processor returns information about extended
|
||||
/// topology enumeration data. Software must detect the presence of CPUID leaf 1FH by verifying
|
||||
/// - the highest leaf index supported by CPUID is >= 1FH
|
||||
/// - CPUID.1FH:EBX[15:0] reports a non-zero value
|
||||
/// If leaf_0x1f is not implemented in cpu used in host, guest OS should turn to leaf_0xb to
|
||||
/// determine the cpu topology.
|
||||
pub fn update_extended_topology_v2_entry(
|
||||
entry: &mut CpuIdEntry,
|
||||
vm_spec: &VmSpec,
|
||||
) -> Result<(), Error> {
|
||||
use cpu_leaf::leaf_0x1f::*;
|
||||
let thread_width = 8 - (vm_spec.threads_per_core - 1).leading_zeros();
|
||||
let core_width = (8 - (vm_spec.cores_per_die - 1).leading_zeros()) + thread_width;
|
||||
let die_width = (8 - (vm_spec.dies_per_socket - 1).leading_zeros()) + core_width;
|
||||
|
||||
// EAX bit 0-4: number of bits to shift x2APIC ID right to get unique topology ID of
|
||||
// next level type all logical processors with same next level ID share current level
|
||||
// EBX bit 0-15: number of enabled logical processors at this level
|
||||
// ECX bit 0-8: level number (same as input)
|
||||
// ECX bit 8-15: level type (00h=invalid, 01h=SMT, 02h=core, 05h=die, otherwise=reserved)
|
||||
// EDX bits 0-31 contain x2APIC ID of current logical processor
|
||||
entry.eax = 0_u32;
|
||||
entry.ebx = 0_u32;
|
||||
entry.ecx = 0_u32;
|
||||
entry.edx = u32::from(vm_spec.cpu_id);
|
||||
|
||||
match entry.index {
|
||||
// Thread Level Topology; index = 0
|
||||
0 => {
|
||||
// To get the next level APIC ID, shift right with at most 1 because we have
|
||||
// maximum 2 hyperthreads per core that can be represented by 1 bit.
|
||||
entry
|
||||
.eax
|
||||
.write_bits_in_range(&eax::APICID_BITRANGE, thread_width);
|
||||
// When cpu_count == 1 or HT is disabled, there is 1 logical core at this level
|
||||
// Otherwise there are 2
|
||||
entry.ebx.write_bits_in_range(
|
||||
&ebx::NUM_LOGICAL_PROCESSORS_BITRANGE,
|
||||
vm_spec.threads_per_core as u32,
|
||||
);
|
||||
entry
|
||||
.ecx
|
||||
.write_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE, LEVEL_TYPE_THREAD);
|
||||
}
|
||||
// Core Level Processor Topology; index = 1
|
||||
1 => {
|
||||
entry
|
||||
.eax
|
||||
.write_bits_in_range(&eax::APICID_BITRANGE, core_width);
|
||||
entry.ebx.write_bits_in_range(
|
||||
&ebx::NUM_LOGICAL_PROCESSORS_BITRANGE,
|
||||
u32::from(vm_spec.threads_per_core * vm_spec.cores_per_die),
|
||||
);
|
||||
entry
|
||||
.ecx
|
||||
.write_bits_in_range(&ecx::LEVEL_NUMBER_BITRANGE, entry.index);
|
||||
entry
|
||||
.ecx
|
||||
.write_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE, LEVEL_TYPE_CORE);
|
||||
}
|
||||
// Die Level Processor Topology; index = 5
|
||||
5 => {
|
||||
entry
|
||||
.eax
|
||||
.write_bits_in_range(&eax::APICID_BITRANGE, die_width);
|
||||
entry.ebx.write_bits_in_range(
|
||||
&ebx::NUM_LOGICAL_PROCESSORS_BITRANGE,
|
||||
u32::from(
|
||||
vm_spec.threads_per_core * vm_spec.cores_per_die * vm_spec.dies_per_socket,
|
||||
),
|
||||
);
|
||||
entry
|
||||
.ecx
|
||||
.write_bits_in_range(&ecx::LEVEL_NUMBER_BITRANGE, entry.index);
|
||||
entry
|
||||
.ecx
|
||||
.write_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE, LEVEL_TYPE_DIE);
|
||||
}
|
||||
level => {
|
||||
entry.ecx = level;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prepare content for CPUID standard level 8000_0002/3/4h: get processor name string.
|
||||
pub fn update_brand_string_entry(entry: &mut CpuIdEntry, vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
let brand_string = &vm_spec.brand_string;
|
||||
entry.eax = brand_string.get_reg_for_leaf(entry.function, BsReg::Eax);
|
||||
entry.ebx = brand_string.get_reg_for_leaf(entry.function, BsReg::Ebx);
|
||||
entry.ecx = brand_string.get_reg_for_leaf(entry.function, BsReg::Ecx);
|
||||
entry.edx = brand_string.get_reg_for_leaf(entry.function, BsReg::Edx);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prepare content for CPUID extended level 8000_001Dh: get cache configuration descriptors.
|
||||
pub fn update_cache_parameters_entry(
|
||||
entry: &mut CpuIdEntry,
|
||||
vm_spec: &VmSpec,
|
||||
) -> Result<(), Error> {
|
||||
use cpu_leaf::leaf_cache_parameters::*;
|
||||
|
||||
// EAX bit 14-25: cores per cache - 1
|
||||
|
||||
match entry.eax.read_bits_in_range(&eax::CACHE_LEVEL_BITRANGE) {
|
||||
// L1 & L2 Cache
|
||||
1 | 2 => {
|
||||
// The L1 & L2 cache is shared by at most 2 hyperthreads
|
||||
entry.eax.write_bits_in_range(
|
||||
&eax::MAX_CPUS_PER_CORE_BITRANGE,
|
||||
(vm_spec.cpu_count > 1 && vm_spec.threads_per_core == 2) as u32,
|
||||
);
|
||||
}
|
||||
// L3 Cache
|
||||
3 => {
|
||||
// The L3 cache is shared among all the logical threads
|
||||
entry.eax.write_bits_in_range(
|
||||
&eax::MAX_CPUS_PER_CORE_BITRANGE,
|
||||
u32::from(vm_spec.cpu_count - 1),
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Replaces the `cpuid` entries corresponding to `function` with the entries from the host's cpuid.
|
||||
pub fn use_host_cpuid_function(
|
||||
cpuid: &mut CpuId,
|
||||
function: u32,
|
||||
use_count: bool,
|
||||
) -> Result<(), Error> {
|
||||
// copy all the CpuId entries, except for the ones with the provided function
|
||||
cpuid.retain(|entry| entry.function != function);
|
||||
|
||||
// add all the host leaves with the provided function
|
||||
let mut count: u32 = 0;
|
||||
while let Ok(entry) = get_cpuid(function, count) {
|
||||
if count > 0 && !use_count {
|
||||
break;
|
||||
}
|
||||
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function,
|
||||
index: count,
|
||||
flags: 0,
|
||||
eax: entry.eax,
|
||||
ebx: entry.ebx,
|
||||
ecx: entry.ecx,
|
||||
edx: entry.edx,
|
||||
padding: [0, 0, 0],
|
||||
})
|
||||
.map_err(Error::FamError)?;
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use kvm_bindings::kvm_cpuid_entry2;
|
||||
|
||||
use super::*;
|
||||
use crate::cpuid::common::tests::get_topoext_fn;
|
||||
use crate::cpuid::cpu_leaf::leaf_0x1f::LEVEL_TYPE_DIE;
|
||||
use crate::cpuid::cpu_leaf::leaf_0xb::LEVEL_TYPE_CORE;
|
||||
use crate::cpuid::cpu_leaf::leaf_0xb::LEVEL_TYPE_THREAD;
|
||||
use crate::cpuid::transformer::VmSpec;
|
||||
|
||||
fn check_update_feature_info_entry(
|
||||
cpu_count: u8,
|
||||
expected_htt: bool,
|
||||
threads_per_core: u8,
|
||||
cores_per_die: u8,
|
||||
dies_per_socket: u8,
|
||||
) {
|
||||
use crate::cpuid::cpu_leaf::leaf_0x1::*;
|
||||
|
||||
let vm_spec = VmSpec::new(
|
||||
0,
|
||||
cpu_count,
|
||||
threads_per_core,
|
||||
cores_per_die,
|
||||
dies_per_socket,
|
||||
VpmuFeatureLevel::Disabled,
|
||||
)
|
||||
.expect("Error creating vm_spec");
|
||||
let entry = &mut kvm_cpuid_entry2 {
|
||||
function: 0x0,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: 0,
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_feature_info_entry(entry, &vm_spec).is_ok());
|
||||
|
||||
assert!(entry.edx.read_bit(edx::HTT_BITINDEX) == expected_htt);
|
||||
assert!(entry.ecx.read_bit(ecx::TSC_DEADLINE_TIMER_BITINDEX));
|
||||
}
|
||||
|
||||
fn check_update_cache_parameters_entry(
|
||||
cpu_count: u8,
|
||||
cache_level: u32,
|
||||
expected_max_cpus_per_core: u32,
|
||||
threads_per_core: u8,
|
||||
cores_per_die: u8,
|
||||
dies_per_socket: u8,
|
||||
) {
|
||||
use crate::cpuid::cpu_leaf::leaf_cache_parameters::*;
|
||||
|
||||
let vm_spec = VmSpec::new(
|
||||
0,
|
||||
cpu_count,
|
||||
threads_per_core,
|
||||
cores_per_die,
|
||||
dies_per_socket,
|
||||
VpmuFeatureLevel::Disabled,
|
||||
)
|
||||
.expect("Error creating vm_spec");
|
||||
let entry = &mut kvm_cpuid_entry2 {
|
||||
function: 0x0,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: *(0_u32).write_bits_in_range(&eax::CACHE_LEVEL_BITRANGE, cache_level),
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_cache_parameters_entry(entry, &vm_spec).is_ok());
|
||||
|
||||
assert!(
|
||||
entry
|
||||
.eax
|
||||
.read_bits_in_range(&eax::MAX_CPUS_PER_CORE_BITRANGE)
|
||||
== expected_max_cpus_per_core
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn check_update_extended_topology_entry(
|
||||
cpu_count: u8,
|
||||
index: u32,
|
||||
expected_apicid_shift_bit: u32,
|
||||
expected_num_logical_processors: u32,
|
||||
expected_level_type: u32,
|
||||
threads_per_core: u8,
|
||||
cores_per_die: u8,
|
||||
dies_per_socket: u8,
|
||||
) {
|
||||
use crate::cpuid::cpu_leaf::leaf_0xb::*;
|
||||
|
||||
let vm_spec = VmSpec::new(
|
||||
0,
|
||||
cpu_count,
|
||||
threads_per_core,
|
||||
cores_per_die,
|
||||
dies_per_socket,
|
||||
VpmuFeatureLevel::Disabled,
|
||||
)
|
||||
.expect("Error creating vm_spec");
|
||||
let entry = &mut kvm_cpuid_entry2 {
|
||||
function: 0x0,
|
||||
index,
|
||||
flags: 0,
|
||||
eax: 0,
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_extended_topology_entry(entry, &vm_spec).is_ok());
|
||||
|
||||
assert!(entry.eax.read_bits_in_range(&eax::APICID_BITRANGE) == expected_apicid_shift_bit);
|
||||
assert!(
|
||||
entry
|
||||
.ebx
|
||||
.read_bits_in_range(&ebx::NUM_LOGICAL_PROCESSORS_BITRANGE)
|
||||
== expected_num_logical_processors
|
||||
);
|
||||
assert!(entry.ecx.read_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE) == expected_level_type);
|
||||
assert!(entry.ecx.read_bits_in_range(&ecx::LEVEL_NUMBER_BITRANGE) == index);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn check_update_extended_topology_v2_entry(
|
||||
cpu_count: u8,
|
||||
index: u32,
|
||||
expected_apicid_shift_bit: u32,
|
||||
expected_num_logical_processors: u32,
|
||||
expected_level_type: u32,
|
||||
threads_per_core: u8,
|
||||
cores_per_die: u8,
|
||||
dies_per_socket: u8,
|
||||
) {
|
||||
use crate::cpuid::cpu_leaf::leaf_0x1f::*;
|
||||
|
||||
let vm_spec = VmSpec::new(
|
||||
0,
|
||||
cpu_count,
|
||||
threads_per_core,
|
||||
cores_per_die,
|
||||
dies_per_socket,
|
||||
VpmuFeatureLevel::Disabled,
|
||||
)
|
||||
.expect("Error creating vm_spec");
|
||||
let entry = &mut kvm_cpuid_entry2 {
|
||||
function: 0x0,
|
||||
index,
|
||||
flags: 0,
|
||||
eax: 0,
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_extended_topology_v2_entry(entry, &vm_spec).is_ok());
|
||||
|
||||
assert!(entry.eax.read_bits_in_range(&eax::APICID_BITRANGE) == expected_apicid_shift_bit);
|
||||
assert!(
|
||||
entry
|
||||
.ebx
|
||||
.read_bits_in_range(&ebx::NUM_LOGICAL_PROCESSORS_BITRANGE)
|
||||
== expected_num_logical_processors
|
||||
);
|
||||
assert!(entry.ecx.read_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE) == expected_level_type);
|
||||
assert!(entry.ecx.read_bits_in_range(&ecx::LEVEL_NUMBER_BITRANGE) == index);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_1vcpu_ht_off() {
|
||||
check_update_feature_info_entry(1, false, 1, 1, 1);
|
||||
|
||||
// test update_deterministic_cache_entry
|
||||
// test L1
|
||||
check_update_cache_parameters_entry(1, 1, 0, 1, 1, 1);
|
||||
// test L2
|
||||
check_update_cache_parameters_entry(1, 2, 0, 1, 1, 1);
|
||||
// test L3
|
||||
check_update_cache_parameters_entry(1, 3, 0, 1, 1, 1);
|
||||
|
||||
// test update_extended_topology_entry
|
||||
// index 0
|
||||
check_update_extended_topology_entry(1, 0, 0, 1, LEVEL_TYPE_THREAD, 1, 1, 1);
|
||||
check_update_extended_topology_v2_entry(1, 0, 0, 1, LEVEL_TYPE_THREAD, 1, 1, 1);
|
||||
// index 1
|
||||
check_update_extended_topology_entry(1, 1, 0, 1, LEVEL_TYPE_CORE, 1, 1, 1);
|
||||
check_update_extended_topology_v2_entry(1, 1, 0, 1, LEVEL_TYPE_CORE, 1, 1, 1);
|
||||
// index 5
|
||||
check_update_extended_topology_v2_entry(1, 5, 0, 1, LEVEL_TYPE_DIE, 1, 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_1vcpu_ht_on() {
|
||||
check_update_feature_info_entry(1, false, 2, 1, 1);
|
||||
|
||||
// test update_deterministic_cache_entry
|
||||
// test L1
|
||||
check_update_cache_parameters_entry(1, 1, 0, 2, 1, 1);
|
||||
// test L2
|
||||
check_update_cache_parameters_entry(1, 2, 0, 2, 1, 1);
|
||||
// test L3
|
||||
check_update_cache_parameters_entry(1, 3, 0, 2, 1, 1);
|
||||
|
||||
// test update_extended_topology_entry
|
||||
// index 0
|
||||
check_update_extended_topology_entry(1, 0, 1, 2, LEVEL_TYPE_THREAD, 2, 1, 1);
|
||||
check_update_extended_topology_v2_entry(1, 0, 1, 2, LEVEL_TYPE_THREAD, 2, 1, 1);
|
||||
// index 1
|
||||
check_update_extended_topology_entry(1, 1, 1, 2, LEVEL_TYPE_CORE, 2, 1, 1);
|
||||
check_update_extended_topology_v2_entry(1, 1, 1, 2, LEVEL_TYPE_CORE, 2, 1, 1);
|
||||
// index 5
|
||||
check_update_extended_topology_v2_entry(1, 5, 1, 2, LEVEL_TYPE_DIE, 2, 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2vcpu_ht_off() {
|
||||
check_update_feature_info_entry(2, true, 1, 2, 1);
|
||||
|
||||
// test update_deterministic_cache_entry
|
||||
// test L1
|
||||
check_update_cache_parameters_entry(2, 1, 0, 1, 2, 1);
|
||||
// test L2
|
||||
check_update_cache_parameters_entry(2, 2, 0, 1, 2, 1);
|
||||
// test L3
|
||||
check_update_cache_parameters_entry(2, 3, 1, 1, 2, 1);
|
||||
|
||||
// test update_extended_topology_entry
|
||||
// index 0
|
||||
check_update_extended_topology_entry(2, 0, 0, 1, LEVEL_TYPE_THREAD, 1, 2, 1);
|
||||
check_update_extended_topology_v2_entry(2, 0, 0, 1, LEVEL_TYPE_THREAD, 1, 2, 1);
|
||||
// index 1
|
||||
check_update_extended_topology_entry(2, 1, 1, 2, LEVEL_TYPE_CORE, 1, 2, 1);
|
||||
check_update_extended_topology_v2_entry(2, 1, 1, 2, LEVEL_TYPE_CORE, 1, 2, 1);
|
||||
// index 5
|
||||
check_update_extended_topology_v2_entry(2, 5, 1, 2, LEVEL_TYPE_DIE, 1, 2, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2vcpu_ht_on() {
|
||||
check_update_feature_info_entry(2, true, 2, 2, 1);
|
||||
|
||||
// test update_deterministic_cache_entry
|
||||
// test L1
|
||||
check_update_cache_parameters_entry(2, 1, 1, 2, 2, 1);
|
||||
// test L2
|
||||
check_update_cache_parameters_entry(2, 2, 1, 2, 2, 1);
|
||||
// test L3
|
||||
check_update_cache_parameters_entry(2, 3, 1, 2, 2, 1);
|
||||
|
||||
// test update_extended_topology_entry
|
||||
// index 0
|
||||
check_update_extended_topology_entry(2, 0, 1, 2, LEVEL_TYPE_THREAD, 2, 2, 1);
|
||||
check_update_extended_topology_v2_entry(2, 0, 1, 2, LEVEL_TYPE_THREAD, 2, 2, 1);
|
||||
// index 1
|
||||
check_update_extended_topology_entry(2, 1, 2, 4, LEVEL_TYPE_CORE, 2, 2, 1);
|
||||
check_update_extended_topology_v2_entry(2, 1, 2, 4, LEVEL_TYPE_CORE, 2, 2, 1);
|
||||
// index 5
|
||||
check_update_extended_topology_v2_entry(2, 5, 2, 4, LEVEL_TYPE_DIE, 2, 2, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2dies_2vcpu_ht_off() {
|
||||
// test update_extended_topology_entry
|
||||
// index 0
|
||||
check_update_extended_topology_entry(2, 0, 0, 1, LEVEL_TYPE_THREAD, 1, 1, 2);
|
||||
check_update_extended_topology_v2_entry(2, 0, 0, 1, LEVEL_TYPE_THREAD, 1, 1, 2);
|
||||
// index 1
|
||||
check_update_extended_topology_entry(2, 1, 0, 1, LEVEL_TYPE_CORE, 1, 1, 2);
|
||||
check_update_extended_topology_v2_entry(2, 1, 0, 1, LEVEL_TYPE_CORE, 1, 1, 2);
|
||||
// index 5
|
||||
check_update_extended_topology_v2_entry(2, 5, 1, 2, LEVEL_TYPE_DIE, 1, 1, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2dies_4vcpu_ht_on() {
|
||||
// test update_extended_topology_entry
|
||||
// index 0
|
||||
check_update_extended_topology_entry(4, 0, 1, 2, LEVEL_TYPE_THREAD, 2, 1, 2);
|
||||
check_update_extended_topology_v2_entry(4, 0, 1, 2, LEVEL_TYPE_THREAD, 2, 1, 2);
|
||||
// index 1
|
||||
check_update_extended_topology_entry(4, 1, 1, 2, LEVEL_TYPE_CORE, 2, 1, 2);
|
||||
check_update_extended_topology_v2_entry(4, 1, 1, 2, LEVEL_TYPE_CORE, 2, 1, 2);
|
||||
// index 5
|
||||
check_update_extended_topology_v2_entry(4, 5, 2, 4, LEVEL_TYPE_DIE, 2, 1, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
fn test_use_host_cpuid_function_with_count() {
|
||||
// try to emulate the extended cache topology leaves
|
||||
let topoext_fn = get_topoext_fn();
|
||||
|
||||
// check that it behaves correctly for TOPOEXT function
|
||||
let mut cpuid = CpuId::new(1).unwrap();
|
||||
cpuid.as_mut_slice()[0].function = topoext_fn;
|
||||
assert!(use_host_cpuid_function(&mut cpuid, topoext_fn, true).is_ok());
|
||||
let entries = cpuid.as_mut_slice();
|
||||
assert!(entries.len() > 1);
|
||||
for (count, entry) in entries.iter_mut().enumerate() {
|
||||
assert!(entry.function == topoext_fn);
|
||||
assert!(entry.index == count as u32);
|
||||
assert!(entry.eax != 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
fn test_use_host_cpuid_function_without_count() {
|
||||
use crate::cpuid::cpu_leaf::leaf_0x1::*;
|
||||
// try to emulate the extended cache topology leaves
|
||||
let feature_info_fn = LEAF_NUM;
|
||||
|
||||
// check that it behaves correctly for TOPOEXT function
|
||||
let mut cpuid = CpuId::new(1).unwrap();
|
||||
cpuid.as_mut_slice()[0].function = feature_info_fn;
|
||||
assert!(use_host_cpuid_function(&mut cpuid, feature_info_fn, false).is_ok());
|
||||
let entries = cpuid.as_mut_slice();
|
||||
assert!(entries.len() == 1);
|
||||
let entry = entries[0];
|
||||
|
||||
assert!(entry.function == feature_info_fn);
|
||||
assert!(entry.index == 0);
|
||||
assert!(entry.eax != 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
fn test_use_host_cpuid_function_err() {
|
||||
let topoext_fn = get_topoext_fn();
|
||||
// check that it returns Err when there are too many entriesentry.function == topoext_fn
|
||||
let mut cpuid = CpuId::new(kvm_bindings::KVM_MAX_CPUID_ENTRIES).unwrap();
|
||||
match use_host_cpuid_function(&mut cpuid, topoext_fn, true) {
|
||||
Err(Error::FamError(vmm_sys_util::fam::Error::SizeLimitExceeded)) => {}
|
||||
_ => panic!("Wrong behavior"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
// Copyright 2021 Alibaba Cloud. All Rights Reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::super::bit_helper::BitHelper;
|
||||
use super::super::cpu_leaf;
|
||||
use super::*;
|
||||
|
||||
fn update_deterministic_cache_entry(entry: &mut CpuIdEntry, vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
use cpu_leaf::leaf_0x4::*;
|
||||
|
||||
common::update_cache_parameters_entry(entry, vm_spec)?;
|
||||
|
||||
// If leaf_0xB or leaf_0x1F is enabled, leaf0x4 won't be used to generate topology information.
|
||||
// In most cases, we could have leaf_0xB in our host cpu. But we keep the leaf_0x4 eax[26,31]
|
||||
// to prevent rare cases.
|
||||
if vm_spec.cpu_count <= 64 {
|
||||
entry.eax.write_bits_in_range(
|
||||
&eax::MAX_CORES_PER_PACKAGE_BITRANGE,
|
||||
u32::from(vm_spec.cpu_count - 1),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_power_management_entry(entry: &mut CpuIdEntry, _vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
// disable pstate feature
|
||||
entry.eax = 0;
|
||||
entry.ebx = 0;
|
||||
entry.ecx = 0;
|
||||
entry.edx = 0;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_perf_mon_entry(entry: &mut CpuIdEntry, vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
use cpu_leaf::leaf_0xa::*;
|
||||
|
||||
// Architectural Performance Monitor Leaf
|
||||
match vm_spec.vpmu_feature {
|
||||
VpmuFeatureLevel::Disabled => {
|
||||
// Disable PMU
|
||||
entry.eax = 0;
|
||||
entry.ebx = 0;
|
||||
entry.ecx = 0;
|
||||
entry.edx = 0;
|
||||
}
|
||||
VpmuFeatureLevel::LimitedlyEnabled => {
|
||||
// Allow minimal vpmu ability (only instuctions and cycles pmu).
|
||||
entry.eax.write_bits_in_range(&eax::PMC_VERSION_ID, 2);
|
||||
entry.eax.write_bits_in_range(&eax::BIT_LEN_PMEVENT, 7);
|
||||
|
||||
// 0(false) means support for the targeted performance monitoring event
|
||||
entry.ebx.write_bit(ebx::CORE_CYCLES_BITINDEX, false);
|
||||
entry.ebx.write_bit(ebx::REF_CYCLES_BITINDEX, false);
|
||||
entry.ebx.write_bit(ebx::INST_RETIRED_BITINDEX, false);
|
||||
entry.ebx.write_bit(ebx::BR_INST_RETIRED_BITINDEX, true);
|
||||
entry.ebx.write_bit(ebx::LLC_MISSES_BITINDEX, true);
|
||||
entry.ebx.write_bit(ebx::LLC_REF_BITINDEX, true);
|
||||
entry.ebx.write_bit(ebx::BR_MIS_RETIRED_BITINDEX, true);
|
||||
}
|
||||
VpmuFeatureLevel::FullyEnabled => {
|
||||
// Allow all supported vpmu ability
|
||||
entry.eax.write_bits_in_range(&eax::PMC_VERSION_ID, 2);
|
||||
entry.eax.write_bits_in_range(&eax::BIT_LEN_PMEVENT, 7);
|
||||
|
||||
// 0(false) means support for the targeted performance monitoring event
|
||||
entry.ebx.write_bit(ebx::CORE_CYCLES_BITINDEX, false);
|
||||
entry.ebx.write_bit(ebx::REF_CYCLES_BITINDEX, false);
|
||||
entry.ebx.write_bit(ebx::INST_RETIRED_BITINDEX, false);
|
||||
entry.ebx.write_bit(ebx::BR_INST_RETIRED_BITINDEX, false);
|
||||
entry.ebx.write_bit(ebx::LLC_MISSES_BITINDEX, false);
|
||||
entry.ebx.write_bit(ebx::LLC_REF_BITINDEX, false);
|
||||
entry.ebx.write_bit(ebx::BR_MIS_RETIRED_BITINDEX, false);
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct IntelCpuidTransformer {}
|
||||
|
||||
impl IntelCpuidTransformer {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuidTransformer for IntelCpuidTransformer {
|
||||
fn process_cpuid(&self, cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
common::use_host_cpuid_function(cpuid, cpu_leaf::leaf_0x0::LEAF_NUM, false)?;
|
||||
self.process_entries(cpuid, vm_spec)
|
||||
}
|
||||
|
||||
fn entry_transformer_fn(&self, entry: &mut CpuIdEntry) -> Option<EntryTransformerFn> {
|
||||
use cpu_leaf::*;
|
||||
|
||||
match entry.function {
|
||||
leaf_0x1::LEAF_NUM => Some(common::update_feature_info_entry),
|
||||
leaf_0x4::LEAF_NUM => Some(intel::update_deterministic_cache_entry),
|
||||
leaf_0x6::LEAF_NUM => Some(intel::update_power_management_entry),
|
||||
leaf_0xa::LEAF_NUM => Some(intel::update_perf_mon_entry),
|
||||
leaf_0xb::LEAF_NUM => Some(common::update_extended_topology_entry),
|
||||
leaf_0x1f::LEAF_NUM => Some(common::update_extended_topology_v2_entry),
|
||||
0x8000_0002..=0x8000_0004 => Some(common::update_brand_string_entry),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use kvm_bindings::kvm_cpuid_entry2;
|
||||
|
||||
use super::*;
|
||||
use crate::cpuid::transformer::VmSpec;
|
||||
|
||||
#[test]
|
||||
fn test_update_perf_mon_entry() {
|
||||
use crate::cpuid::cpu_leaf::leaf_0xa::*;
|
||||
// Test when vpmu is off (level Disabled)
|
||||
let vm_spec =
|
||||
VmSpec::new(0, 1, 1, 1, 1, VpmuFeatureLevel::Disabled).expect("Error creating vm_spec");
|
||||
let entry = &mut kvm_cpuid_entry2 {
|
||||
function: LEAF_NUM,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: 1,
|
||||
ebx: 1,
|
||||
ecx: 1,
|
||||
edx: 1,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_perf_mon_entry(entry, &vm_spec).is_ok());
|
||||
|
||||
assert_eq!(entry.eax, 0);
|
||||
assert_eq!(entry.ebx, 0);
|
||||
assert_eq!(entry.ecx, 0);
|
||||
assert_eq!(entry.edx, 0);
|
||||
|
||||
// Test when only instructions and cycles pmu are enabled (level LimitedlyEnabled)
|
||||
let vm_spec = VmSpec::new(0, 1, 1, 1, 1, VpmuFeatureLevel::LimitedlyEnabled)
|
||||
.expect("Error creating vm_spec");
|
||||
let entry = &mut kvm_cpuid_entry2 {
|
||||
function: 0x0,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: 0,
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_perf_mon_entry(entry, &vm_spec).is_ok());
|
||||
assert_eq!(entry.eax.read_bits_in_range(&eax::PMC_VERSION_ID), 2);
|
||||
assert_eq!(entry.eax.read_bits_in_range(&eax::BIT_LEN_PMEVENT), 7);
|
||||
|
||||
assert!(!entry.ebx.read_bit(ebx::CORE_CYCLES_BITINDEX));
|
||||
assert!(!entry.ebx.read_bit(ebx::INST_RETIRED_BITINDEX));
|
||||
assert!(!entry.ebx.read_bit(ebx::REF_CYCLES_BITINDEX));
|
||||
assert!(entry.ebx.read_bit(ebx::LLC_REF_BITINDEX));
|
||||
assert!(entry.ebx.read_bit(ebx::LLC_MISSES_BITINDEX));
|
||||
assert!(entry.ebx.read_bit(ebx::BR_INST_RETIRED_BITINDEX));
|
||||
assert!(entry.ebx.read_bit(ebx::BR_MIS_RETIRED_BITINDEX));
|
||||
|
||||
// Test when all vpmu features are enabled (level FullyEnabled)
|
||||
let vm_spec = VmSpec::new(0, 1, 1, 1, 1, VpmuFeatureLevel::FullyEnabled)
|
||||
.expect("Error creating vm_spec");
|
||||
let entry = &mut kvm_cpuid_entry2 {
|
||||
function: 0x0,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: 0,
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_perf_mon_entry(entry, &vm_spec).is_ok());
|
||||
|
||||
assert_eq!(entry.eax.read_bits_in_range(&eax::PMC_VERSION_ID), 2);
|
||||
assert_eq!(entry.eax.read_bits_in_range(&eax::BIT_LEN_PMEVENT), 7);
|
||||
|
||||
assert!(!entry.ebx.read_bit(ebx::CORE_CYCLES_BITINDEX));
|
||||
assert!(!entry.ebx.read_bit(ebx::INST_RETIRED_BITINDEX));
|
||||
assert!(!entry.ebx.read_bit(ebx::REF_CYCLES_BITINDEX));
|
||||
assert!(!entry.ebx.read_bit(ebx::LLC_REF_BITINDEX));
|
||||
assert!(!entry.ebx.read_bit(ebx::LLC_MISSES_BITINDEX));
|
||||
assert!(!entry.ebx.read_bit(ebx::BR_INST_RETIRED_BITINDEX));
|
||||
assert!(!entry.ebx.read_bit(ebx::BR_MIS_RETIRED_BITINDEX));
|
||||
}
|
||||
|
||||
fn check_update_deterministic_cache_entry(
|
||||
cpu_count: u8,
|
||||
cache_level: u32,
|
||||
expected_max_cores_per_package: u32,
|
||||
threads_per_core: u8,
|
||||
cores_per_die: u8,
|
||||
dies_per_socket: u8,
|
||||
) {
|
||||
use crate::cpuid::cpu_leaf::leaf_0x4::*;
|
||||
|
||||
let vm_spec = VmSpec::new(
|
||||
0,
|
||||
cpu_count,
|
||||
threads_per_core,
|
||||
cores_per_die,
|
||||
dies_per_socket,
|
||||
VpmuFeatureLevel::Disabled,
|
||||
)
|
||||
.expect("Error creating vm_spec");
|
||||
let entry = &mut kvm_cpuid_entry2 {
|
||||
function: 0x0,
|
||||
index: 0,
|
||||
flags: 0,
|
||||
eax: *(0_u32).write_bits_in_range(&eax::CACHE_LEVEL_BITRANGE, cache_level),
|
||||
ebx: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
padding: [0, 0, 0],
|
||||
};
|
||||
|
||||
assert!(update_deterministic_cache_entry(entry, &vm_spec).is_ok());
|
||||
|
||||
assert!(
|
||||
entry
|
||||
.eax
|
||||
.read_bits_in_range(&eax::MAX_CORES_PER_PACKAGE_BITRANGE)
|
||||
== expected_max_cores_per_package
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_1vcpu_ht_off() {
|
||||
// test update_deterministic_cache_entry
|
||||
// test L1
|
||||
check_update_deterministic_cache_entry(1, 1, 0, 1, 1, 1);
|
||||
// test L2
|
||||
check_update_deterministic_cache_entry(1, 2, 0, 1, 1, 1);
|
||||
// test L3
|
||||
check_update_deterministic_cache_entry(1, 3, 0, 1, 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_1vcpu_ht_on() {
|
||||
// test update_deterministic_cache_entry
|
||||
// test L1
|
||||
check_update_deterministic_cache_entry(1, 1, 0, 2, 1, 1);
|
||||
// test L2
|
||||
check_update_deterministic_cache_entry(1, 2, 0, 2, 1, 1);
|
||||
// test L3
|
||||
check_update_deterministic_cache_entry(1, 3, 0, 2, 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2vcpu_ht_off() {
|
||||
// test update_deterministic_cache_entry
|
||||
// test L1
|
||||
check_update_deterministic_cache_entry(2, 1, 1, 1, 2, 1);
|
||||
// test L2
|
||||
check_update_deterministic_cache_entry(2, 2, 1, 1, 2, 1);
|
||||
// test L3
|
||||
check_update_deterministic_cache_entry(2, 3, 1, 1, 2, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2vcpu_ht_on() {
|
||||
// test update_deterministic_cache_entry
|
||||
// test L1
|
||||
check_update_deterministic_cache_entry(2, 1, 1, 2, 2, 1);
|
||||
// test L2
|
||||
check_update_deterministic_cache_entry(2, 2, 1, 2, 2, 1);
|
||||
// test L3
|
||||
check_update_deterministic_cache_entry(2, 3, 1, 2, 2, 1);
|
||||
}
|
||||
}
|
||||
172
src/dragonball/src/dbs_arch/src/x86_64/cpuid/transformer/mod.rs
Normal file
172
src/dragonball/src/dbs_arch/src/x86_64/cpuid/transformer/mod.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2021 Alibaba Cloud. All Rights Reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::brand_string::{BrandString, Reg as BsReg};
|
||||
use super::common::get_vendor_id;
|
||||
use super::{CpuId, CpuIdEntry};
|
||||
use crate::VpmuFeatureLevel;
|
||||
|
||||
pub mod amd;
|
||||
pub mod common;
|
||||
pub mod intel;
|
||||
|
||||
/// Structure containing the specifications of the VM
|
||||
pub struct VmSpec {
|
||||
/// The vendor id of the CPU
|
||||
cpu_vendor_id: [u8; 12],
|
||||
/// The id of the current logical cpu in the range [0..cpu_count].
|
||||
cpu_id: u8,
|
||||
/// The total number of logical cpus (includes cpus that could be hotplugged).
|
||||
cpu_count: u8,
|
||||
/// The desired brand string for the guest.
|
||||
brand_string: BrandString,
|
||||
/// threads per core for cpu topology information
|
||||
threads_per_core: u8,
|
||||
/// cores per die for cpu topology information
|
||||
cores_per_die: u8,
|
||||
/// dies per socket for cpu topology information
|
||||
dies_per_socket: u8,
|
||||
/// if vpmu feature is Disabled, it means vpmu feature is off (by default)
|
||||
/// if vpmu feature is LimitedlyEnabled, it means minimal vpmu counters are supported (cycles and instructions)
|
||||
/// if vpmu feature is FullyEnabled, it means all vpmu counters are supported
|
||||
vpmu_feature: VpmuFeatureLevel,
|
||||
}
|
||||
|
||||
impl VmSpec {
|
||||
/// Creates a new instance of VmSpec with the specified parameters
|
||||
/// The brand string is deduced from the vendor_id
|
||||
pub fn new(
|
||||
cpu_id: u8,
|
||||
cpu_count: u8,
|
||||
threads_per_core: u8,
|
||||
cores_per_die: u8,
|
||||
dies_per_socket: u8,
|
||||
vpmu_feature: VpmuFeatureLevel,
|
||||
) -> Result<VmSpec, Error> {
|
||||
let cpu_vendor_id = get_vendor_id().map_err(Error::InternalError)?;
|
||||
let brand_string =
|
||||
BrandString::from_vendor_id(&cpu_vendor_id).map_err(Error::BrandString)?;
|
||||
|
||||
Ok(VmSpec {
|
||||
cpu_vendor_id,
|
||||
cpu_id,
|
||||
cpu_count,
|
||||
brand_string,
|
||||
threads_per_core,
|
||||
cores_per_die,
|
||||
dies_per_socket,
|
||||
vpmu_feature,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns an immutable reference to cpu_vendor_id
|
||||
pub fn cpu_vendor_id(&self) -> &[u8; 12] {
|
||||
&self.cpu_vendor_id
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors associated with processing the CPUID leaves.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Error {
|
||||
/// Failed to parse CPU brand string
|
||||
BrandString(super::brand_string::Error),
|
||||
/// The CPU architecture is not supported
|
||||
CpuNotSupported,
|
||||
/// A FamStructWrapper operation has failed
|
||||
FamError(vmm_sys_util::fam::Error),
|
||||
/// A call to an internal helper method failed
|
||||
InternalError(super::common::Error),
|
||||
/// The maximum number of addressable logical CPUs cannot be stored in an `u8`.
|
||||
VcpuCountOverflow,
|
||||
}
|
||||
|
||||
pub type EntryTransformerFn = fn(entry: &mut CpuIdEntry, vm_spec: &VmSpec) -> Result<(), Error>;
|
||||
|
||||
/// Generic trait that provides methods for transforming the cpuid
|
||||
pub trait CpuidTransformer {
|
||||
/// Process the cpuid array and make the desired transformations.
|
||||
fn process_cpuid(&self, cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
self.process_entries(cpuid, vm_spec)
|
||||
}
|
||||
|
||||
/// Iterate through all the cpuid entries and calls the associated transformer for each one.
|
||||
fn process_entries(&self, cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
for entry in cpuid.as_mut_slice().iter_mut() {
|
||||
let maybe_transformer_fn = self.entry_transformer_fn(entry);
|
||||
|
||||
if let Some(transformer_fn) = maybe_transformer_fn {
|
||||
transformer_fn(entry, vm_spec)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the associated transformer for a cpuid entry
|
||||
fn entry_transformer_fn(&self, _entry: &mut CpuIdEntry) -> Option<EntryTransformerFn> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use kvm_bindings::kvm_cpuid_entry2;
|
||||
|
||||
const PROCESSED_FN: u32 = 1;
|
||||
const EXPECTED_INDEX: u32 = 100;
|
||||
|
||||
fn transform_entry(entry: &mut kvm_cpuid_entry2, _vm_spec: &VmSpec) -> Result<(), Error> {
|
||||
entry.index = EXPECTED_INDEX;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct MockCpuidTransformer {}
|
||||
|
||||
impl CpuidTransformer for MockCpuidTransformer {
|
||||
fn entry_transformer_fn(&self, entry: &mut kvm_cpuid_entry2) -> Option<EntryTransformerFn> {
|
||||
match entry.function {
|
||||
PROCESSED_FN => Some(transform_entry),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_cpuid() {
|
||||
let num_entries = 5;
|
||||
|
||||
let mut cpuid = CpuId::new(num_entries).unwrap();
|
||||
let vm_spec = VmSpec::new(0, 1, 1, 1, 1, VpmuFeatureLevel::Disabled);
|
||||
cpuid.as_mut_slice()[0].function = PROCESSED_FN;
|
||||
assert!(MockCpuidTransformer {}
|
||||
.process_cpuid(&mut cpuid, &vm_spec.unwrap())
|
||||
.is_ok());
|
||||
|
||||
assert!(cpuid.as_mut_slice().len() == num_entries);
|
||||
for entry in cpuid.as_mut_slice().iter() {
|
||||
match entry.function {
|
||||
PROCESSED_FN => {
|
||||
assert_eq!(entry.index, EXPECTED_INDEX);
|
||||
}
|
||||
_ => {
|
||||
assert_ne!(entry.index, EXPECTED_INDEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_cpu_architecture_cpuid() {
|
||||
use crate::cpuid::process_cpuid;
|
||||
let num_entries = 5;
|
||||
|
||||
let mut cpuid = CpuId::new(num_entries).unwrap();
|
||||
let mut vm_spec = VmSpec::new(0, 1, 1, 1, 1, VpmuFeatureLevel::Disabled).unwrap();
|
||||
|
||||
vm_spec.cpu_vendor_id = [1; 12];
|
||||
assert!(process_cpuid(&mut cpuid, &vm_spec).is_err());
|
||||
}
|
||||
}
|
||||
119
src/dragonball/src/dbs_arch/src/x86_64/gdt.rs
Normal file
119
src/dragonball/src/dbs_arch/src/x86_64/gdt.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
// For GDT details see arch/x86/include/asm/segment.h
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use kvm_bindings::kvm_segment;
|
||||
|
||||
/// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
|
||||
#[allow(unused_parens)]
|
||||
pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
|
||||
(((u64::from(base) & 0xff00_0000u64) << (56 - 24))
|
||||
| ((u64::from(flags) & 0x0000_f0ffu64) << 40)
|
||||
| ((u64::from(limit) & 0x000f_0000u64) << (48 - 16))
|
||||
| ((u64::from(base) & 0x00ff_ffffu64) << 16)
|
||||
| (u64::from(limit) & 0x0000_ffffu64))
|
||||
}
|
||||
|
||||
#[allow(unused_parens)]
|
||||
fn get_base(entry: u64) -> u64 {
|
||||
((((entry) & 0xFF00_0000_0000_0000) >> 32)
|
||||
| (((entry) & 0x0000_00FF_0000_0000) >> 16)
|
||||
| (((entry) & 0x0000_0000_FFFF_0000) >> 16))
|
||||
}
|
||||
|
||||
fn get_limit(entry: u64) -> u32 {
|
||||
((((entry) & 0x000F_0000_0000_0000) >> 32) | ((entry) & 0x0000_0000_0000_FFFF)) as u32
|
||||
}
|
||||
|
||||
fn get_g(entry: u64) -> u8 {
|
||||
((entry & 0x0080_0000_0000_0000) >> 55) as u8
|
||||
}
|
||||
|
||||
fn get_db(entry: u64) -> u8 {
|
||||
((entry & 0x0040_0000_0000_0000) >> 54) as u8
|
||||
}
|
||||
|
||||
fn get_l(entry: u64) -> u8 {
|
||||
((entry & 0x0020_0000_0000_0000) >> 53) as u8
|
||||
}
|
||||
|
||||
fn get_avl(entry: u64) -> u8 {
|
||||
((entry & 0x0010_0000_0000_0000) >> 52) as u8
|
||||
}
|
||||
|
||||
fn get_p(entry: u64) -> u8 {
|
||||
((entry & 0x0000_8000_0000_0000) >> 47) as u8
|
||||
}
|
||||
|
||||
fn get_dpl(entry: u64) -> u8 {
|
||||
((entry & 0x0000_6000_0000_0000) >> 45) as u8
|
||||
}
|
||||
|
||||
fn get_s(entry: u64) -> u8 {
|
||||
((entry & 0x0000_1000_0000_0000) >> 44) as u8
|
||||
}
|
||||
|
||||
fn get_type(entry: u64) -> u8 {
|
||||
((entry & 0x0000_0F00_0000_0000) >> 40) as u8
|
||||
}
|
||||
|
||||
/// Automatically build the kvm struct for SET_SREGS from the kernel bit fields.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `entry` - The gdt entry.
|
||||
/// * `table_index` - Index of the entry in the gdt table.
|
||||
pub fn kvm_segment_from_gdt(entry: u64, table_index: u8) -> kvm_segment {
|
||||
kvm_segment {
|
||||
base: get_base(entry),
|
||||
limit: get_limit(entry),
|
||||
selector: u16::from(table_index * 8),
|
||||
type_: get_type(entry),
|
||||
present: get_p(entry),
|
||||
dpl: get_dpl(entry),
|
||||
db: get_db(entry),
|
||||
s: get_s(entry),
|
||||
l: get_l(entry),
|
||||
g: get_g(entry),
|
||||
avl: get_avl(entry),
|
||||
padding: 0,
|
||||
unusable: match get_p(entry) {
|
||||
0 => 1,
|
||||
_ => 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn field_parse() {
|
||||
let gdt = gdt_entry(0xA09B, 0x10_0000, 0xfffff);
|
||||
let seg = kvm_segment_from_gdt(gdt, 0);
|
||||
// 0xA09B
|
||||
// 'A'
|
||||
assert_eq!(0x1, seg.g);
|
||||
assert_eq!(0x0, seg.db);
|
||||
assert_eq!(0x1, seg.l);
|
||||
assert_eq!(0x0, seg.avl);
|
||||
// '9'
|
||||
assert_eq!(0x1, seg.present);
|
||||
assert_eq!(0x0, seg.dpl);
|
||||
assert_eq!(0x1, seg.s);
|
||||
// 'B'
|
||||
assert_eq!(0xB, seg.type_);
|
||||
// base and limit
|
||||
assert_eq!(0x10_0000, seg.base);
|
||||
assert_eq!(0xfffff, seg.limit);
|
||||
assert_eq!(0x0, seg.unusable);
|
||||
}
|
||||
}
|
||||
136
src/dragonball/src/dbs_arch/src/x86_64/interrupts.rs
Normal file
136
src/dragonball/src/dbs_arch/src/x86_64/interrupts.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
use kvm_bindings::kvm_lapic_state;
|
||||
use kvm_ioctls::VcpuFd;
|
||||
|
||||
/// Errors thrown while configuring the LAPIC.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Failure in retrieving the LAPIC configuration.
|
||||
GetLapic(kvm_ioctls::Error),
|
||||
/// Failure in modifying the LAPIC configuration.
|
||||
SetLapic(kvm_ioctls::Error),
|
||||
}
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
// Defines poached from apicdef.h kernel header.
|
||||
const APIC_LVT0: usize = 0x350;
|
||||
const APIC_LVT1: usize = 0x360;
|
||||
const APIC_MODE_NMI: u32 = 0x4;
|
||||
const APIC_MODE_EXTINT: u32 = 0x7;
|
||||
|
||||
fn get_klapic_reg(klapic: &kvm_lapic_state, reg_offset: usize) -> u32 {
|
||||
let range = reg_offset..reg_offset + 4;
|
||||
let reg = klapic.regs.get(range).expect("get_klapic_reg range");
|
||||
|
||||
let mut reg_bytes = [0u8; 4];
|
||||
for (byte, read) in reg_bytes.iter_mut().zip(reg.iter().cloned()) {
|
||||
*byte = read as u8;
|
||||
}
|
||||
|
||||
u32::from_le_bytes(reg_bytes)
|
||||
}
|
||||
|
||||
fn set_klapic_reg(klapic: &mut kvm_lapic_state, reg_offset: usize, value: u32) {
|
||||
let range = reg_offset..reg_offset + 4;
|
||||
let reg = klapic.regs.get_mut(range).expect("set_klapic_reg range");
|
||||
|
||||
let value = u32::to_le_bytes(value);
|
||||
for (byte, read) in reg.iter_mut().zip(value.iter().cloned()) {
|
||||
*byte = read as i8;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_parens)]
|
||||
fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
|
||||
(((reg) & !0x700) | ((mode) << 8))
|
||||
}
|
||||
|
||||
/// Configures LAPICs. LAPIC0 is set for external interrupts, LAPIC1 is set for NMI.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `vcpu` - The VCPU object to configure.
|
||||
pub fn set_lint(vcpu: &VcpuFd) -> Result<()> {
|
||||
let mut klapic = vcpu.get_lapic().map_err(Error::GetLapic)?;
|
||||
|
||||
let lvt_lint0 = get_klapic_reg(&klapic, APIC_LVT0);
|
||||
set_klapic_reg(
|
||||
&mut klapic,
|
||||
APIC_LVT0,
|
||||
set_apic_delivery_mode(lvt_lint0, APIC_MODE_EXTINT),
|
||||
);
|
||||
let lvt_lint1 = get_klapic_reg(&klapic, APIC_LVT1);
|
||||
set_klapic_reg(
|
||||
&mut klapic,
|
||||
APIC_LVT1,
|
||||
set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI),
|
||||
);
|
||||
|
||||
vcpu.set_lapic(&klapic).map_err(Error::SetLapic)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use kvm_ioctls::Kvm;
|
||||
|
||||
const KVM_APIC_REG_SIZE: usize = 0x400;
|
||||
|
||||
#[test]
|
||||
fn test_set_and_get_klapic_reg() {
|
||||
let reg_offset = 0x340;
|
||||
let mut klapic = kvm_lapic_state::default();
|
||||
set_klapic_reg(&mut klapic, reg_offset, 3);
|
||||
let value = get_klapic_reg(&klapic, reg_offset);
|
||||
assert_eq!(value, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_set_and_get_klapic_out_of_bounds() {
|
||||
let reg_offset = KVM_APIC_REG_SIZE + 10;
|
||||
let mut klapic = kvm_lapic_state::default();
|
||||
set_klapic_reg(&mut klapic, reg_offset, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_setlint() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
assert!(kvm.check_extension(kvm_ioctls::Cap::Irqchip));
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
//the get_lapic ioctl will fail if there is no irqchip created beforehand.
|
||||
assert!(vm.create_irq_chip().is_ok());
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
let klapic_before: kvm_lapic_state = vcpu.get_lapic().unwrap();
|
||||
|
||||
// Compute the value that is expected to represent LVT0 and LVT1.
|
||||
let lint0 = get_klapic_reg(&klapic_before, APIC_LVT0);
|
||||
let lint1 = get_klapic_reg(&klapic_before, APIC_LVT1);
|
||||
let lint0_mode_expected = set_apic_delivery_mode(lint0, APIC_MODE_EXTINT);
|
||||
let lint1_mode_expected = set_apic_delivery_mode(lint1, APIC_MODE_NMI);
|
||||
|
||||
set_lint(&vcpu).unwrap();
|
||||
|
||||
// Compute the value that represents LVT0 and LVT1 after set_lint.
|
||||
let klapic_actual: kvm_lapic_state = vcpu.get_lapic().unwrap();
|
||||
let lint0_mode_actual = get_klapic_reg(&klapic_actual, APIC_LVT0);
|
||||
let lint1_mode_actual = get_klapic_reg(&klapic_actual, APIC_LVT1);
|
||||
assert_eq!(lint0_mode_expected, lint0_mode_actual);
|
||||
assert_eq!(lint1_mode_expected, lint1_mode_actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_setlint_fails() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
// 'get_lapic' ioctl triggered by the 'set_lint' function will fail if there is no
|
||||
// irqchip created beforehand.
|
||||
assert!(set_lint(&vcpu).is_err());
|
||||
}
|
||||
}
|
||||
15
src/dragonball/src/dbs_arch/src/x86_64/mod.rs
Normal file
15
src/dragonball/src/dbs_arch/src/x86_64/mod.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2021 Alibaba Cloud. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! CPU architecture specific constants and utilities for the `x86_64` architecture.
|
||||
|
||||
/// Definitions for x86 CPUID
|
||||
pub mod cpuid;
|
||||
/// Definitions for x86 Global Descriptor Table
|
||||
pub mod gdt;
|
||||
/// Definitions for x86 interrupts
|
||||
pub mod interrupts;
|
||||
/// Definitions for x86 Model Specific Registers(MSR).
|
||||
pub mod msr;
|
||||
/// Definitions for x86 Registers
|
||||
pub mod regs;
|
||||
778
src/dragonball/src/dbs_arch/src/x86_64/msr.rs
Normal file
778
src/dragonball/src/dbs_arch/src/x86_64/msr.rs
Normal file
@@ -0,0 +1,778 @@
|
||||
/* automatically generated by rust-bindgen */
|
||||
|
||||
// Copyright (C) 2019 Alibaba Cloud. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![allow(missing_docs)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
/// Model Specific Registers (MSRs) related functionality.
|
||||
use std::result;
|
||||
|
||||
use kvm_bindings::MsrList;
|
||||
use kvm_ioctls::Kvm;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// MSR related errors.
|
||||
pub enum Error {
|
||||
/// Getting supported MSRs failed.
|
||||
GetSupportedModelSpecificRegisters(kvm_ioctls::Error),
|
||||
/// Setting up MSRs failed.
|
||||
SetModelSpecificRegisters(kvm_ioctls::Error),
|
||||
/// Failed to set all MSRs.
|
||||
SetModelSpecificRegistersCount,
|
||||
/// Msr error
|
||||
Msr(vmm_sys_util::fam::Error),
|
||||
}
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// MSR range
|
||||
struct MsrRange {
|
||||
/// Base MSR address
|
||||
base: u32,
|
||||
/// Number of MSRs
|
||||
nmsrs: u32,
|
||||
}
|
||||
|
||||
impl MsrRange {
|
||||
/// Returns whether `msr` is contained in this MSR range.
|
||||
fn contains(&self, msr: u32) -> bool {
|
||||
self.base <= msr && msr < self.base + self.nmsrs
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a MsrRange of one msr given as argument.
|
||||
macro_rules! SINGLE_MSR {
|
||||
($msr:expr) => {
|
||||
MsrRange {
|
||||
base: $msr,
|
||||
nmsrs: 1,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Creates a MsrRange of with msr base and count given as arguments.
|
||||
macro_rules! MSR_RANGE {
|
||||
($first:expr, $count:expr) => {
|
||||
MsrRange {
|
||||
base: $first,
|
||||
nmsrs: $count,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// List of MSRs that can be serialized. List is sorted in ascending order of MSRs addresses.
|
||||
static WHITELISTED_MSR_RANGES: &[MsrRange] = &[
|
||||
SINGLE_MSR!(MSR_IA32_P5_MC_ADDR),
|
||||
SINGLE_MSR!(MSR_IA32_P5_MC_TYPE),
|
||||
SINGLE_MSR!(MSR_IA32_TSC),
|
||||
SINGLE_MSR!(MSR_IA32_PLATFORM_ID),
|
||||
SINGLE_MSR!(MSR_IA32_APICBASE),
|
||||
SINGLE_MSR!(MSR_IA32_EBL_CR_POWERON),
|
||||
SINGLE_MSR!(MSR_EBC_FREQUENCY_ID),
|
||||
SINGLE_MSR!(MSR_SMI_COUNT),
|
||||
SINGLE_MSR!(MSR_IA32_FEATURE_CONTROL),
|
||||
SINGLE_MSR!(MSR_IA32_TSC_ADJUST),
|
||||
SINGLE_MSR!(MSR_IA32_SPEC_CTRL),
|
||||
SINGLE_MSR!(MSR_IA32_PRED_CMD),
|
||||
SINGLE_MSR!(MSR_IA32_UCODE_WRITE),
|
||||
SINGLE_MSR!(MSR_IA32_UCODE_REV),
|
||||
SINGLE_MSR!(MSR_IA32_SMBASE),
|
||||
SINGLE_MSR!(MSR_FSB_FREQ),
|
||||
SINGLE_MSR!(MSR_PLATFORM_INFO),
|
||||
SINGLE_MSR!(MSR_PKG_CST_CONFIG_CONTROL),
|
||||
SINGLE_MSR!(MSR_IA32_MPERF),
|
||||
SINGLE_MSR!(MSR_IA32_APERF),
|
||||
SINGLE_MSR!(MSR_MTRRcap),
|
||||
SINGLE_MSR!(MSR_IA32_BBL_CR_CTL3),
|
||||
SINGLE_MSR!(MSR_IA32_SYSENTER_CS),
|
||||
SINGLE_MSR!(MSR_IA32_SYSENTER_ESP),
|
||||
SINGLE_MSR!(MSR_IA32_SYSENTER_EIP),
|
||||
SINGLE_MSR!(MSR_IA32_MCG_CAP),
|
||||
SINGLE_MSR!(MSR_IA32_MCG_STATUS),
|
||||
SINGLE_MSR!(MSR_IA32_MCG_CTL),
|
||||
SINGLE_MSR!(MSR_IA32_PERF_STATUS),
|
||||
SINGLE_MSR!(MSR_IA32_MISC_ENABLE),
|
||||
SINGLE_MSR!(MSR_MISC_FEATURE_CONTROL),
|
||||
SINGLE_MSR!(MSR_MISC_PWR_MGMT),
|
||||
SINGLE_MSR!(MSR_TURBO_RATIO_LIMIT),
|
||||
SINGLE_MSR!(MSR_TURBO_RATIO_LIMIT1),
|
||||
SINGLE_MSR!(MSR_IA32_DEBUGCTLMSR),
|
||||
SINGLE_MSR!(MSR_IA32_LASTBRANCHFROMIP),
|
||||
SINGLE_MSR!(MSR_IA32_LASTBRANCHTOIP),
|
||||
SINGLE_MSR!(MSR_IA32_LASTINTFROMIP),
|
||||
SINGLE_MSR!(MSR_IA32_LASTINTTOIP),
|
||||
SINGLE_MSR!(MSR_IA32_POWER_CTL),
|
||||
MSR_RANGE!(
|
||||
// IA32_MTRR_PHYSBASE0
|
||||
0x200, 0x100
|
||||
),
|
||||
MSR_RANGE!(
|
||||
// MSR_CORE_C3_RESIDENCY
|
||||
// MSR_CORE_C6_RESIDENCY
|
||||
// MSR_CORE_C7_RESIDENCY
|
||||
MSR_CORE_C3_RESIDENCY,
|
||||
3
|
||||
),
|
||||
MSR_RANGE!(MSR_IA32_MC0_CTL, 0x80),
|
||||
SINGLE_MSR!(MSR_RAPL_POWER_UNIT),
|
||||
MSR_RANGE!(
|
||||
// MSR_PKGC3_IRTL
|
||||
// MSR_PKGC6_IRTL
|
||||
// MSR_PKGC7_IRTL
|
||||
MSR_PKGC3_IRTL,
|
||||
3
|
||||
),
|
||||
SINGLE_MSR!(MSR_PKG_POWER_LIMIT),
|
||||
SINGLE_MSR!(MSR_PKG_ENERGY_STATUS),
|
||||
SINGLE_MSR!(MSR_PKG_PERF_STATUS),
|
||||
SINGLE_MSR!(MSR_PKG_POWER_INFO),
|
||||
SINGLE_MSR!(MSR_DRAM_POWER_LIMIT),
|
||||
SINGLE_MSR!(MSR_DRAM_ENERGY_STATUS),
|
||||
SINGLE_MSR!(MSR_DRAM_PERF_STATUS),
|
||||
SINGLE_MSR!(MSR_DRAM_POWER_INFO),
|
||||
SINGLE_MSR!(MSR_CONFIG_TDP_NOMINAL),
|
||||
SINGLE_MSR!(MSR_CONFIG_TDP_LEVEL_1),
|
||||
SINGLE_MSR!(MSR_CONFIG_TDP_LEVEL_2),
|
||||
SINGLE_MSR!(MSR_CONFIG_TDP_CONTROL),
|
||||
SINGLE_MSR!(MSR_TURBO_ACTIVATION_RATIO),
|
||||
SINGLE_MSR!(MSR_IA32_TSCDEADLINE),
|
||||
MSR_RANGE!(APIC_BASE_MSR, APIC_MSR_INDEXES),
|
||||
SINGLE_MSR!(MSR_IA32_BNDCFGS),
|
||||
SINGLE_MSR!(MSR_KVM_WALL_CLOCK_NEW),
|
||||
SINGLE_MSR!(MSR_KVM_SYSTEM_TIME_NEW),
|
||||
SINGLE_MSR!(MSR_KVM_ASYNC_PF_EN),
|
||||
SINGLE_MSR!(MSR_KVM_STEAL_TIME),
|
||||
SINGLE_MSR!(MSR_KVM_PV_EOI_EN),
|
||||
SINGLE_MSR!(MSR_EFER),
|
||||
SINGLE_MSR!(MSR_STAR),
|
||||
SINGLE_MSR!(MSR_LSTAR),
|
||||
SINGLE_MSR!(MSR_CSTAR),
|
||||
SINGLE_MSR!(MSR_SYSCALL_MASK),
|
||||
SINGLE_MSR!(MSR_FS_BASE),
|
||||
SINGLE_MSR!(MSR_GS_BASE),
|
||||
SINGLE_MSR!(MSR_KERNEL_GS_BASE),
|
||||
SINGLE_MSR!(MSR_TSC_AUX),
|
||||
];
|
||||
|
||||
/// Specifies whether a particular MSR should be included in vcpu serialization.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `index` - The index of the MSR that is checked whether it's needed for serialization.
|
||||
pub fn msr_should_serialize(index: u32) -> bool {
|
||||
// Blacklisted MSRs not exported by Linux: IA32_FEATURE_CONTROL and IA32_MCG_CTL
|
||||
if index == MSR_IA32_FEATURE_CONTROL || index == MSR_IA32_MCG_CTL {
|
||||
return false;
|
||||
};
|
||||
WHITELISTED_MSR_RANGES
|
||||
.iter()
|
||||
.any(|range| range.contains(index))
|
||||
}
|
||||
|
||||
/// Returns the list of supported, serializable MSRs.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `kvm_fd` - Structure that holds the KVM's fd.
|
||||
pub fn supported_guest_msrs(kvm_fd: &Kvm) -> Result<MsrList> {
|
||||
let mut msr_list = kvm_fd
|
||||
.get_msr_index_list()
|
||||
.map_err(Error::GetSupportedModelSpecificRegisters)?;
|
||||
|
||||
msr_list.retain(|msr_index| msr_should_serialize(*msr_index));
|
||||
|
||||
Ok(msr_list)
|
||||
}
|
||||
|
||||
/// Base MSR for APIC
|
||||
pub const APIC_BASE_MSR: u32 = 0x800;
|
||||
|
||||
/// Number of APIC MSR indexes
|
||||
pub const APIC_MSR_INDEXES: u32 = 0x400;
|
||||
|
||||
/// Custom MSRs fall in the range 0x4b564d00-0x4b564dff
|
||||
pub const MSR_KVM_WALL_CLOCK_NEW: u32 = 0x4b56_4d00;
|
||||
pub const MSR_KVM_SYSTEM_TIME_NEW: u32 = 0x4b56_4d01;
|
||||
pub const MSR_KVM_ASYNC_PF_EN: u32 = 0x4b56_4d02;
|
||||
pub const MSR_KVM_STEAL_TIME: u32 = 0x4b56_4d03;
|
||||
pub const MSR_KVM_PV_EOI_EN: u32 = 0x4b56_4d04;
|
||||
|
||||
pub const MSR_EFER: u32 = 3221225600;
|
||||
pub const MSR_STAR: u32 = 3221225601;
|
||||
pub const MSR_LSTAR: u32 = 3221225602;
|
||||
pub const MSR_CSTAR: u32 = 3221225603;
|
||||
pub const MSR_SYSCALL_MASK: u32 = 3221225604;
|
||||
pub const MSR_FS_BASE: u32 = 3221225728;
|
||||
pub const MSR_GS_BASE: u32 = 3221225729;
|
||||
pub const MSR_KERNEL_GS_BASE: u32 = 3221225730;
|
||||
pub const MSR_TSC_AUX: u32 = 3221225731;
|
||||
pub const _EFER_SCE: u32 = 0;
|
||||
pub const _EFER_LME: u32 = 8;
|
||||
pub const _EFER_LMA: u32 = 10;
|
||||
pub const _EFER_NX: u32 = 11;
|
||||
pub const _EFER_SVME: u32 = 12;
|
||||
pub const _EFER_LMSLE: u32 = 13;
|
||||
pub const _EFER_FFXSR: u32 = 14;
|
||||
pub const EFER_SCE: u32 = 1;
|
||||
pub const EFER_LME: u32 = 256;
|
||||
pub const EFER_LMA: u32 = 1024;
|
||||
pub const EFER_NX: u32 = 2048;
|
||||
pub const EFER_SVME: u32 = 4096;
|
||||
pub const EFER_LMSLE: u32 = 8192;
|
||||
pub const EFER_FFXSR: u32 = 16384;
|
||||
pub const MSR_IA32_SPEC_CTRL: u32 = 72;
|
||||
pub const SPEC_CTRL_IBRS: u32 = 1;
|
||||
pub const SPEC_CTRL_STIBP: u32 = 2;
|
||||
pub const SPEC_CTRL_SSBD_SHIFT: u32 = 2;
|
||||
pub const SPEC_CTRL_SSBD: u32 = 4;
|
||||
pub const MSR_IA32_PRED_CMD: u32 = 73;
|
||||
pub const PRED_CMD_IBPB: u32 = 1;
|
||||
pub const MSR_IA32_PERFCTR0: u32 = 193;
|
||||
pub const MSR_IA32_PERFCTR1: u32 = 194;
|
||||
pub const MSR_FSB_FREQ: u32 = 205;
|
||||
pub const MSR_PLATFORM_INFO: u32 = 206;
|
||||
pub const MSR_NHM_SNB_PKG_CST_CFG_CTL: u32 = 226;
|
||||
pub const NHM_C3_AUTO_DEMOTE: u32 = 33554432;
|
||||
pub const NHM_C1_AUTO_DEMOTE: u32 = 67108864;
|
||||
pub const ATM_LNC_C6_AUTO_DEMOTE: u32 = 33554432;
|
||||
pub const SNB_C1_AUTO_UNDEMOTE: u32 = 134217728;
|
||||
pub const SNB_C3_AUTO_UNDEMOTE: u32 = 268435456;
|
||||
pub const MSR_MTRRcap: u32 = 254;
|
||||
pub const MSR_IA32_ARCH_CAPABILITIES: u32 = 266;
|
||||
pub const ARCH_CAP_RDCL_NO: u32 = 1;
|
||||
pub const ARCH_CAP_IBRS_ALL: u32 = 2;
|
||||
pub const ARCH_CAP_SKIP_VMENTRY_L1DFLUSH: u32 = 8;
|
||||
pub const ARCH_CAP_SSB_NO: u32 = 16;
|
||||
pub const MSR_IA32_FLUSH_CMD: u32 = 267;
|
||||
pub const L1D_FLUSH: u32 = 1;
|
||||
pub const MSR_PKG_CST_CONFIG_CONTROL: u32 = 226;
|
||||
pub const MSR_IA32_BBL_CR_CTL: u32 = 281;
|
||||
pub const MSR_IA32_BBL_CR_CTL3: u32 = 286;
|
||||
pub const MSR_IA32_SYSENTER_CS: u32 = 372;
|
||||
pub const MSR_IA32_SYSENTER_ESP: u32 = 373;
|
||||
pub const MSR_IA32_SYSENTER_EIP: u32 = 374;
|
||||
pub const MSR_IA32_MCG_CAP: u32 = 377;
|
||||
pub const MSR_IA32_MCG_STATUS: u32 = 378;
|
||||
pub const MSR_IA32_MCG_CTL: u32 = 379;
|
||||
pub const MSR_IA32_MCG_EXT_CTL: u32 = 1232;
|
||||
pub const MSR_OFFCORE_RSP_0: u32 = 422;
|
||||
pub const MSR_OFFCORE_RSP_1: u32 = 423;
|
||||
pub const MSR_TURBO_RATIO_LIMIT: u32 = 429;
|
||||
pub const MSR_TURBO_RATIO_LIMIT1: u32 = 430;
|
||||
pub const MSR_TURBO_RATIO_LIMIT2: u32 = 431;
|
||||
pub const MSR_LBR_SELECT: u32 = 456;
|
||||
pub const MSR_LBR_TOS: u32 = 457;
|
||||
pub const MSR_LBR_NHM_FROM: u32 = 1664;
|
||||
pub const MSR_LBR_NHM_TO: u32 = 1728;
|
||||
pub const MSR_LBR_CORE_FROM: u32 = 64;
|
||||
pub const MSR_LBR_CORE_TO: u32 = 96;
|
||||
pub const MSR_LBR_INFO_0: u32 = 3520;
|
||||
pub const LBR_INFO_CYCLES: u32 = 65535;
|
||||
pub const MSR_IA32_PEBS_ENABLE: u32 = 1009;
|
||||
pub const MSR_IA32_DS_AREA: u32 = 1536;
|
||||
pub const MSR_IA32_PERF_CAPABILITIES: u32 = 837;
|
||||
pub const MSR_PEBS_LD_LAT_THRESHOLD: u32 = 1014;
|
||||
pub const MSR_IA32_RTIT_CTL: u32 = 1392;
|
||||
pub const MSR_IA32_RTIT_STATUS: u32 = 1393;
|
||||
pub const MSR_IA32_RTIT_ADDR0_A: u32 = 1408;
|
||||
pub const MSR_IA32_RTIT_ADDR0_B: u32 = 1409;
|
||||
pub const MSR_IA32_RTIT_ADDR1_A: u32 = 1410;
|
||||
pub const MSR_IA32_RTIT_ADDR1_B: u32 = 1411;
|
||||
pub const MSR_IA32_RTIT_ADDR2_A: u32 = 1412;
|
||||
pub const MSR_IA32_RTIT_ADDR2_B: u32 = 1413;
|
||||
pub const MSR_IA32_RTIT_ADDR3_A: u32 = 1414;
|
||||
pub const MSR_IA32_RTIT_ADDR3_B: u32 = 1415;
|
||||
pub const MSR_IA32_RTIT_CR3_MATCH: u32 = 1394;
|
||||
pub const MSR_IA32_RTIT_OUTPUT_BASE: u32 = 1376;
|
||||
pub const MSR_IA32_RTIT_OUTPUT_MASK: u32 = 1377;
|
||||
pub const MSR_MTRRfix64K_00000: u32 = 592;
|
||||
pub const MSR_MTRRfix16K_80000: u32 = 600;
|
||||
pub const MSR_MTRRfix16K_A0000: u32 = 601;
|
||||
pub const MSR_MTRRfix4K_C0000: u32 = 616;
|
||||
pub const MSR_MTRRfix4K_C8000: u32 = 617;
|
||||
pub const MSR_MTRRfix4K_D0000: u32 = 618;
|
||||
pub const MSR_MTRRfix4K_D8000: u32 = 619;
|
||||
pub const MSR_MTRRfix4K_E0000: u32 = 620;
|
||||
pub const MSR_MTRRfix4K_E8000: u32 = 621;
|
||||
pub const MSR_MTRRfix4K_F0000: u32 = 622;
|
||||
pub const MSR_MTRRfix4K_F8000: u32 = 623;
|
||||
pub const MSR_MTRRdefType: u32 = 767;
|
||||
pub const MSR_IA32_CR_PAT: u32 = 631;
|
||||
pub const MSR_IA32_DEBUGCTLMSR: u32 = 473;
|
||||
pub const MSR_IA32_LASTBRANCHFROMIP: u32 = 475;
|
||||
pub const MSR_IA32_LASTBRANCHTOIP: u32 = 476;
|
||||
pub const MSR_IA32_LASTINTFROMIP: u32 = 477;
|
||||
pub const MSR_IA32_LASTINTTOIP: u32 = 478;
|
||||
pub const DEBUGCTLMSR_LBR: u32 = 1;
|
||||
pub const DEBUGCTLMSR_BTF_SHIFT: u32 = 1;
|
||||
pub const DEBUGCTLMSR_BTF: u32 = 2;
|
||||
pub const DEBUGCTLMSR_TR: u32 = 64;
|
||||
pub const DEBUGCTLMSR_BTS: u32 = 128;
|
||||
pub const DEBUGCTLMSR_BTINT: u32 = 256;
|
||||
pub const DEBUGCTLMSR_BTS_OFF_OS: u32 = 512;
|
||||
pub const DEBUGCTLMSR_BTS_OFF_USR: u32 = 1024;
|
||||
pub const DEBUGCTLMSR_FREEZE_LBRS_ON_PMI: u32 = 2048;
|
||||
pub const MSR_PEBS_FRONTEND: u32 = 1015;
|
||||
pub const MSR_IA32_POWER_CTL: u32 = 508;
|
||||
pub const MSR_IA32_MC0_CTL: u32 = 1024;
|
||||
pub const MSR_IA32_MC0_STATUS: u32 = 1025;
|
||||
pub const MSR_IA32_MC0_ADDR: u32 = 1026;
|
||||
pub const MSR_IA32_MC0_MISC: u32 = 1027;
|
||||
pub const MSR_PKG_C3_RESIDENCY: u32 = 1016;
|
||||
pub const MSR_PKG_C6_RESIDENCY: u32 = 1017;
|
||||
pub const MSR_PKG_C7_RESIDENCY: u32 = 1018;
|
||||
pub const MSR_CORE_C3_RESIDENCY: u32 = 1020;
|
||||
pub const MSR_CORE_C6_RESIDENCY: u32 = 1021;
|
||||
pub const MSR_CORE_C7_RESIDENCY: u32 = 1022;
|
||||
pub const MSR_KNL_CORE_C6_RESIDENCY: u32 = 1023;
|
||||
pub const MSR_PKG_C2_RESIDENCY: u32 = 1549;
|
||||
pub const MSR_PKG_C8_RESIDENCY: u32 = 1584;
|
||||
pub const MSR_PKG_C9_RESIDENCY: u32 = 1585;
|
||||
pub const MSR_PKG_C10_RESIDENCY: u32 = 1586;
|
||||
pub const MSR_PKGC3_IRTL: u32 = 1546;
|
||||
pub const MSR_PKGC6_IRTL: u32 = 1547;
|
||||
pub const MSR_PKGC7_IRTL: u32 = 1548;
|
||||
pub const MSR_PKGC8_IRTL: u32 = 1587;
|
||||
pub const MSR_PKGC9_IRTL: u32 = 1588;
|
||||
pub const MSR_PKGC10_IRTL: u32 = 1589;
|
||||
pub const MSR_RAPL_POWER_UNIT: u32 = 1542;
|
||||
pub const MSR_PKG_POWER_LIMIT: u32 = 1552;
|
||||
pub const MSR_PKG_ENERGY_STATUS: u32 = 1553;
|
||||
pub const MSR_PKG_PERF_STATUS: u32 = 1555;
|
||||
pub const MSR_PKG_POWER_INFO: u32 = 1556;
|
||||
pub const MSR_DRAM_POWER_LIMIT: u32 = 1560;
|
||||
pub const MSR_DRAM_ENERGY_STATUS: u32 = 1561;
|
||||
pub const MSR_DRAM_PERF_STATUS: u32 = 1563;
|
||||
pub const MSR_DRAM_POWER_INFO: u32 = 1564;
|
||||
pub const MSR_PP0_POWER_LIMIT: u32 = 1592;
|
||||
pub const MSR_PP0_ENERGY_STATUS: u32 = 1593;
|
||||
pub const MSR_PP0_POLICY: u32 = 1594;
|
||||
pub const MSR_PP0_PERF_STATUS: u32 = 1595;
|
||||
pub const MSR_PP1_POWER_LIMIT: u32 = 1600;
|
||||
pub const MSR_PP1_ENERGY_STATUS: u32 = 1601;
|
||||
pub const MSR_PP1_POLICY: u32 = 1602;
|
||||
pub const MSR_CONFIG_TDP_NOMINAL: u32 = 1608;
|
||||
pub const MSR_CONFIG_TDP_LEVEL_1: u32 = 1609;
|
||||
pub const MSR_CONFIG_TDP_LEVEL_2: u32 = 1610;
|
||||
pub const MSR_CONFIG_TDP_CONTROL: u32 = 1611;
|
||||
pub const MSR_TURBO_ACTIVATION_RATIO: u32 = 1612;
|
||||
pub const MSR_PLATFORM_ENERGY_STATUS: u32 = 1613;
|
||||
pub const MSR_PKG_WEIGHTED_CORE_C0_RES: u32 = 1624;
|
||||
pub const MSR_PKG_ANY_CORE_C0_RES: u32 = 1625;
|
||||
pub const MSR_PKG_ANY_GFXE_C0_RES: u32 = 1626;
|
||||
pub const MSR_PKG_BOTH_CORE_GFXE_C0_RES: u32 = 1627;
|
||||
pub const MSR_CORE_C1_RES: u32 = 1632;
|
||||
pub const MSR_CC6_DEMOTION_POLICY_CONFIG: u32 = 1640;
|
||||
pub const MSR_MC6_DEMOTION_POLICY_CONFIG: u32 = 1641;
|
||||
pub const MSR_CORE_PERF_LIMIT_REASONS: u32 = 1680;
|
||||
pub const MSR_GFX_PERF_LIMIT_REASONS: u32 = 1712;
|
||||
pub const MSR_RING_PERF_LIMIT_REASONS: u32 = 1713;
|
||||
pub const MSR_PPERF: u32 = 1614;
|
||||
pub const MSR_PERF_LIMIT_REASONS: u32 = 1615;
|
||||
pub const MSR_PM_ENABLE: u32 = 1904;
|
||||
pub const MSR_HWP_CAPABILITIES: u32 = 1905;
|
||||
pub const MSR_HWP_REQUEST_PKG: u32 = 1906;
|
||||
pub const MSR_HWP_INTERRUPT: u32 = 1907;
|
||||
pub const MSR_HWP_REQUEST: u32 = 1908;
|
||||
pub const MSR_HWP_STATUS: u32 = 1911;
|
||||
pub const HWP_BASE_BIT: u32 = 128;
|
||||
pub const HWP_NOTIFICATIONS_BIT: u32 = 256;
|
||||
pub const HWP_ACTIVITY_WINDOW_BIT: u32 = 512;
|
||||
pub const HWP_ENERGY_PERF_PREFERENCE_BIT: u32 = 1024;
|
||||
pub const HWP_PACKAGE_LEVEL_REQUEST_BIT: u32 = 2048;
|
||||
pub const MSR_AMD64_MC0_MASK: u32 = 3221291076;
|
||||
pub const MSR_IA32_MC0_CTL2: u32 = 640;
|
||||
pub const MSR_P6_PERFCTR0: u32 = 193;
|
||||
pub const MSR_P6_PERFCTR1: u32 = 194;
|
||||
pub const MSR_P6_EVNTSEL0: u32 = 390;
|
||||
pub const MSR_P6_EVNTSEL1: u32 = 391;
|
||||
pub const MSR_KNC_PERFCTR0: u32 = 32;
|
||||
pub const MSR_KNC_PERFCTR1: u32 = 33;
|
||||
pub const MSR_KNC_EVNTSEL0: u32 = 40;
|
||||
pub const MSR_KNC_EVNTSEL1: u32 = 41;
|
||||
pub const MSR_IA32_PMC0: u32 = 1217;
|
||||
pub const MSR_AMD64_PATCH_LEVEL: u32 = 139;
|
||||
pub const MSR_AMD64_TSC_RATIO: u32 = 3221225732;
|
||||
pub const MSR_AMD64_NB_CFG: u32 = 3221291039;
|
||||
pub const MSR_AMD64_PATCH_LOADER: u32 = 3221291040;
|
||||
pub const MSR_AMD64_OSVW_ID_LENGTH: u32 = 3221291328;
|
||||
pub const MSR_AMD64_OSVW_STATUS: u32 = 3221291329;
|
||||
pub const MSR_AMD64_LS_CFG: u32 = 3221295136;
|
||||
pub const MSR_AMD64_DC_CFG: u32 = 3221295138;
|
||||
pub const MSR_AMD64_BU_CFG2: u32 = 3221295146;
|
||||
pub const MSR_AMD64_IBSFETCHCTL: u32 = 3221295152;
|
||||
pub const MSR_AMD64_IBSFETCHLINAD: u32 = 3221295153;
|
||||
pub const MSR_AMD64_IBSFETCHPHYSAD: u32 = 3221295154;
|
||||
pub const MSR_AMD64_IBSFETCH_REG_COUNT: u32 = 3;
|
||||
pub const MSR_AMD64_IBSFETCH_REG_MASK: u32 = 7;
|
||||
pub const MSR_AMD64_IBSOPCTL: u32 = 3221295155;
|
||||
pub const MSR_AMD64_IBSOPRIP: u32 = 3221295156;
|
||||
pub const MSR_AMD64_IBSOPDATA: u32 = 3221295157;
|
||||
pub const MSR_AMD64_IBSOPDATA2: u32 = 3221295158;
|
||||
pub const MSR_AMD64_IBSOPDATA3: u32 = 3221295159;
|
||||
pub const MSR_AMD64_IBSDCLINAD: u32 = 3221295160;
|
||||
pub const MSR_AMD64_IBSDCPHYSAD: u32 = 3221295161;
|
||||
pub const MSR_AMD64_IBSOP_REG_COUNT: u32 = 7;
|
||||
pub const MSR_AMD64_IBSOP_REG_MASK: u32 = 127;
|
||||
pub const MSR_AMD64_IBSCTL: u32 = 3221295162;
|
||||
pub const MSR_AMD64_IBSBRTARGET: u32 = 3221295163;
|
||||
pub const MSR_AMD64_IBSOPDATA4: u32 = 3221295165;
|
||||
pub const MSR_AMD64_IBS_REG_COUNT_MAX: u32 = 8;
|
||||
pub const MSR_AMD64_VIRT_SPEC_CTRL: u32 = 3221291295;
|
||||
pub const MSR_F17H_IRPERF: u32 = 3221225705;
|
||||
pub const MSR_F16H_L2I_PERF_CTL: u32 = 3221291568;
|
||||
pub const MSR_F16H_L2I_PERF_CTR: u32 = 3221291569;
|
||||
pub const MSR_F16H_DR1_ADDR_MASK: u32 = 3221295129;
|
||||
pub const MSR_F16H_DR2_ADDR_MASK: u32 = 3221295130;
|
||||
pub const MSR_F16H_DR3_ADDR_MASK: u32 = 3221295131;
|
||||
pub const MSR_F16H_DR0_ADDR_MASK: u32 = 3221295143;
|
||||
pub const MSR_F15H_PERF_CTL: u32 = 3221291520;
|
||||
pub const MSR_F15H_PERF_CTR: u32 = 3221291521;
|
||||
pub const MSR_F15H_NB_PERF_CTL: u32 = 3221291584;
|
||||
pub const MSR_F15H_NB_PERF_CTR: u32 = 3221291585;
|
||||
pub const MSR_F15H_PTSC: u32 = 3221291648;
|
||||
pub const MSR_F15H_IC_CFG: u32 = 3221295137;
|
||||
pub const MSR_FAM10H_MMIO_CONF_BASE: u32 = 3221291096;
|
||||
pub const FAM10H_MMIO_CONF_ENABLE: u32 = 1;
|
||||
pub const FAM10H_MMIO_CONF_BUSRANGE_MASK: u32 = 15;
|
||||
pub const FAM10H_MMIO_CONF_BUSRANGE_SHIFT: u32 = 2;
|
||||
pub const FAM10H_MMIO_CONF_BASE_MASK: u32 = 268435455;
|
||||
pub const FAM10H_MMIO_CONF_BASE_SHIFT: u32 = 20;
|
||||
pub const MSR_FAM10H_NODE_ID: u32 = 3221295116;
|
||||
pub const MSR_F10H_DECFG: u32 = 3221295145;
|
||||
pub const MSR_F10H_DECFG_LFENCE_SERIALIZE_BIT: u32 = 1;
|
||||
pub const MSR_K8_TOP_MEM1: u32 = 3221291034;
|
||||
pub const MSR_K8_TOP_MEM2: u32 = 3221291037;
|
||||
pub const MSR_K8_SYSCFG: u32 = 3221291024;
|
||||
pub const MSR_K8_INT_PENDING_MSG: u32 = 3221291093;
|
||||
pub const K8_INTP_C1E_ACTIVE_MASK: u32 = 402653184;
|
||||
pub const MSR_K8_TSEG_ADDR: u32 = 3221291282;
|
||||
pub const MSR_K8_TSEG_MASK: u32 = 3221291283;
|
||||
pub const K8_MTRRFIXRANGE_DRAM_ENABLE: u32 = 262144;
|
||||
pub const K8_MTRRFIXRANGE_DRAM_MODIFY: u32 = 524288;
|
||||
pub const K8_MTRR_RDMEM_WRMEM_MASK: u32 = 404232216;
|
||||
pub const MSR_K7_EVNTSEL0: u32 = 3221291008;
|
||||
pub const MSR_K7_PERFCTR0: u32 = 3221291012;
|
||||
pub const MSR_K7_EVNTSEL1: u32 = 3221291009;
|
||||
pub const MSR_K7_PERFCTR1: u32 = 3221291013;
|
||||
pub const MSR_K7_EVNTSEL2: u32 = 3221291010;
|
||||
pub const MSR_K7_PERFCTR2: u32 = 3221291014;
|
||||
pub const MSR_K7_EVNTSEL3: u32 = 3221291011;
|
||||
pub const MSR_K7_PERFCTR3: u32 = 3221291015;
|
||||
pub const MSR_K7_CLK_CTL: u32 = 3221291035;
|
||||
pub const MSR_K7_HWCR: u32 = 3221291029;
|
||||
pub const MSR_K7_FID_VID_CTL: u32 = 3221291073;
|
||||
pub const MSR_K7_FID_VID_STATUS: u32 = 3221291074;
|
||||
pub const MSR_K6_WHCR: u32 = 3221225602;
|
||||
pub const MSR_K6_UWCCR: u32 = 3221225605;
|
||||
pub const MSR_K6_EPMR: u32 = 3221225606;
|
||||
pub const MSR_K6_PSOR: u32 = 3221225607;
|
||||
pub const MSR_K6_PFIR: u32 = 3221225608;
|
||||
pub const MSR_IDT_FCR1: u32 = 263;
|
||||
pub const MSR_IDT_FCR2: u32 = 264;
|
||||
pub const MSR_IDT_FCR3: u32 = 265;
|
||||
pub const MSR_IDT_FCR4: u32 = 266;
|
||||
pub const MSR_IDT_MCR0: u32 = 272;
|
||||
pub const MSR_IDT_MCR1: u32 = 273;
|
||||
pub const MSR_IDT_MCR2: u32 = 274;
|
||||
pub const MSR_IDT_MCR3: u32 = 275;
|
||||
pub const MSR_IDT_MCR4: u32 = 276;
|
||||
pub const MSR_IDT_MCR5: u32 = 277;
|
||||
pub const MSR_IDT_MCR6: u32 = 278;
|
||||
pub const MSR_IDT_MCR7: u32 = 279;
|
||||
pub const MSR_IDT_MCR_CTRL: u32 = 288;
|
||||
pub const MSR_VIA_FCR: u32 = 4359;
|
||||
pub const MSR_VIA_LONGHAUL: u32 = 4362;
|
||||
pub const MSR_VIA_RNG: u32 = 4363;
|
||||
pub const MSR_VIA_BCR2: u32 = 4423;
|
||||
pub const MSR_TMTA_LONGRUN_CTRL: u32 = 2156298256;
|
||||
pub const MSR_TMTA_LONGRUN_FLAGS: u32 = 2156298257;
|
||||
pub const MSR_TMTA_LRTI_READOUT: u32 = 2156298264;
|
||||
pub const MSR_TMTA_LRTI_VOLT_MHZ: u32 = 2156298266;
|
||||
pub const MSR_IA32_P5_MC_ADDR: u32 = 0;
|
||||
pub const MSR_IA32_P5_MC_TYPE: u32 = 1;
|
||||
pub const MSR_IA32_TSC: u32 = 16;
|
||||
pub const MSR_IA32_PLATFORM_ID: u32 = 23;
|
||||
pub const MSR_IA32_EBL_CR_POWERON: u32 = 42;
|
||||
pub const MSR_EBC_FREQUENCY_ID: u32 = 44;
|
||||
pub const MSR_SMI_COUNT: u32 = 52;
|
||||
pub const MSR_IA32_FEATURE_CONTROL: u32 = 58;
|
||||
pub const MSR_IA32_TSC_ADJUST: u32 = 59;
|
||||
pub const MSR_IA32_BNDCFGS: u32 = 3472;
|
||||
pub const MSR_IA32_BNDCFGS_RSVD: u32 = 4092;
|
||||
pub const MSR_IA32_XSS: u32 = 3488;
|
||||
pub const FEATURE_CONTROL_LOCKED: u32 = 1;
|
||||
pub const FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX: u32 = 2;
|
||||
pub const FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX: u32 = 4;
|
||||
pub const FEATURE_CONTROL_LMCE: u32 = 1048576;
|
||||
pub const MSR_IA32_APICBASE: u32 = 27;
|
||||
pub const MSR_IA32_APICBASE_BSP: u32 = 256;
|
||||
pub const MSR_IA32_APICBASE_ENABLE: u32 = 2048;
|
||||
pub const MSR_IA32_APICBASE_BASE: u32 = 4294963200;
|
||||
pub const MSR_IA32_TSCDEADLINE: u32 = 1760;
|
||||
pub const MSR_IA32_UCODE_WRITE: u32 = 121;
|
||||
pub const MSR_IA32_UCODE_REV: u32 = 139;
|
||||
pub const MSR_IA32_SMM_MONITOR_CTL: u32 = 155;
|
||||
pub const MSR_IA32_SMBASE: u32 = 158;
|
||||
pub const MSR_IA32_PERF_STATUS: u32 = 408;
|
||||
pub const MSR_IA32_PERF_CTL: u32 = 409;
|
||||
pub const INTEL_PERF_CTL_MASK: u32 = 65535;
|
||||
pub const MSR_AMD_PSTATE_DEF_BASE: u32 = 3221291108;
|
||||
pub const MSR_AMD_PERF_STATUS: u32 = 3221291107;
|
||||
pub const MSR_AMD_PERF_CTL: u32 = 3221291106;
|
||||
pub const MSR_IA32_MPERF: u32 = 231;
|
||||
pub const MSR_IA32_APERF: u32 = 232;
|
||||
pub const MSR_IA32_THERM_CONTROL: u32 = 410;
|
||||
pub const MSR_IA32_THERM_INTERRUPT: u32 = 411;
|
||||
pub const THERM_INT_HIGH_ENABLE: u32 = 1;
|
||||
pub const THERM_INT_LOW_ENABLE: u32 = 2;
|
||||
pub const THERM_INT_PLN_ENABLE: u32 = 16777216;
|
||||
pub const MSR_IA32_THERM_STATUS: u32 = 412;
|
||||
pub const THERM_STATUS_PROCHOT: u32 = 1;
|
||||
pub const THERM_STATUS_POWER_LIMIT: u32 = 1024;
|
||||
pub const MSR_THERM2_CTL: u32 = 413;
|
||||
pub const MSR_THERM2_CTL_TM_SELECT: u32 = 65536;
|
||||
pub const MSR_IA32_MISC_ENABLE: u32 = 416;
|
||||
pub const MSR_IA32_TEMPERATURE_TARGET: u32 = 418;
|
||||
pub const MSR_MISC_FEATURE_CONTROL: u32 = 420;
|
||||
pub const MSR_MISC_PWR_MGMT: u32 = 426;
|
||||
pub const MSR_IA32_ENERGY_PERF_BIAS: u32 = 432;
|
||||
pub const ENERGY_PERF_BIAS_PERFORMANCE: u32 = 0;
|
||||
pub const ENERGY_PERF_BIAS_NORMAL: u32 = 6;
|
||||
pub const ENERGY_PERF_BIAS_POWERSAVE: u32 = 15;
|
||||
pub const MSR_IA32_PACKAGE_THERM_STATUS: u32 = 433;
|
||||
pub const PACKAGE_THERM_STATUS_PROCHOT: u32 = 1;
|
||||
pub const PACKAGE_THERM_STATUS_POWER_LIMIT: u32 = 1024;
|
||||
pub const MSR_IA32_PACKAGE_THERM_INTERRUPT: u32 = 434;
|
||||
pub const PACKAGE_THERM_INT_HIGH_ENABLE: u32 = 1;
|
||||
pub const PACKAGE_THERM_INT_LOW_ENABLE: u32 = 2;
|
||||
pub const PACKAGE_THERM_INT_PLN_ENABLE: u32 = 16777216;
|
||||
pub const THERM_INT_THRESHOLD0_ENABLE: u32 = 32768;
|
||||
pub const THERM_SHIFT_THRESHOLD0: u32 = 8;
|
||||
pub const THERM_MASK_THRESHOLD0: u32 = 32512;
|
||||
pub const THERM_INT_THRESHOLD1_ENABLE: u32 = 8388608;
|
||||
pub const THERM_SHIFT_THRESHOLD1: u32 = 16;
|
||||
pub const THERM_MASK_THRESHOLD1: u32 = 8323072;
|
||||
pub const THERM_STATUS_THRESHOLD0: u32 = 64;
|
||||
pub const THERM_LOG_THRESHOLD0: u32 = 128;
|
||||
pub const THERM_STATUS_THRESHOLD1: u32 = 256;
|
||||
pub const THERM_LOG_THRESHOLD1: u32 = 512;
|
||||
pub const MSR_IA32_MISC_ENABLE_FAST_STRING_BIT: u32 = 0;
|
||||
pub const MSR_IA32_MISC_ENABLE_FAST_STRING: u32 = 1;
|
||||
pub const MSR_IA32_MISC_ENABLE_TCC_BIT: u32 = 1;
|
||||
pub const MSR_IA32_MISC_ENABLE_TCC: u32 = 2;
|
||||
pub const MSR_IA32_MISC_ENABLE_EMON_BIT: u32 = 7;
|
||||
pub const MSR_IA32_MISC_ENABLE_EMON: u32 = 128;
|
||||
pub const MSR_IA32_MISC_ENABLE_BTS_UNAVAIL_BIT: u32 = 11;
|
||||
pub const MSR_IA32_MISC_ENABLE_BTS_UNAVAIL: u32 = 2048;
|
||||
pub const MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL_BIT: u32 = 12;
|
||||
pub const MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL: u32 = 4096;
|
||||
pub const MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP_BIT: u32 = 16;
|
||||
pub const MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP: u32 = 65536;
|
||||
pub const MSR_IA32_MISC_ENABLE_MWAIT_BIT: u32 = 18;
|
||||
pub const MSR_IA32_MISC_ENABLE_MWAIT: u32 = 262144;
|
||||
pub const MSR_IA32_MISC_ENABLE_LIMIT_CPUID_BIT: u32 = 22;
|
||||
pub const MSR_IA32_MISC_ENABLE_LIMIT_CPUID: u32 = 4194304;
|
||||
pub const MSR_IA32_MISC_ENABLE_XTPR_DISABLE_BIT: u32 = 23;
|
||||
pub const MSR_IA32_MISC_ENABLE_XTPR_DISABLE: u32 = 8388608;
|
||||
pub const MSR_IA32_MISC_ENABLE_XD_DISABLE_BIT: u32 = 34;
|
||||
pub const MSR_IA32_MISC_ENABLE_XD_DISABLE: u64 = 17179869184;
|
||||
pub const MSR_IA32_MISC_ENABLE_X87_COMPAT_BIT: u32 = 2;
|
||||
pub const MSR_IA32_MISC_ENABLE_X87_COMPAT: u32 = 4;
|
||||
pub const MSR_IA32_MISC_ENABLE_TM1_BIT: u32 = 3;
|
||||
pub const MSR_IA32_MISC_ENABLE_TM1: u32 = 8;
|
||||
pub const MSR_IA32_MISC_ENABLE_SPLIT_LOCK_DISABLE_BIT: u32 = 4;
|
||||
pub const MSR_IA32_MISC_ENABLE_SPLIT_LOCK_DISABLE: u32 = 16;
|
||||
pub const MSR_IA32_MISC_ENABLE_L3CACHE_DISABLE_BIT: u32 = 6;
|
||||
pub const MSR_IA32_MISC_ENABLE_L3CACHE_DISABLE: u32 = 64;
|
||||
pub const MSR_IA32_MISC_ENABLE_SUPPRESS_LOCK_BIT: u32 = 8;
|
||||
pub const MSR_IA32_MISC_ENABLE_SUPPRESS_LOCK: u32 = 256;
|
||||
pub const MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE_BIT: u32 = 9;
|
||||
pub const MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE: u32 = 512;
|
||||
pub const MSR_IA32_MISC_ENABLE_FERR_BIT: u32 = 10;
|
||||
pub const MSR_IA32_MISC_ENABLE_FERR: u32 = 1024;
|
||||
pub const MSR_IA32_MISC_ENABLE_FERR_MULTIPLEX_BIT: u32 = 10;
|
||||
pub const MSR_IA32_MISC_ENABLE_FERR_MULTIPLEX: u32 = 1024;
|
||||
pub const MSR_IA32_MISC_ENABLE_TM2_BIT: u32 = 13;
|
||||
pub const MSR_IA32_MISC_ENABLE_TM2: u32 = 8192;
|
||||
pub const MSR_IA32_MISC_ENABLE_ADJ_PREF_DISABLE_BIT: u32 = 19;
|
||||
pub const MSR_IA32_MISC_ENABLE_ADJ_PREF_DISABLE: u32 = 524288;
|
||||
pub const MSR_IA32_MISC_ENABLE_SPEEDSTEP_LOCK_BIT: u32 = 20;
|
||||
pub const MSR_IA32_MISC_ENABLE_SPEEDSTEP_LOCK: u32 = 1048576;
|
||||
pub const MSR_IA32_MISC_ENABLE_L1D_CONTEXT_BIT: u32 = 24;
|
||||
pub const MSR_IA32_MISC_ENABLE_L1D_CONTEXT: u32 = 16777216;
|
||||
pub const MSR_IA32_MISC_ENABLE_DCU_PREF_DISABLE_BIT: u32 = 37;
|
||||
pub const MSR_IA32_MISC_ENABLE_DCU_PREF_DISABLE: u64 = 137438953472;
|
||||
pub const MSR_IA32_MISC_ENABLE_TURBO_DISABLE_BIT: u32 = 38;
|
||||
pub const MSR_IA32_MISC_ENABLE_TURBO_DISABLE: u64 = 274877906944;
|
||||
pub const MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE_BIT: u32 = 39;
|
||||
pub const MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE: u64 = 549755813888;
|
||||
pub const MSR_IA32_TSC_DEADLINE: u32 = 1760;
|
||||
pub const MSR_TSX_FORCE_ABORT: u32 = 271;
|
||||
pub const MSR_TFA_RTM_FORCE_ABORT_BIT: u32 = 0;
|
||||
pub const MSR_IA32_MCG_EAX: u32 = 384;
|
||||
pub const MSR_IA32_MCG_EBX: u32 = 385;
|
||||
pub const MSR_IA32_MCG_ECX: u32 = 386;
|
||||
pub const MSR_IA32_MCG_EDX: u32 = 387;
|
||||
pub const MSR_IA32_MCG_ESI: u32 = 388;
|
||||
pub const MSR_IA32_MCG_EDI: u32 = 389;
|
||||
pub const MSR_IA32_MCG_EBP: u32 = 390;
|
||||
pub const MSR_IA32_MCG_ESP: u32 = 391;
|
||||
pub const MSR_IA32_MCG_EFLAGS: u32 = 392;
|
||||
pub const MSR_IA32_MCG_EIP: u32 = 393;
|
||||
pub const MSR_IA32_MCG_RESERVED: u32 = 394;
|
||||
pub const MSR_P4_BPU_PERFCTR0: u32 = 768;
|
||||
pub const MSR_P4_BPU_PERFCTR1: u32 = 769;
|
||||
pub const MSR_P4_BPU_PERFCTR2: u32 = 770;
|
||||
pub const MSR_P4_BPU_PERFCTR3: u32 = 771;
|
||||
pub const MSR_P4_MS_PERFCTR0: u32 = 772;
|
||||
pub const MSR_P4_MS_PERFCTR1: u32 = 773;
|
||||
pub const MSR_P4_MS_PERFCTR2: u32 = 774;
|
||||
pub const MSR_P4_MS_PERFCTR3: u32 = 775;
|
||||
pub const MSR_P4_FLAME_PERFCTR0: u32 = 776;
|
||||
pub const MSR_P4_FLAME_PERFCTR1: u32 = 777;
|
||||
pub const MSR_P4_FLAME_PERFCTR2: u32 = 778;
|
||||
pub const MSR_P4_FLAME_PERFCTR3: u32 = 779;
|
||||
pub const MSR_P4_IQ_PERFCTR0: u32 = 780;
|
||||
pub const MSR_P4_IQ_PERFCTR1: u32 = 781;
|
||||
pub const MSR_P4_IQ_PERFCTR2: u32 = 782;
|
||||
pub const MSR_P4_IQ_PERFCTR3: u32 = 783;
|
||||
pub const MSR_P4_IQ_PERFCTR4: u32 = 784;
|
||||
pub const MSR_P4_IQ_PERFCTR5: u32 = 785;
|
||||
pub const MSR_P4_BPU_CCCR0: u32 = 864;
|
||||
pub const MSR_P4_BPU_CCCR1: u32 = 865;
|
||||
pub const MSR_P4_BPU_CCCR2: u32 = 866;
|
||||
pub const MSR_P4_BPU_CCCR3: u32 = 867;
|
||||
pub const MSR_P4_MS_CCCR0: u32 = 868;
|
||||
pub const MSR_P4_MS_CCCR1: u32 = 869;
|
||||
pub const MSR_P4_MS_CCCR2: u32 = 870;
|
||||
pub const MSR_P4_MS_CCCR3: u32 = 871;
|
||||
pub const MSR_P4_FLAME_CCCR0: u32 = 872;
|
||||
pub const MSR_P4_FLAME_CCCR1: u32 = 873;
|
||||
pub const MSR_P4_FLAME_CCCR2: u32 = 874;
|
||||
pub const MSR_P4_FLAME_CCCR3: u32 = 875;
|
||||
pub const MSR_P4_IQ_CCCR0: u32 = 876;
|
||||
pub const MSR_P4_IQ_CCCR1: u32 = 877;
|
||||
pub const MSR_P4_IQ_CCCR2: u32 = 878;
|
||||
pub const MSR_P4_IQ_CCCR3: u32 = 879;
|
||||
pub const MSR_P4_IQ_CCCR4: u32 = 880;
|
||||
pub const MSR_P4_IQ_CCCR5: u32 = 881;
|
||||
pub const MSR_P4_ALF_ESCR0: u32 = 970;
|
||||
pub const MSR_P4_ALF_ESCR1: u32 = 971;
|
||||
pub const MSR_P4_BPU_ESCR0: u32 = 946;
|
||||
pub const MSR_P4_BPU_ESCR1: u32 = 947;
|
||||
pub const MSR_P4_BSU_ESCR0: u32 = 928;
|
||||
pub const MSR_P4_BSU_ESCR1: u32 = 929;
|
||||
pub const MSR_P4_CRU_ESCR0: u32 = 952;
|
||||
pub const MSR_P4_CRU_ESCR1: u32 = 953;
|
||||
pub const MSR_P4_CRU_ESCR2: u32 = 972;
|
||||
pub const MSR_P4_CRU_ESCR3: u32 = 973;
|
||||
pub const MSR_P4_CRU_ESCR4: u32 = 992;
|
||||
pub const MSR_P4_CRU_ESCR5: u32 = 993;
|
||||
pub const MSR_P4_DAC_ESCR0: u32 = 936;
|
||||
pub const MSR_P4_DAC_ESCR1: u32 = 937;
|
||||
pub const MSR_P4_FIRM_ESCR0: u32 = 932;
|
||||
pub const MSR_P4_FIRM_ESCR1: u32 = 933;
|
||||
pub const MSR_P4_FLAME_ESCR0: u32 = 934;
|
||||
pub const MSR_P4_FLAME_ESCR1: u32 = 935;
|
||||
pub const MSR_P4_FSB_ESCR0: u32 = 930;
|
||||
pub const MSR_P4_FSB_ESCR1: u32 = 931;
|
||||
pub const MSR_P4_IQ_ESCR0: u32 = 954;
|
||||
pub const MSR_P4_IQ_ESCR1: u32 = 955;
|
||||
pub const MSR_P4_IS_ESCR0: u32 = 948;
|
||||
pub const MSR_P4_IS_ESCR1: u32 = 949;
|
||||
pub const MSR_P4_ITLB_ESCR0: u32 = 950;
|
||||
pub const MSR_P4_ITLB_ESCR1: u32 = 951;
|
||||
pub const MSR_P4_IX_ESCR0: u32 = 968;
|
||||
pub const MSR_P4_IX_ESCR1: u32 = 969;
|
||||
pub const MSR_P4_MOB_ESCR0: u32 = 938;
|
||||
pub const MSR_P4_MOB_ESCR1: u32 = 939;
|
||||
pub const MSR_P4_MS_ESCR0: u32 = 960;
|
||||
pub const MSR_P4_MS_ESCR1: u32 = 961;
|
||||
pub const MSR_P4_PMH_ESCR0: u32 = 940;
|
||||
pub const MSR_P4_PMH_ESCR1: u32 = 941;
|
||||
pub const MSR_P4_RAT_ESCR0: u32 = 956;
|
||||
pub const MSR_P4_RAT_ESCR1: u32 = 957;
|
||||
pub const MSR_P4_SAAT_ESCR0: u32 = 942;
|
||||
pub const MSR_P4_SAAT_ESCR1: u32 = 943;
|
||||
pub const MSR_P4_SSU_ESCR0: u32 = 958;
|
||||
pub const MSR_P4_SSU_ESCR1: u32 = 959;
|
||||
pub const MSR_P4_TBPU_ESCR0: u32 = 962;
|
||||
pub const MSR_P4_TBPU_ESCR1: u32 = 963;
|
||||
pub const MSR_P4_TC_ESCR0: u32 = 964;
|
||||
pub const MSR_P4_TC_ESCR1: u32 = 965;
|
||||
pub const MSR_P4_U2L_ESCR0: u32 = 944;
|
||||
pub const MSR_P4_U2L_ESCR1: u32 = 945;
|
||||
pub const MSR_P4_PEBS_MATRIX_VERT: u32 = 1010;
|
||||
pub const MSR_CORE_PERF_FIXED_CTR0: u32 = 777;
|
||||
pub const MSR_CORE_PERF_FIXED_CTR1: u32 = 778;
|
||||
pub const MSR_CORE_PERF_FIXED_CTR2: u32 = 779;
|
||||
pub const MSR_CORE_PERF_FIXED_CTR_CTRL: u32 = 909;
|
||||
pub const MSR_CORE_PERF_GLOBAL_STATUS: u32 = 910;
|
||||
pub const MSR_CORE_PERF_GLOBAL_CTRL: u32 = 911;
|
||||
pub const MSR_CORE_PERF_GLOBAL_OVF_CTRL: u32 = 912;
|
||||
pub const MSR_GEODE_BUSCONT_CONF0: u32 = 6400;
|
||||
pub const MSR_IA32_VMX_BASIC: u32 = 1152;
|
||||
pub const MSR_IA32_VMX_PINBASED_CTLS: u32 = 1153;
|
||||
pub const MSR_IA32_VMX_PROCBASED_CTLS: u32 = 1154;
|
||||
pub const MSR_IA32_VMX_EXIT_CTLS: u32 = 1155;
|
||||
pub const MSR_IA32_VMX_ENTRY_CTLS: u32 = 1156;
|
||||
pub const MSR_IA32_VMX_MISC: u32 = 1157;
|
||||
pub const MSR_IA32_VMX_CR0_FIXED0: u32 = 1158;
|
||||
pub const MSR_IA32_VMX_CR0_FIXED1: u32 = 1159;
|
||||
pub const MSR_IA32_VMX_CR4_FIXED0: u32 = 1160;
|
||||
pub const MSR_IA32_VMX_CR4_FIXED1: u32 = 1161;
|
||||
pub const MSR_IA32_VMX_VMCS_ENUM: u32 = 1162;
|
||||
pub const MSR_IA32_VMX_PROCBASED_CTLS2: u32 = 1163;
|
||||
pub const MSR_IA32_VMX_EPT_VPID_CAP: u32 = 1164;
|
||||
pub const MSR_IA32_VMX_TRUE_PINBASED_CTLS: u32 = 1165;
|
||||
pub const MSR_IA32_VMX_TRUE_PROCBASED_CTLS: u32 = 1166;
|
||||
pub const MSR_IA32_VMX_TRUE_EXIT_CTLS: u32 = 1167;
|
||||
pub const MSR_IA32_VMX_TRUE_ENTRY_CTLS: u32 = 1168;
|
||||
pub const MSR_IA32_VMX_VMFUNC: u32 = 1169;
|
||||
pub const VMX_BASIC_VMCS_SIZE_SHIFT: u32 = 32;
|
||||
pub const VMX_BASIC_TRUE_CTLS: u64 = 36028797018963968;
|
||||
pub const VMX_BASIC_64: u64 = 281474976710656;
|
||||
pub const VMX_BASIC_MEM_TYPE_SHIFT: u32 = 50;
|
||||
pub const VMX_BASIC_MEM_TYPE_MASK: u64 = 16888498602639360;
|
||||
pub const VMX_BASIC_MEM_TYPE_WB: u32 = 6;
|
||||
pub const VMX_BASIC_INOUT: u64 = 18014398509481984;
|
||||
pub const MSR_IA32_VMX_MISC_VMWRITE_SHADOW_RO_FIELDS: u32 = 536870912;
|
||||
pub const MSR_IA32_VMX_MISC_PREEMPTION_TIMER_SCALE: u32 = 31;
|
||||
pub const MSR_VM_CR: u32 = 3221291284;
|
||||
pub const MSR_VM_IGNNE: u32 = 3221291285;
|
||||
pub const MSR_VM_HSAVE_PA: u32 = 3221291287;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_msr_whitelist() {
|
||||
for range in WHITELISTED_MSR_RANGES.iter() {
|
||||
for msr in range.base..(range.base + range.nmsrs) {
|
||||
let should = !matches!(msr, MSR_IA32_FEATURE_CONTROL | MSR_IA32_MCG_CTL);
|
||||
assert_eq!(msr_should_serialize(msr), should);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_msr_contains() {
|
||||
let msr_range_a = MSR_RANGE!(0xEA, 9);
|
||||
let msr_a = 0x8888;
|
||||
assert!(!msr_range_a.contains(msr_a));
|
||||
|
||||
let msr_range_b = MSR_RANGE!(0xCCCC, 5);
|
||||
let msr_b = 0xCCCD;
|
||||
assert!(msr_range_b.contains(msr_b));
|
||||
}
|
||||
|
||||
fn test_supported_msrs() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
assert!(supported_guest_msrs(&kvm).is_ok());
|
||||
}
|
||||
}
|
||||
402
src/dragonball/src/dbs_arch/src/x86_64/regs.rs
Normal file
402
src/dragonball/src/dbs_arch/src/x86_64/regs.rs
Normal file
@@ -0,0 +1,402 @@
|
||||
// Copyright 2021-2022 Alibaba Cloud. All Rights Reserved.
|
||||
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
//! Constants and utilities for x86 CPU generic, system and model specific registers.
|
||||
|
||||
use std::mem;
|
||||
|
||||
use kvm_bindings::{kvm_fpu, kvm_msr_entry, kvm_regs, kvm_sregs, Msrs};
|
||||
use kvm_ioctls::VcpuFd;
|
||||
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory};
|
||||
|
||||
use super::gdt::kvm_segment_from_gdt;
|
||||
use super::msr;
|
||||
|
||||
/// Non-Executable bit in EFER MSR.
|
||||
pub const EFER_NX: u64 = 0x800;
|
||||
/// Long-mode active bit in EFER MSR.
|
||||
pub const EFER_LMA: u64 = 0x400;
|
||||
/// Long-mode enable bit in EFER MSR.
|
||||
pub const EFER_LME: u64 = 0x100;
|
||||
|
||||
/// Protection mode enable bit in CR0.
|
||||
pub const X86_CR0_PE: u64 = 0x1;
|
||||
/// Paging enable bit in CR0.
|
||||
pub const X86_CR0_PG: u64 = 0x8000_0000;
|
||||
/// Physical Address Extension bit in CR4.
|
||||
pub const X86_CR4_PAE: u64 = 0x20;
|
||||
|
||||
/// Errors thrown while setting up x86_64 registers.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Failed to get SREGs for this CPU.
|
||||
GetStatusRegisters(kvm_ioctls::Error),
|
||||
/// Failed to set base registers for this CPU.
|
||||
SetBaseRegisters(kvm_ioctls::Error),
|
||||
/// Failed to configure the FPU.
|
||||
SetFPURegisters(kvm_ioctls::Error),
|
||||
/// Setting up MSRs failed.
|
||||
SetModelSpecificRegisters(kvm_ioctls::Error),
|
||||
/// Failed to set all MSRs.
|
||||
SetModelSpecificRegistersCount,
|
||||
/// Failed to set SREGs for this CPU.
|
||||
SetStatusRegisters(kvm_ioctls::Error),
|
||||
/// Writing the GDT to RAM failed.
|
||||
WriteGDT,
|
||||
/// Writing the IDT to RAM failed.
|
||||
WriteIDT,
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Configure Floating-Point Unit (FPU) registers for a given CPU.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
pub fn setup_fpu(vcpu: &VcpuFd) -> Result<()> {
|
||||
let fpu: kvm_fpu = kvm_fpu {
|
||||
fcw: 0x37f,
|
||||
mxcsr: 0x1f80,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
vcpu.set_fpu(&fpu).map_err(Error::SetFPURegisters)
|
||||
}
|
||||
|
||||
/// Configure Model Specific Registers (MSRs) for a given CPU.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
pub fn setup_msrs(vcpu: &VcpuFd) -> Result<()> {
|
||||
let entry_vec = create_msr_entries();
|
||||
let kvm_msrs =
|
||||
Msrs::from_entries(&entry_vec).map_err(|_| Error::SetModelSpecificRegistersCount)?;
|
||||
|
||||
vcpu.set_msrs(&kvm_msrs)
|
||||
.map_err(Error::SetModelSpecificRegisters)
|
||||
.and_then(|msrs_written| {
|
||||
if msrs_written as u32 != kvm_msrs.as_fam_struct_ref().nmsrs {
|
||||
Err(Error::SetModelSpecificRegistersCount)
|
||||
} else {
|
||||
Ok(msrs_written)
|
||||
}
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Configure base registers for a given CPU.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
/// * `boot_ip` - Starting instruction pointer.
|
||||
/// * `rsp` - Value for RSP register
|
||||
/// * `rbp` - Value for RBP register
|
||||
/// * `rsi` - Value for RSI register
|
||||
pub fn setup_regs(vcpu: &VcpuFd, boot_ip: u64, rsp: u64, rbp: u64, rsi: u64) -> Result<()> {
|
||||
let regs: kvm_regs = kvm_regs {
|
||||
rflags: 0x0000_0000_0000_0002u64,
|
||||
rip: boot_ip,
|
||||
rsp,
|
||||
rbp,
|
||||
rsi,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
vcpu.set_regs(®s).map_err(Error::SetBaseRegisters)
|
||||
}
|
||||
|
||||
/// Configures the segment registers for a given CPU.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `mem` - The memory that will be passed to the guest.
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
/// * `pgtable_addr` - Address of the vcpu pgtable.
|
||||
/// * `gdt_table` - Content of the global descriptor table.
|
||||
/// * `gdt_addr` - Address of the global descriptor table.
|
||||
/// * `idt_addr` - Address of the interrupt descriptor table.
|
||||
pub fn setup_sregs<M: GuestMemory>(
|
||||
mem: &M,
|
||||
vcpu: &VcpuFd,
|
||||
pgtable_addr: GuestAddress,
|
||||
gdt_table: &[u64],
|
||||
gdt_addr: u64,
|
||||
idt_addr: u64,
|
||||
) -> Result<()> {
|
||||
let mut sregs: kvm_sregs = vcpu.get_sregs().map_err(Error::GetStatusRegisters)?;
|
||||
configure_segments_and_sregs(mem, &mut sregs, pgtable_addr, gdt_table, gdt_addr, idt_addr)?;
|
||||
vcpu.set_sregs(&sregs).map_err(Error::SetStatusRegisters)
|
||||
}
|
||||
|
||||
fn configure_segments_and_sregs<M: GuestMemory>(
|
||||
mem: &M,
|
||||
sregs: &mut kvm_sregs,
|
||||
pgtable_addr: GuestAddress,
|
||||
gdt_table: &[u64],
|
||||
gdt_addr: u64,
|
||||
idt_addr: u64,
|
||||
) -> Result<()> {
|
||||
assert!(gdt_table.len() >= 4);
|
||||
let code_seg = kvm_segment_from_gdt(gdt_table[1], 1);
|
||||
let data_seg = kvm_segment_from_gdt(gdt_table[2], 2);
|
||||
let tss_seg = kvm_segment_from_gdt(gdt_table[3], 3);
|
||||
|
||||
// Write segments
|
||||
write_gdt_table(gdt_table, gdt_addr, mem)?;
|
||||
sregs.gdt.base = gdt_addr;
|
||||
sregs.gdt.limit = std::mem::size_of_val(gdt_table) as u16 - 1;
|
||||
|
||||
write_idt_value(0, idt_addr, mem)?;
|
||||
sregs.idt.base = idt_addr;
|
||||
sregs.idt.limit = mem::size_of::<u64>() as u16 - 1;
|
||||
|
||||
sregs.cs = code_seg;
|
||||
sregs.ds = data_seg;
|
||||
sregs.es = data_seg;
|
||||
sregs.fs = data_seg;
|
||||
sregs.gs = data_seg;
|
||||
sregs.ss = data_seg;
|
||||
sregs.tr = tss_seg;
|
||||
|
||||
/* 64-bit protected mode */
|
||||
sregs.cr0 |= X86_CR0_PE;
|
||||
sregs.cr3 = pgtable_addr.raw_value();
|
||||
sregs.cr4 |= X86_CR4_PAE;
|
||||
sregs.cr0 |= X86_CR0_PG;
|
||||
sregs.efer |= EFER_LME | EFER_LMA;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_gdt_table<M: GuestMemory>(gdt_table: &[u64], gdt_addr: u64, guest_mem: &M) -> Result<()> {
|
||||
let boot_gdt_addr = GuestAddress(gdt_addr);
|
||||
for (index, entry) in gdt_table.iter().enumerate() {
|
||||
let addr = guest_mem
|
||||
.checked_offset(boot_gdt_addr, index * mem::size_of::<u64>())
|
||||
.ok_or(Error::WriteGDT)?;
|
||||
guest_mem
|
||||
.write_obj(*entry, addr)
|
||||
.map_err(|_| Error::WriteGDT)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_idt_value<M: GuestMemory>(idt_table: u64, idt_addr: u64, guest_mem: &M) -> Result<()> {
|
||||
let boot_idt_addr = GuestAddress(idt_addr);
|
||||
guest_mem
|
||||
.write_obj(idt_table, boot_idt_addr)
|
||||
.map_err(|_| Error::WriteIDT)
|
||||
}
|
||||
|
||||
#[allow(clippy::vec_init_then_push)]
|
||||
fn create_msr_entries() -> Vec<kvm_msr_entry> {
|
||||
let mut entries = Vec::<kvm_msr_entry>::new();
|
||||
|
||||
entries.push(kvm_msr_entry {
|
||||
index: msr::MSR_IA32_SYSENTER_CS,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
});
|
||||
entries.push(kvm_msr_entry {
|
||||
index: msr::MSR_IA32_SYSENTER_ESP,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
});
|
||||
entries.push(kvm_msr_entry {
|
||||
index: msr::MSR_IA32_SYSENTER_EIP,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
});
|
||||
// x86_64 specific msrs, we only run on x86_64 not x86.
|
||||
entries.push(kvm_msr_entry {
|
||||
index: msr::MSR_STAR,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
});
|
||||
entries.push(kvm_msr_entry {
|
||||
index: msr::MSR_CSTAR,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
});
|
||||
entries.push(kvm_msr_entry {
|
||||
index: msr::MSR_KERNEL_GS_BASE,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
});
|
||||
entries.push(kvm_msr_entry {
|
||||
index: msr::MSR_SYSCALL_MASK,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
});
|
||||
entries.push(kvm_msr_entry {
|
||||
index: msr::MSR_LSTAR,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
});
|
||||
// end of x86_64 specific code
|
||||
entries.push(kvm_msr_entry {
|
||||
index: msr::MSR_IA32_TSC,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
});
|
||||
entries.push(kvm_msr_entry {
|
||||
index: msr::MSR_IA32_MISC_ENABLE,
|
||||
data: u64::from(msr::MSR_IA32_MISC_ENABLE_FAST_STRING),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
entries
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::x86_64::gdt::gdt_entry;
|
||||
use kvm_ioctls::Kvm;
|
||||
use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};
|
||||
|
||||
const BOOT_GDT_OFFSET: u64 = 0x500;
|
||||
const BOOT_IDT_OFFSET: u64 = 0x520;
|
||||
const BOOT_STACK_POINTER: u64 = 0x100_0000;
|
||||
const ZERO_PAGE_START: u64 = 0x7_C000;
|
||||
const BOOT_GDT_MAX: usize = 4;
|
||||
const PML4_START: u64 = 0x9000;
|
||||
|
||||
fn create_guest_mem() -> GuestMemoryMmap {
|
||||
GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap()
|
||||
}
|
||||
|
||||
fn read_u64(gm: &GuestMemoryMmap, offset: u64) -> u64 {
|
||||
let read_addr = GuestAddress(offset);
|
||||
gm.read_obj(read_addr).unwrap()
|
||||
}
|
||||
|
||||
fn validate_segments_and_sregs(gm: &GuestMemoryMmap, sregs: &kvm_sregs) {
|
||||
assert_eq!(0x0, read_u64(gm, BOOT_GDT_OFFSET));
|
||||
assert_eq!(0xaf_9b00_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 8));
|
||||
assert_eq!(0xcf_9300_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 16));
|
||||
assert_eq!(0x8f_8b00_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 24));
|
||||
assert_eq!(0x0, read_u64(gm, BOOT_IDT_OFFSET));
|
||||
|
||||
assert_eq!(0, sregs.cs.base);
|
||||
assert_eq!(0xfffff, sregs.ds.limit);
|
||||
assert_eq!(0x10, sregs.es.selector);
|
||||
assert_eq!(1, sregs.fs.present);
|
||||
assert_eq!(1, sregs.gs.g);
|
||||
assert_eq!(0, sregs.ss.avl);
|
||||
assert_eq!(0, sregs.tr.base);
|
||||
assert_eq!(0xfffff, sregs.tr.limit);
|
||||
assert_eq!(0, sregs.tr.avl);
|
||||
assert!(sregs.cr0 & X86_CR0_PE != 0);
|
||||
assert!(sregs.efer & EFER_LME != 0 && sregs.efer & EFER_LMA != 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_configure_segments_and_sregs() {
|
||||
let mut sregs: kvm_sregs = Default::default();
|
||||
let gm = create_guest_mem();
|
||||
let gdt_table: [u64; BOOT_GDT_MAX] = [
|
||||
gdt_entry(0, 0, 0), // NULL
|
||||
gdt_entry(0xa09b, 0, 0xfffff), // CODE
|
||||
gdt_entry(0xc093, 0, 0xfffff), // DATA
|
||||
gdt_entry(0x808b, 0, 0xfffff), // TSS
|
||||
];
|
||||
configure_segments_and_sregs(
|
||||
&gm,
|
||||
&mut sregs,
|
||||
GuestAddress(PML4_START),
|
||||
&gdt_table,
|
||||
BOOT_GDT_OFFSET,
|
||||
BOOT_IDT_OFFSET,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
validate_segments_and_sregs(&gm, &sregs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_setup_fpu() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
setup_fpu(&vcpu).unwrap();
|
||||
|
||||
let expected_fpu: kvm_fpu = kvm_fpu {
|
||||
fcw: 0x37f,
|
||||
mxcsr: 0x1f80,
|
||||
..Default::default()
|
||||
};
|
||||
let actual_fpu: kvm_fpu = vcpu.get_fpu().unwrap();
|
||||
assert_eq!(expected_fpu.fcw, actual_fpu.fcw);
|
||||
// Setting the mxcsr register from kvm_fpu inside setup_fpu does not influence anything.
|
||||
// See 'kvm_arch_vcpu_ioctl_set_fpu' from arch/x86/kvm/x86.c.
|
||||
// The mxcsr will stay 0 and the assert below fails. Decide whether or not we should
|
||||
// remove it at all.
|
||||
// assert!(expected_fpu.mxcsr == actual_fpu.mxcsr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
fn test_setup_msrs() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
setup_msrs(&vcpu).unwrap();
|
||||
|
||||
// This test will check against the last MSR entry configured (the tenth one).
|
||||
// See create_msr_entries() for details.
|
||||
let test_kvm_msrs_entry = [kvm_msr_entry {
|
||||
index: msr::MSR_IA32_MISC_ENABLE,
|
||||
..Default::default()
|
||||
}];
|
||||
let mut kvm_msrs = Msrs::from_entries(&test_kvm_msrs_entry).unwrap();
|
||||
|
||||
// kvm_ioctls::get_msrs() returns the number of msrs that it succeeded in reading.
|
||||
// We only want to read one in this test case scenario.
|
||||
let read_nmsrs = vcpu.get_msrs(&mut kvm_msrs).unwrap();
|
||||
// Validate it only read one.
|
||||
assert_eq!(read_nmsrs, 1);
|
||||
|
||||
// Official entries that were setup when we did setup_msrs. We need to assert that the
|
||||
// tenth one (i.e the one with index msr_index::MSR_IA32_MISC_ENABLE has the data we
|
||||
// expect.
|
||||
let entry_vec = create_msr_entries();
|
||||
assert_eq!(entry_vec[9], kvm_msrs.as_slice()[0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_setup_regs() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
|
||||
let expected_regs: kvm_regs = kvm_regs {
|
||||
rflags: 0x0000_0000_0000_0002u64,
|
||||
rip: 1,
|
||||
rsp: BOOT_STACK_POINTER,
|
||||
rbp: BOOT_STACK_POINTER,
|
||||
rsi: ZERO_PAGE_START,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
setup_regs(
|
||||
&vcpu,
|
||||
expected_regs.rip,
|
||||
BOOT_STACK_POINTER,
|
||||
BOOT_STACK_POINTER,
|
||||
ZERO_PAGE_START,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let actual_regs: kvm_regs = vcpu.get_regs().unwrap();
|
||||
assert_eq!(actual_regs, expected_regs);
|
||||
}
|
||||
}
|
||||
26
src/dragonball/src/dbs_boot/Cargo.toml
Normal file
26
src/dragonball/src/dbs_boot/Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "dbs-boot"
|
||||
version = "0.4.0"
|
||||
authors = ["Alibaba Dragonball Team"]
|
||||
description = "Traits and structs for booting sandbox"
|
||||
license = "Apache-2.0 AND BSD-3-Clause"
|
||||
edition = "2018"
|
||||
homepage = "https://github.com/openanolis/dragonball-sandbox"
|
||||
repository = "https://github.com/openanolis/dragonball-sandbox"
|
||||
keywords = ["dragonball", "boot", "VMM"]
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
dbs-arch = { path = "../dbs_arch" }
|
||||
kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] }
|
||||
kvm-ioctls = "0.12.0"
|
||||
lazy_static = "1"
|
||||
libc = "0.2.39"
|
||||
thiserror = "1"
|
||||
vm-memory = "0.9.0"
|
||||
vm-fdt = "0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
vm-memory = { version = "0.9.0", features = ["backend-mmap"] }
|
||||
device_tree = ">=1.1.0"
|
||||
dbs-device = { path = "../dbs_device" }
|
||||
1
src/dragonball/src/dbs_boot/LICENSE
Symbolic link
1
src/dragonball/src/dbs_boot/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../LICENSE
|
||||
24
src/dragonball/src/dbs_boot/README.md
Normal file
24
src/dragonball/src/dbs_boot/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# dbs-boot
|
||||
|
||||
## Design
|
||||
|
||||
The `dbs-boot` crate is a collection of constants, structs and utilities used to boot virtual machines.
|
||||
|
||||
## Submodule List
|
||||
|
||||
This repository contains the following submodules:
|
||||
| Name | Arch| Description |
|
||||
| --- | --- | --- |
|
||||
| [`bootparam`](src/x86_64/bootparam.rs) | x86_64 | Magic addresses externally used to lay out x86_64 VMs |
|
||||
| [fdt](src/aarch64/fdt.rs) | aarch64| Create FDT for Aarch64 systems |
|
||||
| [layout](src/x86_64/layout.rs) | x86_64 | x86_64 layout constants |
|
||||
| [layout](src/aarch64/layout.rs/) | aarch64 | aarch64 layout constants |
|
||||
| [mptable](src/x86_64/mptable.rs) | x86_64 | MP Table configurations used for defining VM boot status |
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
Part of the code is derived from the [Firecracker](https://github.com/firecracker-microvm/firecracker) project.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0.
|
||||
1
src/dragonball/src/dbs_boot/THIRD-PARTY
Symbolic link
1
src/dragonball/src/dbs_boot/THIRD-PARTY
Symbolic link
@@ -0,0 +1 @@
|
||||
../../THIRD-PARTY
|
||||
608
src/dragonball/src/dbs_boot/src/aarch64/fdt.rs
Normal file
608
src/dragonball/src/dbs_boot/src/aarch64/fdt.rs
Normal file
@@ -0,0 +1,608 @@
|
||||
// Copyright 2022 Alibaba Cloud. All Rights Reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
//! Create Flatten Device Tree (FDT) for ARM64 systems.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use dbs_arch::gic::its::ItsType::{self, PciMsiIts, PlatformMsiIts};
|
||||
use dbs_arch::gic::GICDevice;
|
||||
use dbs_arch::{pmu::VIRTUAL_PMU_IRQ, VpmuFeatureLevel};
|
||||
use dbs_arch::{DeviceInfoForFDT, DeviceType};
|
||||
|
||||
use vm_fdt::FdtWriter;
|
||||
use vm_memory::GuestMemoryRegion;
|
||||
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory};
|
||||
|
||||
use super::fdt_utils::*;
|
||||
use super::Error;
|
||||
use crate::Result;
|
||||
|
||||
// This is a value for uniquely identifying the FDT node declaring the interrupt controller.
|
||||
const GIC_PHANDLE: u32 = 1;
|
||||
// This is a value for uniquely identifying the FDT node containing the clock definition.
|
||||
const CLOCK_PHANDLE: u32 = 2;
|
||||
// This is a value for uniquely identifying the FDT node containing the plaform msi ITS definition.
|
||||
const GIC_PLATFORM_MSI_ITS_PHANDLE: u32 = 3;
|
||||
// This is a value for uniquely identifying the FDT node containing the pci msi ITS definition.
|
||||
const GIC_PCI_MSI_ITS_PHANDLE: u32 = 4;
|
||||
// According to the arm, gic-v3.txt document, ITS' #msi-cells is fixed at 1.
|
||||
const GIC_PLATFORM_MSI_ITS_CELLS_SIZE: u32 = 1;
|
||||
|
||||
// Read the documentation specified when appending the root node to the FDT.
|
||||
const ADDRESS_CELLS: u32 = 0x2;
|
||||
const SIZE_CELLS: u32 = 0x2;
|
||||
|
||||
// As per kvm tool and
|
||||
// https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.txt
|
||||
// Look for "The 1st cell..."
|
||||
const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
|
||||
const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
|
||||
|
||||
// From https://elixir.bootlin.com/linux/v4.9.62/source/include/dt-bindings/interrupt-controller/irq.h#L17
|
||||
const IRQ_TYPE_EDGE_RISING: u32 = 1;
|
||||
const IRQ_TYPE_LEVEL_HI: u32 = 4;
|
||||
|
||||
/// Creates the flattened device tree for this aarch64 microVM.
|
||||
pub fn create_fdt<T>(
|
||||
fdt_vm_info: FdtVmInfo,
|
||||
_fdt_numa_info: FdtNumaInfo,
|
||||
fdt_device_info: FdtDeviceInfo<T>,
|
||||
) -> Result<Vec<u8>>
|
||||
where
|
||||
T: DeviceInfoForFDT + Clone + Debug,
|
||||
{
|
||||
let mut fdt = FdtWriter::new()?;
|
||||
|
||||
// For an explanation why these nodes were introduced in the blob take a look at
|
||||
// https://github.com/torvalds/linux/blob/master/Documentation/devicetree/booting-without-of.txt#L845
|
||||
// Look for "Required nodes and properties".
|
||||
|
||||
// Header or the root node as per above mentioned documentation.
|
||||
let root_node = fdt.begin_node("")?;
|
||||
fdt.property_string("compatible", "linux,dummy-virt")?;
|
||||
// For info on #address-cells and size-cells read "Note about cells and address representation"
|
||||
// from the above mentioned txt file.
|
||||
fdt.property_u32("#address-cells", ADDRESS_CELLS)?;
|
||||
fdt.property_u32("#size-cells", SIZE_CELLS)?;
|
||||
// This is not mandatory but we use it to point the root node to the node
|
||||
// containing description of the interrupt controller for this VM.
|
||||
fdt.property_u32("interrupt-parent", GIC_PHANDLE)?;
|
||||
create_cpu_nodes(&mut fdt, &fdt_vm_info)?;
|
||||
create_memory_node(&mut fdt, fdt_vm_info.get_guest_memory())?;
|
||||
create_chosen_node(&mut fdt, &fdt_vm_info)?;
|
||||
create_gic_node(&mut fdt, fdt_device_info.get_irqchip())?;
|
||||
create_timer_node(&mut fdt)?;
|
||||
create_clock_node(&mut fdt)?;
|
||||
create_psci_node(&mut fdt)?;
|
||||
fdt_device_info
|
||||
.get_mmio_device_info()
|
||||
.map_or(Ok(()), |v| create_devices_node(&mut fdt, v))?;
|
||||
create_pmu_node(&mut fdt, fdt_vm_info.get_vpmu_feature())?;
|
||||
|
||||
// End Header node.
|
||||
fdt.end_node(root_node)?;
|
||||
|
||||
// Allocate another buffer so we can format and then write fdt to guest.
|
||||
let fdt_final = fdt.finish()?;
|
||||
|
||||
// Write FDT to memory.
|
||||
let fdt_address = GuestAddress(super::get_fdt_addr(fdt_vm_info.get_guest_memory()));
|
||||
fdt_vm_info
|
||||
.get_guest_memory()
|
||||
.write_slice(fdt_final.as_slice(), fdt_address)?;
|
||||
Ok(fdt_final)
|
||||
}
|
||||
|
||||
// Following are the auxiliary function for creating the different nodes that we append to our FDT.
|
||||
fn create_cpu_nodes(fdt: &mut FdtWriter, fdt_vm_info: &FdtVmInfo) -> Result<()> {
|
||||
// See https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/arm/cpus.yaml.
|
||||
let cpus_node = fdt.begin_node("cpus")?;
|
||||
// As per documentation, on ARM v8 64-bit systems value should be set to 2.
|
||||
fdt.property_u32("#address-cells", 0x02)?;
|
||||
fdt.property_u32("#size-cells", 0x0)?;
|
||||
let vcpu_mpidr = fdt_vm_info.get_vcpu_mpidr();
|
||||
let vcpu_boot_onlined = fdt_vm_info.get_boot_onlined();
|
||||
let num_cpus = vcpu_mpidr.len();
|
||||
|
||||
for (cpu_index, mpidr) in vcpu_mpidr.iter().enumerate().take(num_cpus) {
|
||||
let cpu_name = format!("cpu@{cpu_index:x}");
|
||||
let cpu_node = fdt.begin_node(&cpu_name)?;
|
||||
fdt.property_string("device_type", "cpu")?;
|
||||
fdt.property_string("compatible", "arm,arm-v8")?;
|
||||
if num_cpus > 1 {
|
||||
// This is required on armv8 64-bit. See aforementioned documentation.
|
||||
fdt.property_string("enable-method", "psci")?;
|
||||
}
|
||||
// boot-onlined attribute is used to indicate whether this cpu should be onlined at boot.
|
||||
// 0 means offline, 1 means online.
|
||||
fdt.property_u32("boot-onlined", vcpu_boot_onlined[cpu_index])?;
|
||||
// Set the field to first 24 bits of the MPIDR - Multiprocessor Affinity Register.
|
||||
// See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488c/BABHBJCI.html.
|
||||
fdt.property_u64("reg", mpidr & 0x7FFFFF)?;
|
||||
fdt.end_node(cpu_node)?;
|
||||
}
|
||||
fdt.end_node(cpus_node)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_memory_node<M: GuestMemory>(fdt: &mut FdtWriter, guest_mem: &M) -> Result<()> {
|
||||
// See https://github.com/torvalds/linux/blob/v5.9/Documentation/devicetree/booting-without-of.rst
|
||||
for region in guest_mem.iter() {
|
||||
let memory_name = format!("memory@{:x}", region.start_addr().raw_value());
|
||||
let mem_reg_prop = &[region.start_addr().raw_value(), region.len()];
|
||||
let memory_node = fdt.begin_node(&memory_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", mem_reg_prop)?;
|
||||
fdt.end_node(memory_node)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_chosen_node(fdt: &mut FdtWriter, fdt_vm_info: &FdtVmInfo) -> Result<()> {
|
||||
let chosen_node = fdt.begin_node("chosen")?;
|
||||
fdt.property_string("bootargs", fdt_vm_info.get_cmdline())?;
|
||||
|
||||
if let Some(initrd_config) = fdt_vm_info.get_initrd_config() {
|
||||
fdt.property_u64("linux,initrd-start", initrd_config.address.raw_value())?;
|
||||
fdt.property_u64(
|
||||
"linux,initrd-end",
|
||||
initrd_config.address.raw_value() + initrd_config.size as u64,
|
||||
)?;
|
||||
}
|
||||
|
||||
fdt.end_node(chosen_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn append_its_common_property(fdt: &mut FdtWriter, registers_prop: &[u64]) -> Result<()> {
|
||||
fdt.property_string("compatible", "arm,gic-v3-its")?;
|
||||
fdt.property_null("msi-controller")?;
|
||||
fdt.property_array_u64("reg", registers_prop)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_its_node(
|
||||
fdt: &mut FdtWriter,
|
||||
gic_device: &dyn GICDevice,
|
||||
its_type: ItsType,
|
||||
) -> Result<()> {
|
||||
let reg = gic_device.get_its_reg_range(&its_type);
|
||||
if let Some(registers) = reg {
|
||||
// There are two types of its, pci_msi_its and platform_msi_its.
|
||||
// If this is pci_msi_its, the fdt node of its is required to have no
|
||||
// #msi-cells attribute. If this is platform_msi_its, the #msi-cells
|
||||
// attribute of its fdt node is required, and the value is 1.
|
||||
match its_type {
|
||||
PlatformMsiIts => {
|
||||
let its_node = fdt.begin_node("gic-platform-its")?;
|
||||
append_its_common_property(fdt, ®isters)?;
|
||||
fdt.property_u32("phandle", GIC_PLATFORM_MSI_ITS_PHANDLE)?;
|
||||
fdt.property_u32("#msi-cells", GIC_PLATFORM_MSI_ITS_CELLS_SIZE)?;
|
||||
fdt.end_node(its_node)?;
|
||||
}
|
||||
PciMsiIts => {
|
||||
let its_node = fdt.begin_node("gic-pci-its")?;
|
||||
append_its_common_property(fdt, ®isters)?;
|
||||
fdt.property_u32("phandle", GIC_PCI_MSI_ITS_PHANDLE)?;
|
||||
fdt.end_node(its_node)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_gic_node(fdt: &mut FdtWriter, gic_device: &dyn GICDevice) -> Result<()> {
|
||||
let gic_reg_prop = gic_device.device_properties();
|
||||
|
||||
let intc_node = fdt.begin_node("intc")?;
|
||||
fdt.property_string("compatible", gic_device.fdt_compatibility())?;
|
||||
fdt.property_null("interrupt-controller")?;
|
||||
// "interrupt-cells" field specifies the number of cells needed to encode an
|
||||
// interrupt source. The type shall be a <u32> and the value shall be 3 if no PPI affinity description
|
||||
// is required.
|
||||
fdt.property_u32("#interrupt-cells", 3)?;
|
||||
fdt.property_array_u64("reg", gic_reg_prop)?;
|
||||
fdt.property_u32("phandle", GIC_PHANDLE)?;
|
||||
fdt.property_u32("#address-cells", 2)?;
|
||||
fdt.property_u32("#size-cells", 2)?;
|
||||
fdt.property_null("ranges")?;
|
||||
let gic_intr_prop = &[
|
||||
GIC_FDT_IRQ_TYPE_PPI,
|
||||
gic_device.fdt_maint_irq(),
|
||||
IRQ_TYPE_LEVEL_HI,
|
||||
];
|
||||
|
||||
fdt.property_array_u32("interrupts", gic_intr_prop)?;
|
||||
create_its_node(fdt, gic_device, PlatformMsiIts)?;
|
||||
create_its_node(fdt, gic_device, PciMsiIts)?;
|
||||
fdt.end_node(intc_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_clock_node(fdt: &mut FdtWriter) -> Result<()> {
|
||||
// The Advanced Peripheral Bus (APB) is part of the Advanced Microcontroller Bus Architecture
|
||||
// (AMBA) protocol family. It defines a low-cost interface that is optimized for minimal power
|
||||
// consumption and reduced interface complexity.
|
||||
// PCLK is the clock source and this node defines exactly the clock for the APB.
|
||||
let clock_node = fdt.begin_node("apb-pclk")?;
|
||||
fdt.property_string("compatible", "fixed-clock")?;
|
||||
fdt.property_u32("#clock-cells", 0x0)?;
|
||||
fdt.property_u32("clock-frequency", 24000000)?;
|
||||
fdt.property_string("clock-output-names", "clk24mhz")?;
|
||||
fdt.property_u32("phandle", CLOCK_PHANDLE)?;
|
||||
fdt.end_node(clock_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_timer_node(fdt: &mut FdtWriter) -> Result<()> {
|
||||
// See
|
||||
// https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/interrupt-controller/arch_timer.txt
|
||||
// These are fixed interrupt numbers for the timer device.
|
||||
let irqs = [13, 14, 11, 10];
|
||||
let compatible = "arm,armv8-timer";
|
||||
|
||||
let mut timer_reg_cells: Vec<u32> = Vec::new();
|
||||
for &irq in irqs.iter() {
|
||||
timer_reg_cells.push(GIC_FDT_IRQ_TYPE_PPI);
|
||||
timer_reg_cells.push(irq);
|
||||
timer_reg_cells.push(IRQ_TYPE_LEVEL_HI);
|
||||
}
|
||||
|
||||
let timer_node = fdt.begin_node("timer")?;
|
||||
fdt.property_string("compatible", compatible)?;
|
||||
fdt.property_null("always-on")?;
|
||||
fdt.property_array_u32("interrupts", &timer_reg_cells)?;
|
||||
fdt.end_node(timer_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_psci_node(fdt: &mut FdtWriter) -> Result<()> {
|
||||
let compatible = "arm,psci-0.2";
|
||||
let psci_node = fdt.begin_node("psci")?;
|
||||
fdt.property_string("compatible", compatible)?;
|
||||
// Two methods available: hvc and smc.
|
||||
// As per documentation, PSCI calls between a guest and hypervisor may use the HVC conduit instead of SMC.
|
||||
// So, since we are using kvm, we need to use hvc.
|
||||
fdt.property_string("method", "hvc")?;
|
||||
fdt.end_node(psci_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_virtio_node<T: DeviceInfoForFDT + Clone + Debug>(
|
||||
fdt: &mut FdtWriter,
|
||||
dev_info: &T,
|
||||
) -> Result<()> {
|
||||
let device_reg_prop = &[dev_info.addr(), dev_info.length()];
|
||||
let irq_number = dev_info.irq().map_err(|_| Error::InvalidArguments)?;
|
||||
let irq_property = &[GIC_FDT_IRQ_TYPE_SPI, irq_number, IRQ_TYPE_EDGE_RISING];
|
||||
|
||||
let virtio_mmio_node = fdt.begin_node(&format!("virtio_mmio@{:x}", dev_info.addr()))?;
|
||||
fdt.property_string("compatible", "virtio,mmio")?;
|
||||
fdt.property_array_u64("reg", device_reg_prop)?;
|
||||
fdt.property_array_u32("interrupts", irq_property)?;
|
||||
fdt.property_u32("interrupt-parent", GIC_PHANDLE)?;
|
||||
fdt.end_node(virtio_mmio_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_serial_node<T: DeviceInfoForFDT + Clone + Debug>(
|
||||
fdt: &mut FdtWriter,
|
||||
dev_info: &T,
|
||||
) -> Result<()> {
|
||||
let serial_reg_prop = &[dev_info.addr(), dev_info.length()];
|
||||
let irq_number = dev_info.irq().map_err(|_| Error::InvalidArguments)?;
|
||||
let irq_property = &[GIC_FDT_IRQ_TYPE_SPI, irq_number, IRQ_TYPE_EDGE_RISING];
|
||||
|
||||
let uart_node = fdt.begin_node(&format!("uart@{:x}", dev_info.addr()))?;
|
||||
fdt.property_string("compatible", "ns16550a")?;
|
||||
fdt.property_array_u64("reg", serial_reg_prop)?;
|
||||
fdt.property_u32("clocks", CLOCK_PHANDLE)?;
|
||||
fdt.property_string("clock-names", "apb_pclk")?;
|
||||
fdt.property_array_u32("interrupts", irq_property)?;
|
||||
fdt.end_node(uart_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_rtc_node<T: DeviceInfoForFDT + Clone + Debug>(
|
||||
fdt: &mut FdtWriter,
|
||||
dev_info: &T,
|
||||
) -> Result<()> {
|
||||
let compatible = b"arm,pl031\0arm,primecell\0";
|
||||
let rtc_reg_prop = &[dev_info.addr(), dev_info.length()];
|
||||
let irq_number = dev_info.irq().map_err(|_| Error::InvalidArguments)?;
|
||||
let irq_property = &[GIC_FDT_IRQ_TYPE_SPI, irq_number, IRQ_TYPE_LEVEL_HI];
|
||||
|
||||
let rtc_node = fdt.begin_node(&format!("rtc@{:x}", dev_info.addr()))?;
|
||||
fdt.property("compatible", compatible)?;
|
||||
fdt.property_array_u64("reg", rtc_reg_prop)?;
|
||||
fdt.property_array_u32("interrupts", irq_property)?;
|
||||
fdt.property_u32("clocks", CLOCK_PHANDLE)?;
|
||||
fdt.property_string("clock-names", "apb_pclk")?;
|
||||
fdt.end_node(rtc_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_devices_node<T: DeviceInfoForFDT + Clone + Debug>(
|
||||
fdt: &mut FdtWriter,
|
||||
dev_info: &HashMap<(DeviceType, String), T>,
|
||||
) -> Result<()> {
|
||||
// Serial devices need to be registered in order
|
||||
let mut ordered_serial_device: Vec<&T> = Vec::new();
|
||||
// Create one temp Vec to store all virtio devices
|
||||
let mut ordered_virtio_device: Vec<&T> = Vec::new();
|
||||
|
||||
for ((device_type, _device_id), info) in dev_info {
|
||||
match device_type {
|
||||
DeviceType::RTC => create_rtc_node(fdt, info)?,
|
||||
DeviceType::Serial => {
|
||||
ordered_serial_device.push(info);
|
||||
}
|
||||
DeviceType::Virtio(_) => {
|
||||
ordered_virtio_device.push(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort out serial devices by address from low to high and insert them into fdt table.
|
||||
ordered_serial_device.sort_by_key(|a| a.addr());
|
||||
for serial_device_info in ordered_serial_device.drain(..) {
|
||||
create_serial_node(fdt, serial_device_info)?;
|
||||
}
|
||||
// Sort out virtio devices by address from low to high and insert them into fdt table.
|
||||
ordered_virtio_device.sort_by_key(|a| a.addr());
|
||||
for ordered_device_info in ordered_virtio_device.drain(..) {
|
||||
create_virtio_node(fdt, ordered_device_info)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_pmu_node(fdt: &mut FdtWriter, vpmu_feature: VpmuFeatureLevel) -> Result<()> {
|
||||
if vpmu_feature == VpmuFeatureLevel::Disabled {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let pmu_node = fdt.begin_node("pmu")?;
|
||||
fdt.property_string("compatible", "arm,armv8-pmuv3")?;
|
||||
let pmu_intr_prop = [GIC_FDT_IRQ_TYPE_PPI, VIRTUAL_PMU_IRQ, IRQ_TYPE_LEVEL_HI];
|
||||
fdt.property_array_u32("interrupts", &pmu_intr_prop)?;
|
||||
fdt.end_node(pmu_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::cmp::min;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use dbs_arch::{gic::create_gic, pmu::initialize_pmu};
|
||||
use device_tree::DeviceTree;
|
||||
use kvm_bindings::{kvm_vcpu_init, KVM_ARM_VCPU_PMU_V3, KVM_ARM_VCPU_PSCI_0_2};
|
||||
use kvm_ioctls::{Kvm, VcpuFd, VmFd};
|
||||
use vm_memory::GuestMemoryMmap;
|
||||
|
||||
use super::super::tests::MMIODeviceInfo;
|
||||
use super::*;
|
||||
use crate::layout::{DRAM_MEM_MAX_SIZE, DRAM_MEM_START, FDT_MAX_SIZE};
|
||||
use crate::InitrdConfig;
|
||||
|
||||
const LEN: u64 = 4096;
|
||||
|
||||
fn arch_memory_regions(size: usize) -> Vec<(GuestAddress, usize)> {
|
||||
let dram_size = min(size as u64, DRAM_MEM_MAX_SIZE) as usize;
|
||||
vec![(GuestAddress(DRAM_MEM_START), dram_size)]
|
||||
}
|
||||
|
||||
// The `load` function from the `device_tree` will mistakenly check the actual size
|
||||
// of the buffer with the allocated size. This works around that.
|
||||
fn set_size(buf: &mut [u8], pos: usize, val: usize) {
|
||||
buf[pos] = ((val >> 24) & 0xff) as u8;
|
||||
buf[pos + 1] = ((val >> 16) & 0xff) as u8;
|
||||
buf[pos + 2] = ((val >> 8) & 0xff) as u8;
|
||||
buf[pos + 3] = (val & 0xff) as u8;
|
||||
}
|
||||
|
||||
// Initialize vcpu for pmu test
|
||||
fn initialize_vcpu_with_pmu(vm: &VmFd, vcpu: &VcpuFd) -> Result<()> {
|
||||
let mut kvi: kvm_vcpu_init = kvm_vcpu_init::default();
|
||||
vm.get_preferred_target(&mut kvi)
|
||||
.expect("Cannot get preferred target");
|
||||
kvi.features[0] = 1 << KVM_ARM_VCPU_PSCI_0_2 | 1 << KVM_ARM_VCPU_PMU_V3;
|
||||
vcpu.vcpu_init(&kvi).map_err(|_| Error::InvalidArguments)?;
|
||||
initialize_pmu(vm, vcpu).map_err(|_| Error::InvalidArguments)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Create fdt dtb file
|
||||
fn create_dtb_file(name: &str, dtb: &[u8]) {
|
||||
// Control whether to create new dtb files for unit test.
|
||||
// Usage: FDT_CREATE_DTB=1 cargo test
|
||||
if env::var("FDT_CREATE_DTB").is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use this code when wanting to generate a new DTB sample.
|
||||
// Do manually check dtb files with dtc
|
||||
// See https://git.kernel.org/pub/scm/utils/dtc/dtc.git/plain/Documentation/manual.txt
|
||||
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
let mut output = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(path.join(format!("src/aarch64/test/{name}")))
|
||||
.unwrap();
|
||||
output
|
||||
.set_len(FDT_MAX_SIZE as u64)
|
||||
.map_err(|_| Error::InvalidArguments)
|
||||
.unwrap();
|
||||
output.write_all(dtb).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_fdt_with_devices() {
|
||||
let regions = arch_memory_regions(FDT_MAX_SIZE + 0x1000);
|
||||
let mem = GuestMemoryMmap::<()>::from_ranges(®ions).expect("Cannot initialize memory");
|
||||
let dev_info: HashMap<(DeviceType, String), MMIODeviceInfo> = [
|
||||
(
|
||||
(DeviceType::Serial, DeviceType::Serial.to_string()),
|
||||
MMIODeviceInfo::new(0, 1),
|
||||
),
|
||||
(
|
||||
(DeviceType::Virtio(1), "virtio".to_string()),
|
||||
MMIODeviceInfo::new(LEN, 2),
|
||||
),
|
||||
(
|
||||
(DeviceType::RTC, "rtc".to_string()),
|
||||
MMIODeviceInfo::new(2 * LEN, 3),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let gic = create_gic(&vm, 1).unwrap();
|
||||
let vpmu_feature = VpmuFeatureLevel::Disabled;
|
||||
assert!(create_fdt(
|
||||
FdtVmInfo::new(
|
||||
&mem,
|
||||
"console=tty0",
|
||||
None,
|
||||
FdtVcpuInfo::new(vec![0], vec![1], vpmu_feature, false)
|
||||
),
|
||||
FdtNumaInfo::default(),
|
||||
FdtDeviceInfo::new(Some(&dev_info), gic.as_ref())
|
||||
)
|
||||
.is_ok())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_fdt() {
|
||||
let regions = arch_memory_regions(FDT_MAX_SIZE + 0x1000);
|
||||
let mem = GuestMemoryMmap::<()>::from_ranges(®ions).expect("Cannot initialize memory");
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let gic = create_gic(&vm, 1).unwrap();
|
||||
let vpmu_feature = VpmuFeatureLevel::Disabled;
|
||||
let dtb = create_fdt(
|
||||
FdtVmInfo::new(
|
||||
&mem,
|
||||
"console=tty0",
|
||||
None,
|
||||
FdtVcpuInfo::new(vec![0], vec![1], vpmu_feature, false),
|
||||
),
|
||||
FdtNumaInfo::default(),
|
||||
FdtDeviceInfo::<MMIODeviceInfo>::new(None, gic.as_ref()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
create_dtb_file("output.dtb", &dtb);
|
||||
|
||||
let bytes = include_bytes!("test/output.dtb");
|
||||
let pos = 4;
|
||||
let val = FDT_MAX_SIZE;
|
||||
let mut buf = vec![];
|
||||
buf.extend_from_slice(bytes);
|
||||
set_size(&mut buf, pos, val);
|
||||
|
||||
let original_fdt = DeviceTree::load(&buf).unwrap();
|
||||
let generated_fdt = DeviceTree::load(&dtb).unwrap();
|
||||
assert_eq!(format!("{original_fdt:?}"), format!("{generated_fdt:?}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_fdt_with_initrd() {
|
||||
let regions = arch_memory_regions(FDT_MAX_SIZE + 0x1000);
|
||||
let mem = GuestMemoryMmap::<()>::from_ranges(®ions).expect("Cannot initialize memory");
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let gic = create_gic(&vm, 1).unwrap();
|
||||
let initrd = InitrdConfig {
|
||||
address: GuestAddress(0x10000000),
|
||||
size: 0x1000,
|
||||
};
|
||||
let vpmu_feature = VpmuFeatureLevel::Disabled;
|
||||
let dtb = create_fdt(
|
||||
FdtVmInfo::new(
|
||||
&mem,
|
||||
"console=tty0",
|
||||
Some(&initrd),
|
||||
FdtVcpuInfo::new(vec![0], vec![1], vpmu_feature, false),
|
||||
),
|
||||
FdtNumaInfo::default(),
|
||||
FdtDeviceInfo::<MMIODeviceInfo>::new(None, gic.as_ref()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
create_dtb_file("output_with_initrd.dtb", &dtb);
|
||||
|
||||
let bytes = include_bytes!("test/output_with_initrd.dtb");
|
||||
let pos = 4;
|
||||
let val = FDT_MAX_SIZE;
|
||||
let mut buf = vec![];
|
||||
buf.extend_from_slice(bytes);
|
||||
set_size(&mut buf, pos, val);
|
||||
|
||||
let original_fdt = DeviceTree::load(&buf).unwrap();
|
||||
let generated_fdt = DeviceTree::load(&dtb).unwrap();
|
||||
assert_eq!(format!("{original_fdt:?}"), format!("{generated_fdt:?}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_fdt_with_pmu() {
|
||||
let regions = arch_memory_regions(FDT_MAX_SIZE + 0x1000);
|
||||
let mem = GuestMemoryMmap::<()>::from_ranges(®ions).expect("Cannot initialize memory");
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
let gic = create_gic(&vm, 1).unwrap();
|
||||
|
||||
assert!(initialize_vcpu_with_pmu(&vm, &vcpu).is_ok());
|
||||
|
||||
let vpmu_feature = VpmuFeatureLevel::FullyEnabled;
|
||||
let dtb = create_fdt(
|
||||
FdtVmInfo::new(
|
||||
&mem,
|
||||
"console=tty0",
|
||||
None,
|
||||
FdtVcpuInfo::new(vec![0], vec![1], vpmu_feature, false),
|
||||
),
|
||||
FdtNumaInfo::default(),
|
||||
FdtDeviceInfo::<MMIODeviceInfo>::new(None, gic.as_ref()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
create_dtb_file("output_with_pmu.dtb", &dtb);
|
||||
|
||||
let bytes = include_bytes!("test/output_with_pmu.dtb");
|
||||
let pos = 4;
|
||||
let val = FDT_MAX_SIZE;
|
||||
let mut buf = vec![];
|
||||
buf.extend_from_slice(bytes);
|
||||
set_size(&mut buf, pos, val);
|
||||
|
||||
let original_fdt = DeviceTree::load(&buf).unwrap();
|
||||
let generated_fdt = DeviceTree::load(&dtb).unwrap();
|
||||
assert_eq!(format!("{original_fdt:?}"), format!("{generated_fdt:?}"));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user