diff --git a/.github/workflows/openvmm-ci.yaml b/.github/workflows/openvmm-ci.yaml index 667681619a..778e07ecd9 100644 --- a/.github/workflows/openvmm-ci.yaml +++ b/.github/workflows/openvmm-ci.yaml @@ -30,7 +30,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -40,7 +40,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -48,7 +48,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -189,7 +189,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -199,7 +199,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -207,7 +207,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -358,7 +358,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -368,7 +368,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -376,7 +376,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -622,7 +622,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -632,7 +632,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -640,7 +640,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -925,7 +925,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -935,7 +935,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -943,7 +943,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -1232,7 +1232,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -1242,7 +1242,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -1250,7 +1250,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -2819,7 +2819,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -2829,7 +2829,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -2837,7 +2837,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3132,7 +3132,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3142,7 +3142,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3150,7 +3150,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3362,7 +3362,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3372,7 +3372,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3380,7 +3380,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3472,7 +3472,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3482,7 +3482,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3490,7 +3490,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3727,7 +3727,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3737,7 +3737,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3745,7 +3745,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3958,7 +3958,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3968,7 +3968,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3976,7 +3976,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -4220,7 +4220,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -4230,7 +4230,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -4238,7 +4238,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -4523,7 +4523,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -4533,7 +4533,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -4541,7 +4541,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -4882,7 +4882,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -4892,7 +4892,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -4900,7 +4900,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -5233,7 +5233,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -5243,7 +5243,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -5251,7 +5251,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) diff --git a/.github/workflows/openvmm-docs-ci.yaml b/.github/workflows/openvmm-docs-ci.yaml index dde305e02e..b72459c45f 100644 --- a/.github/workflows/openvmm-docs-ci.yaml +++ b/.github/workflows/openvmm-docs-ci.yaml @@ -27,7 +27,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -37,7 +37,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -45,7 +45,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -185,7 +185,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -195,7 +195,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -203,7 +203,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -350,7 +350,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -360,7 +360,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -368,7 +368,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) diff --git a/.github/workflows/openvmm-docs-pr.yaml b/.github/workflows/openvmm-docs-pr.yaml index ced3f85490..acb3d90e9d 100644 --- a/.github/workflows/openvmm-docs-pr.yaml +++ b/.github/workflows/openvmm-docs-pr.yaml @@ -35,7 +35,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -45,7 +45,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -53,7 +53,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -193,7 +193,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -203,7 +203,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -211,7 +211,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -358,7 +358,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -368,7 +368,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -376,7 +376,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) diff --git a/.github/workflows/openvmm-pr-release.yaml b/.github/workflows/openvmm-pr-release.yaml index 96915bcff1..b70fc08f3d 100644 --- a/.github/workflows/openvmm-pr-release.yaml +++ b/.github/workflows/openvmm-pr-release.yaml @@ -32,7 +32,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -42,7 +42,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -50,7 +50,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -191,7 +191,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -201,7 +201,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -209,7 +209,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -360,7 +360,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -370,7 +370,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -378,7 +378,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -624,7 +624,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -634,7 +634,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -642,7 +642,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -927,7 +927,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -937,7 +937,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -945,7 +945,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -1234,7 +1234,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -1244,7 +1244,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -1252,7 +1252,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -2821,7 +2821,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -2831,7 +2831,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -2839,7 +2839,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3134,7 +3134,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3144,7 +3144,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3152,7 +3152,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3364,7 +3364,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3374,7 +3374,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3382,7 +3382,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3474,7 +3474,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3484,7 +3484,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3492,7 +3492,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3729,7 +3729,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3739,7 +3739,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3747,7 +3747,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3960,7 +3960,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3970,7 +3970,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3978,7 +3978,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -4222,7 +4222,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -4232,7 +4232,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -4240,7 +4240,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -4525,7 +4525,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -4535,7 +4535,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -4543,7 +4543,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -4884,7 +4884,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -4894,7 +4894,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -4902,7 +4902,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -5208,7 +5208,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -5218,7 +5218,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -5226,7 +5226,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) diff --git a/.github/workflows/openvmm-pr.yaml b/.github/workflows/openvmm-pr.yaml index 8aa4633923..a686027506 100644 --- a/.github/workflows/openvmm-pr.yaml +++ b/.github/workflows/openvmm-pr.yaml @@ -38,7 +38,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -48,7 +48,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -56,7 +56,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -197,7 +197,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -207,7 +207,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -215,7 +215,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -367,7 +367,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -377,7 +377,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -385,7 +385,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -607,7 +607,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -617,7 +617,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -625,7 +625,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -871,7 +871,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -881,7 +881,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -889,7 +889,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -1174,7 +1174,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -1184,7 +1184,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -1192,7 +1192,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -1481,7 +1481,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -1491,7 +1491,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -1499,7 +1499,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3062,7 +3062,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3072,7 +3072,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3080,7 +3080,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3297,7 +3297,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3307,7 +3307,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3315,7 +3315,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3611,7 +3611,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3621,7 +3621,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3629,7 +3629,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -3780,7 +3780,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -3790,7 +3790,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -3798,7 +3798,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -4035,7 +4035,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -4045,7 +4045,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -4053,7 +4053,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -4266,7 +4266,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -4276,7 +4276,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -4284,7 +4284,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -4528,7 +4528,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -4538,7 +4538,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -4546,7 +4546,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -4831,7 +4831,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -4841,7 +4841,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -4849,7 +4849,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -5190,7 +5190,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -5200,7 +5200,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -5208,7 +5208,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) @@ -5514,7 +5514,7 @@ jobs: set -x i=0; while [ $i -lt 5 ] && ! sudo apt-get update; do let "i=i+1"; sleep 1; done; sudo apt-get -o DPkg::Lock::Timeout=60 install gcc -y - curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.89.0 -y + curl --fail --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=1.90.0 -y . "$HOME/.cargo/env" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" rustup show @@ -5524,7 +5524,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/x86_64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'X64' name: rustup (Windows X64) @@ -5532,7 +5532,7 @@ jobs: - run: | set -x curl --fail -sSfLo rustup-init.exe https://win.rustup.rs/aarch64 --output rustup-init - ./rustup-init.exe -y --default-toolchain=1.89.0 + ./rustup-init.exe -y --default-toolchain=1.90.0 echo "$USERPROFILE\\.cargo\\bin" >> $GITHUB_PATH if: runner.os == 'Windows' && runner.arch == 'ARM64' name: rustup (Windows ARM64) diff --git a/Cargo.lock b/Cargo.lock index 8e6734c771..127ae15d5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4522,6 +4522,7 @@ dependencies = [ "gdma_defs", "guestmem", "inspect", + "inspect_counters", "mana_driver", "mesh", "net_backend", @@ -4865,7 +4866,6 @@ dependencies = [ "chipset_device", "device_emulators", "disk_backend", - "event-listener", "futures", "futures-concurrency", "guestmem", diff --git a/Cargo.toml b/Cargo.toml index fa081b8457..01cf617ac1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ exclude = [ ] [workspace.package] -rust-version = "1.89" +rust-version = "1.90" edition = "2024" [workspace.dependencies] diff --git a/flowey/flowey_hvlite/src/pipelines/checkin_gates.rs b/flowey/flowey_hvlite/src/pipelines/checkin_gates.rs index e706996d66..66391b2c07 100644 --- a/flowey/flowey_hvlite/src/pipelines/checkin_gates.rs +++ b/flowey/flowey_hvlite/src/pipelines/checkin_gates.rs @@ -974,8 +974,7 @@ impl IntoPipeline for CheckinGatesCli { label: "aarch64-windows", target: CommonTriple::AARCH64_WINDOWS_MSVC, resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_aarch64, - // disable hyper-v tests for now until runners are updated - nextest_filter_expr: "all() & !test(hyperv)".to_string(), + nextest_filter_expr: "all()".to_string(), test_artifacts: vec![ KnownTestArtifacts::Ubuntu2404ServerAarch64Vhd, KnownTestArtifacts::Windows11EnterpriseAarch64Vhdx, diff --git a/flowey/flowey_lib_hvlite/src/_jobs/cfg_versions.rs b/flowey/flowey_lib_hvlite/src/_jobs/cfg_versions.rs index d65d354785..8d32e3c722 100644 --- a/flowey/flowey_lib_hvlite/src/_jobs/cfg_versions.rs +++ b/flowey/flowey_lib_hvlite/src/_jobs/cfg_versions.rs @@ -22,7 +22,7 @@ pub const LXUTIL: &str = "10.0.26100.1-240331-1435.ge-release"; pub const MDBOOK: &str = "0.4.40"; pub const MDBOOK_ADMONISH: &str = "1.18.0"; pub const MDBOOK_MERMAID: &str = "0.14.0"; -pub const RUSTUP_TOOLCHAIN: &str = "1.89.0"; +pub const RUSTUP_TOOLCHAIN: &str = "1.90.0"; pub const MU_MSVM: &str = "25.1.5"; pub const NEXTEST: &str = "0.9.101"; pub const NODEJS: &str = "18.x"; diff --git a/openhcl/hcl/src/ioctl.rs b/openhcl/hcl/src/ioctl.rs index 8026b8d560..748f4f9589 100644 --- a/openhcl/hcl/src/ioctl.rs +++ b/openhcl/hcl/src/ioctl.rs @@ -1299,7 +1299,7 @@ impl MshvHvcall { assert!(size_of::() <= HV_PAGE_SIZE as usize); } assert_size::(); - assert!(variable_input.len() % 8 == 0); + assert!(variable_input.len().is_multiple_of(8)); let input = [input.as_bytes(), variable_input].concat(); if input.len() > HV_PAGE_SIZE as usize { diff --git a/openhcl/hcl/src/vmsa.rs b/openhcl/hcl/src/vmsa.rs index 7532ec7cf3..85de8f9d6c 100644 --- a/openhcl/hcl/src/vmsa.rs +++ b/openhcl/hcl/src/vmsa.rs @@ -33,7 +33,7 @@ impl<'a, T> VmsaWrapper<'a, T> { impl> VmsaWrapper<'_, T> { /// 64 bit register read fn get_u64(&self, offset: usize) -> u64 { - assert!(offset % 8 == 0); + assert!(offset.is_multiple_of(8)); let vmsa_raw = &self.vmsa; let v = u64::from_ne_bytes(vmsa_raw.as_bytes()[offset..offset + 8].try_into().unwrap()); if is_protected(self.bitmap, offset) { @@ -44,7 +44,7 @@ impl> VmsaWrapper<'_, T> { } /// 32 bit register read fn get_u32(&self, offset: usize) -> u32 { - assert!(offset % 4 == 0); + assert!(offset.is_multiple_of(4)); (self.get_u64(offset & !7) >> ((offset & 4) * 8)) as u32 } /// 128 bit register read @@ -77,7 +77,7 @@ impl> VmsaWrapper<'_, T> { impl> VmsaWrapper<'_, T> { /// 64 bit value to set in register fn set_u64(&self, v: u64, offset: usize) -> u64 { - assert!(offset % 8 == 0); + assert!(offset.is_multiple_of(8)); if is_protected(self.bitmap, offset) { v ^ self.vmsa.register_protection_nonce } else { @@ -86,7 +86,7 @@ impl> VmsaWrapper<'_, T> { } /// 32 bit value to set in register fn set_u32(&self, v: u32, offset: usize) -> u32 { - assert!(offset % 4 == 0); + assert!(offset.is_multiple_of(4)); let val = (v as u64) << ((offset & 4) * 8); (self.set_u64(val, offset & !7) >> ((offset & 4) * 8)) as u32 } diff --git a/openhcl/openhcl_boot/src/arch/x86_64/address_space.rs b/openhcl/openhcl_boot/src/arch/x86_64/address_space.rs index a666073218..191966ee22 100644 --- a/openhcl/openhcl_boot/src/arch/x86_64/address_space.rs +++ b/openhcl/openhcl_boot/src/arch/x86_64/address_space.rs @@ -254,7 +254,7 @@ static LOCAL_MAP_INITIALIZED: SingleThreaded> = SingleThreaded(Cell:: /// It returns a LocalMap structure with a static lifetime. /// `va` is the virtual address of the local map region. It must be 2MB aligned. pub fn init_local_map(va: u64) -> LocalMap<'static> { - assert!(va % X64_LARGE_PAGE_SIZE == 0); + assert!(va.is_multiple_of(X64_LARGE_PAGE_SIZE)); // SAFETY: The va for the local map is part of the measured build. This routine will only be // called once. The boot shim is a single threaded environment, the contained assertion is @@ -292,7 +292,7 @@ impl TdxHypercallPage { unsafe { let entry = get_pde_for_va(va); assert!(entry.is_present() & entry.is_large_page()); - assert!(va % X64_LARGE_PAGE_SIZE == 0); + assert!(va.is_multiple_of(X64_LARGE_PAGE_SIZE)); assert!(entry.tdx_is_shared()); TdxHypercallPage(va) } diff --git a/openhcl/openhcl_boot/src/arch/x86_64/memory.rs b/openhcl/openhcl_boot/src/arch/x86_64/memory.rs index bcf72c41d5..469f676674 100644 --- a/openhcl/openhcl_boot/src/arch/x86_64/memory.rs +++ b/openhcl/openhcl_boot/src/arch/x86_64/memory.rs @@ -126,7 +126,7 @@ pub fn setup_vtl2_memory(shim_params: &ShimParams, partition_info: &PartitionInf // TODO: find an approach that does not require re-using the ram_buffer if shim_params.isolation_type == IsolationType::Tdx { let free_buffer = ram_buffer.as_mut_ptr() as u64; - assert!(free_buffer % X64_LARGE_PAGE_SIZE == 0); + assert!(free_buffer.is_multiple_of(X64_LARGE_PAGE_SIZE)); // SAFETY: The bottom 2MB region of the ram_buffer is unused by the shim // The region is aligned to 2MB, and mapped as a large page let tdx_io_page = unsafe { diff --git a/openhcl/openhcl_boot/src/arch/x86_64/snp.rs b/openhcl/openhcl_boot/src/arch/x86_64/snp.rs index 87c07d61a8..ebdfb607b3 100644 --- a/openhcl/openhcl_boot/src/arch/x86_64/snp.rs +++ b/openhcl/openhcl_boot/src/arch/x86_64/snp.rs @@ -89,9 +89,9 @@ fn pvalidate( validate: bool, ) -> Result { if large_page { - assert!(va % x86defs::X64_LARGE_PAGE_SIZE == 0); + assert!(va.is_multiple_of(x86defs::X64_LARGE_PAGE_SIZE)); } else { - assert!(va % hvdef::HV_PAGE_SIZE == 0) + assert!(va.is_multiple_of(hvdef::HV_PAGE_SIZE)) } let validate_page = validate as u32; @@ -149,7 +149,7 @@ pub fn set_page_acceptance( MemoryRange::from_4k_gpn_range(page_base..page_base + 1), true, ); - if page_base % pages_per_large_page == 0 && page_count >= pages_per_large_page { + if page_base.is_multiple_of(pages_per_large_page) && page_count >= pages_per_large_page { let res = pvalidate(page_base, mapping.data.as_ptr() as u64, true, validate)?; match res { AcceptGpaStatus::Success => { diff --git a/openhcl/openhcl_boot/src/hypercall.rs b/openhcl/openhcl_boot/src/hypercall.rs index 6217ac8b9f..1b4b764103 100644 --- a/openhcl/openhcl_boot/src/hypercall.rs +++ b/openhcl/openhcl_boot/src/hypercall.rs @@ -41,7 +41,7 @@ impl HvcallPage { let addr = self.buffer.as_ptr() as u64; // These should be page-aligned - assert!(addr % HV_PAGE_SIZE == 0); + assert!(addr.is_multiple_of(HV_PAGE_SIZE)); addr } diff --git a/openhcl/sidecar_client/src/lib.rs b/openhcl/sidecar_client/src/lib.rs index 1c381cba69..928521f36f 100644 --- a/openhcl/sidecar_client/src/lib.rs +++ b/openhcl/sidecar_client/src/lib.rs @@ -350,7 +350,7 @@ struct VpSharedPages { register_page: hvdef::HvX64RegisterPage, } -const _: () = assert!(size_of::() % PAGE_SIZE == 0); +const _: () = assert!(size_of::().is_multiple_of(PAGE_SIZE)); impl Drop for SidecarVp<'_> { fn drop(&mut self) { diff --git a/openhcl/underhill_mem/src/mapping.rs b/openhcl/underhill_mem/src/mapping.rs index ca6cba834b..5329280f87 100644 --- a/openhcl/underhill_mem/src/mapping.rs +++ b/openhcl/underhill_mem/src/mapping.rs @@ -404,7 +404,8 @@ impl GuestMemoryBitmap { } fn init(&mut self, range: MemoryRange, state: bool) -> Result<(), MappingError> { - if range.start() % (PAGE_SIZE as u64 * 8) != 0 || range.end() % (PAGE_SIZE as u64 * 8) != 0 + if !range.start().is_multiple_of(PAGE_SIZE as u64 * 8) + || !range.end().is_multiple_of(PAGE_SIZE as u64 * 8) { return Err(MappingError::BadAlignment(range)); } diff --git a/openvmm/hvlite_core/src/worker/vm_loaders/igvm.rs b/openvmm/hvlite_core/src/worker/vm_loaders/igvm.rs index 7894b8bd94..5aa7dc3b0a 100644 --- a/openvmm/hvlite_core/src/worker/vm_loaders/igvm.rs +++ b/openvmm/hvlite_core/src/worker/vm_loaders/igvm.rs @@ -99,7 +99,7 @@ pub enum Error { } fn from_memory_range(range: &MemoryRange) -> IGVM_VHS_MEMORY_RANGE { - assert!(range.len() % HV_PAGE_SIZE == 0); + assert!(range.len().is_multiple_of(HV_PAGE_SIZE)); IGVM_VHS_MEMORY_RANGE { starting_gpa_page_number: range.start() / HV_PAGE_SIZE, number_of_pages: range.len() / HV_PAGE_SIZE, @@ -107,7 +107,7 @@ fn from_memory_range(range: &MemoryRange) -> IGVM_VHS_MEMORY_RANGE { } fn memory_map_entry(range: &MemoryRange) -> IGVM_VHS_MEMORY_MAP_ENTRY { - assert!(range.len() % HV_PAGE_SIZE == 0); + assert!(range.len().is_multiple_of(HV_PAGE_SIZE)); IGVM_VHS_MEMORY_MAP_ENTRY { starting_gpa_page_number: range.start() / HV_PAGE_SIZE, number_of_pages: range.len() / HV_PAGE_SIZE, @@ -879,7 +879,7 @@ fn load_igvm_x86( data_type, ref data, } => { - debug_assert!(data.len() as u64 % HV_PAGE_SIZE == 0); + debug_assert!((data.len() as u64).is_multiple_of(HV_PAGE_SIZE)); // TODO: only 4k or empty page data supported right now assert!(data.len() as u64 == HV_PAGE_SIZE || data.is_empty()); diff --git a/support/fdt/src/parser.rs b/support/fdt/src/parser.rs index 914fbe48da..8b137c92e8 100644 --- a/support/fdt/src/parser.rs +++ b/support/fdt/src/parser.rs @@ -185,7 +185,7 @@ impl<'a> Parser<'a> { /// Create a new instance of a FDT parser. pub fn new(buf: &'a [u8]) -> Result> { - if buf.as_ptr() as usize % size_of::() != 0 { + if !(buf.as_ptr() as usize).is_multiple_of(size_of::()) { return Err(Error(ErrorKind::BufferAlignment)); } @@ -235,7 +235,7 @@ impl<'a> Parser<'a> { let struct_offset = u32::from(header.off_dt_struct) as usize; let struct_len = u32::from(header.size_dt_struct) as usize; - if struct_offset % size_of::() != 0 { + if !struct_offset.is_multiple_of(size_of::()) { return Err(Error(ErrorKind::StructureBlockAlignment)); } diff --git a/support/pal/src/unix/affinity.rs b/support/pal/src/unix/affinity.rs index 1ae3774273..f65021d9ba 100644 --- a/support/pal/src/unix/affinity.rs +++ b/support/pal/src/unix/affinity.rs @@ -64,7 +64,7 @@ impl CpuSet { /// This is useful for parsing the output of `/sys/devices/system/cpu/topology`. pub fn set_mask_hex_string(&mut self, string_mask: &[u8]) -> Result<(), InvalidHexString> { let err = || InvalidHexString(String::from_utf8_lossy(string_mask).into_owned()); - if string_mask.len() % 2 != 0 { + if !string_mask.len().is_multiple_of(2) { return Err(err()); } let mask = string_mask diff --git a/support/pal/src/unix/process/linux.rs b/support/pal/src/unix/process/linux.rs index e7d44306d3..95fc1d238e 100644 --- a/support/pal/src/unix/process/linux.rs +++ b/support/pal/src/unix/process/linux.rs @@ -90,7 +90,7 @@ impl Builder<'_> { // Common page sizes are 4KiB, 16KiB, and 64KiB. The stack size must be a multiple // of the page size. let stack_len: usize = std::cmp::max(16 * 1024, page_size); - assert!(stack_len % page_size == 0); + assert!(stack_len.is_multiple_of(page_size)); // Create a stack with one guard page. let stack_len = stack_len + page_size; diff --git a/support/sparse_mmap/fuzz/fuzz_sparse_mmap.rs b/support/sparse_mmap/fuzz/fuzz_sparse_mmap.rs index 02fb9abf19..2678d85447 100644 --- a/support/sparse_mmap/fuzz/fuzz_sparse_mmap.rs +++ b/support/sparse_mmap/fuzz/fuzz_sparse_mmap.rs @@ -153,7 +153,7 @@ fn generate_blocks( blocks.push(block); } - if max_pages % num_blocks != 0 { + if !max_pages.is_multiple_of(num_blocks) { let offset = num_blocks * safe_mul!(pages_per_block, page_size); let len = (max_pages % num_blocks) * page_size; let block = Block::new(offset, len)?; diff --git a/support/sparse_mmap/src/unix.rs b/support/sparse_mmap/src/unix.rs index 3ca51556c2..1cd5e84fc9 100644 --- a/support/sparse_mmap/src/unix.rs +++ b/support/sparse_mmap/src/unix.rs @@ -184,7 +184,7 @@ impl SparseMapping { fn validate_offset_len(&self, offset: usize, len: usize) -> io::Result { let end = offset.checked_add(len).ok_or(io::ErrorKind::InvalidInput)?; let page_size = page_size(); - if offset % page_size != 0 || end % page_size != 0 || end > self.len { + if !offset.is_multiple_of(page_size) || !end.is_multiple_of(page_size) || end > self.len { return Err(io::ErrorKind::InvalidInput.into()); } Ok(end) diff --git a/support/sparse_mmap/src/windows.rs b/support/sparse_mmap/src/windows.rs index 9d9c0cc303..a529e5af58 100644 --- a/support/sparse_mmap/src/windows.rs +++ b/support/sparse_mmap/src/windows.rs @@ -379,7 +379,7 @@ impl SparseMapping { fn validate_offset_len(&self, offset: usize, len: usize) -> io::Result { let end = offset.checked_add(len).ok_or(io::ErrorKind::InvalidInput)?; - if offset % PAGE_SIZE != 0 || end % PAGE_SIZE != 0 || end > self.len { + if !offset.is_multiple_of(PAGE_SIZE) || !end.is_multiple_of(PAGE_SIZE) || end > self.len { return Err(io::ErrorKind::InvalidInput.into()); } Ok(end) diff --git a/support/ucs2/src/lib.rs b/support/ucs2/src/lib.rs index 6318b87d90..3b6492f21b 100644 --- a/support/ucs2/src/lib.rs +++ b/support/ucs2/src/lib.rs @@ -153,7 +153,7 @@ impl Ucs2LeSlice { /// Validate that the provided `&[u8]` is a valid null-terminated UCS-2 LE /// string, truncating the slice to the position of the first null u16. pub fn from_slice_with_nul(buf: &[u8]) -> Result<&Ucs2LeSlice, Ucs2ParseError> { - if buf.len() % 2 != 0 { + if !buf.len().is_multiple_of(2) { return Err(Ucs2ParseError::NotMultiple2); } diff --git a/vm/acpi/src/aml.rs b/vm/acpi/src/aml.rs new file mode 100644 index 0000000000..eaa469e805 --- /dev/null +++ b/vm/acpi/src/aml.rs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Shared utilities for generating ACPI Machine Language (AML), +//! particularly for use in differentiated and secondary system +//! description tables (DSDT and SSDT, respectively). + +pub mod devices; +pub mod helpers; +pub mod objects; +pub mod ops; +pub mod resources; + +pub use self::devices::*; +pub use self::helpers::*; +pub use self::objects::*; +pub use self::ops::*; +pub use self::resources::*; + +#[cfg(test)] +pub mod test_helpers { + pub fn verify_expected_bytes(actual: &[u8], expected: &[u8]) { + assert_eq!( + actual.len(), + expected.len(), + "Length of buffer does not match" + ); + for i in 0..actual.len() { + assert_eq!(actual[i], expected[i], "Mismatch at index {}", i); + } + } +} diff --git a/vm/acpi/src/aml/devices.rs b/vm/acpi/src/aml/devices.rs new file mode 100644 index 0000000000..56863232d9 --- /dev/null +++ b/vm/acpi/src/aml/devices.rs @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Utilities for encoding devices into ACPI Machine Language (AML). + +use super::helpers::*; +use super::objects::*; +use super::ops::*; + +/// An AML Method +pub struct Method { + pub name: [u8; 4], + pub sync_level: u8, + pub is_serialized: bool, + pub arg_count: u8, + operations: Vec, +} + +impl Method { + /// Constructs a new [`Method`]. + pub fn new(name: &[u8; 4]) -> Self { + let local_name: [u8; 4] = [name[0], name[1], name[2], name[3]]; + Self { + name: local_name, + sync_level: 0, + is_serialized: false, + arg_count: 0, + operations: vec![], + } + } + + /// Set the number of arguments the method accepts. + pub fn set_arg_count(&mut self, arg_count: u8) { + self.arg_count = arg_count; + } + + /// Add an operation to the method body. + pub fn add_operation(&mut self, op: &impl OperationObject) { + op.append_to_vec(&mut self.operations); + } +} + +impl AmlObject for Method { + fn append_to_vec(&self, byte_stream: &mut Vec) { + byte_stream.push(0x14); + byte_stream.extend_from_slice(&encode_package_len(5 + self.operations.len())); + byte_stream.extend_from_slice(&self.name); + byte_stream.push( + self.sync_level << 4 | if self.is_serialized { 1 << 3 } else { 0 } | self.arg_count, + ); + byte_stream.extend_from_slice(&self.operations); + } +} + +/// An AML Device +pub struct Device { + name: Vec, + objects: Vec, +} + +impl Device { + /// Construct a new [`Device`] + pub fn new(name: &[u8]) -> Self { + Self { + name: encode_name(name), + objects: vec![], + } + } + + /// Add an object to the body of the device. + pub fn add_object(&mut self, obj: &impl AmlObject) { + obj.append_to_vec(&mut self.objects); + } +} + +impl AmlObject for Device { + // A device object consists of the extended identifier (0x5b 0x82) followed by the length, the name and then the + // contained objects. + fn append_to_vec(&self, byte_stream: &mut Vec) { + byte_stream.push(0x5b); + byte_stream.push(0x82); + let length = self.name.len() + self.objects.len(); + byte_stream.extend_from_slice(&encode_package_len(length)); + byte_stream.extend_from_slice(&self.name); + byte_stream.extend_from_slice(&self.objects); + } +} + +/// An EISA identifier for a device. +pub struct EisaId(pub [u8; 7]); + +impl AmlObject for EisaId { + fn append_to_vec(&self, byte_stream: &mut Vec) { + let mut id: [u8; 4] = [0; 4]; + id[0] = (self.0[0] - b'@') << 2 | (self.0[1] - b'@') >> 3; + id[1] = (self.0[1] & 7) << 5 | (self.0[2] - b'@'); + id[2] = char_to_hex(self.0[3]) << 4 | char_to_hex(self.0[4]); + id[3] = char_to_hex(self.0[5]) << 4 | char_to_hex(self.0[6]); + byte_stream.append(&mut encode_integer(u32::from_le_bytes(id) as u64)); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::aml::test_helpers::verify_expected_bytes; + + #[test] + fn verify_eisaid() { + let eisa_id = EisaId(*b"PNP0003"); + let bytes = eisa_id.to_bytes(); + verify_expected_bytes(&bytes, &[0xc, 0x41, 0xd0, 0, 0x3]); + } + + #[test] + fn verify_method() { + let op = AndOp { + operand1: vec![b'S', b'T', b'A', b'_'], + operand2: encode_integer(13), + target_name: vec![b'S', b'T', b'A', b'_'], + }; + let mut method = Method::new(b"_DIS"); + method.add_operation(&op); + let bytes = method.to_bytes(); + verify_expected_bytes( + &bytes, + &[ + 0x14, 0x11, 0x5F, 0x44, 0x49, 0x53, 0x00, 0x7b, b'S', b'T', b'A', b'_', 0x0a, 0x0d, + b'S', b'T', b'A', b'_', + ], + ); + } + + #[test] + fn verify_device_object() { + let package = Package(vec![0]); + let nobj = NamedObject::new(b"FOO", &package); + let mut device = Device::new(b"DEV"); + device.add_object(&nobj); + let bytes = device.to_bytes(); + verify_expected_bytes( + &bytes, + &[ + 0x5b, 0x82, 14, b'D', b'E', b'V', b'_', 8, b'F', b'O', b'O', b'_', 0x12, 3, 1, 0, + ], + ); + } +} diff --git a/vm/acpi/src/dsdt/helpers.rs b/vm/acpi/src/aml/helpers.rs similarity index 97% rename from vm/acpi/src/dsdt/helpers.rs rename to vm/acpi/src/aml/helpers.rs index 9bfbdfa001..72bd565861 100644 --- a/vm/acpi/src/dsdt/helpers.rs +++ b/vm/acpi/src/aml/helpers.rs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +//! Utilities for encoding various types into ACPI Machine Language (AML). + pub fn encode_name(name: &[u8]) -> Vec { let mut encoded_name: Vec = Vec::new(); let mut segments: Vec<[u8; 4]> = Vec::new(); @@ -153,7 +155,7 @@ pub fn char_to_hex(value: u8) -> u8 { #[cfg(test)] mod tests { use super::*; - use crate::dsdt::tests::verify_expected_bytes; + use crate::aml::test_helpers::verify_expected_bytes; #[test] fn verify_simple_name() { diff --git a/vm/acpi/src/dsdt/objects.rs b/vm/acpi/src/aml/objects.rs similarity index 87% rename from vm/acpi/src/dsdt/objects.rs rename to vm/acpi/src/aml/objects.rs index 67e768755a..7ebe10540c 100644 --- a/vm/acpi/src/dsdt/objects.rs +++ b/vm/acpi/src/aml/objects.rs @@ -1,9 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +//! Utilities for encoding various objects into ACPI Machine Language (AML). + use super::helpers::*; -pub trait DsdtObject { +/// A trait indicating that a particular type can be serialized +/// into a byte stream of ACPI Machine Language (AML). +pub trait AmlObject { fn append_to_vec(&self, byte_stream: &mut Vec); fn to_bytes(&self) -> Vec { @@ -13,13 +17,15 @@ pub trait DsdtObject { } } +/// A named AML object. pub struct NamedObject { name: Vec, object: Vec, } impl NamedObject { - pub fn new(name: &[u8], object: &impl DsdtObject) -> Self { + /// Construct a new [`NamedObject`] + pub fn new(name: &[u8], object: &impl AmlObject) -> Self { let encoded_name = encode_name(name); assert!(!encoded_name.is_empty()); NamedObject { @@ -29,7 +35,7 @@ impl NamedObject { } } -impl DsdtObject for NamedObject { +impl AmlObject for NamedObject { // A named object consists of the identifier (0x8) followed by the 4-byte name fn append_to_vec(&self, byte_stream: &mut Vec) { byte_stream.push(8); @@ -40,7 +46,7 @@ impl DsdtObject for NamedObject { pub struct GenericObject>(pub T); -impl DsdtObject for GenericObject +impl AmlObject for GenericObject where T: AsRef<[u8]>, { @@ -50,11 +56,13 @@ where } } +/// A named AML integer. pub struct NamedInteger { data: NamedObject, } impl NamedInteger { + /// Construct a new [`NamedInteger`] pub fn new(name: &[u8], value: u64) -> Self { Self { data: NamedObject::new(name, &GenericObject(encode_integer(value))), @@ -62,17 +70,19 @@ impl NamedInteger { } } -impl DsdtObject for NamedInteger { +impl AmlObject for NamedInteger { fn append_to_vec(&self, byte_stream: &mut Vec) { self.data.append_to_vec(byte_stream); } } +/// A named AML string. pub struct NamedString { data: NamedObject, } impl NamedString { + /// Construct a new [`NamedString`] pub fn new(name: &[u8], value: &[u8]) -> Self { Self { data: NamedObject::new(name, &GenericObject(encode_string(value))), @@ -80,18 +90,19 @@ impl NamedString { } } -impl DsdtObject for NamedString { +impl AmlObject for NamedString { fn append_to_vec(&self, byte_stream: &mut Vec) { self.data.append_to_vec(byte_stream); } } +/// A structured AML package. pub struct StructuredPackage> { pub elem_count: u8, pub elem_data: T, } -impl DsdtObject for StructuredPackage +impl AmlObject for StructuredPackage where T: AsRef<[u8]>, { @@ -108,7 +119,7 @@ where pub struct Package>(pub T); -impl DsdtObject for Package +impl AmlObject for Package where T: AsRef<[u8]>, { @@ -124,7 +135,7 @@ where pub struct Buffer>(pub T); -impl DsdtObject for Buffer +impl AmlObject for Buffer where T: AsRef<[u8]>, { @@ -143,7 +154,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::dsdt::tests::verify_expected_bytes; + use crate::aml::test_helpers::verify_expected_bytes; #[test] fn verify_package() { diff --git a/vm/acpi/src/dsdt/ops.rs b/vm/acpi/src/aml/ops.rs similarity index 88% rename from vm/acpi/src/dsdt/ops.rs rename to vm/acpi/src/aml/ops.rs index 52395a2059..7ec108b4da 100644 --- a/vm/acpi/src/dsdt/ops.rs +++ b/vm/acpi/src/aml/ops.rs @@ -1,6 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +//! Utilities for encoding procedural operations into ACPI +//! Machine Language (AML). + +/// An AML operation. pub trait OperationObject { fn append_to_vec(&self, byte_stream: &mut Vec); @@ -11,6 +15,7 @@ pub trait OperationObject { } } +/// A bitwise AND AML operation. pub struct AndOp { pub operand1: Vec, pub operand2: Vec, @@ -26,6 +31,7 @@ impl OperationObject for AndOp { } } +/// A bitwise OR AML operation. pub struct OrOp { pub operand1: Vec, pub operand2: Vec, @@ -41,6 +47,7 @@ impl OperationObject for OrOp { } } +/// An AML operation to return from a procedure. pub struct ReturnOp { pub result: Vec, } @@ -55,8 +62,8 @@ impl OperationObject for ReturnOp { #[cfg(test)] mod tests { use super::*; - use crate::dsdt::encode_integer; - use crate::dsdt::tests::verify_expected_bytes; + use crate::aml::encode_integer; + use crate::aml::test_helpers::verify_expected_bytes; #[test] fn verify_and_operation() { diff --git a/vm/acpi/src/dsdt/resources.rs b/vm/acpi/src/aml/resources.rs similarity index 93% rename from vm/acpi/src/dsdt/resources.rs rename to vm/acpi/src/aml/resources.rs index 692ab78c98..9c6179f5ef 100644 --- a/vm/acpi/src/dsdt/resources.rs +++ b/vm/acpi/src/aml/resources.rs @@ -1,8 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +//! Utilities for encoding various resource types into ACPI Machine Language (AML). + use super::objects::*; +/// An AML resource. pub trait ResourceObject { fn append_to_vec(&self, byte_stream: &mut Vec); @@ -13,6 +16,7 @@ pub trait ResourceObject { } } +/// A 32-bit, fixed-address AML memory resource. pub struct Memory32Fixed { is_writeable: bool, base_address: u32, @@ -20,6 +24,7 @@ pub struct Memory32Fixed { } impl Memory32Fixed { + /// Construct a new [`Memory32Fixed`]. pub fn new(base_address: u32, length: u32, is_writeable: bool) -> Self { Self { is_writeable, @@ -40,6 +45,7 @@ impl ResourceObject for Memory32Fixed { #[repr(u8)] #[derive(Copy, Clone, Debug)] +/// Attributes for AML memory resources. pub enum MemoryAttribute { Memory = 0, _Reserved = 8, @@ -49,6 +55,7 @@ pub enum MemoryAttribute { #[repr(u8)] #[derive(Copy, Clone, Debug)] +/// Cache types for AML memory resources. pub enum MemoryCacheType { _NonCacheable = 0, Cacheable = 2, @@ -56,6 +63,7 @@ pub enum MemoryCacheType { _CacheableAndPrefetchable = 6, } +/// A 32-bit AML memory resource. pub struct DwordMemory { pub length: u32, pub translation_offset: u32, @@ -89,8 +97,8 @@ impl ResourceObject for DwordMemory { } } -#[cfg(test)] impl DwordMemory { + /// Construct a new [`DwordMemory`]. pub fn new(address: u32, length: u32) -> Self { assert!(address as u64 + length as u64 - 1 <= u32::MAX as u64); Self { @@ -109,6 +117,7 @@ impl DwordMemory { } } +/// A 64-bit AML memory resource. pub struct QwordMemory { pub is_io_backed: bool, pub attributes: MemoryAttribute, @@ -120,6 +129,7 @@ pub struct QwordMemory { } impl QwordMemory { + /// Construct a new [`QwordMemory`]. pub fn new(address: u64, length: u64) -> Self { assert!(address as u128 + length as u128 - 1 <= u64::MAX as u128); Self { @@ -183,6 +193,7 @@ impl ResourceObject for BusNumber { } } +/// An ACPI interrupt. pub struct Interrupt { pub is_wake_capable: bool, pub is_shared: bool, @@ -193,6 +204,7 @@ pub struct Interrupt { } impl Interrupt { + /// Construct a new [`Interrupt`]. pub fn new(number: u32) -> Self { Self { is_wake_capable: false, @@ -220,6 +232,7 @@ impl ResourceObject for Interrupt { } } +/// An ACPI IO port. pub struct IoPort { pub is_16bit_aware: bool, pub base_address: u16, @@ -229,6 +242,7 @@ pub struct IoPort { } impl IoPort { + /// Construct a new [`IoPort`]. pub fn new(start: u16, end: u16, length: u8) -> Self { Self { is_16bit_aware: true, @@ -251,21 +265,24 @@ impl ResourceObject for IoPort { } } +/// A group of current ACPI resources (CRS) pub struct CurrentResourceSettings { resources: Vec, } impl CurrentResourceSettings { + /// Construct a new [`CurrentResourceSettings`]. pub fn new() -> Self { Self { resources: vec![] } } + /// Add a resource to the collection. pub fn add_resource(&mut self, resource: &impl ResourceObject) { resource.append_to_vec(&mut self.resources); } } -impl DsdtObject for CurrentResourceSettings { +impl AmlObject for CurrentResourceSettings { fn append_to_vec(&self, byte_stream: &mut Vec) { let mut resource_bytes = self.resources.clone(); // Add end of resource marker @@ -280,7 +297,7 @@ impl DsdtObject for CurrentResourceSettings { #[cfg(test)] mod tests { use super::*; - use crate::dsdt::tests::verify_expected_bytes; + use crate::aml::test_helpers::verify_expected_bytes; #[test] fn verify_memory_resource_object() { diff --git a/vm/acpi/src/builder.rs b/vm/acpi/src/builder.rs index e0277e8e9b..649db3e669 100644 --- a/vm/acpi/src/builder.rs +++ b/vm/acpi/src/builder.rs @@ -111,7 +111,7 @@ impl Builder { pub fn append(&mut self, table: &Table<'_>) -> u64 { let addr = self.base_addr + self.v.len() as u64; let len = table.append_to_vec(&self.oem, &mut self.v); - if len % 8 != 0 { + if !len.is_multiple_of(8) { self.v.extend_from_slice(&[0; 8][..8 - len % 8]); } if table.signature != *b"XSDT" && table.signature != *b"DSDT" { @@ -127,7 +127,7 @@ impl Builder { self.tables.push(self.base_addr + offset); } self.v.extend_from_slice(data); - if data.len() % 8 != 0 { + if !data.len().is_multiple_of(8) { self.v.extend_from_slice(&[0; 8][..8 - data.len() % 8]); } self.base_addr + offset diff --git a/vm/acpi/src/dsdt.rs b/vm/acpi/src/dsdt.rs index 8102a9baee..b24c97c55b 100644 --- a/vm/acpi/src/dsdt.rs +++ b/vm/acpi/src/dsdt.rs @@ -1,16 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -pub mod helpers; -pub mod objects; -pub mod ops; -pub mod resources; - -pub use helpers::*; +pub use crate::aml::*; use memory_range::MemoryRange; -pub use objects::*; -pub use ops::*; -pub use resources::*; use x86defs::apic::APIC_BASE_ADDRESS; use zerocopy::FromBytes; use zerocopy::Immutable; @@ -21,9 +13,9 @@ use zerocopy::KnownLayout; #[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] pub struct DescriptionHeader { pub signature: u32, - _length: u32, + _length: u32, // placeholder, filled in during serialization to bytes pub revision: u8, - _checksum: u8, + _checksum: u8, // placeholder, filled in during serialization to bytes pub oem_id: [u8; 6], pub oem_table_id: u64, pub oem_revision: u32, @@ -31,91 +23,6 @@ pub struct DescriptionHeader { pub creator_rev: u32, } -pub struct Method { - pub name: [u8; 4], - pub sync_level: u8, - pub is_serialized: bool, - pub arg_count: u8, - operations: Vec, -} - -impl Method { - pub fn new(name: &[u8; 4]) -> Self { - let local_name: [u8; 4] = [name[0], name[1], name[2], name[3]]; - Self { - name: local_name, - sync_level: 0, - is_serialized: false, - arg_count: 0, - operations: vec![], - } - } - - pub fn set_arg_count(&mut self, arg_count: u8) { - self.arg_count = arg_count; - } - - pub fn add_operation(&mut self, op: &impl OperationObject) { - op.append_to_vec(&mut self.operations); - } -} - -impl DsdtObject for Method { - fn append_to_vec(&self, byte_stream: &mut Vec) { - byte_stream.push(0x14); - byte_stream.extend_from_slice(&encode_package_len(5 + self.operations.len())); - byte_stream.extend_from_slice(&self.name); - byte_stream.push( - self.sync_level << 4 | if self.is_serialized { 1 << 3 } else { 0 } | self.arg_count, - ); - byte_stream.extend_from_slice(&self.operations); - } -} - -pub struct EisaId(pub [u8; 7]); - -impl DsdtObject for EisaId { - fn append_to_vec(&self, byte_stream: &mut Vec) { - let mut id: [u8; 4] = [0; 4]; - id[0] = (self.0[0] - b'@') << 2 | (self.0[1] - b'@') >> 3; - id[1] = (self.0[1] & 7) << 5 | (self.0[2] - b'@'); - id[2] = char_to_hex(self.0[3]) << 4 | char_to_hex(self.0[4]); - id[3] = char_to_hex(self.0[5]) << 4 | char_to_hex(self.0[6]); - byte_stream.append(&mut encode_integer(u32::from_le_bytes(id) as u64)); - } -} - -pub struct Device { - name: Vec, - objects: Vec, -} - -impl Device { - pub fn new(name: &[u8]) -> Self { - Self { - name: encode_name(name), - objects: vec![], - } - } - - pub fn add_object(&mut self, obj: &impl DsdtObject) { - obj.append_to_vec(&mut self.objects); - } -} - -impl DsdtObject for Device { - // A device object consists of the extended identifier (0x5b 0x82) followed by the length, the name and then the - // contained objects. - fn append_to_vec(&self, byte_stream: &mut Vec) { - byte_stream.push(0x5b); - byte_stream.push(0x82); - let length = self.name.len() + self.objects.len(); - byte_stream.extend_from_slice(&encode_package_len(length)); - byte_stream.extend_from_slice(&self.name); - byte_stream.extend_from_slice(&self.objects); - } -} - pub struct PciRoutingTableEntry { pub address: u32, pub pin: u8, @@ -139,7 +46,7 @@ impl PciRoutingTable { } } -impl DsdtObject for PciRoutingTable { +impl AmlObject for PciRoutingTable { fn append_to_vec(&self, byte_stream: &mut Vec) { let mut table_data: Vec = Vec::with_capacity(self.entries.len() * 10); for entry in self.entries.iter() { @@ -213,7 +120,7 @@ impl Dsdt { byte_stream } - pub fn add_object(&mut self, obj: &impl DsdtObject) { + pub fn add_object(&mut self, obj: &impl AmlObject) { obj.append_to_vec(&mut self.objects); } @@ -458,6 +365,7 @@ impl Dsdt { #[cfg(test)] mod tests { use super::*; + use crate::aml::test_helpers::verify_expected_bytes; pub fn verify_header(bytes: &[u8]) { assert!(bytes.len() >= 36); @@ -515,58 +423,6 @@ mod tests { assert_eq!(creator_rev, 0x5000000); } - pub fn verify_expected_bytes(actual: &[u8], expected: &[u8]) { - assert_eq!( - actual.len(), - expected.len(), - "Length of buffer does not match" - ); - for i in 0..actual.len() { - assert_eq!(actual[i], expected[i], "Mismatch at index {}", i); - } - } - - #[test] - fn verify_eisaid() { - let eisa_id = EisaId(*b"PNP0003"); - let bytes = eisa_id.to_bytes(); - verify_expected_bytes(&bytes, &[0xc, 0x41, 0xd0, 0, 0x3]); - } - - #[test] - fn verify_method() { - let op = AndOp { - operand1: vec![b'S', b'T', b'A', b'_'], - operand2: encode_integer(13), - target_name: vec![b'S', b'T', b'A', b'_'], - }; - let mut method = Method::new(b"_DIS"); - method.add_operation(&op); - let bytes = method.to_bytes(); - verify_expected_bytes( - &bytes, - &[ - 0x14, 0x11, 0x5F, 0x44, 0x49, 0x53, 0x00, 0x7b, b'S', b'T', b'A', b'_', 0x0a, 0x0d, - b'S', b'T', b'A', b'_', - ], - ); - } - - #[test] - fn verify_device_object() { - let package = Package(vec![0]); - let nobj = NamedObject::new(b"FOO", &package); - let mut device = Device::new(b"DEV"); - device.add_object(&nobj); - let bytes = device.to_bytes(); - verify_expected_bytes( - &bytes, - &[ - 0x5b, 0x82, 14, b'D', b'E', b'V', b'_', 8, b'F', b'O', b'O', b'_', 0x12, 3, 1, 0, - ], - ); - } - #[test] fn verify_simple_table() { let mut dsdt = Dsdt::new(); diff --git a/vm/acpi/src/lib.rs b/vm/acpi/src/lib.rs index 8d38191d82..77d223fdda 100644 --- a/vm/acpi/src/lib.rs +++ b/vm/acpi/src/lib.rs @@ -6,5 +6,7 @@ #![expect(missing_docs)] #![forbid(unsafe_code)] +mod aml; pub mod builder; pub mod dsdt; +pub mod ssdt; diff --git a/vm/acpi/src/ssdt.rs b/vm/acpi/src/ssdt.rs new file mode 100644 index 0000000000..3a1590542e --- /dev/null +++ b/vm/acpi/src/ssdt.rs @@ -0,0 +1,214 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +pub use crate::aml::*; +use memory_range::MemoryRange; +use zerocopy::FromBytes; +use zerocopy::Immutable; +use zerocopy::IntoBytes; +use zerocopy::KnownLayout; + +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] +pub struct DescriptionHeader { + pub signature: u32, + _length: u32, // placeholder, filled in during serialization to bytes + pub revision: u8, + _checksum: u8, // placeholder, filled in during serialization to bytes + pub oem_id: [u8; 6], + pub oem_table_id: u64, + pub oem_revision: u32, + pub creator_id: u32, + pub creator_rev: u32, +} + +fn encode_pcie_name(mut pcie_index: u32) -> Vec { + assert!(pcie_index < 1000); + let mut temp = "PCI0".as_bytes().to_vec(); + let mut i = temp.len() - 1; + while pcie_index > 0 { + temp[i] = b'0' + (pcie_index % 10) as u8; + pcie_index /= 10; + i -= 1; + } + temp +} + +pub struct Ssdt { + description_header: DescriptionHeader, + objects: Vec, +} + +impl Ssdt { + pub fn new() -> Self { + Self { + description_header: DescriptionHeader { + signature: u32::from_le_bytes(*b"SSDT"), + _length: 0, + revision: 2, + _checksum: 0, + oem_id: *b"MSFTVM", + oem_table_id: 0x313054445353, // b'SSDT01' + oem_revision: 1, + creator_id: u32::from_le_bytes(*b"MSFT"), + creator_rev: 0x01000000, + }, + objects: vec![], + } + } + + pub fn to_bytes(&self) -> Vec { + let mut byte_stream = Vec::new(); + byte_stream.extend_from_slice(self.description_header.as_bytes()); + byte_stream.extend_from_slice(&self.objects); + + let length = byte_stream.len(); + byte_stream[4..8].copy_from_slice(&u32::try_from(length).unwrap().to_le_bytes()); + let mut checksum: u8 = 0; + for byte in &byte_stream { + checksum = checksum.wrapping_add(*byte); + } + + byte_stream[9] = (!checksum).wrapping_add(1); + byte_stream + } + + pub fn add_object(&mut self, obj: &impl AmlObject) { + obj.append_to_vec(&mut self.objects); + } + + /// Adds a PCI Express root complex with the specified bus number and MMIO ranges. + /// + /// ```text + /// Device(\_SB.PCI) + /// { + /// Name(_HID, PNP0A08) + /// Name(_UID, ) + /// Name(_SEG, ) + /// Name(_BBN, ) + /// Name(_CRS, ResourceTemplate() + /// { + /// WordBusNumber(...) // Bus number range + /// QWordMemory() // Low MMIO + /// QWordMemory() // High MMIO + /// }) + /// } + /// ``` + pub fn add_pcie( + &mut self, + index: u32, + segment: u16, + start_bus: u8, + end_bus: u8, + low_mmio: MemoryRange, + high_mmio: MemoryRange, + ) { + let mut pcie = Device::new(encode_pcie_name(index).as_slice()); + pcie.add_object(&NamedObject::new(b"_HID", &EisaId(*b"PNP0A08"))); + pcie.add_object(&NamedInteger::new(b"_UID", index.into())); + pcie.add_object(&NamedInteger::new(b"_SEG", segment.into())); + pcie.add_object(&NamedInteger::new(b"_BBN", start_bus.into())); + + // TODO: Add an _OSC method to grant native PCIe control. Linux ignores + // OSC and assumes control, but Windows will skip initialization of + // some PCIe features when OSC is not granted. + + let mut crs = CurrentResourceSettings::new(); + crs.add_resource(&BusNumber::new( + start_bus.into(), + (end_bus as u16) - (start_bus as u16) + 1, + )); + crs.add_resource(&QwordMemory::new( + low_mmio.start(), + low_mmio.end() - low_mmio.start(), + )); + crs.add_resource(&QwordMemory::new( + high_mmio.start(), + high_mmio.end() - high_mmio.start(), + )); + pcie.add_object(&crs); + + self.add_object(&pcie); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::aml::test_helpers::verify_expected_bytes; + + pub fn verify_header(bytes: &[u8]) { + assert!(bytes.len() >= 36); + + // signature + assert_eq!(bytes[0], b'S'); + assert_eq!(bytes[1], b'S'); + assert_eq!(bytes[2], b'D'); + assert_eq!(bytes[3], b'T'); + + // length + let ssdt_len = u32::from_le_bytes(bytes[4..8].try_into().unwrap()); + assert_eq!(ssdt_len as usize, bytes.len()); + + // revision + assert_eq!(bytes[8], 2); + + // Validate checksum bytes[9] by verifying content adds to zero. + let mut checksum: u8 = 0; + for byte in bytes.iter() { + checksum = checksum.wrapping_add(*byte); + } + assert_eq!(checksum, 0); + + // oem_id + assert_eq!(bytes[10], b'M'); + assert_eq!(bytes[11], b'S'); + assert_eq!(bytes[12], b'F'); + assert_eq!(bytes[13], b'T'); + assert_eq!(bytes[14], b'V'); + assert_eq!(bytes[15], b'M'); + + // oem_table_id + assert_eq!(bytes[16], b'S'); + assert_eq!(bytes[17], b'S'); + assert_eq!(bytes[18], b'D'); + assert_eq!(bytes[19], b'T'); + assert_eq!(bytes[20], b'0'); + assert_eq!(bytes[21], b'1'); + assert_eq!(bytes[22], 0); + assert_eq!(bytes[23], 0); + + // oem_revision + let oem_revision = u32::from_le_bytes(bytes[24..28].try_into().unwrap()); + assert_eq!(oem_revision, 1); + + // creator_id + assert_eq!(bytes[28], b'M'); + assert_eq!(bytes[29], b'S'); + assert_eq!(bytes[30], b'F'); + assert_eq!(bytes[31], b'T'); + + // creator_rev + let creator_rev = u32::from_le_bytes(bytes[32..36].try_into().unwrap()); + assert_eq!(creator_rev, 0x01000000); + } + + #[test] + pub fn verify_pcie_name_encoding() { + assert_eq!(encode_pcie_name(0), b"PCI0".to_vec()); + assert_eq!(encode_pcie_name(1), b"PCI1".to_vec()); + assert_eq!(encode_pcie_name(2), b"PCI2".to_vec()); + assert_eq!(encode_pcie_name(54), b"PC54".to_vec()); + assert_eq!(encode_pcie_name(294), b"P294".to_vec()); + } + + #[test] + fn verify_simple_table() { + let mut ssdt = Ssdt::new(); + let nobj = NamedObject::new(b"_S0", &Package(vec![0, 0])); + ssdt.add_object(&nobj); + let bytes = ssdt.to_bytes(); + verify_header(&bytes); + verify_expected_bytes(&bytes[36..], &[8, b'_', b'S', b'0', b'_', 0x12, 4, 2, 0, 0]); + } +} diff --git a/vm/acpi_spec/src/lib.rs b/vm/acpi_spec/src/lib.rs index 92da23dd7a..4107577176 100644 --- a/vm/acpi_spec/src/lib.rs +++ b/vm/acpi_spec/src/lib.rs @@ -13,6 +13,7 @@ extern crate alloc; pub mod aspt; pub mod fadt; pub mod madt; +pub mod mcfg; pub mod pptt; pub mod srat; diff --git a/vm/acpi_spec/src/mcfg.rs b/vm/acpi_spec/src/mcfg.rs new file mode 100644 index 0000000000..6f9f76150e --- /dev/null +++ b/vm/acpi_spec/src/mcfg.rs @@ -0,0 +1,154 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#[cfg(feature = "alloc")] +pub use self::alloc_parse::*; + +use super::Table; +use crate::packed_nums::*; +use core::mem::size_of; +use static_assertions::const_assert_eq; +use thiserror::Error; +use zerocopy::FromBytes; +use zerocopy::Immutable; +use zerocopy::IntoBytes; +use zerocopy::KnownLayout; +use zerocopy::Ref; +use zerocopy::Unaligned; + +#[repr(C)] +#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)] +pub struct McfgHeader { + pub rsvd: u64_ne, +} + +impl McfgHeader { + pub fn new() -> Self { + McfgHeader { rsvd: 0.into() } + } +} + +impl Table for McfgHeader { + const SIGNATURE: [u8; 4] = *b"MCFG"; +} + +pub const MCFG_REVISION: u8 = 1; + +#[repr(C)] +#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)] +pub struct McfgSegmentBusRange { + pub ecam_base: u64_ne, + pub segment: u16_ne, + pub start_bus: u8, + pub end_bus: u8, + pub rsvd: u32_ne, +} + +const_assert_eq!(size_of::(), 16); + +impl McfgSegmentBusRange { + pub fn new(ecam_base: u64, segment: u16, start_bus: u8, end_bus: u8) -> Self { + Self { + ecam_base: ecam_base.into(), + segment: segment.into(), + start_bus, + end_bus, + rsvd: 0.into(), + } + } +} + +#[derive(Debug, Error)] +pub enum ParseMcfgError { + #[error("could not read standard ACPI header")] + MissingAcpiHeader, + #[error("invalid signature. expected b\"MCFG\", found {0:?}")] + InvalidSignature([u8; 4]), + #[error("mismatched lengh, header: {0}, actual: {1}")] + MismatchedLength(usize, usize), + #[error("could not read fixed MCFG header")] + MissingFixedHeader, + #[error("could not read segment bus range structure")] + BadSegmentBusRange, +} + +pub fn parse_mcfg<'a>( + raw_mcfg: &'a [u8], + mut on_segment_bus_range: impl FnMut(&'a McfgSegmentBusRange), +) -> Result<(&'a crate::Header, &'a McfgHeader), ParseMcfgError> { + let raw_mcfg_len = raw_mcfg.len(); + let (acpi_header, buf) = Ref::<_, crate::Header>::from_prefix(raw_mcfg) + .map_err(|_| ParseMcfgError::MissingAcpiHeader)?; + + if acpi_header.signature != *b"MCFG" { + return Err(ParseMcfgError::InvalidSignature(acpi_header.signature)); + } + + if acpi_header.length.get() as usize != raw_mcfg_len { + return Err(ParseMcfgError::MismatchedLength( + acpi_header.length.get() as usize, + raw_mcfg_len, + )); + } + + let (mcfg_header, mut buf) = + Ref::<_, McfgHeader>::from_prefix(buf).map_err(|_| ParseMcfgError::MissingFixedHeader)?; + + while !buf.is_empty() { + let (sbr, rest) = Ref::<_, McfgSegmentBusRange>::from_prefix(buf) + .map_err(|_| ParseMcfgError::BadSegmentBusRange)?; + on_segment_bus_range(Ref::into_ref(sbr)); + buf = rest + } + + Ok((Ref::into_ref(acpi_header), Ref::into_ref(mcfg_header))) +} + +#[cfg(feature = "alloc")] +pub mod alloc_parse { + use super::*; + use alloc::vec::Vec; + + #[derive(Debug)] + pub struct BorrowedMcfg<'a> { + pub acpi_header: &'a crate::Header, + pub mcfg_header: &'a McfgHeader, + pub segment_bus_ranges: Vec<&'a McfgSegmentBusRange>, + } + + #[derive(Debug)] + pub struct OwnedMcfg { + pub acpi_header: crate::Header, + pub mcfg_header: McfgHeader, + pub segment_bus_ranges: Vec, + } + + impl From> for OwnedMcfg { + fn from(b: BorrowedMcfg<'_>) -> Self { + OwnedMcfg { + acpi_header: *b.acpi_header, + mcfg_header: *b.mcfg_header, + segment_bus_ranges: b.segment_bus_ranges.into_iter().copied().collect(), + } + } + } + + impl BorrowedMcfg<'_> { + pub fn new(raw_mcfg: &[u8]) -> Result, ParseMcfgError> { + let mut segment_bus_ranges = Vec::new(); + let (acpi_header, mcfg_header) = parse_mcfg(raw_mcfg, |x| segment_bus_ranges.push(x))?; + + Ok(BorrowedMcfg { + acpi_header, + mcfg_header, + segment_bus_ranges, + }) + } + } + + impl OwnedMcfg { + pub fn new(raw_mcfg: &[u8]) -> Result { + Ok(BorrowedMcfg::new(raw_mcfg)?.into()) + } + } +} diff --git a/vm/devices/chipset/src/dma.rs b/vm/devices/chipset/src/dma.rs index 9cceec22a3..d64b5f4470 100644 --- a/vm/devices/chipset/src/dma.rs +++ b/vm/devices/chipset/src/dma.rs @@ -283,7 +283,7 @@ impl Controller { fn read(&mut self, reg: u16) -> Result { let res = if reg < 8 { let channel = reg as usize / 2; - let data = if reg % 2 == 0 { + let data = if reg.is_multiple_of(2) { // Address port. if !self.flip_flop_high { self.latched_address = self.channels[channel].address; @@ -328,7 +328,7 @@ impl Controller { fn write(&mut self, reg: u16, data: u8) -> IoResult { if reg < 8 { let channel = reg as usize / 2; - let mem = if reg % 2 == 0 { + let mem = if reg.is_multiple_of(2) { // Address port. &mut self.channels[channel].address } else { diff --git a/vm/devices/firmware/uefi_nvram_specvars/src/signature_list.rs b/vm/devices/firmware/uefi_nvram_specvars/src/signature_list.rs index d79e8b400b..236a556ae8 100644 --- a/vm/devices/firmware/uefi_nvram_specvars/src/signature_list.rs +++ b/vm/devices/firmware/uefi_nvram_specvars/src/signature_list.rs @@ -347,7 +347,7 @@ impl<'a> ParseSignatureSha256<'a> { // sha256 has consistent signature sizes, so we can perform some early // validation as an optimization - if buf.len() % expected_signature_size != 0 { + if !buf.len().is_multiple_of(expected_signature_size) { return Err(ParseError::Sha256TruncatedData); } diff --git a/vm/devices/hyperv_ic/src/kvp.rs b/vm/devices/hyperv_ic/src/kvp.rs index 0bce619a3f..9098da73ae 100644 --- a/vm/devices/hyperv_ic/src/kvp.rs +++ b/vm/devices/hyperv_ic/src/kvp.rs @@ -699,7 +699,7 @@ fn msg2( } fn parse_str(v: &[u16], n: u32) -> anyhow::Result { - if n % 2 != 0 { + if !n.is_multiple_of(2) { anyhow::bail!("invalid string length"); } let v = v.get(..n as usize / 2).context("string length too large")?; diff --git a/vm/devices/net/gdma/src/dma.rs b/vm/devices/net/gdma/src/dma.rs index aac9f3432f..91f23b0592 100644 --- a/vm/devices/net/gdma/src/dma.rs +++ b/vm/devices/net/gdma/src/dma.rs @@ -52,7 +52,7 @@ impl DmaRegion { pub fn is_aligned_to(&self, align: usize) -> bool { assert!(align <= PAGE_SIZE64 as usize); - (self.start | self.len) % align == 0 + (self.start | self.len).is_multiple_of(align) } pub fn range(&self) -> PagedRange<'_> { diff --git a/vm/devices/net/gdma/src/queues.rs b/vm/devices/net/gdma/src/queues.rs index 9259715428..786448d4bb 100644 --- a/vm/devices/net/gdma/src/queues.rs +++ b/vm/devices/net/gdma/src/queues.rs @@ -254,7 +254,10 @@ impl Wq { let old_len = self.available(); assert!(old_len <= self.cap); let new_len = val.wrapping_sub(self.head); - if self.head % WQE_ALIGNMENT as u32 == 0 && new_len > old_len && new_len <= self.cap { + if self.head.is_multiple_of(WQE_ALIGNMENT as u32) + && new_len > old_len + && new_len <= self.cap + { self.tail = val; self.waker.take() } else { diff --git a/vm/devices/net/mana_driver/src/queues.rs b/vm/devices/net/mana_driver/src/queues.rs index 77488dd1b8..2c55dc4b1c 100644 --- a/vm/devices/net/mana_driver/src/queues.rs +++ b/vm/devices/net/mana_driver/src/queues.rs @@ -291,7 +291,7 @@ impl Wq { /// Advances the head, indicating that `n` more bytes are available in the ring. pub fn advance_head(&mut self, n: u32) { - assert!(n % WQE_ALIGNMENT as u32 == 0); + assert!(n.is_multiple_of(WQE_ALIGNMENT as u32)); self.head = self.head.wrapping_add(n); } diff --git a/vm/devices/net/net_mana/Cargo.toml b/vm/devices/net/net_mana/Cargo.toml index 8a5e167402..c8870908e2 100644 --- a/vm/devices/net/net_mana/Cargo.toml +++ b/vm/devices/net/net_mana/Cargo.toml @@ -17,6 +17,7 @@ guestmem.workspace = true vmcore.workspace = true inspect = { workspace = true, features = ["std"] } +inspect_counters.workspace = true pal_async.workspace = true safeatomic.workspace = true diff --git a/vm/devices/net/net_mana/src/lib.rs b/vm/devices/net/net_mana/src/lib.rs index 51af084038..0db14d77a7 100644 --- a/vm/devices/net/net_mana/src/lib.rs +++ b/vm/devices/net/net_mana/src/lib.rs @@ -28,6 +28,7 @@ use guestmem::GuestMemory; use inspect::Inspect; use inspect::InspectMut; use inspect::SensitivityLevel; +use inspect_counters::Counter; use mana_driver::mana::BnicEq; use mana_driver::mana::BnicWq; use mana_driver::mana::ResourceArena; @@ -608,34 +609,21 @@ struct PostedTx { bounced_len_with_padding: u32, } -#[derive(Default)] +#[derive(Default, Inspect)] struct QueueStats { - tx_events: u64, - tx_packets: u64, - tx_errors: u64, - tx_dropped: u64, - tx_stuck: u64, + tx_events: Counter, + tx_packets: Counter, + tx_errors: Counter, + tx_dropped: Counter, + tx_stuck: Counter, - rx_events: u64, - rx_packets: u64, - rx_errors: u64, + rx_events: Counter, + rx_packets: Counter, + rx_errors: Counter, - interrupts: u64, -} + interrupts: Counter, -impl Inspect for QueueStats { - fn inspect(&self, req: inspect::Request<'_>) { - req.respond() - .counter("tx_events", self.tx_events) - .counter("tx_packets", self.tx_packets) - .counter("tx_errors", self.tx_errors) - .counter("tx_dropped", self.tx_dropped) - .counter("tx_stuck", self.tx_stuck) - .counter("rx_events", self.rx_events) - .counter("rx_packets", self.rx_packets) - .counter("rx_errors", self.rx_errors) - .counter("interrupts", self.interrupts); - } + tx_packets_coalesced: Counter, } impl InspectMut for ManaQueue { @@ -861,10 +849,10 @@ impl Queue for ManaQueue { let cq_id = u32::from_le_bytes(eqe.data[..4].try_into().unwrap()) & 0xffffff; if cq_id == self.tx_cq.id() { - self.stats.tx_events += 1; + self.stats.tx_events.increment(); self.tx_cq_armed = false; } else if cq_id == self.rx_cq.id() { - self.stats.rx_events += 1; + self.stats.rx_events.increment(); self.rx_cq_armed = false; } else { tracing::error!(cq_id, "unknown cq id"); @@ -889,7 +877,7 @@ impl Queue for ManaQueue { } std::task::ready!(self.interrupt.poll(cx)); - self.stats.interrupts += 1; + self.stats.interrupts.increment(); } } @@ -951,7 +939,7 @@ impl Queue for ManaQueue { .atomic_read(&mut data); self.pool.write_data(rx.id, &data); } - self.stats.rx_packets += 1; + self.stats.rx_packets.increment(); packets[i] = rx.id; i += 1; } @@ -964,7 +952,7 @@ impl Queue for ManaQueue { "invalid rx cqe type" ); self.trace_rx_wqe_from_offset(rx_oob.rx_wqe_offset); - self.stats.rx_errors += 1; + self.stats.rx_errors.increment(); self.avail_rx.push_back(rx.id); } } @@ -1025,13 +1013,13 @@ impl Queue for ManaQueue { let tx_oob = ManaTxCompOob::read_from_prefix(&cqe.data[..]).unwrap().0; // TODO: zerocopy: use-rest-of-range (https://github.com/microsoft/openvmm/issues/759) match tx_oob.cqe_hdr.cqe_type() { CQE_TX_OKAY => { - self.stats.tx_packets += 1; + self.stats.tx_packets.increment(); } CQE_TX_GDMA_ERR => { // Hardware hit an error with the packet coming from the Guest. // CQE_TX_GDMA_ERR is how the Hardware indicates that it has disabled the queue. - self.stats.tx_errors += 1; - self.stats.tx_stuck += 1; + self.stats.tx_errors.increment(); + self.stats.tx_stuck.increment(); self.trace_tx_error(cqe.params, tx_oob, done.len()); // Return a TryRestart error to indicate that the queue needs to be restarted. return Err(TxError::TryRestart(anyhow::anyhow!("TX GDMA error"))); @@ -1039,7 +1027,7 @@ impl Queue for ManaQueue { CQE_TX_INVALID_OOB => { // Invalid OOB means the metadata didn't match how the Hardware parsed the packet. // This is somewhat common, usually due to Encapsulation, and only the affects the specific packet. - self.stats.tx_errors += 1; + self.stats.tx_errors.increment(); self.trace_tx_error(cqe.params, tx_oob, done.len()); } ty => { @@ -1048,7 +1036,7 @@ impl Queue for ManaQueue { vendor_error = tx_oob.cqe_hdr.vendor_err(), "tx completion error" ); - self.stats.tx_errors += 1; + self.stats.tx_errors.increment(); } } let packet = self.posted_tx.pop_front().unwrap(); @@ -1058,7 +1046,7 @@ impl Queue for ManaQueue { } packet.id } else if let Some(id) = self.dropped_tx.pop_front() { - self.stats.tx_dropped += 1; + self.stats.tx_dropped.increment(); id } else { if !self.tx_cq_armed { @@ -1335,6 +1323,7 @@ impl ManaQueue { size: tail.len, }; } + self.stats.tx_packets_coalesced.increment(); &sgl[..segment_count] }; @@ -1631,7 +1620,7 @@ mod tests { let mut segments = Vec::new(); let segment_len = packet_len / num_segments; - assert!(packet_len % num_segments == 0); + assert!(packet_len.is_multiple_of(num_segments)); assert!(sent_data.len() == packet_len); segments.push(TxSegment { ty: net_backend::TxSegmentType::Head(net_backend::TxMetadata { diff --git a/vm/devices/net/netvsp/src/lib.rs b/vm/devices/net/netvsp/src/lib.rs index 248deb571b..5099c84e71 100644 --- a/vm/devices/net/netvsp/src/lib.rs +++ b/vm/devices/net/netvsp/src/lib.rs @@ -4344,12 +4344,7 @@ impl Coordinator { ); initial_rx = (RX_RESERVED_CONTROL_BUFFERS..state.buffers.recv_buffer.count) - .filter(|&n| { - states - .clone() - .flatten() - .all(|s| (s.state.rx_bufs.is_free(n))) - }) + .filter(|&n| states.clone().flatten().all(|s| s.state.rx_bufs.is_free(n))) .map(RxId) .collect::>(); diff --git a/vm/devices/pci/pci_core/src/capabilities/mod.rs b/vm/devices/pci/pci_core/src/capabilities/mod.rs index 3c467f236f..581753eac1 100644 --- a/vm/devices/pci/pci_core/src/capabilities/mod.rs +++ b/vm/devices/pci/pci_core/src/capabilities/mod.rs @@ -3,6 +3,8 @@ //! PCI capabilities. +pub use self::pci_express::FlrHandler; +pub use self::pci_express::PciExpressCapability; pub use self::read_only::ReadOnlyCapability; use inspect::Inspect; diff --git a/vm/devices/pci/pci_core/src/cfg_space_emu.rs b/vm/devices/pci/pci_core/src/cfg_space_emu.rs index e358d82fa8..67cee08dba 100644 --- a/vm/devices/pci/pci_core/src/cfg_space_emu.rs +++ b/vm/devices/pci/pci_core/src/cfg_space_emu.rs @@ -531,7 +531,7 @@ impl ConfigSpaceType0Emulator { self.state.interrupt_line = ((val & 0xff00) >> 8) as u8; } // all other base regs are noops - _ if offset < 0x40 && offset % 4 == 0 => (), + _ if offset < 0x40 && offset.is_multiple_of(4) => (), // rest of the range is reserved for extended device capabilities _ if (0x40..0x100).contains(&offset) => { if let Some((cap_index, cap_offset)) = diff --git a/vm/devices/storage/disk_backend/src/sync_wrapper.rs b/vm/devices/storage/disk_backend/src/sync_wrapper.rs index 2687c0c887..cbd8fc61d9 100644 --- a/vm/devices/storage/disk_backend/src/sync_wrapper.rs +++ b/vm/devices/storage/disk_backend/src/sync_wrapper.rs @@ -91,7 +91,7 @@ impl BlockingDisk { fn read(&mut self, buf: &mut [u8]) -> io::Result { // If the buffer size is a multiple of sector size and the buffer is not dirty // use the read_full_sector method - if buf.len() % self.inner.sector_size() as usize == 0 && !self.buffer_dirty { + if buf.len().is_multiple_of(self.inner.sector_size() as usize) && !self.buffer_dirty { return self.read_full_sector(buf); } // Buffer size is not multiple of sector size @@ -122,7 +122,7 @@ impl BlockingDisk { fn write(&mut self, buf: &[u8]) -> io::Result { // If the buffer size is a multiple of sector size and the buffer is not dirty // use the write_full_sector method - if buf.len() % self.inner.sector_size() as usize == 0 && !self.buffer_dirty { + if buf.len().is_multiple_of(self.inner.sector_size() as usize) && !self.buffer_dirty { return self.write_full_sector(buf); } // Buffer size is not multiple of sector size diff --git a/vm/devices/storage/disk_get_vmgs/src/lib.rs b/vm/devices/storage/disk_get_vmgs/src/lib.rs index 4905070016..a80a33cda7 100644 --- a/vm/devices/storage/disk_get_vmgs/src/lib.rs +++ b/vm/devices/storage/disk_get_vmgs/src/lib.rs @@ -123,7 +123,7 @@ impl GetVmgsDisk { Err(NewGetVmgsDiskError::InvalidPhysicalSectorSize) } else if sector_count.checked_mul(sector_size as u64).is_none() { Err(NewGetVmgsDiskError::InvalidSectorCount) - } else if sector_count % (physical_sector_size / sector_size) as u64 != 0 { + } else if !sector_count.is_multiple_of((physical_sector_size / sector_size) as u64) { Err(NewGetVmgsDiskError::IncompletePhysicalSector) } else if max_transfer_size < physical_sector_size { Err(NewGetVmgsDiskError::InvalidMaxTransferSize) diff --git a/vm/devices/storage/disk_nvme/nvme_driver/src/tests.rs b/vm/devices/storage/disk_nvme/nvme_driver/src/tests.rs index 00da807209..7f2be9fa6a 100644 --- a/vm/devices/storage/disk_nvme/nvme_driver/src/tests.rs +++ b/vm/devices/storage/disk_nvme/nvme_driver/src/tests.rs @@ -361,6 +361,7 @@ async fn test_nvme_fault_injection(driver: DefaultDriver, fault_configuration: F msix_count: MSIX_COUNT, max_io_queues: IO_QUEUE_COUNT, subsystem_id: Guid::new_random(), + flr_support: false, }, fault_configuration, ); diff --git a/vm/devices/storage/disk_striped/src/lib.rs b/vm/devices/storage/disk_striped/src/lib.rs index ee31e79f4a..e37dc57e74 100644 --- a/vm/devices/storage/disk_striped/src/lib.rs +++ b/vm/devices/storage/disk_striped/src/lib.rs @@ -251,7 +251,7 @@ impl StripedDisk { let sector_count = devices[0].sector_count(); let read_only = devices[0].is_read_only(); let chunk_size_in_bytes = chunk_size_in_bytes.unwrap_or(CHUNK_SIZE_128K); - if chunk_size_in_bytes % sector_size != 0 { + if !chunk_size_in_bytes.is_multiple_of(sector_size) { return Err(NewDeviceError::InvalidChunkSize( chunk_size_in_bytes, sector_size, @@ -296,7 +296,7 @@ impl StripedDisk { )); } - if logic_sector_count % (devices.len() as u64 * sector_count_per_chunk) != 0 { + if !logic_sector_count.is_multiple_of(devices.len() as u64 * sector_count_per_chunk) { return Err(NewDeviceError::InvalidStripingDiskSize( logic_sector_count, devices.len() as u64 * sector_count_per_chunk, diff --git a/vm/devices/storage/disklayer_ram/src/lib.rs b/vm/devices/storage/disklayer_ram/src/lib.rs index 6e97e9dd94..24bc842125 100644 --- a/vm/devices/storage/disklayer_ram/src/lib.rs +++ b/vm/devices/storage/disklayer_ram/src/lib.rs @@ -113,7 +113,7 @@ impl RamDiskLayer { if size == 0 { return Err(Error::EmptyDisk); } - if size % SECTOR_SIZE as u64 != 0 { + if !size.is_multiple_of(SECTOR_SIZE as u64) { return Err(Error::NotSectorMultiple { disk_size: size, sector_size: SECTOR_SIZE, diff --git a/vm/devices/storage/nvme/src/workers.rs b/vm/devices/storage/nvme/src/workers.rs index 205e5a65bc..64924a9bd0 100644 --- a/vm/devices/storage/nvme/src/workers.rs +++ b/vm/devices/storage/nvme/src/workers.rs @@ -24,6 +24,6 @@ const MAX_DATA_TRANSFER_SIZE: usize = 256 * 1024; const _: () = assert!( MAX_DATA_TRANSFER_SIZE.is_power_of_two() - && MAX_DATA_TRANSFER_SIZE % PAGE_SIZE == 0 + && MAX_DATA_TRANSFER_SIZE.is_multiple_of(PAGE_SIZE) && MAX_DATA_TRANSFER_SIZE / PAGE_SIZE > 1 ); diff --git a/vm/devices/storage/nvme_resources/src/fault.rs b/vm/devices/storage/nvme_resources/src/fault.rs index e62b7c8b3f..d2decf08f1 100644 --- a/vm/devices/storage/nvme_resources/src/fault.rs +++ b/vm/devices/storage/nvme_resources/src/fault.rs @@ -128,7 +128,7 @@ impl AdminQueueFaultConfig { if self .admin_submission_queue_faults .iter() - .any(|(c, _)| (pattern == *c)) + .any(|(c, _)| pattern == *c) { panic!( "Duplicate submission queue fault for Compare {:?} and Mask {:?}", @@ -153,7 +153,7 @@ impl AdminQueueFaultConfig { if self .admin_completion_queue_faults .iter() - .any(|(c, _)| (pattern == *c)) + .any(|(c, _)| pattern == *c) { panic!( "Duplicate completion queue fault for Compare {:?} and Mask {:?}", diff --git a/vm/devices/storage/nvme_resources/src/lib.rs b/vm/devices/storage/nvme_resources/src/lib.rs index 4b7d55616b..f7053c9b18 100644 --- a/vm/devices/storage/nvme_resources/src/lib.rs +++ b/vm/devices/storage/nvme_resources/src/lib.rs @@ -43,6 +43,8 @@ pub struct NvmeFaultControllerHandle { pub max_io_queues: u16, /// The initial set of namespaces. pub namespaces: Vec, + /// Whether to enable flr support. + pub flr_support: bool, /// Configuration for the fault pub fault_config: FaultConfiguration, } diff --git a/vm/devices/storage/nvme_test/Cargo.toml b/vm/devices/storage/nvme_test/Cargo.toml index 1598dcad6d..1e0f49d914 100644 --- a/vm/devices/storage/nvme_test/Cargo.toml +++ b/vm/devices/storage/nvme_test/Cargo.toml @@ -27,7 +27,6 @@ inspect.workspace = true mesh.workspace = true pal_async.workspace = true async-trait.workspace = true -event-listener.workspace = true futures.workspace = true futures-concurrency.workspace = true parking_lot.workspace = true diff --git a/vm/devices/storage/nvme_test/src/pci.rs b/vm/devices/storage/nvme_test/src/pci.rs index c1d4815195..0b631d7cf2 100644 --- a/vm/devices/storage/nvme_test/src/pci.rs +++ b/vm/devices/storage/nvme_test/src/pci.rs @@ -34,6 +34,8 @@ use nvme_resources::fault::FaultConfiguration; use nvme_resources::fault::PciFaultBehavior; use parking_lot::Mutex; use pci_core::capabilities::msix::MsixEmulator; +use pci_core::capabilities::pci_express::FlrHandler; +use pci_core::capabilities::pci_express::PciExpressCapability; use pci_core::cfg_space_emu::BarMemoryKind; use pci_core::cfg_space_emu::ConfigSpaceType0Emulator; use pci_core::cfg_space_emu::DeviceBars; @@ -49,6 +51,32 @@ use vmcore::save_restore::SaveRestore; use vmcore::save_restore::SavedStateNotSupported; use vmcore::vm_task::VmTaskDriverSource; +/// FLR handler that signals reset requests. +#[derive(Inspect)] +struct NvmeFlrHandler { + #[inspect(skip)] + reset_requested: Arc, +} + +impl NvmeFlrHandler { + fn new() -> (Self, Arc) { + let reset_requested = Arc::new(std::sync::atomic::AtomicBool::new(false)); + ( + Self { + reset_requested: reset_requested.clone(), + }, + reset_requested, + ) + } +} + +impl FlrHandler for NvmeFlrHandler { + fn initiate_flr(&self) { + self.reset_requested + .store(true, std::sync::atomic::Ordering::SeqCst); + } +} + /// An NVMe controller. #[derive(InspectMut)] pub struct NvmeFaultController { @@ -61,7 +89,9 @@ pub struct NvmeFaultController { #[inspect(flatten, mut)] workers: NvmeWorkers, #[inspect(skip)] - fault_configuration: FaultConfiguration, + flr_reset_requested: Option>, + #[inspect(skip)] + worker_context: NvmeWorkersContext, } #[derive(Inspect)] @@ -107,6 +137,8 @@ pub struct NvmeFaultControllerCaps { /// The subsystem ID, used as part of the subnqn field of the identify /// controller response. pub subsystem_id: Guid, + /// Whether to advertise Function Level Reset (FLR) support. + pub flr_support: bool, } impl NvmeFaultController { @@ -130,6 +162,20 @@ impl NvmeFaultController { BarMemoryKind::Intercept(register_mmio.new_io_region("msix", msix.bar_len())), ); + // Prepare capabilities list + let mut capabilities: Vec> = + vec![Box::new(msix_cap)]; + + // Optionally add PCI Express capability with FLR support + let flr_reset_requested = if caps.flr_support { + let (flr_handler, reset_requested) = NvmeFlrHandler::new(); + let pcie_cap = PciExpressCapability::new(Some(Arc::new(flr_handler))); + capabilities.push(Box::new(pcie_cap)); + Some(reset_requested) + } else { + None + }; + let cfg_space = ConfigSpaceType0Emulator::new( HardwareIds { vendor_id: VENDOR_ID, @@ -141,7 +187,7 @@ impl NvmeFaultController { type0_sub_vendor_id: 0, type0_sub_system_id: 0, }, - vec![Box::new(msix_cap)], + capabilities, bars, ); @@ -150,16 +196,18 @@ impl NvmeFaultController { .collect(); let qe_sizes = Arc::new(Default::default()); - let admin = NvmeWorkers::new(NvmeWorkersContext { - driver_source, + let worker_context = NvmeWorkersContext { + driver_source: driver_source.clone(), mem: guest_memory, interrupts, max_sqs: caps.max_io_queues, max_cqs: caps.max_io_queues, qe_sizes: Arc::clone(&qe_sizes), subsystem_id: caps.subsystem_id, - fault_configuration: fault_configuration.clone(), - }); + fault_configuration, + }; + + let admin = NvmeWorkers::new(worker_context.clone()); Self { cfg_space, @@ -167,7 +215,8 @@ impl NvmeFaultController { registers: RegState::new(), workers: admin, qe_sizes, - fault_configuration, + flr_reset_requested, + worker_context, } } @@ -233,15 +282,15 @@ impl NvmeFaultController { if addr >= 0x1000 { // Doorbell write. let base = addr - 0x1000; - let index = base >> DOORBELL_STRIDE_BITS; - if (index << DOORBELL_STRIDE_BITS) != base { + let db_id = base >> DOORBELL_STRIDE_BITS; + if (db_id << DOORBELL_STRIDE_BITS) != base { return IoResult::Err(InvalidRegister); } let Ok(data) = data.try_into() else { return IoResult::Err(IoError::InvalidAccessSize); }; - let data = u32::from_ne_bytes(data); - self.workers.doorbell(index, data); + let value = u32::from_ne_bytes(data); + self.workers.doorbell(db_id, value); return IoResult::Ok; } @@ -347,6 +396,7 @@ impl NvmeFaultController { if cc.en() { // If any fault was configured for cc.en() process it here match self + .worker_context .fault_configuration .pci_fault .controller_management_fault_enable @@ -446,7 +496,8 @@ impl ChangeDeviceState for NvmeFaultController { registers, qe_sizes, workers, - fault_configuration: _, + flr_reset_requested: _, + worker_context: _, } = self; workers.reset().await; cfg_space.reset(); @@ -501,7 +552,23 @@ impl PciConfigSpace for NvmeFaultController { } fn pci_cfg_write(&mut self, offset: u16, value: u32) -> IoResult { - self.cfg_space.write_u32(offset, value) + let result = self.cfg_space.write_u32(offset, value); + + // Check for FLR requests + if let Some(flr_requested) = &self.flr_reset_requested { + // According to the spec, FLR bit should always read 0, reset it before responding. + if flr_requested.swap(false, std::sync::atomic::Ordering::SeqCst) { + // FLR entails a state agnostic hard-reset. Instead of just resetting the controller, + // create a completely new worker backend to ensure clean state. + self.cfg_space.reset(); + self.registers = RegState::new(); + *self.qe_sizes.lock() = Default::default(); + + self.workers = NvmeWorkers::new(self.worker_context.clone()); + } + } + + result } } diff --git a/vm/devices/storage/nvme_test/src/queue.rs b/vm/devices/storage/nvme_test/src/queue.rs index 0b603c1c94..5ac0d3b8b5 100644 --- a/vm/devices/storage/nvme_test/src/queue.rs +++ b/vm/devices/storage/nvme_test/src/queue.rs @@ -3,266 +3,342 @@ //! NVMe submission and completion queue types. +use crate::DOORBELL_STRIDE_BITS; use crate::spec; use guestmem::GuestMemory; use guestmem::GuestMemoryError; use inspect::Inspect; +use parking_lot::RwLock; use std::sync::Arc; -use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; +use std::task::Context; +use std::task::Poll; +use std::task::Waker; +use std::task::ready; use thiserror::Error; use vmcore::interrupt::Interrupt; -pub const ILLEGAL_DOORBELL_VALUE: u32 = 0xffffffff; - -#[derive(Default, Inspect)] -#[inspect(transparent)] -pub struct DoorbellRegister { - #[inspect(hex)] - value: AtomicU32, - #[inspect(skip)] - event: event_listener::Event, +pub struct DoorbellMemory { + mem: GuestMemory, + offset: u64, + event_idx_offset: Option, + wakers: Vec>, } -impl DoorbellRegister { - pub fn new() -> Self { - Self::default() +pub struct InvalidDoorbell; + +impl DoorbellMemory { + pub fn new(num_qids: u16) -> Self { + Self { + mem: GuestMemory::allocate((num_qids as usize) << DOORBELL_STRIDE_BITS), + offset: 0, + event_idx_offset: None, + wakers: (0..num_qids).map(|_| None).collect(), + } } - pub fn write(&self, value: u32) { - self.value.store(value, Ordering::SeqCst); - self.event.notify(usize::MAX); + /// Update the memory used to store the doorbell values. This is used to + /// support shadow doorbells, where the values are directly in guest memory. + pub fn replace_mem( + &mut self, + mem: GuestMemory, + offset: u64, + event_idx_offset: Option, + ) -> Result<(), GuestMemoryError> { + // Copy the current doorbell values into the new memory. + let len = self.wakers.len() << DOORBELL_STRIDE_BITS; + let mut current = vec![0; len]; + self.mem.read_at(self.offset, &mut current)?; + mem.write_at(offset, ¤t)?; + if let Some(event_idx_offset) = event_idx_offset { + // Catch eventidx up to the current doorbell value. + mem.write_at(event_idx_offset, ¤t)?; + } + self.mem = mem; + self.offset = offset; + self.event_idx_offset = event_idx_offset; + Ok(()) } - pub fn read(&self) -> u32 { - self.value.load(Ordering::SeqCst) + pub fn try_write(&self, db_id: u16, value: u32) -> Result<(), InvalidDoorbell> { + if (db_id as usize) >= self.wakers.len() { + return Err(InvalidDoorbell); + } + self.write(db_id, value); + Ok(()) } - pub async fn wait_read(&self, value: u32) -> u32 { - let v = self.read(); - if value != v { - return v; + fn write(&self, db_id: u16, value: u32) { + assert!((db_id as usize) < self.wakers.len()); + let addr = self + .offset + .wrapping_add((db_id as u64) << DOORBELL_STRIDE_BITS); + if let Err(err) = self.mem.write_plain(addr, &value) { + tracelimit::error_ratelimited!( + error = &err as &dyn std::error::Error, + "failed to write doorbell memory" + ); } - loop { - let listener = self.event.listen(); - let v = self.read(); - if value != v { - break v; - } - listener.await; + if let Some(waker) = &self.wakers[db_id as usize] { + waker.wake_by_ref(); + } + } + + fn read(&self, db_id: u16) -> Option { + assert!((db_id as usize) < self.wakers.len()); + self.mem + .read_plain( + self.offset + .wrapping_add((db_id as u64) << DOORBELL_STRIDE_BITS), + ) + .inspect_err(|err| { + tracelimit::error_ratelimited!( + error = err as &dyn std::error::Error, + "failed to read doorbell memory" + ); + }) + .ok() + } + + fn has_event_idx(&self) -> bool { + self.event_idx_offset.is_some() + } + + fn write_event_idx(&self, db_id: u16, val: u32) { + assert!((db_id as usize) < self.wakers.len()); + if let Err(err) = self.mem.write_plain( + self.event_idx_offset + .unwrap() + .wrapping_add((db_id as u64) << DOORBELL_STRIDE_BITS), + &val, + ) { + tracelimit::error_ratelimited!( + error = &err as &dyn std::error::Error, + "failed to read event_idx memory" + ) } } + + fn read_event_idx(&self, db_id: u16) -> Option { + assert!((db_id as usize) < self.wakers.len()); + self.mem + .read_plain( + self.event_idx_offset? + .wrapping_add((db_id as u64) << DOORBELL_STRIDE_BITS), + ) + .inspect_err(|err| { + tracelimit::error_ratelimited!( + error = err as &dyn std::error::Error, + "failed to read doorbell memory" + ); + }) + .ok() + } } -#[derive(Copy, Clone, Default, Inspect, Debug)] -pub struct ShadowDoorbell { +#[derive(Inspect)] +#[inspect(extra = "Self::inspect_shadow")] +struct DoorbellState { + #[inspect(hex)] + current: u32, #[inspect(hex)] - pub shadow_db_gpa: u64, + event_idx: u32, + db_id: u16, + db_offset: u64, #[inspect(hex)] - pub event_idx_gpa: u64, + len: u32, + #[inspect(skip)] + doorbells: Arc>, + #[inspect(skip)] + registered_waker: Option, } -impl ShadowDoorbell { - // See NVMe Spec version 2.0a, Section 5.8 -- Doorbell Buffer Config Command for - // an explanation of this math. - pub fn new( - shadow_db_evt_idx_base: ShadowDoorbell, - qid: u16, - is_sq: bool, - doorbell_stride_bits: usize, - ) -> ShadowDoorbell { - let offset = match is_sq { - true => 0u64, - false => 1u64, - }; - let shadow_db_gpa = shadow_db_evt_idx_base.shadow_db_gpa - + (qid as u64 * 2 + offset) * (4 << (doorbell_stride_bits - 2)); - let event_idx_gpa = shadow_db_evt_idx_base.event_idx_gpa - + (qid as u64 * 2 + offset) * (4 << (doorbell_stride_bits - 2)); - ShadowDoorbell { - shadow_db_gpa, - event_idx_gpa, +impl DoorbellState { + fn inspect_shadow(&self, resp: &mut inspect::Response<'_>) { + resp.field_with("doorbell", || { + self.doorbells.read().read(self.db_id).map(inspect::AsHex) + }) + .field_with("shadow_event_idx", || { + self.doorbells + .read() + .read_event_idx(self.db_id) + .map(inspect::AsHex) + }); + } + + fn new(doorbells: Arc>, db_id: u16, len: u32) -> Self { + Self { + current: 0, + event_idx: 0, + len, + doorbells, + registered_waker: None, + db_id, + db_offset: (db_id as u64) << DOORBELL_STRIDE_BITS, + } + } + + fn probe_inner(&mut self, update_event_idx: bool) -> Option { + // Try to read forward. + let doorbell = self.doorbells.read(); + let val = doorbell.read(self.db_id)?; + if val != self.current { + return Some(val); + } + + if self.event_idx == val || !update_event_idx || !doorbell.has_event_idx() { + return None; + } + + // Update the event index so that the guest will write the real doorbell + // on the next update. + doorbell.write_event_idx(self.db_id, val); + self.event_idx = val; + + // Double check after a memory barrier. + std::sync::atomic::fence(Ordering::SeqCst); + let val = doorbell.read(self.db_id)?; + if val != self.current { Some(val) } else { None } + } + + fn probe(&mut self, update_event_idx: bool) -> Result { + // If shadow doorbells are in use, use that instead of what was written to the doorbell + // register, as it may be more current. + if let Some(val) = self.probe_inner(update_event_idx) { + if val >= self.len { + return Err(QueueError::InvalidDoorbell { val, len: self.len }); + } + self.current = val; + Ok(true) + } else { + Ok(false) + } + } + + fn poll(&mut self, cx: &mut Context<'_>) -> Poll> { + // Ensure we get woken up whenever the doorbell is written to. + if self + .registered_waker + .as_ref() + .is_none_or(|w| !cx.waker().will_wake(w)) + { + let _old_waker = + self.doorbells.write().wakers[self.db_id as usize].replace(cx.waker().clone()); + self.registered_waker = Some(cx.waker().clone()); + } + if !self.probe(true)? { + return Poll::Pending; } + Poll::Ready(Ok(())) } } #[derive(Inspect)] pub struct SubmissionQueue { - #[inspect(hex)] - cached_tail: u32, - tail: Arc, + tail: DoorbellState, + mem: GuestMemory, #[inspect(hex)] head: u32, #[inspect(hex)] gpa: u64, - #[inspect(hex)] - len: u32, - #[inspect(with = "Option::is_some")] - shadow_db_evt_idx: Option, - #[inspect(hex)] - evt_idx: u32, } #[derive(Debug, Error)] pub enum QueueError { - #[error("invalid doorbell tail {0:#x}")] - InvalidTail(u32), - #[error("invalid doorbell head {0:#x}")] - InvalidHead(u32), + #[error("invalid doorbell value {val:#x}, len {len:#x}")] + InvalidDoorbell { val: u32, len: u32 }, #[error("queue access error")] Memory(#[source] GuestMemoryError), } impl SubmissionQueue { pub fn new( - tail: Arc, + doorbells: Arc>, + db_id: u16, gpa: u64, len: u16, - shadow_db_evt_idx: Option, + mem: GuestMemory, ) -> Self { - tail.write(0); + doorbells.read().write(db_id, 0); Self { - cached_tail: 0, - tail, + tail: DoorbellState::new(doorbells, db_id, len.into()), head: 0, gpa, - len: len.into(), - shadow_db_evt_idx, - evt_idx: 0, + mem, } } /// This function returns a future for the next entry in the submission queue. It also /// has a side effect of updating the tail. - /// - /// Note that this function returns a future that must be cancellable, which means that the - /// parts after an await may never run. The tail update side effect is benign, so - /// that can happen before the await. - pub async fn next(&mut self, mem: &GuestMemory) -> Result { - // If shadow doorbells are in use, use that instead of what was written to the doorbell - // register, as it may be more current. - if let Some(shadow_db_evt_idx) = self.shadow_db_evt_idx { - let shadow_tail = mem - .read_plain(shadow_db_evt_idx.shadow_db_gpa) - .map_err(QueueError::Memory)?; - - // ILLEGAL_DOORBELL_VALUE is the initial state. The guest will overwrite - // it when it first uses the shadow. - if shadow_tail != ILLEGAL_DOORBELL_VALUE { - self.cached_tail = shadow_tail; - self.tail.write(self.cached_tail); - } - } - while self.cached_tail == self.head { - self.cached_tail = self.tail.wait_read(self.cached_tail).await; - } - if self.cached_tail >= self.len { - return Err(QueueError::InvalidTail(self.cached_tail)); + pub fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll> { + let tail = self.tail.current; + if tail == self.head { + ready!(self.tail.poll(cx))?; } - let command: spec::Command = mem - .read_plain(self.gpa.wrapping_add(self.head as u64 * 64)) + let command: spec::Command = self + .mem + .read_plain( + self.gpa + .wrapping_add(self.head as u64 * size_of::() as u64), + ) .map_err(QueueError::Memory)?; - self.head = advance(self.head, self.len); - Ok(command) + self.head = advance(self.head, self.tail.len); + Poll::Ready(Ok(command)) } pub fn sqhd(&self) -> u16 { self.head as u16 } - - /// This function lets the driver know what doorbell value we consumed, allowing - /// it to elide the next ring, maybe. - pub fn advance_evt_idx(&mut self, mem: &GuestMemory) -> Result<(), QueueError> { - self.evt_idx = advance(self.evt_idx, self.len); - if let Some(shadow_db_evt_idx) = self.shadow_db_evt_idx { - mem.write_plain(shadow_db_evt_idx.event_idx_gpa, &self.evt_idx) - .map_err(QueueError::Memory)?; - } - Ok(()) - } - - /// This function updates the shadow doorbell values of a queue that is - /// potentially already in use. - pub fn update_shadow_db(&mut self, mem: &GuestMemory, sdb: ShadowDoorbell) { - self.shadow_db_evt_idx = Some(sdb); - self.evt_idx = self.cached_tail; - // Write the illegal value out to the buffer, so that we can tell - // if Linux has ever written a valid value. - let _ = mem.write_plain(sdb.shadow_db_gpa, &ILLEGAL_DOORBELL_VALUE); - } } #[derive(Inspect)] pub struct CompletionQueue { #[inspect(hex)] tail: u32, - #[inspect(hex)] - cached_head: u32, - head: Arc, + head: DoorbellState, phase: bool, #[inspect(hex)] gpa: u64, - #[inspect(hex)] - len: u32, #[inspect(with = "Option::is_some")] interrupt: Option, - shadow_db_evt_idx: Option, + mem: GuestMemory, } impl CompletionQueue { pub fn new( - head: Arc, + doorbells: Arc>, + db_id: u16, + mem: GuestMemory, interrupt: Option, gpa: u64, len: u16, - shadow_db_evt_idx: Option, ) -> Self { - head.write(0); + doorbells.read().write(db_id, 0); Self { tail: 0, - cached_head: 0, - head, + head: DoorbellState::new(doorbells, db_id, len.into()), phase: true, gpa, - len: len.into(), interrupt, - shadow_db_evt_idx, + mem, } } /// Wait for free completions. - pub async fn wait_ready(&mut self, mem: &GuestMemory) -> Result<(), QueueError> { - let next_tail = advance(self.tail, self.len); - // If shadow doorbells are in use, use that instead of what was written to the doorbell - // register, as it may be more current. - if let Some(shadow_db_evt_idx) = self.shadow_db_evt_idx { - let shadow_head = mem - .read_plain(shadow_db_evt_idx.shadow_db_gpa) - .map_err(QueueError::Memory)?; - - // ILLEGAL_DOORBELL_VALUE is the initial state. The guest will overwrite - // it when it first uses the shadow. - if shadow_head != ILLEGAL_DOORBELL_VALUE { - self.cached_head = shadow_head; - self.head.write(self.cached_head); - } - } - while self.cached_head == next_tail { - self.cached_head = self.head.wait_read(self.cached_head).await; + pub fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + let next_tail = advance(self.tail, self.head.len); + if self.head.current == next_tail { + ready!(self.head.poll(cx))?; } - if self.cached_head >= self.len { - return Err(QueueError::InvalidHead(self.cached_head)); - } - Ok(()) + Poll::Ready(Ok(())) } - pub fn write( - &mut self, - mem: &GuestMemory, - mut data: spec::Completion, - ) -> Result { - if self.cached_head == advance(self.tail, self.len) { + pub fn write(&mut self, mut data: spec::Completion) -> Result { + let next = advance(self.tail, self.head.len); + // Check the doorbell register instead of requiring the caller to + // go around the slow path and call `poll_ready`. + if self.head.current == next && !self.head.probe(false)? { return Ok(false); } data.status.set_phase(self.phase); @@ -273,52 +349,27 @@ impl CompletionQueue { // This is necessary to ensure the guest can observe the full completion // once it observes the phase bit change (which is in the high part). let [low, high]: [u64; 2] = zerocopy::transmute!(data); - let gpa = self.gpa.wrapping_add(self.tail as u64 * 16); - mem.write_plain(gpa, &low).map_err(QueueError::Memory)?; + let gpa = self + .gpa + .wrapping_add(self.tail as u64 * size_of::() as u64); + self.mem + .write_plain(gpa, &low) + .map_err(QueueError::Memory)?; std::sync::atomic::fence(Ordering::Release); - mem.write_plain(gpa + 8, &high) + self.mem + .write_plain(gpa + 8, &high) .map_err(QueueError::Memory)?; std::sync::atomic::fence(Ordering::Release); if let Some(interrupt) = &self.interrupt { interrupt.deliver(); } - self.tail = advance(self.tail, self.len); + self.tail = next; if self.tail == 0 { self.phase = !self.phase; } Ok(true) } - - /// This method updates the EVT_IDX field to match the shadow doorbell - /// value, thus signalling to the guest driver that the next completion - /// removed should involve a doorbell ring. In this emulator, such - /// a thing (the ring) is only necessary when the number of un-spoken-for - /// completion queue entries is getting small. (Completion queue entries - /// are spoken for when a command is removed from the SQ). - pub fn catch_up_evt_idx( - &mut self, - force: bool, - io_outstanding: u32, - mem: &GuestMemory, - ) -> Result<(), QueueError> { - if let Some(shadow_db_evt_idx) = self.shadow_db_evt_idx { - if force | (io_outstanding >= self.len - 3) { - mem.write_plain(shadow_db_evt_idx.event_idx_gpa, &self.cached_head) - .map_err(QueueError::Memory)?; - } - } - Ok(()) - } - - /// This function updates the shadow doorbell values of a queue that is - /// potentially already in use. - pub fn update_shadow_db(&mut self, mem: &GuestMemory, sdb: ShadowDoorbell) { - self.shadow_db_evt_idx = Some(sdb); - // Write the illegal value out to the buffer, so that we can tell - // if Linux has ever written a valid value. - let _ = mem.write_plain(sdb.shadow_db_gpa, &ILLEGAL_DOORBELL_VALUE); - } } fn advance(n: u32, l: u32) -> u32 { diff --git a/vm/devices/storage/nvme_test/src/resolver.rs b/vm/devices/storage/nvme_test/src/resolver.rs index 50c518d879..27cfc352df 100644 --- a/vm/devices/storage/nvme_test/src/resolver.rs +++ b/vm/devices/storage/nvme_test/src/resolver.rs @@ -63,6 +63,7 @@ impl AsyncResolveResource msix_count: resource.msix_count, max_io_queues: resource.max_io_queues, subsystem_id: resource.subsystem_id, + flr_support: resource.flr_support, }, resource.fault_config, ); diff --git a/vm/devices/storage/nvme_test/src/tests.rs b/vm/devices/storage/nvme_test/src/tests.rs index f0a40aba7e..eec09d4183 100644 --- a/vm/devices/storage/nvme_test/src/tests.rs +++ b/vm/devices/storage/nvme_test/src/tests.rs @@ -2,5 +2,6 @@ // Licensed under the MIT License. mod controller_tests; +mod flr_tests; mod shadow_doorbell_tests; mod test_helpers; diff --git a/vm/devices/storage/nvme_test/src/tests/controller_tests.rs b/vm/devices/storage/nvme_test/src/tests/controller_tests.rs index e62cd06b4b..a1973c6c7f 100644 --- a/vm/devices/storage/nvme_test/src/tests/controller_tests.rs +++ b/vm/devices/storage/nvme_test/src/tests/controller_tests.rs @@ -9,6 +9,7 @@ use crate::PAGE_SIZE64; use crate::command_match::CommandMatchBuilder; use crate::prp::PrpRange; use crate::spec; +use crate::tests::test_helpers::find_pci_capability; use crate::tests::test_helpers::read_completion_from_queue; use crate::tests::test_helpers::test_memory; use crate::tests::test_helpers::write_command_to_queue; @@ -50,6 +51,7 @@ fn instantiate_controller( msix_count: 64, max_io_queues: 64, subsystem_id: Guid::new_random(), + flr_support: false, // TODO: Add tests with flr support. }, fault_configuration, ); @@ -124,38 +126,23 @@ pub async fn instantiate_and_build_admin_queue( nvmec.pci_cfg_write(0x20, BAR0_LEN as u32).unwrap(); // Find the MSI-X cap struct. - let mut cfg_dword = 0; - nvmec.pci_cfg_read(0x34, &mut cfg_dword).unwrap(); - cfg_dword &= 0xff; - loop { - // Read a cap struct header and pull out the fields. - let mut cap_header = 0; - nvmec - .pci_cfg_read(cfg_dword as u16, &mut cap_header) - .unwrap(); - if cap_header & 0xff == 0x11 { - // Read the table BIR and offset. - let mut table_loc = 0; - nvmec - .pci_cfg_read(cfg_dword as u16 + 4, &mut table_loc) - .unwrap(); - // Code in other places assumes that the MSI-X table is at the beginning - // of BAR 4. If this becomes a fluid concept, capture the values - // here and use them, rather than just asserting on them. - assert_eq!(table_loc & 0x7, 4); - assert_eq!(table_loc >> 3, 0); - - // Found MSI-X, enable it. - nvmec.pci_cfg_write(cfg_dword as u16, 0x80000000).unwrap(); - break; - } - // Isolate the ptr to the next cap struct. - cfg_dword = (cap_header >> 8) & 0xff; - if cfg_dword == 0 { - // Hit the end. - panic!(); - } - } + let cfg_dword = + find_pci_capability(&mut nvmec, 0x11).expect("MSI-X capability should be present"); + + // Read the table BIR and offset. + let mut table_loc = 0; + nvmec + .pci_cfg_read(cfg_dword as u16 + 4, &mut table_loc) + .unwrap(); + + // Code in other places assumes that the MSI-X table is at the beginning + // of BAR 4. If this becomes a fluid concept, capture the values + // here and use them, rather than just asserting on them. + assert_eq!(table_loc & 0x7, 4); + assert_eq!(table_loc >> 3, 0); + + // Found MSI-X, enable it. + nvmec.pci_cfg_write(cfg_dword as u16, 0x80000000).unwrap(); // Turn on MMIO access by writing to the Command register in config space. Enable // MMIO and DMA. diff --git a/vm/devices/storage/nvme_test/src/tests/flr_tests.rs b/vm/devices/storage/nvme_test/src/tests/flr_tests.rs new file mode 100644 index 0000000000..42fb703f58 --- /dev/null +++ b/vm/devices/storage/nvme_test/src/tests/flr_tests.rs @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Tests for Function Level Reset (FLR) functionality. + +use std::time::Duration; + +use super::test_helpers::TestNvmeMmioRegistration; +use crate::NvmeFaultController; +use crate::NvmeFaultControllerCaps; +use crate::tests::test_helpers::find_pci_capability; +use chipset_device::pci::PciConfigSpace; +use guestmem::GuestMemory; +use guid::Guid; +use mesh::CellUpdater; +use nvme_resources::fault::AdminQueueFaultConfig; +use nvme_resources::fault::FaultConfiguration; +use nvme_resources::fault::PciFaultConfig; +use pal_async::DefaultDriver; +use pal_async::async_test; +use pal_async::timer::PolledTimer; +use pci_core::capabilities::pci_express::PCI_EXPRESS_DEVICE_CAPS_FLR_BIT_MASK; +use pci_core::msi::MsiInterruptSet; +use pci_core::spec::caps::CapabilityId; +use pci_core::spec::caps::pci_express::PciExpressCapabilityHeader; +use vmcore::vm_task::SingleDriverBackend; +use vmcore::vm_task::VmTaskDriverSource; +use zerocopy::IntoBytes; + +fn instantiate_controller_with_flr( + driver: DefaultDriver, + gm: &GuestMemory, + flr_support: bool, +) -> NvmeFaultController { + let vm_task_driver = VmTaskDriverSource::new(SingleDriverBackend::new(driver)); + let mut msi_interrupt_set = MsiInterruptSet::new(); + let mut mmio_reg = TestNvmeMmioRegistration {}; + + NvmeFaultController::new( + &vm_task_driver, + gm.clone(), + &mut msi_interrupt_set, + &mut mmio_reg, + NvmeFaultControllerCaps { + msix_count: 64, + max_io_queues: 64, + subsystem_id: Guid::new_random(), + flr_support, + }, + FaultConfiguration { + fault_active: CellUpdater::new(false).cell(), + admin_fault: AdminQueueFaultConfig::new(), + pci_fault: PciFaultConfig::new(), + }, + ) +} + +#[async_test] +async fn test_flr_capability_advertised(driver: DefaultDriver) { + let gm = test_memory(); + let mut controller = instantiate_controller_with_flr(driver, &gm, true); + + // Find the PCI Express capability + let cap_ptr = find_pci_capability(&mut controller, CapabilityId::PCI_EXPRESS.0) + .expect("PCI Express capability should be present when FLR is enabled"); + + // Read Device Capabilities register to check FLR support + let mut device_caps = 0u32; + controller + .pci_cfg_read( + cap_ptr + PciExpressCapabilityHeader::DEVICE_CAPS.0, + &mut device_caps, + ) + .unwrap(); + + // Check Function Level Reset bit (bit 28 in Device Capabilities) + let flr_supported = (device_caps & PCI_EXPRESS_DEVICE_CAPS_FLR_BIT_MASK) != 0; + assert!( + flr_supported, + "FLR should be advertised in Device Capabilities" + ); +} + +#[async_test] +async fn test_no_flr_capability_when_disabled(driver: DefaultDriver) { + let gm = test_memory(); + let mut controller = instantiate_controller_with_flr(driver, &gm, false); + + // Find the PCI Express capability - it should not be present + let pcie_cap_offset = find_pci_capability(&mut controller, CapabilityId::PCI_EXPRESS.0); + + assert!( + pcie_cap_offset.is_none(), + "PCI Express capability should not be present when FLR is disabled" + ); +} + +#[async_test] +async fn test_flr_trigger(driver: DefaultDriver) { + let gm = test_memory(); + let mut controller = instantiate_controller_with_flr(driver.clone(), &gm, true); + + // Set the ACQ base to 0x1000 and the ASQ base to 0x2000. + let mut qword = 0x1000; + controller.write_bar0(0x30, qword.as_bytes()).unwrap(); + qword = 0x2000; + controller.write_bar0(0x28, qword.as_bytes()).unwrap(); + + // Set the queues so that they have four entries apiece. + let mut dword = 0x30003; + controller.write_bar0(0x24, dword.as_bytes()).unwrap(); + + // Enable the controller. + controller.read_bar0(0x14, dword.as_mut_bytes()).unwrap(); + dword |= 1; + controller.write_bar0(0x14, dword.as_bytes()).unwrap(); + controller.read_bar0(0x14, dword.as_mut_bytes()).unwrap(); + assert!(dword & 1 != 0); + + // Read CSTS + controller.read_bar0(0x1c, dword.as_mut_bytes()).unwrap(); + assert!(dword & 2 == 0); + + // Find the PCI Express capability + let pcie_cap_offset = find_pci_capability(&mut controller, CapabilityId::PCI_EXPRESS.0); + + let pcie_cap_offset = pcie_cap_offset.expect("PCI Express capability should be present"); + + // Read Device Control/Status register to get initial state + let device_ctl_sts_offset = pcie_cap_offset + PciExpressCapabilityHeader::DEVICE_CTL_STS.0; + let mut initial_ctl_sts = 0u32; + controller + .pci_cfg_read(device_ctl_sts_offset, &mut initial_ctl_sts) + .unwrap(); + + // Trigger FLR by setting the Initiate Function Level Reset bit (bit 15 in Device Control) + let flr_bit = 1u32 << 15; + let new_ctl_sts = initial_ctl_sts | flr_bit; + controller + .pci_cfg_write(device_ctl_sts_offset, new_ctl_sts) + .unwrap(); + + // According to the spec, we must wait at least 100ms after issuing an FLR before accessing the device again. + PolledTimer::new(&driver) + .sleep(Duration::from_millis(100)) + .await; + + // The FLR bit should always read 0, even after the reset. + let mut post_flr_ctl_sts = 0u32; + controller + .pci_cfg_read(device_ctl_sts_offset, &mut post_flr_ctl_sts) + .unwrap(); + assert_eq!( + post_flr_ctl_sts & flr_bit, + 0, + "FLR bit should always read 0, even after the reset." + ); + + // Check that the controller is disabled after FLR + controller.read_bar0(0x14, dword.as_mut_bytes()).unwrap(); + assert!(dword == 0); +} + +fn test_memory() -> GuestMemory { + GuestMemory::allocate(0x10000) +} diff --git a/vm/devices/storage/nvme_test/src/tests/shadow_doorbell_tests.rs b/vm/devices/storage/nvme_test/src/tests/shadow_doorbell_tests.rs index 7bd6e478be..a4bf449623 100644 --- a/vm/devices/storage/nvme_test/src/tests/shadow_doorbell_tests.rs +++ b/vm/devices/storage/nvme_test/src/tests/shadow_doorbell_tests.rs @@ -1,10 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::DOORBELL_STRIDE_BITS; use crate::PAGE_SIZE64; use crate::prp::PrpRange; -use crate::queue::ShadowDoorbell; use crate::spec; use crate::tests::controller_tests::instantiate_and_build_admin_queue; use crate::tests::controller_tests::wait_for_msi; @@ -170,22 +168,19 @@ async fn test_setup_sq_ring_with_shadow(driver: DefaultDriver) { let sq_buf = PrpRange::new(vec![SQ_BASE], 0, PAGE_SIZE64).unwrap(); let gm = test_memory(); let int_controller = TestPciInterruptController::new(); - let sdb_base = ShadowDoorbell { - shadow_db_gpa: DOORBELL_BUFFER_BASE, - event_idx_gpa: EVT_IDX_BUFFER_BASE, - }; - let sq_sdb = ShadowDoorbell::new(sdb_base, 0, true, DOORBELL_STRIDE_BITS.into()); let mut backoff = Backoff::new(&driver); // Check that the old value was 0, just to be sure. - let sdb = gm.read_plain::(sq_sdb.shadow_db_gpa).unwrap(); + let sdb = gm.read_plain::(DOORBELL_BUFFER_BASE).unwrap(); assert_eq!(sdb, 0); let mut nvmec = setup_shadow_doorbells(driver.clone(), &cq_buf, &sq_buf, &gm, &int_controller, None).await; - let sdb = gm.read_plain::(sq_sdb.shadow_db_gpa).unwrap(); - assert_eq!(sdb, crate::queue::ILLEGAL_DOORBELL_VALUE); + let sdb = gm.read_plain::(DOORBELL_BUFFER_BASE).unwrap(); + // The doorbell value should be current (one admin queue command has been + // issued). + assert_eq!(sdb, 1); /* From the NVMe Spec (ver. 2.0a): B.5 Updating Controller Doorbell Properties using a Shadow Doorbell Buffer @@ -231,15 +226,16 @@ async fn test_setup_sq_ring_with_shadow(driver: DefaultDriver) { let new_sq_db = 2u32; // Update the shadow. - gm.write_plain::(sq_sdb.shadow_db_gpa, &new_sq_db) + gm.write_plain::(DOORBELL_BUFFER_BASE, &new_sq_db) .unwrap(); // Ring the admin queue doorbell. nvmec.write_bar0(0x1000, new_sq_db.as_bytes()).unwrap(); backoff.back_off().await; - let sq_evt_idx = gm.read_plain::(sq_sdb.event_idx_gpa).unwrap(); - assert_eq!(sq_evt_idx, 2); + // The current implementation will advance the event index immediately. + let sq_evt_idx = gm.read_plain::(EVT_IDX_BUFFER_BASE).unwrap(); + assert_eq!(sq_evt_idx, new_sq_db); } #[async_test] diff --git a/vm/devices/storage/nvme_test/src/tests/test_helpers.rs b/vm/devices/storage/nvme_test/src/tests/test_helpers.rs index 60a22f7eb6..e123a50d44 100644 --- a/vm/devices/storage/nvme_test/src/tests/test_helpers.rs +++ b/vm/devices/storage/nvme_test/src/tests/test_helpers.rs @@ -3,12 +3,14 @@ //! Mock types for unit-testing various NVMe behaviors. +use crate::NvmeFaultController; use crate::PAGE_SIZE; use crate::PAGE_SIZE64; use crate::prp::PrpRange; use crate::spec; use chipset_device::mmio::ControlMmioIntercept; use chipset_device::mmio::RegisterMmioIntercept; +use chipset_device::pci::PciConfigSpace; use guestmem::GuestMemory; use parking_lot::Mutex; use pci_core::msi::MsiControl; @@ -148,3 +150,32 @@ pub fn read_completion_from_queue( gm.read_plain::(gpa).unwrap() } + +// Returns the offset for the PCI capability or None if not found. Caps the max length of the list to 100 to avoid infinite loops. +pub fn find_pci_capability(controller: &mut NvmeFaultController, cap_id: u8) -> Option { + let mut cfg_dword = 0; + controller.pci_cfg_read(0x34, &mut cfg_dword).unwrap(); // Cap_ptr is always at 0x34 + cfg_dword &= 0xff; + let mut max_caps = 100; // Limit to avoid infinite loop + loop { + if max_caps == 0 { + return None; + } + // Read a cap struct header and pull out the fields. + let mut cap_header = 0; + controller + .pci_cfg_read(cfg_dword as u16, &mut cap_header) + .unwrap(); + if cap_header & 0xff == cap_id as u32 { + break; + } + // Isolate the ptr to the next cap struct. + cfg_dword = (cap_header >> 8) & 0xff; + if cfg_dword == 0 { + return None; + } + max_caps -= 1; + } + + Some(cfg_dword as u16) +} diff --git a/vm/devices/storage/nvme_test/src/workers.rs b/vm/devices/storage/nvme_test/src/workers.rs index cfb0bd17e6..a4a1f68ee9 100644 --- a/vm/devices/storage/nvme_test/src/workers.rs +++ b/vm/devices/storage/nvme_test/src/workers.rs @@ -25,6 +25,6 @@ const MAX_DATA_TRANSFER_SIZE: usize = 256 * 1024; const _: () = assert!( MAX_DATA_TRANSFER_SIZE.is_power_of_two() - && MAX_DATA_TRANSFER_SIZE % PAGE_SIZE == 0 + && MAX_DATA_TRANSFER_SIZE.is_multiple_of(PAGE_SIZE) && MAX_DATA_TRANSFER_SIZE / PAGE_SIZE > 1 ); diff --git a/vm/devices/storage/nvme_test/src/workers/admin.rs b/vm/devices/storage/nvme_test/src/workers/admin.rs index ed95821b22..1ba6959f35 100644 --- a/vm/devices/storage/nvme_test/src/workers/admin.rs +++ b/vm/devices/storage/nvme_test/src/workers/admin.rs @@ -19,9 +19,8 @@ use crate::error::NvmeError; use crate::namespace::Namespace; use crate::prp::PrpRange; use crate::queue::CompletionQueue; -use crate::queue::DoorbellRegister; +use crate::queue::DoorbellMemory; use crate::queue::QueueError; -use crate::queue::ShadowDoorbell; use crate::queue::SubmissionQueue; use crate::spec; use disk_backend::Disk; @@ -39,9 +38,11 @@ use pal_async::task::Spawn; use pal_async::task::Task; use pal_async::timer::PolledTimer; use parking_lot::Mutex; +use parking_lot::RwLock; use std::collections::BTreeMap; use std::collections::btree_map; use std::future::pending; +use std::future::poll_fn; use std::io::Cursor; use std::io::Write; use std::sync::Arc; @@ -72,7 +73,7 @@ pub struct AdminConfig { #[inspect(skip)] pub interrupts: Vec, #[inspect(skip)] - pub doorbells: Vec>, + pub doorbells: Arc>, #[inspect(display)] pub subsystem_id: Guid, pub max_sqs: u16, @@ -102,8 +103,6 @@ pub struct AdminState { io_cqs: Vec>, #[inspect(skip)] sq_delete_response: mesh::Receiver, - #[inspect(with = "Option::is_some")] - shadow_db_evt_gpa_base: Option, #[inspect(iter_by_index)] asynchronous_event_requests: Vec, #[inspect( @@ -132,7 +131,6 @@ struct IoSq { driver: VmTaskDriver, pending_delete_cid: Option, cqid: Option, - shadow_db_evt_idx: Option, } #[derive(Inspect)] @@ -143,7 +141,6 @@ struct IoCq { len: u16, interrupt: Option, sqid: Option, - shadow_db_evt_idx: Option, } impl AdminState { @@ -169,18 +166,24 @@ impl AdminState { .collect(); let mut state = Self { - admin_sq: SubmissionQueue::new(handler.config.doorbells[0].clone(), asq, asqs, None), + admin_sq: SubmissionQueue::new( + handler.config.doorbells.clone(), + 0, + asq, + asqs, + handler.config.mem.clone(), + ), admin_cq: CompletionQueue::new( - handler.config.doorbells[1].clone(), + handler.config.doorbells.clone(), + 1, + handler.config.mem.clone(), Some(handler.config.interrupts[0].clone()), acq, acqs, - None, ), io_sqs: Vec::new(), io_cqs: Vec::new(), sq_delete_response: Default::default(), - shadow_db_evt_gpa_base: None, asynchronous_event_requests: Vec::new(), changed_namespaces: Vec::new(), notified_changed_namespaces: false, @@ -207,9 +210,6 @@ impl AdminState { /// Caller must ensure that no queues are active. fn set_max_queues(&mut self, handler: &AdminHandler, num_sqs: u16, num_cqs: u16) { - let num_qids = 2 + num_sqs.max(num_cqs) * 2; - assert!(handler.config.doorbells.len() >= num_qids as usize); - self.io_sqs.truncate(num_sqs.into()); self.io_sqs .extend((self.io_sqs.len()..num_sqs.into()).map(|i| { @@ -233,7 +233,6 @@ impl AdminState { )), pending_delete_cid: None, cqid: None, - shadow_db_evt_idx: None, driver, } })); @@ -410,12 +409,11 @@ impl AdminHandler { let event = loop { // Wait for there to be room for a completion for the next // command or the completed sq deletion. - state.admin_cq.wait_ready(&self.config.mem).await?; + poll_fn(|cx| state.admin_cq.poll_ready(cx)).await?; if !state.changed_namespaces.is_empty() && !state.notified_changed_namespaces { if let Some(cid) = state.asynchronous_event_requests.pop() { state.admin_cq.write( - &self.config.mem, spec::Completion { dw0: spec::AsynchronousEventRequestDw0::new() .with_event_type(spec::AsynchronousEventType::NOTICE.0) @@ -435,7 +433,7 @@ impl AdminHandler { } } - let next_command = state.admin_sq.next(&self.config.mem).map(Event::Command); + let next_command = poll_fn(|cx| state.admin_sq.poll_next(cx)).map(Event::Command); let sq_delete_complete = async { let Some(sqid) = state.sq_delete_response.next().await else { pending().await @@ -461,10 +459,6 @@ impl AdminHandler { state: &mut AdminState, event: Result, ) -> Result<(), QueueError> { - // For the admin queue, update Evt_IDX at the beginning of command - // processing, just to keep it simple. - state.admin_sq.advance_evt_idx(&self.config.mem)?; - let (command_processed, cid, result) = match event? { Event::Command(command) => { let mut command = command?; @@ -516,7 +510,7 @@ impl AdminHandler { let result = match opcode { spec::AdminOpcode::IDENTIFY => self - .handle_identify(&command) + .handle_identify(state, &command) .map(|()| Some(Default::default())), spec::AdminOpcode::GET_FEATURES => { self.handle_get_features(state, &command).await.map(Some) @@ -543,9 +537,13 @@ impl AdminHandler { spec::AdminOpcode::GET_LOG_PAGE => self .handle_get_log_page(state, &command) .map(|()| Some(Default::default())), - spec::AdminOpcode::DOORBELL_BUFFER_CONFIG => self - .handle_doorbell_buffer_config(state, &command) - .map(|()| Some(Default::default())), + spec::AdminOpcode::DOORBELL_BUFFER_CONFIG + if self.supports_shadow_doorbells(state) => + { + self.handle_doorbell_buffer_config(state, &command) + .await + .map(|()| Some(Default::default())) + } opcode => { tracelimit::warn_ratelimited!(?opcode, "unsupported opcode"); Err(spec::Status::INVALID_COMMAND_OPCODE.into()) @@ -657,13 +655,15 @@ impl AdminHandler { } } - state.admin_cq.write(&self.config.mem, completion)?; - // Again, for simplicity, update EVT_IDX here. - state.admin_cq.catch_up_evt_idx(true, 0, &self.config.mem)?; + state.admin_cq.write(completion)?; Ok(()) } - fn handle_identify(&mut self, command: &spec::Command) -> Result<(), NvmeError> { + fn handle_identify( + &mut self, + state: &AdminState, + command: &spec::Command, + ) -> Result<(), NvmeError> { let cdw10: spec::Cdw10Identify = command.cdw10.into(); // All identify results are 4096 bytes. let mut buf = [0u64; 512]; @@ -671,7 +671,7 @@ impl AdminHandler { match spec::Cns(cdw10.cns()) { spec::Cns::CONTROLLER => { let id = spec::IdentifyController::mut_from_prefix(buf).unwrap().0; // TODO: zerocopy: from-prefix (mut_from_prefix): use-rest-of-range (https://github.com/microsoft/openvmm/issues/759) - *id = self.identify_controller(); + *id = self.identify_controller(state); write!( Cursor::new(&mut id.subnqn[..]), @@ -717,8 +717,7 @@ impl AdminHandler { Ok(()) } - fn identify_controller(&self) -> spec::IdentifyController { - let oacs = spec::OptionalAdminCommandSupport::from(0).with_doorbell_buffer_config(true); + fn identify_controller(&self, state: &AdminState) -> spec::IdentifyController { spec::IdentifyController { vid: VENDOR_ID, ssvid: VENDOR_ID, @@ -749,7 +748,8 @@ impl AdminHandler { .with_present(true) .with_broadcast_flush_behavior(spec::BroadcastFlushBehavior::NOT_SUPPORTED.0), cntrltype: spec::ControllerType::IO_CONTROLLER, - oacs, + oacs: spec::OptionalAdminCommandSupport::new() + .with_doorbell_buffer_config(self.supports_shadow_doorbells(state)), ..FromZeros::new_zeroed() } } @@ -880,22 +880,11 @@ impl AdminHandler { return Err(spec::Status::INVALID_QUEUE_SIZE.into()); } - let mut shadow_db_evt_idx: Option = None; - if let Some(shadow_db_evt_gpa_base) = state.shadow_db_evt_gpa_base { - shadow_db_evt_idx = Some(ShadowDoorbell::new( - shadow_db_evt_gpa_base, - cqid, - false, - DOORBELL_STRIDE_BITS.into(), - )); - } - *io_queue = Some(IoCq { gpa, len: len0 + 1, interrupt, sqid: None, - shadow_db_evt_idx, }); Ok(()) } @@ -948,19 +937,8 @@ impl AdminHandler { return Err(spec::Status::INVALID_QUEUE_SIZE.into()); } - if let Some(shadow_db_evt_gpa_base) = state.shadow_db_evt_gpa_base { - sq.shadow_db_evt_idx = Some(ShadowDoorbell::new( - shadow_db_evt_gpa_base, - sqid, - true, - DOORBELL_STRIDE_BITS.into(), - )); - } - cq.sqid = Some(sqid); sq.cqid = Some(cqid); - let sq_tail = self.config.doorbells[sqid as usize * 2].clone(); - let cq_head = self.config.doorbells[cqid as usize * 2 + 1].clone(); let interrupt = cq .interrupt .map(|iv| self.config.interrupts[iv as usize].clone()); @@ -969,14 +947,14 @@ impl AdminHandler { let cq_gpa = cq.gpa; let cq_len = cq.len; let state = IoState::new( + &self.config.mem, + self.config.doorbells.clone(), sq_gpa, sq_len, - sq_tail, - sq.shadow_db_evt_idx, + sqid, cq_gpa, cq_len, - cq_head, - cq.shadow_db_evt_idx, + cqid, interrupt, namespaces, ); @@ -1122,57 +1100,33 @@ impl AdminHandler { Ok(()) } - fn handle_doorbell_buffer_config( + fn supports_shadow_doorbells(&self, state: &AdminState) -> bool { + let num_queues = state.io_sqs.len().max(state.io_cqs.len()) + 1; + let len = num_queues * (2 << DOORBELL_STRIDE_BITS); + // The spec only allows a single shadow doorbell page. + len <= PAGE_SIZE + } + + async fn handle_doorbell_buffer_config( &self, state: &mut AdminState, command: &spec::Command, ) -> Result<(), NvmeError> { + // Validated by caller. + assert!(self.supports_shadow_doorbells(state)); + let shadow_db_gpa = command.dptr[0]; let event_idx_gpa = command.dptr[1]; - - if (shadow_db_gpa == 0) - || (shadow_db_gpa & 0xfff != 0) - || (event_idx_gpa == 0) - || (event_idx_gpa & 0xfff != 0) - || (shadow_db_gpa == event_idx_gpa) - { - return Err(spec::Status::INVALID_FIELD_IN_COMMAND.into()); + if (shadow_db_gpa | event_idx_gpa) & !PAGE_MASK != 0 { + return Err(NvmeError::from(spec::Status::INVALID_FIELD_IN_COMMAND)); } - // Stash the base values for use in data queue creation. - let sdb_base = ShadowDoorbell { - shadow_db_gpa, - event_idx_gpa, - }; - state.shadow_db_evt_gpa_base = Some(sdb_base); + self.config + .doorbells + .write() + .replace_mem(self.config.mem.clone(), shadow_db_gpa, Some(event_idx_gpa)) + .map_err(|err| NvmeError::new(spec::Status::DATA_TRANSFER_ERROR, err))?; - // Update the admin queue to use shadow doorbells. - state.admin_sq.update_shadow_db( - &self.config.mem, - ShadowDoorbell::new(sdb_base, 0, true, DOORBELL_STRIDE_BITS.into()), - ); - state.admin_cq.update_shadow_db( - &self.config.mem, - ShadowDoorbell::new(sdb_base, 0, false, DOORBELL_STRIDE_BITS.into()), - ); - - // Update any data queues with the new shadow doorbell base. - for (qid, sq) in state.io_sqs.iter_mut().enumerate() { - if !sq.task.has_state() { - continue; - } - let gm = self.config.mem.clone(); - - // Data queue pairs are qid + 1, because the admin queue isn't in this vector. - let sq_sdb = - ShadowDoorbell::new(sdb_base, qid as u16 + 1, true, DOORBELL_STRIDE_BITS.into()); - let cq_sdb = - ShadowDoorbell::new(sdb_base, qid as u16 + 1, false, DOORBELL_STRIDE_BITS.into()); - - sq.task.update_with(move |sq, sq_state| { - sq.update_shadow_db(&gm, sq_state.unwrap(), sq_sdb, cq_sdb); - }); - } Ok(()) } diff --git a/vm/devices/storage/nvme_test/src/workers/coordinator.rs b/vm/devices/storage/nvme_test/src/workers/coordinator.rs index 7016857eab..37c6531fe0 100644 --- a/vm/devices/storage/nvme_test/src/workers/coordinator.rs +++ b/vm/devices/storage/nvme_test/src/workers/coordinator.rs @@ -8,7 +8,8 @@ use super::admin::AdminConfig; use super::admin::AdminHandler; use super::admin::AdminState; use super::admin::NsidConflict; -use crate::queue::DoorbellRegister; +use crate::queue::DoorbellMemory; +use crate::queue::InvalidDoorbell; use disk_backend::Disk; use futures::FutureExt; use futures::StreamExt; @@ -23,7 +24,7 @@ use mesh::rpc::RpcSend; use nvme_resources::fault::FaultConfiguration; use pal_async::task::Spawn; use pal_async::task::Task; -use parking_lot::Mutex; +use parking_lot::RwLock; use std::future::pending; use std::sync::Arc; use task_control::TaskControl; @@ -31,14 +32,15 @@ use vmcore::interrupt::Interrupt; use vmcore::vm_task::VmTaskDriver; use vmcore::vm_task::VmTaskDriverSource; +#[derive(Clone)] /// An input context for the NvmeWorkers -pub struct NvmeWorkersContext<'a> { - pub driver_source: &'a VmTaskDriverSource, +pub struct NvmeWorkersContext { + pub driver_source: VmTaskDriverSource, pub mem: GuestMemory, pub interrupts: Vec, pub max_sqs: u16, pub max_cqs: u16, - pub qe_sizes: Arc>, + pub qe_sizes: Arc>, pub subsystem_id: Guid, pub fault_configuration: FaultConfiguration, } @@ -46,7 +48,7 @@ pub struct NvmeWorkersContext<'a> { pub struct NvmeWorkers { _task: Task<()>, send: mesh::Sender, - doorbells: Vec>, + doorbells: Arc>, state: EnableState, } @@ -65,7 +67,7 @@ impl InspectMut for NvmeWorkers { } impl NvmeWorkers { - pub fn new(context: NvmeWorkersContext<'_>) -> Self { + pub fn new(context: NvmeWorkersContext) -> Self { let NvmeWorkersContext { driver_source, mem, @@ -78,15 +80,13 @@ impl NvmeWorkers { } = context; let num_qids = 2 + max_sqs.max(max_cqs) * 2; - let doorbells: Vec<_> = (0..num_qids) - .map(|_| Arc::new(DoorbellRegister::new())) - .collect(); + let doorbells = Arc::new(RwLock::new(DoorbellMemory::new(num_qids))); let driver = driver_source.simple(); let handler: AdminHandler = AdminHandler::new( driver.clone(), AdminConfig { - driver_source: driver_source.clone(), + driver_source, mem, interrupts, doorbells: doorbells.clone(), @@ -118,11 +118,9 @@ impl NvmeWorkers { } } - pub fn doorbell(&self, index: u16, value: u32) { - if let Some(doorbell) = self.doorbells.get(index as usize) { - doorbell.write(value); - } else { - tracelimit::warn_ratelimited!(index, value, "unknown doorbell"); + pub fn doorbell(&self, db_id: u16, value: u32) { + if let Err(InvalidDoorbell) = self.doorbells.read().try_write(db_id, value) { + tracelimit::error_ratelimited!(db_id, "write to invalid doorbell index"); } } diff --git a/vm/devices/storage/nvme_test/src/workers/io.rs b/vm/devices/storage/nvme_test/src/workers/io.rs index 3247889fe2..03a3f8c944 100644 --- a/vm/devices/storage/nvme_test/src/workers/io.rs +++ b/vm/devices/storage/nvme_test/src/workers/io.rs @@ -7,9 +7,8 @@ use crate::error::CommandResult; use crate::error::NvmeError; use crate::namespace::Namespace; use crate::queue::CompletionQueue; -use crate::queue::DoorbellRegister; +use crate::queue::DoorbellMemory; use crate::queue::QueueError; -use crate::queue::ShadowDoorbell; use crate::queue::SubmissionQueue; use crate::spec; use crate::spec::nvm; @@ -17,9 +16,11 @@ use crate::workers::MAX_DATA_TRANSFER_SIZE; use futures_concurrency::future::Race; use guestmem::GuestMemory; use inspect::Inspect; +use parking_lot::RwLock; use std::collections::BTreeMap; use std::future::Future; use std::future::pending; +use std::future::poll_fn; use std::pin::Pin; use std::sync::Arc; use task_control::AsyncRun; @@ -59,20 +60,27 @@ enum IoQueueState { impl IoState { pub fn new( + mem: &GuestMemory, + doorbell: Arc>, sq_gpa: u64, sq_len: u16, - sq_tail: Arc, - sq_sdb_idx_gpas: Option, + sq_id: u16, cq_gpa: u64, cq_len: u16, - cq_head: Arc, - cq_sdb_idx_gpas: Option, + cq_id: u16, interrupt: Option, namespaces: BTreeMap>, ) -> Self { Self { - sq: SubmissionQueue::new(sq_tail, sq_gpa, sq_len, sq_sdb_idx_gpas), - cq: CompletionQueue::new(cq_head, interrupt, cq_gpa, cq_len, cq_sdb_idx_gpas), + sq: SubmissionQueue::new(doorbell.clone(), sq_id * 2, sq_gpa, sq_len, mem.clone()), + cq: CompletionQueue::new( + doorbell, + cq_id * 2 + 1, + mem.clone(), + interrupt, + cq_gpa, + cq_len, + ), namespaces, ios: FuturesUnordered::new(), io_count: 0, @@ -103,14 +111,12 @@ struct IoResult { cid: u16, opcode: nvm::NvmOpcode, result: Result, - advance_evt_idx: bool, } impl AsyncRun for IoHandler { async fn run(&mut self, stop: &mut StopTask<'_>, state: &mut IoState) -> Result<(), Cancelled> { - let mem = self.mem.clone(); stop.until_stopped(async { - if let Err(err) = self.process(state, &mem).await { + if let Err(err) = self.process(state).await { tracing::error!(error = &err as &dyn std::error::Error, "io handler failed"); } }) @@ -148,11 +154,7 @@ impl IoHandler { } } - async fn process( - &mut self, - state: &mut IoState, - mem: &GuestMemory, - ) -> Result<(), HandlerError> { + async fn process(&mut self, state: &mut IoState) -> Result<(), HandlerError> { loop { let deleting = match state.queue_state { IoQueueState::Active => { @@ -160,7 +162,7 @@ impl IoHandler { // to post an immediate result or to post an IO completion. It's not // strictly necessary to start a new IO, but handling that special // case is not worth the complexity. - state.cq.wait_ready(mem).await?; + poll_fn(|cx| state.cq.poll_ready(cx)).await?; false } IoQueueState::Deleting => { @@ -181,7 +183,7 @@ impl IoHandler { let next_sqe = async { if state.io_count < MAX_IO_QUEUE_DEPTH && !deleting { - Event::Sq(state.sq.next(&self.mem).await) + Event::Sq(poll_fn(|cx| state.sq.poll_next(cx)).await) } else { pending().await } @@ -198,12 +200,6 @@ impl IoHandler { let event = (next_sqe, next_io_completion).race().await; let (cid, result) = match event { Event::Io(io_result) => { - if io_result.advance_evt_idx { - let result = state.sq.advance_evt_idx(&self.mem); - if result.is_err() { - tracelimit::warn_ratelimited!("failure to advance evt_idx"); - } - } state.io_count -= 1; let result = match io_result.result { Ok(cr) => cr, @@ -226,21 +222,6 @@ impl IoHandler { if let Some(ns) = state.namespaces.get(&command.nsid) { let ns = ns.clone(); - // If the queue depth is low, immediately update the evt_idx, so that - // the guest driver will ring the doorbell again. If the queue depth is - // high, defer this until I/O completion, on the theory that high queue - // depth workloads won't wait before enqueuing more work. - // - // TODO: Update later after performance testing, perhaps to something - // like to 2*(number of VPs)/(number of queue pairs). - let mut advance_evt_idx = true; - if state.io_count <= 1 { - let result = state.sq.advance_evt_idx(&self.mem); - if result.is_err() { - tracelimit::warn_ratelimited!("failure to advance evt_idx"); - } - advance_evt_idx = false; - } let io = Box::pin(async move { let result = ns.nvm_command(MAX_DATA_TRANSFER_SIZE, &command).await; IoResult { @@ -248,7 +229,6 @@ impl IoHandler { opcode: nvm::NvmOpcode(command.cdw0.opcode()), cid, result, - advance_evt_idx, } }); state.ios.push(io); @@ -256,10 +236,6 @@ impl IoHandler { continue; } - let result = state.sq.advance_evt_idx(&self.mem); - if result.is_err() { - tracelimit::warn_ratelimited!("failure to advance evt_idx"); - } (cid, spec::Status::INVALID_NAMESPACE_OR_FORMAT.into()) } }; @@ -272,25 +248,11 @@ impl IoHandler { cid, status: spec::CompletionStatus::new().with_status(result.status.0), }; - if !state.cq.write(&self.mem, completion)? { + if !state.cq.write(completion)? { assert!(deleting); tracelimit::warn_ratelimited!("dropped i/o completion during queue deletion"); } - state - .cq - .catch_up_evt_idx(false, state.io_count as u32, &self.mem)?; } Ok(()) } - - pub fn update_shadow_db( - &mut self, - mem: &GuestMemory, - state: &mut IoState, - sq_sdb: ShadowDoorbell, - cq_sdb: ShadowDoorbell, - ) { - state.sq.update_shadow_db(mem, sq_sdb); - state.cq.update_shadow_db(mem, cq_sdb); - } } diff --git a/vm/devices/support/fs/lxutil/src/windows/fs.rs b/vm/devices/support/fs/lxutil/src/windows/fs.rs index d2131f2da7..746e815fff 100644 --- a/vm/devices/support/fs/lxutil/src/windows/fs.rs +++ b/vm/devices/support/fs/lxutil/src/windows/fs.rs @@ -517,7 +517,7 @@ pub fn allocation_size_to_block_count(allocation_size: i64, block_size: u32) -> if size >= block_size as u64 { result = size / LX_UTIL_FS_ALLOCATION_BLOCK_SIZE; - if size % LX_UTIL_FS_ALLOCATION_BLOCK_SIZE != 0 { + if !size.is_multiple_of(LX_UTIL_FS_ALLOCATION_BLOCK_SIZE) { result += 1; } } diff --git a/vm/devices/user_driver/src/lockmem.rs b/vm/devices/user_driver/src/lockmem.rs index b7cb190630..c2ab79eee9 100644 --- a/vm/devices/user_driver/src/lockmem.rs +++ b/vm/devices/user_driver/src/lockmem.rs @@ -90,7 +90,7 @@ impl Drop for Mapping { impl LockedMemory { pub fn new(len: usize) -> anyhow::Result { - if len % PAGE_SIZE != 0 { + if !len.is_multiple_of(PAGE_SIZE) { anyhow::bail!("not a page-size multiple"); } let mapping = Mapping::new(len).context("failed to create mapping")?; diff --git a/vm/devices/user_driver/src/vfio.rs b/vm/devices/user_driver/src/vfio.rs index 0bc6a7670b..835e665090 100644 --- a/vm/devices/user_driver/src/vfio.rs +++ b/vm/devices/user_driver/src/vfio.rs @@ -402,7 +402,9 @@ impl DeviceRegisterIo for vfio_sys::MappedRegion { impl MappedRegionWithFallback { fn mapping(&self, offset: usize) -> *mut T { - assert!(offset <= self.mapping.len() - size_of::() && offset % align_of::() == 0); + assert!( + offset <= self.mapping.len() - size_of::() && offset.is_multiple_of(align_of::()) + ); if cfg!(feature = "mmio_simulate_fallback") { return std::ptr::NonNull::dangling().as_ptr(); } diff --git a/vm/devices/virtio/virtio_p9/src/lib.rs b/vm/devices/virtio/virtio_p9/src/lib.rs index 13fbc174db..1310f5787f 100644 --- a/vm/devices/virtio/virtio_p9/src/lib.rs +++ b/vm/devices/virtio/virtio_p9/src/lib.rs @@ -64,8 +64,8 @@ impl LegacyVirtioDevice for VirtioPlan9Device { } fn read_registers_u32(&self, offset: u16) -> u32 { - assert!(self.tag.len() % 4 == 0); - assert!(offset % 4 == 0); + assert!(self.tag.len().is_multiple_of(4)); + assert!(offset.is_multiple_of(4)); let offset = offset as usize; if offset < self.tag.len() { diff --git a/vm/devices/virtio/virtiofs/src/section.rs b/vm/devices/virtio/virtiofs/src/section.rs index 782bfcbd6b..3b19480a7c 100644 --- a/vm/devices/virtio/virtiofs/src/section.rs +++ b/vm/devices/virtio/virtiofs/src/section.rs @@ -384,7 +384,7 @@ impl fuse::Fuse for SectionFs { let mut inodes = self.inodes.lock(); let inode = inodes.get_mut(request.node_id()).ok_or(lx::Error::EINVAL)?; - if arg.offset != 0 || arg.length % PAGE_SIZE != 0 { + if arg.offset != 0 || !arg.length.is_multiple_of(PAGE_SIZE) { return Err(lx::Error::EINVAL); } let size = arg.length; diff --git a/vm/devices/vmbus/vmbfs/src/protocol.rs b/vm/devices/vmbus/vmbfs/src/protocol.rs index 4eb00e07d2..1826b41f46 100644 --- a/vm/devices/vmbus/vmbfs/src/protocol.rs +++ b/vm/devices/vmbus/vmbfs/src/protocol.rs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#![allow(dead_code)] + use bitfield_struct::bitfield; use guid::Guid; use open_enum::open_enum; diff --git a/vm/devices/vmbus/vmbus_channel/src/gpadl_ring.rs b/vm/devices/vmbus/vmbus_channel/src/gpadl_ring.rs index 9bfa1e0ae4..d069254db7 100644 --- a/vm/devices/vmbus/vmbus_channel/src/gpadl_ring.rs +++ b/vm/devices/vmbus/vmbus_channel/src/gpadl_ring.rs @@ -40,7 +40,7 @@ impl AlignedGpadlView { return Err(gpadl); } let range = gpadl.first().unwrap(); - if range.len() % ring::PAGE_SIZE != 0 || range.offset() != 0 { + if !range.len().is_multiple_of(ring::PAGE_SIZE) || range.offset() != 0 { return Err(gpadl); } let count = range.gpns().len() as u32; diff --git a/vm/devices/vmbus/vmbus_ring/src/lib.rs b/vm/devices/vmbus/vmbus_ring/src/lib.rs index 86672519d6..7017370dcb 100644 --- a/vm/devices/vmbus/vmbus_ring/src/lib.rs +++ b/vm/devices/vmbus/vmbus_ring/src/lib.rs @@ -443,8 +443,8 @@ pub trait RingMem: Send { /// /// `read_at` may be faster for large or variable-sized reads. fn read_aligned(&self, addr: usize, data: &mut [u8]) { - debug_assert!(addr % 8 == 0); - debug_assert!(data.len() % 8 == 0); + debug_assert!(addr.is_multiple_of(8)); + debug_assert!(data.len().is_multiple_of(8)); self.read_at(addr, data) } @@ -457,8 +457,8 @@ pub trait RingMem: Send { /// /// `write_at` may be faster for large or variable-sized writes. fn write_aligned(&self, addr: usize, data: &[u8]) { - debug_assert!(addr % 8 == 0); - debug_assert!(data.len() % 8 == 0); + debug_assert!(addr.is_multiple_of(8)); + debug_assert!(data.len().is_multiple_of(8)); self.write_at(addr, data) } @@ -671,8 +671,8 @@ impl RingMem for PagedRingMem { #[inline] fn read_aligned(&self, addr: usize, data: &mut [u8]) { - debug_assert!(addr % 8 == 0); - debug_assert!(data.len() % 8 == 0); + debug_assert!(addr.is_multiple_of(8)); + debug_assert!(data.len().is_multiple_of(8)); for (i, b) in data.chunks_exact_mut(8).enumerate() { let addr = (addr & !7) + i * 8; let page = addr / PAGE_SIZE; @@ -689,8 +689,8 @@ impl RingMem for PagedRingMem { #[inline] fn write_aligned(&self, addr: usize, data: &[u8]) { - debug_assert!(addr % 8 == 0); - debug_assert!(data.len() % 8 == 0); + debug_assert!(addr.is_multiple_of(8)); + debug_assert!(data.len().is_multiple_of(8)); for (i, b) in data.chunks_exact(8).enumerate() { let addr = (addr & !7) + i * 8; let page = addr / PAGE_SIZE; @@ -1274,7 +1274,7 @@ impl InnerRing { } fn validate(&self, p: u32) -> Result { - if p >= self.size || p % 8 != 0 { + if p >= self.size || !p.is_multiple_of(8) { Err(Error::InvalidRingPointer) } else { Ok(p) diff --git a/vm/devices/vmbus/vmbus_server/src/tests.rs b/vm/devices/vmbus/vmbus_server/src/tests.rs index 421ea1e397..03b84e591b 100644 --- a/vm/devices/vmbus/vmbus_server/src/tests.rs +++ b/vm/devices/vmbus/vmbus_server/src/tests.rs @@ -167,7 +167,7 @@ impl SynicPortAccess for MockSynic { _vtl: Vtl, _vp: u32, _sint: u8, - ) -> Result, vmcore::synic::HypervisorError> { + ) -> Result, vmcore::synic::HypervisorError> { Ok(Box::new(MockGuestMessagePort { send: self.message_send.clone(), spawner: self.spawner.clone(), @@ -183,7 +183,7 @@ impl SynicPortAccess for MockSynic { _sint: u8, _flag: u16, _monitor_info: Option, - ) -> Result, vmcore::synic::HypervisorError> { + ) -> Result, vmcore::synic::HypervisorError> { Ok(Box::new(MockGuestPort {})) } diff --git a/vm/hv1/hv1_hypercall/src/support.rs b/vm/hv1/hv1_hypercall/src/support.rs index c95be2d9a6..0cbfc7d9db 100644 --- a/vm/hv1/hv1_hypercall/src/support.rs +++ b/vm/hv1/hv1_hypercall/src/support.rs @@ -293,7 +293,7 @@ impl<'a, T: HypercallIo> InnerDispatcher<'a, T> { } // The buffer must be 8 byte aligned. - if len != 0 && gpa % 8 != 0 { + if len != 0 && !gpa.is_multiple_of(8) { return Err(HvError::from(HypercallParseError::Unaligned)); } diff --git a/vm/hv1/hv1_hypercall/src/tests.rs b/vm/hv1/hv1_hypercall/src/tests.rs index b9852f209e..9ae6df3a59 100644 --- a/vm/hv1/hv1_hypercall/src/tests.rs +++ b/vm/hv1/hv1_hypercall/src/tests.rs @@ -916,7 +916,7 @@ impl TestController { where InputHeaderT: IntoBytes + FromBytes + Sized + Copy + Immutable + KnownLayout, { - assert!(size_of::() % 8 == 0); + assert!(size_of::().is_multiple_of(8)); *InputHeaderT::ref_from_bytes(vec![FILL_PATTERN; size_of::() / 8].as_bytes()) .unwrap() } @@ -946,7 +946,7 @@ impl TestController { where OutputT: IntoBytes + FromBytes + FromZeros + Sized + Copy + Immutable + KnownLayout, { - assert!(size_of::() % 16 == 0); + assert!(size_of::().is_multiple_of(16)); *OutputT::ref_from_bytes(vec![!FILL_PATTERN; size_of::() / 8].as_bytes()) .unwrap() } @@ -1223,9 +1223,9 @@ where OutputT: IntoBytes + FromBytes + Sized + Immutable + KnownLayout, OutRepT: IntoBytes + FromBytes + Sized + Immutable + KnownLayout, { - assert!(size_of::() % 8 == 0); - assert!(size_of::() % 8 == 0); - assert!(var_header.len() % 8 == 0); + assert!(size_of::().is_multiple_of(8)); + assert!(size_of::().is_multiple_of(8)); + assert!(var_header.len().is_multiple_of(8)); assert!(params.in_offset < PAGE_SIZE); assert!(params.out_offset < PAGE_SIZE); assert!(size_of::() == 0 || output_reps.is_empty()); @@ -1672,14 +1672,16 @@ fn hypercall_rep(test_params: TestParams) { ); let elements_processed = test_params.test_result.expected_elements_processed(); - let elements_processed = - if test_params.fast && elements_processed % 2 != 0 && elements_processed < rep_count { - // Since only 16 byte writes are supported, the top 8 bytes are 0s. - assert_eq!(output_reps[elements_processed], 0); - elements_processed + 1 - } else { - elements_processed - }; + let elements_processed = if test_params.fast + && !elements_processed.is_multiple_of(2) + && elements_processed < rep_count + { + // Since only 16 byte writes are supported, the top 8 bytes are 0s. + assert_eq!(output_reps[elements_processed], 0); + elements_processed + 1 + } else { + elements_processed + }; assert_eq!( output_reps[elements_processed..].as_bytes(), @@ -1692,11 +1694,13 @@ fn hypercall_rep(test_params: TestParams) { .as_bytes() .len(); - if (rep_start * size_of::()) % 16 != 0 { + if !(rep_start * size_of::()).is_multiple_of(16) { expected_output_size += (rep_start * size_of::()) % 16; } - if (test_params.test_result.expected_elements_processed() * size_of::()) % 16 != 0 { + if !(test_params.test_result.expected_elements_processed() * size_of::()) + .is_multiple_of(16) + { expected_output_size += 16 - ((test_params.test_result.expected_elements_processed() * size_of::()) % 16); } @@ -1862,14 +1866,16 @@ fn hypercall_variable_rep(test_params: TestParams) { ); let elements_processed = test_params.test_result.expected_elements_processed(); - let elements_processed = - if test_params.fast && elements_processed % 2 != 0 && elements_processed < rep_count { - // Since only 16 byte writes are supported, the top 8 bytes are 0s. - assert_eq!(output_reps[elements_processed], 0); - elements_processed + 1 - } else { - elements_processed - }; + let elements_processed = if test_params.fast + && !elements_processed.is_multiple_of(2) + && elements_processed < rep_count + { + // Since only 16 byte writes are supported, the top 8 bytes are 0s. + assert_eq!(output_reps[elements_processed], 0); + elements_processed + 1 + } else { + elements_processed + }; assert_eq!( output_reps[elements_processed..].as_bytes(), @@ -1881,11 +1887,13 @@ fn hypercall_variable_rep(test_params: TestParams) { .as_bytes() .len(); - if (rep_start * size_of::()) % 16 != 0 { + if !(rep_start * size_of::()).is_multiple_of(16) { expected_output_size += (rep_start * size_of::()) % 16; } - if (test_params.test_result.expected_elements_processed() * size_of::()) % 16 != 0 { + if !(test_params.test_result.expected_elements_processed() * size_of::()) + .is_multiple_of(16) + { expected_output_size += 16 - ((test_params.test_result.expected_elements_processed() * size_of::()) % 16); } @@ -2264,7 +2272,7 @@ fn max_test_fast_rep_count(abi: &TestHypercallAbi) -> usize { // of that is for the input reps (8 bytes each in our tests) and half is for the output // reps. However output must be on a 16 byte alignment. let mut max_rep_count = (abi.max_fast_output_size() - size_of::()) / (8 + 8); - if max_rep_count % 2 != 0 { + if !max_rep_count.is_multiple_of(2) { max_rep_count -= 1; } diff --git a/vm/loader/igvmfilegen/src/file_loader.rs b/vm/loader/igvmfilegen/src/file_loader.rs index 8adb920ff1..baae840fa1 100644 --- a/vm/loader/igvmfilegen/src/file_loader.rs +++ b/vm/loader/igvmfilegen/src/file_loader.rs @@ -1109,29 +1109,29 @@ impl ImageLoad for IgvmVtlLoader ); } - if size_bytes % PAGE_SIZE_4K != 0 { + if !size_bytes.is_multiple_of(PAGE_SIZE_4K) { anyhow::bail!("relocation size {size_bytes:#x} must be a multiple of 4K"); } - if relocation_alignment % PAGE_SIZE_4K != 0 { + if !relocation_alignment.is_multiple_of(PAGE_SIZE_4K) { anyhow::bail!( "relocation alignment {relocation_alignment:#x} must be a multiple of 4K" ); } - if gpa % relocation_alignment != 0 { + if !gpa.is_multiple_of(relocation_alignment) { anyhow::bail!( "relocation base {gpa:#x} must be aligned to relocation alignment {relocation_alignment:#x}" ); } - if minimum_relocation_gpa % relocation_alignment != 0 { + if !minimum_relocation_gpa.is_multiple_of(relocation_alignment) { anyhow::bail!( "relocation minimum GPA {minimum_relocation_gpa:#x} must be aligned to relocation alignment {relocation_alignment:#x}" ); } - if maximum_relocation_gpa % relocation_alignment != 0 { + if !maximum_relocation_gpa.is_multiple_of(relocation_alignment) { anyhow::bail!( "relocation maximum GPA {maximum_relocation_gpa:#x} must be aligned to relocation alignment {relocation_alignment:#x}" ); diff --git a/vm/loader/page_table/src/x64.rs b/vm/loader/page_table/src/x64.rs index bbf13e7aaf..3bfba937e8 100644 --- a/vm/loader/page_table/src/x64.rs +++ b/vm/loader/page_table/src/x64.rs @@ -347,12 +347,12 @@ impl PageTableBuilder { panic!("more than 512 gb size not supported"); } - if self.size % X64_LARGE_PAGE_SIZE != 0 { + if !self.size.is_multiple_of(X64_LARGE_PAGE_SIZE) { panic!("size not 2mb aligned"); } // start_gpa and size must be 2MB aligned. - if self.start_gpa % X64_LARGE_PAGE_SIZE != 0 { + if !self.start_gpa.is_multiple_of(X64_LARGE_PAGE_SIZE) { panic!("start_gpa not 2mb aligned"); } diff --git a/vm/loader/src/linux.rs b/vm/loader/src/linux.rs index 43b0879f43..037b4436a1 100644 --- a/vm/loader/src/linux.rs +++ b/vm/loader/src/linux.rs @@ -202,7 +202,7 @@ pub struct LoadInfo { /// Check if an address is aligned to a page. fn check_address_alignment(address: u64) -> Result<(), Error> { - if address % HV_PAGE_SIZE != 0 { + if !address.is_multiple_of(HV_PAGE_SIZE) { Err(Error::UnalignedAddress(address)) } else { Ok(()) @@ -337,7 +337,7 @@ pub fn load_config( IdentityMapSize::Size4Gb, None, ); - assert!(page_table.len() as u64 % HV_PAGE_SIZE == 0); + assert!((page_table.len() as u64).is_multiple_of(HV_PAGE_SIZE)); importer .import_pages( registers.page_table_address / HV_PAGE_SIZE, diff --git a/vm/loader/src/paravisor.rs b/vm/loader/src/paravisor.rs index 9a3b861eba..2bf076676f 100644 --- a/vm/loader/src/paravisor.rs +++ b/vm/loader/src/paravisor.rs @@ -147,11 +147,11 @@ where // --- Low memory, 2MB aligned --- // Paravisor memory ranges must be 2MB (large page) aligned. - if memory_start_address % X64_LARGE_PAGE_SIZE != 0 { + if !memory_start_address.is_multiple_of(X64_LARGE_PAGE_SIZE) { return Err(Error::MemoryUnaligned(memory_start_address)); } - if memory_size % X64_LARGE_PAGE_SIZE != 0 { + if !memory_size.is_multiple_of(X64_LARGE_PAGE_SIZE) { return Err(Error::MemoryUnaligned(memory_size)); } @@ -423,7 +423,7 @@ where let page_table = page_table_builder.build(); - assert!(page_table.len() as u64 % HV_PAGE_SIZE == 0); + assert!((page_table.len() as u64).is_multiple_of(HV_PAGE_SIZE)); let page_table_page_base = page_table_region_start / HV_PAGE_SIZE; assert!(page_table.len() as u64 <= page_table_region_size); @@ -898,11 +898,11 @@ where let memory_size = memory_page_count * HV_PAGE_SIZE; // Paravisor memory ranges must be 2MB (large page) aligned. - if memory_start_address % u64::from(Arm64PageSize::Large) != 0 { + if !memory_start_address.is_multiple_of(u64::from(Arm64PageSize::Large)) { return Err(Error::MemoryUnaligned(memory_start_address)); } - if memory_size % u64::from(Arm64PageSize::Large) != 0 { + if !memory_size.is_multiple_of(u64::from(Arm64PageSize::Large)) { return Err(Error::MemoryUnaligned(memory_size)); } @@ -1153,7 +1153,7 @@ where memory_attribute_indirection, page_table_region_size as usize, ); - assert!(page_tables.len() as u64 % HV_PAGE_SIZE == 0); + assert!((page_tables.len() as u64).is_multiple_of(HV_PAGE_SIZE)); let page_table_page_base = page_table_region_start / HV_PAGE_SIZE; assert!(page_tables.len() as u64 <= page_table_region_size); assert!(page_table_region_size as usize > page_tables.len()); diff --git a/vm/page_pool_alloc/src/lib.rs b/vm/page_pool_alloc/src/lib.rs index c55ba44655..9a47911ea4 100644 --- a/vm/page_pool_alloc/src/lib.rs +++ b/vm/page_pool_alloc/src/lib.rs @@ -858,7 +858,7 @@ impl Drop for PagePoolAllocator { impl user_driver::DmaClient for PagePoolAllocator { fn allocate_dma_buffer(&self, len: usize) -> anyhow::Result { - if len as u64 % PAGE_SIZE != 0 { + if !(len as u64).is_multiple_of(PAGE_SIZE) { anyhow::bail!("not a page-size multiple"); } diff --git a/vm/vmcore/guestmem/src/lib.rs b/vm/vmcore/guestmem/src/lib.rs index 0a5ae932ae..4561830d7f 100644 --- a/vm/vmcore/guestmem/src/lib.rs +++ b/vm/vmcore/guestmem/src/lib.rs @@ -1406,7 +1406,7 @@ impl GuestMemory { op: GuestMemoryOperation, err: GuestMemoryBackingError, ) -> GuestMemoryError { - let range = gpa_len.map(|(gpa, len)| (gpa..gpa.wrapping_add(len))); + let range = gpa_len.map(|(gpa, len)| gpa..gpa.wrapping_add(len)); GuestMemoryError::new(&self.inner.debug_name, range, op, err) } @@ -1948,7 +1948,7 @@ impl GuestMemory { let mut byte_index = 0; let mut len = range.len(); let mut page = 0; - if offset % PAGE_SIZE != 0 { + if !offset.is_multiple_of(PAGE_SIZE) { let head_len = std::cmp::min(len, PAGE_SIZE - (offset % PAGE_SIZE)); let addr = gpn_to_gpa(gpns[page]).map_err(GuestMemoryBackingError::gpn)? + offset as u64 % PAGE_SIZE64; diff --git a/vm/vmcore/memory_range/src/lib.rs b/vm/vmcore/memory_range/src/lib.rs index 9aa04efddc..bfc202adf3 100644 --- a/vm/vmcore/memory_range/src/lib.rs +++ b/vm/vmcore/memory_range/src/lib.rs @@ -232,7 +232,7 @@ impl MemoryRange { #[track_caller] pub fn split_at_offset(&self, offset: u64) -> (Self, Self) { assert!(offset <= self.len()); - assert!(offset % PAGE_SIZE == 0); + assert!(offset.is_multiple_of(PAGE_SIZE)); ( Self { start: self.start, diff --git a/vm/vmgs/vmgs_format/src/lib.rs b/vm/vmgs/vmgs_format/src/lib.rs index 6959f77061..bac084a5cf 100644 --- a/vm/vmgs/vmgs_format/src/lib.rs +++ b/vm/vmgs/vmgs_format/src/lib.rs @@ -173,7 +173,7 @@ pub struct VmgsFileTable { } const_assert!(size_of::() == 4096); -const_assert!(size_of::() as u32 % VMGS_BYTES_PER_BLOCK == 0); +const_assert!((size_of::() as u32).is_multiple_of(VMGS_BYTES_PER_BLOCK)); pub const VMGS_FILE_TABLE_BLOCK_SIZE: u32 = size_of::() as u32 / VMGS_BYTES_PER_BLOCK; @@ -184,7 +184,7 @@ pub struct VmgsExtendedFileTable { } const_assert!(size_of::() == 4096); -const_assert!(size_of::() as u32 % VMGS_BYTES_PER_BLOCK == 0); +const_assert!((size_of::() as u32).is_multiple_of(VMGS_BYTES_PER_BLOCK)); pub const VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE: u32 = size_of::() as u32 / VMGS_BYTES_PER_BLOCK; diff --git a/vm/vmgs/vmgs_lib/src/lib.rs b/vm/vmgs/vmgs_lib/src/lib.rs index 0280da10dd..f48b0e3b1e 100644 --- a/vm/vmgs/vmgs_lib/src/lib.rs +++ b/vm/vmgs/vmgs_lib/src/lib.rs @@ -297,7 +297,9 @@ async fn do_create( } } - if file_size != 0 && file_size < (VMGS_BYTES_PER_BLOCK * 4) as u64 || file_size % 512 != 0 { + if file_size != 0 && file_size < (VMGS_BYTES_PER_BLOCK * 4) as u64 + || !file_size.is_multiple_of(512) + { return Err(VmgsError::InvalidFileSize); } let file_size = if file_size == 0 { diff --git a/vm/vmgs/vmgstool/src/main.rs b/vm/vmgs/vmgstool/src/main.rs index 6fb991a295..68a2d1cd7a 100644 --- a/vm/vmgs/vmgstool/src/main.rs +++ b/vm/vmgs/vmgstool/src/main.rs @@ -509,7 +509,7 @@ fn vhdfiledisk_create( const SECTOR_SIZE: u64 = 512; let file_size = req_file_size.unwrap_or(VMGS_DEFAULT_CAPACITY); - if file_size < MIN_VMGS_FILE_SIZE || file_size % SECTOR_SIZE != 0 { + if file_size < MIN_VMGS_FILE_SIZE || !file_size.is_multiple_of(SECTOR_SIZE) { return Err(Error::InvalidVmgsFileSize( file_size, format!( diff --git a/vm/x86/tdcall/src/lib.rs b/vm/x86/tdcall/src/lib.rs index a7bc83b9e8..94d0fb760a 100644 --- a/vm/x86/tdcall/src/lib.rs +++ b/vm/x86/tdcall/src/lib.rs @@ -488,7 +488,7 @@ pub fn accept_pages( let mut range = range; while !range.is_empty() { // Attempt to accept in large page chunks if possible. - if range.start() % x86defs::X64_LARGE_PAGE_SIZE == 0 + if range.start().is_multiple_of(x86defs::X64_LARGE_PAGE_SIZE) && range.len() >= x86defs::X64_LARGE_PAGE_SIZE { match tdcall_accept_pages(call, range.start_4k_gpn(), true) { @@ -566,7 +566,7 @@ pub fn set_page_attributes( let mut range = range; while !range.is_empty() { // Attempt to set in large page chunks if possible. - if range.start() % x86defs::X64_LARGE_PAGE_SIZE == 0 + if range.start().is_multiple_of(x86defs::X64_LARGE_PAGE_SIZE) && range.len() >= x86defs::X64_LARGE_PAGE_SIZE { let mapping = TdgMemPageAttrWriteRcx::new() diff --git a/vm/x86/x86emu/src/emulator.rs b/vm/x86/x86emu/src/emulator.rs index 5e5abca9ae..93561a70fb 100644 --- a/vm/x86/x86emu/src/emulator.rs +++ b/vm/x86/x86emu/src/emulator.rs @@ -405,7 +405,7 @@ impl<'a, T: Cpu> Emulator<'a, T> { ) -> Result<(), Error> { match alignment { AlignmentMode::Aligned(a) => { - if gva % a != 0 { + if !gva.is_multiple_of(a) { Err(Error::InstructionException( Exception::GENERAL_PROTECTION_FAULT, Some(0), @@ -419,7 +419,7 @@ impl<'a, T: Cpu> Emulator<'a, T> { && self.cpu.rflags().alignment_check() && self.cpu.cr0() & x86defs::X64_CR0_AM != 0 { - if gva % len as u64 != 0 { + if !gva.is_multiple_of(len as u64) { Err(Error::InstructionException( Exception::ALIGNMENT_CHECK, None, diff --git a/vmm_core/src/synic.rs b/vmm_core/src/synic.rs index 47b280a2c1..1627d7ec9b 100644 --- a/vmm_core/src/synic.rs +++ b/vmm_core/src/synic.rs @@ -163,7 +163,7 @@ impl SynicPortAccess for SynicPorts { vtl: Vtl, vp: u32, sint: u8, - ) -> Result, vmcore::synic::HypervisorError> { + ) -> Result, vmcore::synic::HypervisorError> { Ok(Box::new(DirectGuestMessagePort { partition: Arc::clone(&self.partition), vtl, @@ -180,7 +180,7 @@ impl SynicPortAccess for SynicPorts { sint: u8, flag: u16, _monitor_info: Option, - ) -> Result, vmcore::synic::HypervisorError> { + ) -> Result, vmcore::synic::HypervisorError> { Ok(self.partition.new_guest_event_port(vtl, vp, sint, flag)) } diff --git a/vmm_core/virt_kvm/src/arch/aarch64/mod.rs b/vmm_core/virt_kvm/src/arch/aarch64/mod.rs index caa8b13748..3bc5a4e47c 100644 --- a/vmm_core/virt_kvm/src/arch/aarch64/mod.rs +++ b/vmm_core/virt_kvm/src/arch/aarch64/mod.rs @@ -506,7 +506,9 @@ impl KvmProtoPartition<'_> { const GIC_ALIGNMENT: u64 = 0x10000; let gic_dist_base: u64 = self.config.processor_topology.gic_distributor_base(); let gic_redist_base: u64 = self.config.processor_topology.gic_redistributors_base(); - if gic_dist_base % GIC_ALIGNMENT != 0 || gic_redist_base % GIC_ALIGNMENT != 0 { + if !gic_dist_base.is_multiple_of(GIC_ALIGNMENT) + || !gic_redist_base.is_multiple_of(GIC_ALIGNMENT) + { return Err(KvmError::Misaligned); } diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/openhcl_servicing.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/openhcl_servicing.rs index 4d089d66bf..db48724d7a 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/openhcl_servicing.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/openhcl_servicing.rs @@ -145,9 +145,9 @@ async fn basic( } /// Test servicing an OpenHCL VM from the current version to itself -/// with NVMe keepalive support. -#[openvmm_test(openhcl_linux_direct_x64 [LATEST_LINUX_DIRECT_TEST_X64])] -async fn keepalive( +/// with NVMe keepalive support and no vmbus redirect. +#[openvmm_test(openhcl_linux_direct_x64[LATEST_LINUX_DIRECT_TEST_X64])] +async fn keepalive_no_device( config: PetriVmBuilder, (igvm_file,): (ResolvedArtifact,), ) -> anyhow::Result<()> { @@ -163,6 +163,25 @@ async fn keepalive( .await } +/// Test servicing an OpenHCL VM from the current version to itself +/// with NVMe keepalive support. +#[openvmm_test(openhcl_uefi_x64[nvme](vhd(ubuntu_2204_server_x64))[LATEST_STANDARD_X64])] +async fn keepalive_with_device( + config: PetriVmBuilder, + (igvm_file,): (ResolvedArtifact,), +) -> anyhow::Result<()> { + openhcl_servicing_core( + config.with_vmbus_redirect(true), // Need this to attach the NVMe device + "OPENHCL_ENABLE_VTL2_GPA_POOL=512 OPENHCL_SIDECAR=off", // disable sidecar until #1345 is fixed + igvm_file, + OpenHclServicingFlags { + enable_nvme_keepalive: true, + ..Default::default() + }, + ) + .await +} + #[vmm_test( openvmm_openhcl_linux_direct_x64 [LATEST_LINUX_DIRECT_TEST_X64, RELEASE_25_05_LINUX_DIRECT_X64], hyperv_openhcl_uefi_aarch64(vhd(ubuntu_2404_server_aarch64))[RELEASE_25_05_STANDARD_AARCH64, LATEST_STANDARD_AARCH64] @@ -420,6 +439,7 @@ async fn create_keepalive_test_config( .into_resource(), }], fault_config: fault_configuration, + flr_support: false, } .into_resource(), }) diff --git a/vmm_tests/vmm_tests/tests/tests/x86_64/openhcl_uefi.rs b/vmm_tests/vmm_tests/tests/tests/x86_64/openhcl_uefi.rs index 8a8e595221..eb648259ec 100644 --- a/vmm_tests/vmm_tests/tests/tests/x86_64/openhcl_uefi.rs +++ b/vmm_tests/vmm_tests/tests/tests/x86_64/openhcl_uefi.rs @@ -5,12 +5,9 @@ use anyhow::Context; use futures::StreamExt; -use petri::OpenHclServicingFlags; use petri::PetriVmBuilder; use petri::ProcessorTopology; -use petri::ResolvedArtifact; use petri::openvmm::OpenVmmPetriBackend; -use petri_artifacts_vmm_test::artifacts::openhcl_igvm::LATEST_STANDARD_X64; use vmm_test_macros::openvmm_test; use vmm_test_macros::openvmm_test_no_agent; @@ -34,37 +31,6 @@ async fn nvme_relay_test_core( Ok(()) } -/// Servicing tests with NVMe devices attached. -async fn nvme_relay_servicing_core( - config: PetriVmBuilder, - openhcl_cmdline: &str, - new_openhcl: ResolvedArtifact, - flags: OpenHclServicingFlags, -) -> Result<(), anyhow::Error> { - let (mut vm, agent) = config - .with_openhcl_command_line(openhcl_cmdline) - .with_vmbus_redirect(true) - .run() - .await?; - - agent.ping().await?; - - // Test that inspect serialization works with the old version. - vm.test_inspect_openhcl().await?; - - vm.restart_openhcl(new_openhcl, flags).await?; - - agent.ping().await?; - - // Test that inspect serialization works with the new version. - vm.test_inspect_openhcl().await?; - - agent.power_off().await?; - vm.wait_for_clean_teardown().await?; - - Ok(()) -} - /// Test an OpenHCL uefi VM with a NVME disk assigned to VTL2 that boots /// linux, with vmbus relay. This should expose a disk to VTL0 via vmbus. #[openvmm_test(openhcl_uefi_x64[nvme](vhd(ubuntu_2204_server_x64)))] @@ -95,27 +61,6 @@ async fn nvme_relay_private_pool( nvme_relay_test_core(config, "OPENHCL_ENABLE_VTL2_GPA_POOL=512").await } -/// Servicing test of an OpenHCL uefi VM with a NVME disk assigned to VTL2 that boots -/// linux, with vmbus relay. This should expose a disk to VTL0 via vmbus. -/// Use the private pool override to test the private pool dma path. -/// Pass 'keepalive' servicing flag which impacts NVMe save/restore. -#[openvmm_test(openhcl_uefi_x64[nvme](vhd(ubuntu_2204_server_x64))[LATEST_STANDARD_X64])] -async fn nvme_keepalive( - config: PetriVmBuilder, - (igvm_file,): (ResolvedArtifact,), -) -> Result<(), anyhow::Error> { - nvme_relay_servicing_core( - config, - "OPENHCL_ENABLE_VTL2_GPA_POOL=512 OPENHCL_SIDECAR=off", // disable sidecar until #1345 is fixed - igvm_file, - OpenHclServicingFlags { - enable_nvme_keepalive: true, - ..Default::default() - }, - ) - .await -} - /// Boot the UEFI firmware, with a VTL2 range automatically configured by /// hvlite. #[openvmm_test_no_agent(openhcl_uefi_x64(none))] diff --git a/xsync/Cargo.toml b/xsync/Cargo.toml index b634266cbd..5f2ca75ffb 100644 --- a/xsync/Cargo.toml +++ b/xsync/Cargo.toml @@ -7,7 +7,7 @@ members = ["xsync"] resolver = "2" [workspace.package] -rust-version = "1.89" +rust-version = "1.90" edition = "2024" [workspace.dependencies]