diff --git a/.config/cache-config.env b/.config/cache-config.env index 774a4628b40..cbd68655cd1 100644 --- a/.config/cache-config.env +++ b/.config/cache-config.env @@ -1,3 +1,3 @@ -CACHE_VERSION=v1 +CACHE_VERSION=v1_56_dev BAZEL_REMOTE_CACHE="https://storage.googleapis.com/android-cuttlefish-cache" diff --git a/.github/actions/gigabyte-ampere-cuttlefish-installer-check-preseed-after-install-script/action.yaml b/.github/actions/gigabyte-ampere-cuttlefish-installer-check-preseed-after-install-script/action.yaml index 502cd5b9975..83c81bf2419 100644 --- a/.github/actions/gigabyte-ampere-cuttlefish-installer-check-preseed-after-install-script/action.yaml +++ b/.github/actions/gigabyte-ampere-cuttlefish-installer-check-preseed-after-install-script/action.yaml @@ -17,16 +17,16 @@ runs: apt-get update apt-get upgrade -y apt-get install -y sudo + apt-get install -y ca-certificates apt-get install -y initramfs-tools apt-get install -y grub-efi-arm64 apt-get install -y lsb-release apt-get install -y pciutils apt-get install -y apt-show-versions - apt-get install -y linux-image-arm64 linux-headers-arm64 - - name: Setup non-free repository + - name: Setup base and non-free repository shell: bash run: | - echo "deb http://deb.debian.org/debian $(lsb_release -c -s) contrib non-free non-free-firmware" | tee /etc/apt/sources.list.d/debian-nonfree-1.list + echo "deb http://deb.debian.org/debian $(lsb_release -c -s) main contrib non-free non-free-firmware" | tee /etc/apt/sources.list apt -o Apt::Get::Assume-Yes=true -o APT::Color=0 -o DPkgPM::Progress-Fancy=0 update - name: Setup backports repository shell: bash @@ -35,6 +35,12 @@ runs: echo "deb http://deb.debian.org/debian $(lsb_release -c -s)-backports main contrib non-free non-free-firmware" | tee /etc/apt/sources.list.d/debian-backports-1.list apt -o Apt::Get::Assume-Yes=true -o APT::Color=0 -o DPkgPM::Progress-Fancy=0 update fi + - name: Setup pinned snapshot source + working-directory: ./gigabyte-ampere-cuttlefish-installer + shell: bash + run: | + mkdir -p /root/debian-snapshot + cp -f preseed/debian-snapshot.list /root/debian-snapshot/ - name: Check nVidia GPU emulation shell: bash run: echo "EMULATE_NVIDIA_GPU=${{ inputs.emulate-nvidia }}" >> $GITHUB_ENV diff --git a/.github/actions/run-cw-sharded-e2e-test/action.yaml b/.github/actions/run-cw-sharded-e2e-test/action.yaml index 4687e55504f..dbfd2252437 100644 --- a/.github/actions/run-cw-sharded-e2e-test/action.yaml +++ b/.github/actions/run-cw-sharded-e2e-test/action.yaml @@ -16,6 +16,8 @@ runs: with: name: android-cuttlefish-e2etest-image-tar github-token: ${{ github.token }} + - name: Upgrade crun + uses: ./.github/actions/upgrade-crun - name: Run tests shell: bash env: @@ -42,7 +44,7 @@ runs: sudo podman run --name tester -d --privileged --pids-limit=8192 -v /tmp/cw_bazel:/tmp/cw_bazel -v .:/src/workspace -w /src/workspace/e2etests android-cuttlefish-e2etest:latest # Compute tests for this runner - all_tests=$( sudo podman exec --user=testrunner -it tester bazel --output_user_root=/tmp/cw_bazel/output query --noshow_progress 'kind("go_test", orchestration/...) except attr(flaky, 1, //...) except attr(tags, "[\[ ]host-ready-special[,\]]", //...)' | grep -e "^\/\/" | sort ) + all_tests=$( sudo podman exec --user=testrunner tester bazel --output_user_root=/tmp/cw_bazel/output query --noshow_progress 'kind("go_test", orchestration/...) except attr(flaky, 1, //...) except attr(tags, "[\[ ]host-ready-special[,\]]", //...)' | grep -e "^\/\/" | sort ) all_tests_count=$(echo "${all_tests}" | wc --lines) echo "ALL TESTS: ${all_tests_count}" echo "${all_tests}" @@ -70,11 +72,10 @@ runs: # Run tests targets=( $(echo ${runner_tests}) ) for t in "${targets[@]}"; do - t="${t::-1}" echo "running test: ${t}" sudo podman rm -f tester sudo podman run --name tester -d --privileged --pids-limit=8192 -v /tmp/cw_bazel:/tmp/cw_bazel -v .:/src/workspace -w /src/workspace/e2etests android-cuttlefish-e2etest:latest - sudo podman exec --user=testrunner -it tester bazel --output_user_root=/tmp/cw_bazel/output test --sandbox_writable_path=/home/testrunner $t + sudo podman exec --user=testrunner tester bazel --output_user_root=/tmp/cw_bazel/output test --sandbox_writable_path=/home/testrunner $t done - name: Upload test logs if: always() diff --git a/.github/actions/upgrade-crun/action.yaml b/.github/actions/upgrade-crun/action.yaml new file mode 100644 index 00000000000..53dc2120d50 --- /dev/null +++ b/.github/actions/upgrade-crun/action.yaml @@ -0,0 +1,29 @@ +name: 'Upgrade crun' +description: 'Upgrade crun to latest version to avoid OCI compatibility issues' +runs: + using: "composite" + steps: + - name: Upgrade crun + shell: bash + run: | + TARGET_VERSION="1.28" + EXPECTED_HASH="2aa6b7024a9c9f153895c0d11ae233d3758f54844011c3a039e3e89048d01d42" + CRUN_PATH=$(which crun || echo "/usr/bin/crun") + echo "Current crun path: $CRUN_PATH" + if [ -f "$CRUN_PATH" ]; then + echo "Current crun version:" + $CRUN_PATH --version + fi + + TEMP_CRUN=$(mktemp) + echo "Downloading crun $TARGET_VERSION..." + curl -L -o "$TEMP_CRUN" https://github.com/containers/crun/releases/download/${TARGET_VERSION}/crun-${TARGET_VERSION}-linux-amd64 + + echo "Verifying hash..." + echo "$EXPECTED_HASH $TEMP_CRUN" | sha256sum --check + + echo "Installing crun..." + sudo mv "$TEMP_CRUN" "$CRUN_PATH" + sudo chmod +x "$CRUN_PATH" + echo "Upgraded crun version:" + $CRUN_PATH --version diff --git a/.github/workflows/gemini-dispatch.yml b/.github/workflows/gemini-dispatch.yml index 5d6cce49646..5f10a5d25c7 100644 --- a/.github/workflows/gemini-dispatch.yml +++ b/.github/workflows/gemini-dispatch.yml @@ -69,7 +69,7 @@ jobs: - name: 'Extract command' id: 'extract_command' - uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' # ratchet:actions/github-script@v7 + uses: 'actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd' # ratchet:actions/github-script@v8 env: EVENT_TYPE: '${{ github.event_name }}.${{ github.event.action }}' REQUEST: '${{ github.event.comment.body || github.event.review.body || github.event.issue.body }}' diff --git a/.github/workflows/gigabyte-ampere-cuttlefish-installer.yaml b/.github/workflows/gigabyte-ampere-cuttlefish-installer.yaml index 8e52f6694db..32ef8127919 100644 --- a/.github/workflows/gigabyte-ampere-cuttlefish-installer.yaml +++ b/.github/workflows/gigabyte-ampere-cuttlefish-installer.yaml @@ -56,7 +56,7 @@ jobs: nvidia_gpu: ["true", "false"] steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Check preseed after install script uses: ./.github/actions/gigabyte-ampere-cuttlefish-installer-check-preseed-after-install-script with: @@ -72,11 +72,11 @@ jobs: image: debian@sha256:13f29b6806e531c3ff3b565bb6eed73f2132506c8c9d41bb996065ca20fb27f2 # debian:trixie-20260223 (amd64) steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Build iso installer uses: ./.github/actions/build-gigabyte-ampere-cuttlefish-installer - name: Upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts path: gigabyte-ampere-cuttlefish-installer/preseed-mini.iso.xz @@ -93,9 +93,9 @@ jobs: TEST_DISK_SIZE: "10G" steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts - name: Prepare test environment @@ -137,9 +137,9 @@ jobs: working-directory: ./gigabyte-ampere-cuttlefish-installer steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts - name: Prepare test environment @@ -185,9 +185,9 @@ jobs: working-directory: ./gigabyte-ampere-cuttlefish-installer steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts - name: Prepare test environment @@ -233,9 +233,9 @@ jobs: working-directory: ./gigabyte-ampere-cuttlefish-installer steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts - name: Prepare test environment @@ -281,9 +281,9 @@ jobs: working-directory: ./gigabyte-ampere-cuttlefish-installer steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts - name: Prepare test environment @@ -301,6 +301,9 @@ jobs: run: | screen -d -m -L -Logfile console_001.log ./installer-iso-run-qemu.sh while ! egrep "[^[:space:]]+[[:space:]]login:" console_001.log; do sleep 30; done + # The login prompt appears before the system is usable, so wait for the + # boot to complete. "degraded" means booted with some unit failed. + until sshpass -p cuttlefish ssh -o "StrictHostKeyChecking no" -o "UserKnownHostsFile /dev/null" -o "ConnectTimeout 60" -p 33322 vsoc-01@localhost 'systemctl is-system-running | grep -qE "running|degraded"'; do sleep 30; done cp -f console_001.log console_001_p1.log CONSOLELINES=$(cat console_001_p1.log | wc -l) cat console_001_p1.log @@ -361,9 +364,9 @@ jobs: working-directory: ./gigabyte-ampere-cuttlefish-installer steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: gigabyte-ampere-cuttlefish-installer-artifacts - name: Prepare test environment @@ -381,6 +384,9 @@ jobs: run: | screen -d -m -L -Logfile console_001.log ./installer-iso-run-qemu.sh while ! egrep "[^[:space:]]+[[:space:]]login:" console_001.log; do sleep 30; done + # The login prompt appears before the system is usable, so wait for the + # boot to complete. "degraded" means booted with some unit failed. + until sshpass -p cuttlefish ssh -o "StrictHostKeyChecking no" -o "UserKnownHostsFile /dev/null" -o "ConnectTimeout 60" -p 33322 vsoc-01@localhost 'systemctl is-system-running | grep -qE "running|degraded"'; do sleep 30; done cp -f console_001.log console_001_p1.log CONSOLELINES=$(cat console_001_p1.log | wc -l) cat console_001_p1.log diff --git a/.github/workflows/presubmit.yaml b/.github/workflows/presubmit.yaml index 70b043abc5b..a1fc6b6c533 100644 --- a/.github/workflows/presubmit.yaml +++ b/.github/workflows/presubmit.yaml @@ -304,6 +304,8 @@ jobs: with: name: debs_amd64 github-token: ${{ github.token }} + - name: Upgrade crun + uses: ./.github/actions/upgrade-crun - name: Build image run: | tar -xvf debs_amd64.tar @@ -363,6 +365,8 @@ jobs: with: name: android-cuttlefish-e2etest-image-tar github-token: ${{ github.token }} + - name: Upgrade crun + uses: ./.github/actions/upgrade-crun - name: Run tests run: | sudo podman info @@ -373,7 +377,7 @@ jobs: # Flaky tests would be executed in clean container everytime, rather than using `cvd reset` # in a tainted container. sudo podman run --name tester -d --privileged --pids-limit=8192 -v /tmp/cw_bazel:/tmp/cw_bazel -v .:/src/workspace -w /src/workspace/e2etests android-cuttlefish-e2etest:latest - targets=$(sudo podman exec --user=testrunner -it tester sh -c "stty -onlcr && bazel --output_user_root=/tmp/cw_bazel/output query --noshow_progress 'attr(flaky, 1, orchestration/...)' 2>/dev/null" | sort) + targets=$(sudo podman exec --user=testrunner tester bazel --output_user_root=/tmp/cw_bazel/output query --noshow_progress 'attr(flaky, 1, orchestration/...)' | sort) echo "FLAKY TARGETS:" echo "${targets}" echo "" @@ -385,7 +389,7 @@ jobs: echo "Attempt: ${attempt} of ${ATTEMPTS}" sudo podman rm -f tester sudo podman run --name tester -d --privileged --pids-limit=8192 -v /tmp/cw_bazel:/tmp/cw_bazel -v .:/src/workspace -w /src/workspace/e2etests android-cuttlefish-e2etest:latest - sudo podman exec --user=testrunner -it tester bazel --output_user_root=/tmp/cw_bazel/output test --test_timeout 600 --sandbox_writable_path=/home/testrunner --flaky_test_attempts=1 ${t} && break || true + sudo podman exec --user=testrunner tester bazel --output_user_root=/tmp/cw_bazel/output test --test_timeout 600 --sandbox_writable_path=/home/testrunner --flaky_test_attempts=1 ${t} && break || true attempt=$((attempt+1)) done if [[ ${attempt} -gt ${ATTEMPTS} ]]; then @@ -397,14 +401,14 @@ jobs: # Run verify_access_token_test sudo podman run --name tester -d --privileged --pids-limit=8192 -v /tmp/cw_bazel:/tmp/cw_bazel -v .:/src/workspace -w /src/workspace/e2etests android-cuttlefish-e2etest:latest sleep 30s # Add delay before restarting cuttlefish-host_orchestrator service. - sudo podman exec -it tester sh -c 'echo "orchestrator_android_build_url=http://localhost:8090" >> /etc/default/cuttlefish-host_orchestrator && service cuttlefish-host_orchestrator restart' - sudo podman exec --user=testrunner -it tester bazel --output_user_root=/tmp/cw_bazel/output test //orchestration/verify_access_token_test:verify_access_token_test_test + sudo podman exec tester sh -c 'echo "orchestrator_android_build_url=http://localhost:8090" >> /etc/default/cuttlefish-host_orchestrator && service cuttlefish-host_orchestrator restart' + sudo podman exec --user=testrunner tester bazel --output_user_root=/tmp/cw_bazel/output test //orchestration/verify_access_token_test:verify_access_token_test_test sudo podman rm -f tester # Run create_with_gce_metadata_credentials_test sudo podman run --name tester -d --privileged --pids-limit=8192 -v /tmp/cw_bazel:/tmp/cw_bazel -v .:/src/workspace -w /src/workspace/e2etests android-cuttlefish-e2etest:latest sleep 30s # Add delay before restarting cuttlefish-host_orchestrator service. - sudo podman exec -it tester sh -c 'echo "build_api_credentials_use_gce_metadata=true" >> /etc/default/cuttlefish-host_orchestrator && service cuttlefish-host_orchestrator restart' - sudo podman exec --user=testrunner -it tester bazel --output_user_root=/tmp/cw_bazel/output test //orchestration/create_with_gce_metadata_credentials_test:create_with_gce_metadata_credentials_test_test + sudo podman exec tester sh -c 'echo "build_api_credentials_use_gce_metadata=true" >> /etc/default/cuttlefish-host_orchestrator && service cuttlefish-host_orchestrator restart' + sudo podman exec --user=testrunner tester bazel --output_user_root=/tmp/cw_bazel/output test //orchestration/create_with_gce_metadata_credentials_test:create_with_gce_metadata_credentials_test_test sudo podman rm -f tester - name: Upload test logs if: always() @@ -509,6 +513,8 @@ jobs: uses: actions/download-artifact@v7 with: name: cuttlefish-orchestration-amd64 + - name: Upgrade crun + uses: ./.github/actions/upgrade-crun - name: Load docker image as podman image run: | sudo sysctl -w kernel.unprivileged_userns_clone=1 diff --git a/base/cvd/build_external/hedron_compile_commands/hedron_compile_commands.MODULE.bazel b/base/cvd/build_external/hedron_compile_commands/hedron_compile_commands.MODULE.bazel index 9bbd5435a3a..ca897581c4b 100644 --- a/base/cvd/build_external/hedron_compile_commands/hedron_compile_commands.MODULE.bazel +++ b/base/cvd/build_external/hedron_compile_commands/hedron_compile_commands.MODULE.bazel @@ -2,9 +2,8 @@ # https://github.com/hedronvision/bazel-compile-commands-extractor git_override( module_name = "hedron_compile_commands", - # While waiting for - # https://github.com/hedronvision/bazel-compile-commands-extractor/pull/219 - # to be merged. - commit = "f5fbd4cee671d8d908f37c83abaf70fba5928fc7", - remote = "https://github.com/mikael-s-persson/bazel-compile-commands-extractor", + # Upstream is unmaintained, see + # https://github.com/helly25/bazel-compile-commands-extractor/blob/1f9360db1834d115c48279d38a97f30d64d3fd4a/FORK.md + commit = "1f9360db1834d115c48279d38a97f30d64d3fd4a", + remote = "https://github.com/helly25/bazel-compile-commands-extractor.git", ) diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.cpp b/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.cpp index 826ceb7b0e9..d6bdc7681f0 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.cpp +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.cpp @@ -142,6 +142,8 @@ DEFINE_vec(netsim_uwb, fmt::format("{}", CF_DEFAULTS_NETSIM_UWB), "[Experimental] Connect Uwb radio to netsim."); DEFINE_vec(netsim_nfc, fmt::format("{}", CF_DEFAULTS_NETSIM_NFC), "[Experimental] Connect Nfc radio to netsim."); +DEFINE_vec(netsim_modem, fmt::format("{}", CF_DEFAULTS_NETSIM_MODEM), + "[Experimental] Connect Modem radio to netsim."); DEFINE_string(netsim_args, CF_DEFAULTS_NETSIM_ARGS, "Space-separated list of netsim args."); @@ -337,6 +339,11 @@ DEFINE_vec(protected_vm, fmt::format("{}", CF_DEFAULTS_PROTECTED_VM), DEFINE_vec(mte, fmt::format("{}", CF_DEFAULTS_MTE), "Enable MTE"); +DEFINE_vec(enable_pkvm, fmt::format("{}", CF_DEFAULTS_ENABLE_PKVM), + "Provision the guest to run pKVM so it can host its own protected " + "VMs; requires a nested-virt capable host, --vm_manager=crosvm " + "and an arm64 guest."); + DEFINE_vec(enable_audio, fmt::format("{}", CF_DEFAULTS_ENABLE_AUDIO), "Whether to play or capture audio"); diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.h b/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.h index bb4c32857c8..53419829155 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.h +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.h @@ -69,6 +69,7 @@ DECLARE_vec(netsim); DECLARE_vec(netsim_bt); DECLARE_vec(netsim_uwb); DECLARE_vec(netsim_nfc); +DECLARE_vec(netsim_modem); DECLARE_string(netsim_args); DECLARE_bool(enable_automotive_proxy); @@ -159,6 +160,8 @@ DECLARE_vec(protected_vm); DECLARE_vec(mte); +DECLARE_vec(enable_pkvm); + DECLARE_vec(enable_audio); DECLARE_vec(enable_jcard_simulator); diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/bootconfig_args.cpp b/base/cvd/cuttlefish/host/commands/assemble_cvd/bootconfig_args.cpp index b23de528129..04151bb83d9 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/bootconfig_args.cpp +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/bootconfig_args.cpp @@ -231,7 +231,11 @@ Result> BootconfigArgsFromConfig( std::to_string(instance.vsock_guest_cid()); } - if (instance.enable_modem_simulator() && + // Export modem simulator ports for both standalone modem_simulator and + // netsim. The guest RIL (ag/40529282) polls ro.boot.modem_simulator_ports on + // startup to connect over VSOCK regardless of which daemon handles the host + // connection. + if ((instance.enable_modem_simulator() || instance.enable_modem_netsim()) && !instance.modem_simulator_ports().empty()) { bootconfig_args["androidboot.modem_simulator_ports"] = instance.modem_simulator_ports(); @@ -259,15 +263,23 @@ Result> BootconfigArgsFromConfig( } // TODO(b/217564326): improve this checks for a hypervisor in the VM. - if (instance.target_arch() == Arch::X86 || - instance.target_arch() == Arch::X86_64) { - bootconfig_args["androidboot.hypervisor.version"] = - "cf-" + ToString(config.vm_manager()); - bootconfig_args["androidboot.hypervisor.vm.supported"] = "1"; - } else { - bootconfig_args["androidboot.hypervisor.vm.supported"] = "0"; + switch (instance.target_arch()) { + case Arch::Arm64: + // The guest bootloader reports the hypervisor properties. + break; + case Arch::X86: + case Arch::X86_64: + bootconfig_args["androidboot.hypervisor.version"] = + "cf-" + ToString(config.vm_manager()); + bootconfig_args["androidboot.hypervisor.vm.supported"] = "1"; + bootconfig_args["androidboot.hypervisor.protected_vm.supported"] = "0"; + break; + case Arch::Arm: + case Arch::RiscV64: + bootconfig_args["androidboot.hypervisor.vm.supported"] = "0"; + bootconfig_args["androidboot.hypervisor.protected_vm.supported"] = "0"; + break; } - bootconfig_args["androidboot.hypervisor.protected_vm.supported"] = "0"; if (!instance.kernel_path().empty()) { bootconfig_args["androidboot.kernel_hotswapped"] = "1"; } @@ -323,6 +335,26 @@ Result> BootconfigArgsFromConfig( ? "com.android.hardware.graphics.composer.drm_hwcomposer" : "com.android.hardware.graphics.composer.ranchu"; + const GpuMode gpu_mode = instance.gpu_mode(); + if (gpu_mode != GpuMode::None) { + if (IsGfxstreamMode(gpu_mode)) { + if (instance.has_vulkan_gfxstream_apex()) { + bootconfig_args["androidboot.vendor.apex.com.google.cf.vulkan"] = + "com.google.cf.vulkan.gfxstream"; + } + } else if (gpu_mode == GpuMode::GuestLavapipe) { + if (instance.has_vulkan_lavapipe_apex()) { + bootconfig_args["androidboot.vendor.apex.com.google.cf.vulkan"] = + "com.google.cf.vulkan.lavapipe"; + } + } else if (gpu_mode == GpuMode::GuestSwiftshader) { + if (instance.has_vulkan_swiftshader_apex()) { + bootconfig_args["androidboot.vendor.apex.com.google.cf.vulkan"] = + "com.google.cf.vulkan.swiftshader"; + } + } + } + if (instance.vhal_proxy_server_port()) { bootconfig_args["androidboot.vhal_proxy_server_port"] = std::to_string(instance.vhal_proxy_server_port()); diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/android_composite_disk_config.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/android_composite_disk_config.cc index 2c31ea21708..f5743b2e9d3 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/android_composite_disk_config.cc +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/android_composite_disk_config.cc @@ -50,6 +50,7 @@ constexpr struct { std::string_view init_boot = "init_boot"; std::string_view metadata = "metadata"; std::string_view misc = "misc"; + std::string_view pvmfw = "pvmfw"; std::string_view super = "super"; std::string_view userdata = "userdata"; std::string_view vbmeta = "vbmeta"; @@ -83,6 +84,7 @@ Result> AndroidCompositeDiskConfig( const std::set ab_partitions = { kPartitions.boot, kPartitions.init_boot, + kPartitions.pvmfw, kPartitions.vbmeta, kPartitions.vbmeta_system, kPartitions.vbmeta_system_dlkm, diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp b/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp index fe50c8bb3b5..3400681a391 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp @@ -88,7 +88,7 @@ Result> BootConfigPartition::CreateIfNeeded( VLOG(0) << "bootconfig size is " << bootconfig.size(); ssize_t bytesWritten = WriteAll(bootconfig_fd, bootconfig); - CF_EXPECT(WriteAll(bootconfig_fd, bootconfig) == bootconfig.size(), + CF_EXPECT(bytesWritten == bootconfig.size(), "Failed to write bootconfig to \"" << bootconfig_path << "\""); VLOG(0) << "Bootconfig parameters from vendor boot image and config are " << ReadFile(bootconfig_path); diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/flags.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/flags.cc index 50bf154a74c..965da7da722 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/flags.cc +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/flags.cc @@ -410,7 +410,7 @@ Result InitializeCuttlefishConfiguration( "_openwrt"); static constexpr char kDefaultSecure[] = - "oemlock,guest_keymint_insecure,guest_gatekeeper_insecure,weaver"; + "oemlock,guest_keymint_insecure,guest_gatekeeper_insecure"; SetCommandLineOptionWithMode("secure_hals", kDefaultSecure, google::FlagSettingMode::SET_FLAGS_DEFAULT); auto secure_hals = CF_EXPECT(ParseSecureHals(FLAGS_secure_hals)); @@ -456,9 +456,6 @@ Result InitializeCuttlefishConfiguration( tmp_config_obj.set_ap_rootfs_image(ap_rootfs_image); tmp_config_obj.set_ap_kernel_image(FLAGS_ap_kernel_image); - tmp_config_obj.set_enable_host_nfc(FLAGS_enable_host_nfc); - tmp_config_obj.set_enable_host_nfc_connector(FLAGS_enable_host_nfc); - // get flag default values and store into map auto name_to_default_value = CurrentFlagsToDefaultValue(); // old flags but vectorized for multi-device instances @@ -467,33 +464,15 @@ Result InitializeCuttlefishConfiguration( // netsim flags allow all radios or selecting a specific radio std::vector netsim_all_radios_vec = CF_EXPECT(GET_FLAG_BOOL_VALUE(netsim)); - bool any_netsim_all_radios = - std::any_of(netsim_all_radios_vec.begin(), netsim_all_radios_vec.end(), - [](bool e) { return e; }); std::vector netsim_bt_vec = CF_EXPECT(GET_FLAG_BOOL_VALUE(netsim_bt)); - bool any_netsim_bt = std::any_of(netsim_bt_vec.begin(), netsim_bt_vec.end(), - [](bool e) { return e; }); std::vector netsim_uwb_vec = CF_EXPECT(GET_FLAG_BOOL_VALUE(netsim_uwb)); bool any_netsim_uwb = std::any_of( netsim_uwb_vec.begin(), netsim_uwb_vec.end(), [](bool e) { return e; }); std::vector netsim_nfc_vec = CF_EXPECT(GET_FLAG_BOOL_VALUE(netsim_nfc)); bool any_netsim_nfc = std::any_of( netsim_nfc_vec.begin(), netsim_nfc_vec.end(), [](bool e) { return e; }); - bool netsim_has_bt = any_netsim_all_radios || any_netsim_bt; - bool netsim_has_uwb = any_netsim_all_radios || any_netsim_uwb; - bool netsim_has_nfc = any_netsim_all_radios || any_netsim_nfc; - - // These flags inform NetsimServer::ResultSetup which radios it owns. - if (netsim_has_bt) { - tmp_config_obj.netsim_radio_enable( - CuttlefishConfig::NetsimRadio::Bluetooth); - } - if (netsim_has_uwb) { - tmp_config_obj.netsim_radio_enable(CuttlefishConfig::NetsimRadio::Uwb); - } - if (netsim_has_nfc) { - tmp_config_obj.netsim_radio_enable(CuttlefishConfig::NetsimRadio::Nfc); - } + std::vector netsim_modem_vec = + CF_EXPECT(GET_FLAG_BOOL_VALUE(netsim_modem)); bool any_not_netsim_bt = false; bool any_not_netsim_uwb = false; @@ -594,6 +573,8 @@ Result InitializeCuttlefishConfiguration( std::vector vhost_net_vec = CF_EXPECT(GET_FLAG_BOOL_VALUE(vhost_net)); std::vector vhost_user_vsock_vec = CF_EXPECT(GET_FLAG_STR_VALUE(vhost_user_vsock)); + std::vector enable_pkvm_vec = + CF_EXPECT(GET_FLAG_BOOL_VALUE(enable_pkvm)); std::vector ril_dns_vec = CF_EXPECT(GET_FLAG_STR_VALUE(ril_dns)); std::vector enable_jcard_simulator_vec = CF_EXPECT(GET_FLAG_BOOL_VALUE(enable_jcard_simulator)); @@ -921,6 +902,14 @@ Result InitializeCuttlefishConfiguration( vhost_user_vsock_vec[instance_index]); } + if (enable_pkvm_vec[instance_index]) { + CF_EXPECT_EQ(tmp_config_obj.vm_manager(), VmmMode::kCrosvm, + "Only crosvm supports --enable_pkvm"); + CF_EXPECT_EQ(guest_configs[instance_index].target_arch, Arch::Arm64, + "--enable_pkvm requires an arm64 guest"); + } + instance.set_enable_pkvm(enable_pkvm_vec[instance_index]); + if (use_random_serial_vec[instance_index]) { instance.set_serial_number( RandomSerialNumber("CFCVD" + std::to_string(num))); @@ -1027,9 +1016,12 @@ Result InitializeCuttlefishConfiguration( guest_enforce_security_values.ForIndex(instance_index)); instance.set_pause_in_bootloader(pause_in_bootloader_vec[instance_index]); instance.set_run_as_daemon(daemon_values.ForIndex(instance_index)); + bool is_modem_netsim = netsim_all_radios_vec[instance_index] || + netsim_modem_vec[instance_index]; instance.set_enable_modem_simulator( enable_modem_simulator_vec[instance_index] && - !enable_minimal_mode_vec[instance_index]); + !enable_minimal_mode_vec[instance_index] && !is_modem_netsim); + instance.set_enable_modem_netsim(is_modem_netsim); instance.set_modem_simulator_instance_number( modem_simulator_count_vec[instance_index]); instance.set_modem_simulator_sim_type( @@ -1085,7 +1077,9 @@ Result InitializeCuttlefishConfiguration( instance.set_enable_host_uwb_connector(FLAGS_enable_host_uwb && !is_uwb_netsim); - bool is_any_netsim = is_netsim_all || is_bt_netsim || is_uwb_netsim; + bool is_nfc_netsim = is_netsim_all || netsim_nfc_vec[instance_index]; + bool is_any_netsim = is_netsim_all || is_bt_netsim || is_uwb_netsim || + is_nfc_netsim || is_modem_netsim; instance.set_uuid(uuid_vec[instance_index]); diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/flags_defaults.h b/base/cvd/cuttlefish/host/commands/assemble_cvd/flags_defaults.h index 8b47a93d487..1bf2d62bef9 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/flags_defaults.h +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/flags_defaults.h @@ -100,6 +100,7 @@ #define CF_DEFAULTS_SECURE_HALS CF_DEFAULTS_DYNAMIC_STRING #define CF_DEFAULTS_PROTECTED_VM false #define CF_DEFAULTS_MTE false +#define CF_DEFAULTS_ENABLE_PKVM false // Kernel default parameters #define CF_DEFAULTS_ENABLE_KERNEL_LOG true @@ -175,6 +176,7 @@ #define CF_DEFAULTS_NETSIM_BT true #define CF_DEFAULTS_NETSIM_UWB true #define CF_DEFAULTS_NETSIM_NFC false +#define CF_DEFAULTS_NETSIM_MODEM false // Netsim default parameters #define CF_DEFAULTS_NETSIM_ARGS "" diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/graphics_flags.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/graphics_flags.cc index 2e44a1da86f..b4a8aac7d91 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/graphics_flags.cc +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/graphics_flags.cc @@ -117,13 +117,26 @@ GetGpuModeRequirementsMap() { const RequirementWithReason kGuestSupportsGfxstream{ .func = [](const CommonState& common) { - return common.guest_config.gfxstream_supported; + return common.guest_config.gfxstream_supported || + common.guest_config.has_vulkan_gfxstream_apex; }, .success_explanation = "The guest supports Gfxstream.", .failure_explanation = "The guest does not support Gfxstream. This is configured in the " "`android-info.txt` file associated with the guest target build.", }; + const RequirementWithReason kGuestSupportsLavapipe{ + .func = + [](const CommonState& common) { + return common.guest_config.guest_lavapipe_supported || + common.guest_config.has_vulkan_lavapipe_apex; + }, + .success_explanation = "The guest supports Lavapipe.", + .failure_explanation = + "The guest does not claim support for Lavapipe. This is " + "configured in the `android-info.txt` file associated with the guest " + "target build.", + }; const RequirementWithReason kHostGlesAvailable{ .func = [](const CommonState& common) { @@ -285,6 +298,12 @@ GetGpuModeRequirementsMap() { kNotUsingHostQemu, }, }, + { + GpuMode::GuestLavapipe, + { + kGuestSupportsLavapipe, + }, + }, { GpuMode::GuestSwiftshader, /*no requirements*/ {}, @@ -469,12 +488,18 @@ std::vector GetGpuModeCandidates(const GuestConfig& guest_config) { gpu_mode_candidates.push_back(GpuMode::GfxstreamGuestAngleHostSwiftshader); gpu_mode_candidates.push_back(GpuMode::GfxstreamGuestAngleHostLavapipe); gpu_mode_candidates.push_back(GpuMode::GuestSwiftshader); + if (guest_config.guest_lavapipe_supported) { + gpu_mode_candidates.push_back(GpuMode::GuestLavapipe); + } } else { gpu_mode_candidates.push_back(GpuMode::Gfxstream); gpu_mode_candidates.push_back(GpuMode::GuestSwiftshader); gpu_mode_candidates.push_back(GpuMode::GfxstreamGuestAngle); gpu_mode_candidates.push_back(GpuMode::GfxstreamGuestAngleHostSwiftshader); gpu_mode_candidates.push_back(GpuMode::GfxstreamGuestAngleHostLavapipe); + if (guest_config.guest_lavapipe_supported) { + gpu_mode_candidates.push_back(GpuMode::GuestLavapipe); + } } gpu_mode_candidates.push_back(GpuMode::None); @@ -578,7 +603,7 @@ Result SelectGpuVhostUserMode(const GpuMode gpu_mode, gpu_vhost_user_mode_arg == kGpuVhostUserModeOn || gpu_vhost_user_mode_arg == kGpuVhostUserModeOff); if (gpu_vhost_user_mode_arg == kGpuVhostUserModeAuto) { - if (gpu_mode == GpuMode::GuestSwiftshader) { + if (IsGuestRenderingMode(gpu_mode)) { VLOG(0) << "GPU vhost user auto mode: not needed for --gpu_mode=" << GpuModeString(gpu_mode) << ". Not enabling vhost user gpu."; return false; @@ -784,6 +809,12 @@ Result ConfigureGpuSettings( const std::string& guest_renderer_preload_arg, VmmMode vmm, const GuestConfig& guest_config, CuttlefishConfig::MutableInstanceSpecific& instance) { + instance.set_has_vulkan_gfxstream_apex( + guest_config.has_vulkan_gfxstream_apex); + instance.set_has_vulkan_lavapipe_apex(guest_config.has_vulkan_lavapipe_apex); + instance.set_has_vulkan_swiftshader_apex( + guest_config.has_vulkan_swiftshader_apex); + #ifdef __APPLE__ (void)graphics_availability; (void)gpu_vhost_user_mode_arg; diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/guest_config.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/guest_config.cc index 3a30217db86..803cb1157c5 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/guest_config.cc +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/guest_config.cc @@ -130,6 +130,19 @@ Result ParseGuestConfigTextProto(const std::string& guest_config_path, guest_config.gpu_mode_candidates.push_back(it->second); } + if (graphics_config.has_vulkan_gfxstream_apex_supported()) { + guest_config.has_vulkan_gfxstream_apex = + graphics_config.vulkan_gfxstream_apex_supported(); + } + if (graphics_config.has_vulkan_lavapipe_apex_supported()) { + guest_config.has_vulkan_lavapipe_apex = + graphics_config.vulkan_lavapipe_apex_supported(); + } + if (graphics_config.has_vulkan_swiftshader_apex_supported()) { + guest_config.has_vulkan_swiftshader_apex = + graphics_config.vulkan_swiftshader_apex_supported(); + } + const auto& input_config = proto_config.input(); if (input_config.has_mouse_supported()) { guest_config.mouse_supported = input_config.mouse_supported(); @@ -205,7 +218,6 @@ Result ParseGuestConfigTxt(const std::string& guest_config_path, guest_config.gfxstream_supported = MapHasValue(info, "gfxstream", "supported"); - guest_config.gfxstream_gl_program_binary_link_status_supported = MapHasValue(info, "gfxstream_gl_program_binary_link_status", "supported"); @@ -221,6 +233,13 @@ Result ParseGuestConfigTxt(const std::string& guest_config_path, } } + guest_config.has_vulkan_gfxstream_apex = + MapHasValue(info, "vulkan_gfxstream_apex", "supported"); + guest_config.has_vulkan_lavapipe_apex = + MapHasValue(info, "vulkan_lavapipe_apex", "supported"); + guest_config.has_vulkan_swiftshader_apex = + MapHasValue(info, "vulkan_swiftshader_apex", "supported"); + guest_config.mouse_supported = MapHasValue(info, "mouse", "supported"); guest_config.gamepad_supported = MapHasValue(info, "gamepad", "supported"); diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/guest_config.h b/base/cvd/cuttlefish/host/commands/assemble_cvd/guest_config.h index f039ffecccd..054900551c3 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/guest_config.h +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/guest_config.h @@ -40,6 +40,7 @@ struct GuestConfig { std::string android_version_number; bool gfxstream_supported = false; bool gfxstream_gl_program_binary_link_status_supported = false; + bool guest_lavapipe_supported = false; bool vhost_user_vsock = false; bool supports_bgra_framebuffers = false; bool prefer_drm_virgl_when_supported = false; @@ -54,6 +55,9 @@ struct GuestConfig { int blank_data_image_mb = 0; bool lights_server_enabled = true; // true for backwards compatibility std::vector gpu_mode_candidates; + bool has_vulkan_lavapipe_apex = false; + bool has_vulkan_gfxstream_apex = false; + bool has_vulkan_swiftshader_apex = false; }; PrettyStruct Pretty(const GuestConfig&, diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/proto/guest_config.proto b/base/cvd/cuttlefish/host/commands/assemble_cvd/proto/guest_config.proto index 2f778206090..8fd1e76360f 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/proto/guest_config.proto +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/proto/guest_config.proto @@ -48,6 +48,15 @@ message Graphics { // The list of gpu modes supported by the guest in order of preference. repeated GpuMode gpu_mode_candidates = 5; + + // Whether the guest has a separate apex for the Gfxstream Vulkan driver. + bool vulkan_gfxstream_apex_supported = 6; + + // Whether the guest has a separate apex for the Lavapipe Vulkan driver. + bool vulkan_lavapipe_apex_supported = 7; + + // Whether the guest has a separate apex for the Swiftshader Vulkan driver. + bool vulkan_swiftshader_apex_supported = 8; } message Audio { @@ -88,7 +97,7 @@ message Audio { bool mute_control_enabled = 1; Volume volume_control = 2; } - + message Stream { uint32 id = 1; ChannelLayout channel_layout = 2; diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/super_image_mixer.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/super_image_mixer.cc index d53a21ddd08..359a1134757 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/super_image_mixer.cc +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/super_image_mixer.cc @@ -48,14 +48,23 @@ namespace { constexpr char kMiscInfoPath[] = "META/misc_info.txt"; constexpr char kDynamicPartitionsPath[] = "META/dynamic_partitions_info.txt"; constexpr std::array kVendorTargetImages = { - "IMAGES/boot.img", "IMAGES/dtbo.img", - "IMAGES/init_boot.img", "IMAGES/odm.img", - "IMAGES/odm_dlkm.img", "IMAGES/recovery.img", - "IMAGES/system_dlkm.img", "IMAGES/userdata.img", - "IMAGES/vbmeta.img", "IMAGES/vbmeta_system_dlkm.img", - "IMAGES/vbmeta_vendor.img", "IMAGES/vbmeta_vendor_dlkm.img", - "IMAGES/vendor.img", "IMAGES/vendor_boot.img", - "IMAGES/vendor_dlkm.img", "IMAGES/vendor_kernel_boot.img", + "IMAGES/boot.img", + "IMAGES/dtbo.img", + "IMAGES/init_boot.img", + "IMAGES/odm.img", + "IMAGES/odm_dlkm.img", + "IMAGES/pvmfw.img", + "IMAGES/recovery.img", + "IMAGES/system_dlkm.img", + "IMAGES/userdata.img", + "IMAGES/vbmeta.img", + "IMAGES/vbmeta_system_dlkm.img", + "IMAGES/vbmeta_vendor.img", + "IMAGES/vbmeta_vendor_dlkm.img", + "IMAGES/vendor.img", + "IMAGES/vendor_boot.img", + "IMAGES/vendor_dlkm.img", + "IMAGES/vendor_kernel_boot.img", }; constexpr std::array kVendorTargetBuildProps = { "ODM/build.prop", diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/vendor_dlkm_utils.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/vendor_dlkm_utils.cc index 5706837a937..ccc5216029a 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/vendor_dlkm_utils.cc +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/vendor_dlkm_utils.cc @@ -131,6 +131,7 @@ std::vector GetRamdiskModules( "vmw_vsock_virtio_transport.ko", "vmw_vsock_virtio_transport_common.ko", "vsock.ko", + "pkvm_iommu_temp.ko", // TODO(b/176860479) once virt_wifi is deprecated fully, // these following modules can be loaded in second stage init "libarc4.ko", diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/commands/start.cpp b/base/cvd/cuttlefish/host/commands/cvd/cli/commands/start.cpp index ff1468b5f59..9b049eadb05 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/commands/start.cpp +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/commands/start.cpp @@ -326,27 +326,47 @@ Result> GetCvdInternalStartFlags( return flags; } -} // namespace - -CvdStartCommandHandler::CvdStartCommandHandler( - InstanceManager& instance_manager) - : instance_manager_(instance_manager) { - own_flags_.daemon = true; -} +bool CanBypassToSingleInstance(const LocalInstance& instance, + const LocalInstanceGroup& group, + const std::vector& subcmd_args) { + if (instance.State() != cvd::INSTANCE_STATE_STOPPED) { + return false; + } + if (group.StartTime() == TimeStamp{}) { + return false; + } -static bool HasUnsafeFlagsForBypass(const std::vector& args) { - std::vector args_copy = args; + std::vector args_copy = subcmd_args; bool daemon = true; - std::string report_anonymous = ""; std::vector safe_flags = { GflagsCompatFlag("daemon", daemon), - GflagsCompatFlag("report_anonymous_usage_stats", report_anonymous), }; - auto res = ConsumeFlags(safe_flags, args_copy); - if (!res.ok()) { - return true; + const Result res = ConsumeFlags(safe_flags, args_copy); + if (!res.ok() || !daemon || !args_copy.empty()) { + return false; + } + + const std::vector& instances = group.Instances(); + if (instances.empty()) { + return false; + } + const LocalInstance& main_instance = instances[0]; + if (instance.Id() == main_instance.Id()) { + return false; } - return !args_copy.empty(); + if (main_instance.State() != cvd::INSTANCE_STATE_RUNNING) { + return false; + } + + return true; +} + +} // namespace + +CvdStartCommandHandler::CvdStartCommandHandler( + InstanceManager& instance_manager) + : instance_manager_(instance_manager) { + own_flags_.daemon = true; } Result CvdStartCommandHandler::Handle(const CommandRequest& request) { @@ -364,13 +384,11 @@ Result CvdStartCommandHandler::Handle(const CommandRequest& request) { auto [instance, group] = CF_EXPECT(selector::SelectInstance(instance_manager_, request)); - if (instance.State() == cvd::INSTANCE_STATE_STOPPED && - group.StartTime() != TimeStamp{} && - !HasUnsafeFlagsForBypass(subcmd_args)) { + if (CanBypassToSingleInstance(instance, group, subcmd_args)) { CF_EXPECT(LaunchSingleInstance(instance, group, request)); return {}; } else { - VLOG(1) << "Instance is not in stopped state. Proceeding with " + VLOG(1) << "Cannot bypass to single instance start. Proceeding with " "normal group start."; } } diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/cf_configs_instances.cpp b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/cf_configs_instances.cpp index 3db64992ad5..2dc06fd594e 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/cf_configs_instances.cpp +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/cf_configs_instances.cpp @@ -34,7 +34,30 @@ namespace cuttlefish { +namespace { + using cvd::config::EnvironmentSpecification; +using cvd::config::Instance; + +std::string ConfigFlag(const Instance& instance) { + if (instance.has_config()) { + return instance.config(); + } + return ""; +} + +std::vector GenerateConfigFlags( + const EnvironmentSpecification& cfg) { + for (const auto& instance : cfg.instances()) { + if (instance.has_config() && !instance.config().empty()) { + return std::vector{ + GenerateInstanceFlag("config", cfg, ConfigFlag)}; + } + } + return {}; +} + +} // namespace Result> GenerateInstancesFlags( const EnvironmentSpecification& cfg) { @@ -46,6 +69,7 @@ Result> GenerateInstancesFlags( res = MergeResults(std::move(res), CF_EXPECT(GenerateVmFlags(cfg))); res = MergeResults(std::move(res), GenerateConnectivityFlags(cfg)); res = MergeResults(std::move(res), CF_EXPECT(GenerateMediaFlags(cfg))); + res = MergeResults(std::move(res), GenerateConfigFlags(cfg)); return res; } diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/doc/vm.dot b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/doc/vm.dot index 9b998fa9133..4212c5b055b 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/doc/vm.dot +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/doc/vm.dot @@ -16,6 +16,7 @@ graph { vm--config vm--memory_mb vm--custom_actions + vm--enable_pkvm vm--vm_manager crosvm_binary_dir [label = "binary_dir"] qemu_binary_dir [label = "binary_dir"] diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/doc/vm.png b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/doc/vm.png index 6fbb93215eb..d4f063f1850 100644 Binary files a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/doc/vm.png and b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/doc/vm.png differ diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/doc/vm.svg b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/doc/vm.svg index 7a54f7d5f58..0dcbd63a445 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/doc/vm.svg +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/doc/vm.svg @@ -1,474 +1,486 @@ - - - - - + + + +%3 + vm - -vm + +vm cpus - -cpus + +cpus vm--cpus - + vm_manager - -vm_manager + +vm_manager vm--vm_manager - + - + vm--vm_manager - + vsock_guest_cid - -vsock_guest_cid + +vsock_guest_cid vm--vsock_guest_cid - + enable_minimal_mode - -enable_minimal_mode + +enable_minimal_mode vm--enable_minimal_mode - + restart_subprocesses - -restart_subprocesses + +restart_subprocesses vm--restart_subprocesses - + setupwizard_mode - -setupwizard_mode + +setupwizard_mode vm--setupwizard_mode - + smt - -smt + +smt vm--smt - + use_allocd - -use_allocd + +use_allocd vm--use_allocd - + use_sdcard - -use_sdcard + +use_sdcard vm--use_sdcard - + uuid - -uuid + +uuid vm--uuid - + file_verbosity - -file_verbosity + +file_verbosity vm--file_verbosity - + verbosity - -verbosity + +verbosity vm--verbosity - + Run_file_discovery - -Run_file_discovery + +Run_file_discovery vm--Run_file_discovery - + config - -config + +config vm--config - + memory_mb - -memory_mb + +memory_mb vm--memory_mb - + custom_actions - -custom_actions + +custom_actions vm--custom_actions - + + + + +enable_pkvm + +enable_pkvm + + + +vm--enable_pkvm + - + security - -security + +security - + vm--security - + - + kernel - -kernel + +kernel - + vm--kernel - + - + crosvm - -crosvm + +crosvm - + vm_manager--crosvm - + - + qemu - -qemu + +qemu - + vm_manager--qemu - + - + gem5 - -gem5 + +gem5 - + vm_manager--gem5 - + - + crosvm_binary_dir - -binary_dir + +binary_dir - + qemu_binary_dir - -binary_dir + +binary_dir - + gem5_binary_dir - -binary_dir + +binary_dir - + crosvm--crosvm_binary_dir - + - + seccomp_policy_dir - -seccomp_policy_dir + +seccomp_policy_dir - + crosvm--seccomp_policy_dir - + - + enable_sandbox - -enable_sandbox + +enable_sandbox - + crosvm--enable_sandbox - + - + simple_media_device - -simple_media_device + +simple_media_device - + crosvm--simple_media_device - + - + v4l2_proxy - -v4l2_proxy + +v4l2_proxy - + crosvm--v4l2_proxy - + - + qemu--qemu_binary_dir - + - + gem5--gem5_binary_dir - + - + checkpoint_dir - -checkpoint_dir + +checkpoint_dir - + gem5--checkpoint_dir - + - + debug_file - -debug_file + +debug_file - + gem5--debug_file - + - + debug_flags - -debug_flags + +debug_flags - + gem5--debug_flags - + - + guest_enforce_security - -guest_enforce_security + +guest_enforce_security - + security--guest_enforce_security - + - + serial_number - -serial_number + +serial_number - + security--serial_number - + - + secure_hals - -secure_hals + +secure_hals - + security--secure_hals - + - + enable_kernel_log - -enable_kernel_log + +enable_kernel_log - + kernel--enable_kernel_log - + - + kgdb - -kgdb + +kgdb - + kernel--kgdb - + - + gdb_port - -gdb_port + +gdb_port - + kernel--gdb_port - + - + console - -console + +console - + kernel--console - + - + extra_kernel_cmdline - -extra_kernel_cmdline + +extra_kernel_cmdline - + kernel--extra_kernel_cmdline - + - + initramfs_path - -initramfs_path + +initramfs_path - + kernel--initramfs_path - + - + path - -path + +path - + kernel--path - + diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/flags_parser_test.cc b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/flags_parser_test.cc index b0ca0d1c7b8..7747564ce60 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/flags_parser_test.cc +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/flags_parser_test.cc @@ -374,7 +374,7 @@ TEST(FlagsParserTest, ParseMediaSplaneSingleInstance) { << "Invalid Json string"; auto serialized_data = LaunchCvdParserTester(json_configs); EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace(); - EXPECT_TRUE(FindConfig(*serialized_data, "--media=type=v4l2_emulated_camera_splane")) + EXPECT_TRUE(FindConfig(*serialized_data, "--media=v4l2_emulated_camera_splane")) << "media flag is missing or wrongly formatted"; } @@ -407,7 +407,7 @@ TEST(FlagsParserTest, ParseMediaSplaneTwoDevices) { auto serialized_data = LaunchCvdParserTester(json_configs); EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace(); EXPECT_EQ(std::count(serialized_data->begin(), serialized_data->end(), - "--media=type=v4l2_emulated_camera_splane"), + "--media=v4l2_emulated_camera_splane"), 2); } @@ -437,7 +437,7 @@ TEST(FlagsParserTest, ParseMediaMplane) { auto serialized_data = LaunchCvdParserTester(json_configs); EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace(); - EXPECT_TRUE(FindConfig(*serialized_data, "--media=type=v4l2_emulated_camera_mplane")) + EXPECT_TRUE(FindConfig(*serialized_data, "--media=v4l2_emulated_camera_mplane")) << "media flag is missing or wrongly formatted"; } @@ -469,7 +469,7 @@ TEST(FlagsParserTest, ParseMediaV4l2Proxy) { auto serialized_data = LaunchCvdParserTester(json_configs); EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace(); - EXPECT_TRUE(FindConfig(*serialized_data, "--media=type=v4l2_proxy")) + EXPECT_TRUE(FindConfig(*serialized_data, "--media=v4l2_proxy")) << "media flag is missing or wrongly formatted"; } @@ -600,4 +600,85 @@ TEST(ConnectivityFlagsParserTest, ParseModemSimulatorSimTypeInvalidInt) { << "modem_simulator_sim_type flag is missing or wrongly formatted"; } +TEST(ConfigFlagsParserTest, ParseSingleInstanceConfig) { + const char* test_string = R""""( +{ + "instances" : + [ + { + "config": "phone" + } + ] +} + )""""; + + Json::Value json_configs; + std::string json_text(test_string); + + EXPECT_TRUE(ParseJsonString(json_text, json_configs)) + << "Invalid Json string"; + const Result> serialized_data = + LaunchCvdParserTester(json_configs); + ASSERT_THAT(serialized_data, IsOk()); + EXPECT_TRUE(FindConfig(*serialized_data, "--config=phone")) + << "config flag is missing or wrongly formatted"; +} + +TEST(ConfigFlagsParserTest, ParseMultiInstanceConfig) { + const char* test_string = R""""( +{ + "instances" : + [ + { + "config": "phone" + }, + { + "config": "tv" + } + ] +} + )""""; + + Json::Value json_configs; + std::string json_text(test_string); + + EXPECT_TRUE(ParseJsonString(json_text, json_configs)) + << "Invalid Json string"; + const Result> serialized_data = + LaunchCvdParserTester(json_configs); + ASSERT_THAT(serialized_data, IsOk()); + EXPECT_TRUE(FindConfig(*serialized_data, "--config=phone,tv")) + << "config multi-instance flag is missing or wrongly formatted"; +} + +TEST(ConfigFlagsParserTest, ParseMultiInstanceConfigPartial) { + const char* test_string = R""""( +{ + "instances" : + [ + { + "config": "phone" + }, + { + "vm": { + "crosvm":{ + } + } + } + ] +} + )""""; + + Json::Value json_configs; + std::string json_text(test_string); + + EXPECT_TRUE(ParseJsonString(json_text, json_configs)) + << "Invalid Json string"; + const Result> serialized_data = + LaunchCvdParserTester(json_configs); + ASSERT_THAT(serialized_data, IsOk()); + EXPECT_TRUE(FindConfig(*serialized_data, "--config=phone,")) + << "config partial multi-instance flag is missing or wrongly formatted"; +} + } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/golang/load_config.pb.go b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/golang/load_config.pb.go index 66cc46d4f09..80041941764 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/golang/load_config.pb.go +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/golang/load_config.pb.go @@ -1,7 +1,22 @@ +// +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.10 -// protoc v6.33.0 +// protoc-gen-go v1.36.11 +// protoc v3.21.12 // source: cuttlefish/host/commands/cvd/cli/parser/load_config.proto package golang @@ -21,12 +36,18 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// Specifies the emulated user space page size. type UserPageSize int32 const ( + // Kernel page size will be used. UserPageSize_USER_PAGE_SIZE_UNSPECIFIED UserPageSize = 0 - UserPageSize_USER_PAGE_SIZE_16KB UserPageSize = 1 - UserPageSize_USER_PAGE_SIZE_64KB UserPageSize = 2 + // Request a 16KB page size. + // On x86_64, this implies emulation via kernel command line (e.g., + // page_shift=14). + UserPageSize_USER_PAGE_SIZE_16KB UserPageSize = 1 + // Request a 64KB page size. + UserPageSize_USER_PAGE_SIZE_64KB UserPageSize = 2 ) // Enum value maps for UserPageSize. @@ -120,14 +141,16 @@ func (ModemSimulatorSimType) EnumDescriptor() ([]byte, []int) { } type EnvironmentSpecification struct { - state protoimpl.MessageState `protogen:"open.v1"` - Instances []*Instance `protobuf:"bytes,1,rep,name=instances,proto3" json:"instances,omitempty"` - Fetch *Fetch `protobuf:"bytes,2,opt,name=fetch,proto3,oneof" json:"fetch,omitempty"` - Metrics *Metrics `protobuf:"bytes,3,opt,name=metrics,proto3,oneof" json:"metrics,omitempty"` - Common *Common `protobuf:"bytes,4,opt,name=common,proto3,oneof" json:"common,omitempty"` - NetsimBt *bool `protobuf:"varint,5,opt,name=netsim_bt,json=netsimBt,proto3,oneof" json:"netsim_bt,omitempty"` - NetsimUwb *bool `protobuf:"varint,6,opt,name=netsim_uwb,json=netsimUwb,proto3,oneof" json:"netsim_uwb,omitempty"` - NetsimArgs []string `protobuf:"bytes,7,rep,name=netsim_args,json=netsimArgs,proto3" json:"netsim_args,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Instances []*Instance `protobuf:"bytes,1,rep,name=instances,proto3" json:"instances,omitempty"` + Fetch *Fetch `protobuf:"bytes,2,opt,name=fetch,proto3,oneof" json:"fetch,omitempty"` + // deprecated v1 metrics enabling field, is not read/used + Metrics *Metrics `protobuf:"bytes,3,opt,name=metrics,proto3,oneof" json:"metrics,omitempty"` + Common *Common `protobuf:"bytes,4,opt,name=common,proto3,oneof" json:"common,omitempty"` + NetsimBt *bool `protobuf:"varint,5,opt,name=netsim_bt,json=netsimBt,proto3,oneof" json:"netsim_bt,omitempty"` + NetsimUwb *bool `protobuf:"varint,6,opt,name=netsim_uwb,json=netsimUwb,proto3,oneof" json:"netsim_uwb,omitempty"` + NetsimArgs []string `protobuf:"bytes,7,rep,name=netsim_args,json=netsimArgs,proto3" json:"netsim_args,omitempty"` + NetsimModem *bool `protobuf:"varint,8,opt,name=netsim_modem,json=netsimModem,proto3,oneof" json:"netsim_modem,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -211,14 +234,25 @@ func (x *EnvironmentSpecification) GetNetsimArgs() []string { return nil } +func (x *EnvironmentSpecification) GetNetsimModem() bool { + if x != nil && x.NetsimModem != nil { + return *x.NetsimModem + } + return false +} + +// TODO: chadreynolds - when we can make breaking changes and update, use this +// in EnvironmentSpecification instead of individual fields type EnvironmentOptions struct { - state protoimpl.MessageState `protogen:"open.v1"` - Fetch *Fetch `protobuf:"bytes,1,opt,name=fetch,proto3,oneof" json:"fetch,omitempty"` - Metrics *Metrics `protobuf:"bytes,2,opt,name=metrics,proto3,oneof" json:"metrics,omitempty"` - Common *Common `protobuf:"bytes,3,opt,name=common,proto3,oneof" json:"common,omitempty"` - NetsimBt *bool `protobuf:"varint,4,opt,name=netsim_bt,json=netsimBt,proto3,oneof" json:"netsim_bt,omitempty"` - NetsimUwb *bool `protobuf:"varint,5,opt,name=netsim_uwb,json=netsimUwb,proto3,oneof" json:"netsim_uwb,omitempty"` - NetsimArgs []string `protobuf:"bytes,6,rep,name=netsim_args,json=netsimArgs,proto3" json:"netsim_args,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Fetch *Fetch `protobuf:"bytes,1,opt,name=fetch,proto3,oneof" json:"fetch,omitempty"` + // deprecated v1 metrics enabling field, is not read/used + Metrics *Metrics `protobuf:"bytes,2,opt,name=metrics,proto3,oneof" json:"metrics,omitempty"` + Common *Common `protobuf:"bytes,3,opt,name=common,proto3,oneof" json:"common,omitempty"` + NetsimBt *bool `protobuf:"varint,4,opt,name=netsim_bt,json=netsimBt,proto3,oneof" json:"netsim_bt,omitempty"` + NetsimUwb *bool `protobuf:"varint,5,opt,name=netsim_uwb,json=netsimUwb,proto3,oneof" json:"netsim_uwb,omitempty"` + NetsimArgs []string `protobuf:"bytes,6,rep,name=netsim_args,json=netsimArgs,proto3" json:"netsim_args,omitempty"` + NetsimModem *bool `protobuf:"varint,7,opt,name=netsim_modem,json=netsimModem,proto3,oneof" json:"netsim_modem,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -295,6 +329,13 @@ func (x *EnvironmentOptions) GetNetsimArgs() []string { return nil } +func (x *EnvironmentOptions) GetNetsimModem() bool { + if x != nil && x.NetsimModem != nil { + return *x.NetsimModem + } + return false +} + type Common struct { state protoimpl.MessageState `protogen:"open.v1"` GroupName *string `protobuf:"bytes,1,opt,name=group_name,json=groupName,proto3,oneof" json:"group_name,omitempty"` @@ -432,17 +473,18 @@ func (x *Fetch) GetProjectId() string { } type Instance struct { - state protoimpl.MessageState `protogen:"open.v1"` - Name *string `protobuf:"bytes,1,opt,name=name,proto3,oneof" json:"name,omitempty"` - Vm *Vm `protobuf:"bytes,2,opt,name=vm,proto3,oneof" json:"vm,omitempty"` - Boot *Boot `protobuf:"bytes,3,opt,name=boot,proto3,oneof" json:"boot,omitempty"` - Security *Security `protobuf:"bytes,4,opt,name=security,proto3,oneof" json:"security,omitempty"` - Disk *Disk `protobuf:"bytes,5,opt,name=disk,proto3,oneof" json:"disk,omitempty"` - Graphics *Graphics `protobuf:"bytes,6,opt,name=graphics,proto3,oneof" json:"graphics,omitempty"` - Streaming *Streaming `protobuf:"bytes,7,opt,name=streaming,proto3,oneof" json:"streaming,omitempty"` - Connectivity *Connectivity `protobuf:"bytes,8,opt,name=connectivity,proto3,oneof" json:"connectivity,omitempty"` - ImportTemplate *string `protobuf:"bytes,9,opt,name=import_template,json=@import,proto3,oneof" json:"import_template,omitempty"` - Media *Media `protobuf:"bytes,10,opt,name=media,proto3,oneof" json:"media,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Name *string `protobuf:"bytes,1,opt,name=name,proto3,oneof" json:"name,omitempty"` + Vm *Vm `protobuf:"bytes,2,opt,name=vm,proto3,oneof" json:"vm,omitempty"` + Boot *Boot `protobuf:"bytes,3,opt,name=boot,proto3,oneof" json:"boot,omitempty"` + Security *Security `protobuf:"bytes,4,opt,name=security,proto3,oneof" json:"security,omitempty"` + Disk *Disk `protobuf:"bytes,5,opt,name=disk,proto3,oneof" json:"disk,omitempty"` + Graphics *Graphics `protobuf:"bytes,6,opt,name=graphics,proto3,oneof" json:"graphics,omitempty"` + Streaming *Streaming `protobuf:"bytes,7,opt,name=streaming,proto3,oneof" json:"streaming,omitempty"` + Connectivity *Connectivity `protobuf:"bytes,8,opt,name=connectivity,proto3,oneof" json:"connectivity,omitempty"` + // TODO: b/337089452 - handle outside of proto logic + ImportTemplate *string `protobuf:"bytes,9,opt,name=import_template,json=@import,proto3,oneof" json:"import_template,omitempty"` + Media *Media `protobuf:"bytes,10,opt,name=media,proto3,oneof" json:"media,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1362,8 +1404,9 @@ func (*V4L2EmulatedCameraMplane) Descriptor() ([]byte, []int) { } type V4L2Proxy struct { - state protoimpl.MessageState `protogen:"open.v1"` - DevicePath *string `protobuf:"bytes,1,opt,name=device_path,json=devicePath,proto3,oneof" json:"device_path,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + // Path to a V4L2 device to expose to the guest. E.g. `/dev/video0`. + DevicePath *string `protobuf:"bytes,1,opt,name=device_path,json=devicePath,proto3,oneof" json:"device_path,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1419,7 +1462,9 @@ type Vm struct { // *Vm_Qemu Vmm isVm_Vmm `protobuf_oneof:"vmm"` CustomActions []*CustomAction `protobuf:"bytes,9,rep,name=custom_actions,json=customActions,proto3" json:"custom_actions,omitempty"` - PageSize *UserPageSize `protobuf:"varint,10,opt,name=page_size,json=pageSize,proto3,enum=cuttlefish.cvd.config.UserPageSize,oneof" json:"page_size,omitempty"` + // Desired user space page size. + PageSize *UserPageSize `protobuf:"varint,10,opt,name=page_size,json=pageSize,proto3,enum=cuttlefish.cvd.config.UserPageSize,oneof" json:"page_size,omitempty"` + EnablePkvm *bool `protobuf:"varint,11,opt,name=enable_pkvm,json=enablePkvm,proto3,oneof" json:"enable_pkvm,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1537,6 +1582,13 @@ func (x *Vm) GetPageSize() UserPageSize { return UserPageSize_USER_PAGE_SIZE_UNSPECIFIED } +func (x *Vm) GetEnablePkvm() bool { + if x != nil && x.EnablePkvm != nil { + return *x.EnablePkvm + } + return false +} + type isVm_Vmm interface { isVm_Vmm() } @@ -1987,7 +2039,7 @@ var File_cuttlefish_host_commands_cvd_cli_parser_load_config_proto protoreflect. const file_cuttlefish_host_commands_cvd_cli_parser_load_config_proto_rawDesc = "" + "\n" + - "9cuttlefish/host/commands/cvd/cli/parser/load_config.proto\x12\x15cuttlefish.cvd.config\"\xb2\x03\n" + + "9cuttlefish/host/commands/cvd/cli/parser/load_config.proto\x12\x15cuttlefish.cvd.config\"\xeb\x03\n" + "\x18EnvironmentSpecification\x12=\n" + "\tinstances\x18\x01 \x03(\v2\x1f.cuttlefish.cvd.config.InstanceR\tinstances\x127\n" + "\x05fetch\x18\x02 \x01(\v2\x1c.cuttlefish.cvd.config.FetchH\x00R\x05fetch\x88\x01\x01\x12=\n" + @@ -1997,14 +2049,16 @@ const file_cuttlefish_host_commands_cvd_cli_parser_load_config_proto_rawDesc = " "\n" + "netsim_uwb\x18\x06 \x01(\bH\x04R\tnetsimUwb\x88\x01\x01\x12\x1f\n" + "\vnetsim_args\x18\a \x03(\tR\n" + - "netsimArgsB\b\n" + + "netsimArgs\x12&\n" + + "\fnetsim_modem\x18\b \x01(\bH\x05R\vnetsimModem\x88\x01\x01B\b\n" + "\x06_fetchB\n" + "\n" + "\b_metricsB\t\n" + "\a_commonB\f\n" + "\n" + "_netsim_btB\r\n" + - "\v_netsim_uwb\"\xed\x02\n" + + "\v_netsim_uwbB\x0f\n" + + "\r_netsim_modem\"\xa6\x03\n" + "\x12EnvironmentOptions\x127\n" + "\x05fetch\x18\x01 \x01(\v2\x1c.cuttlefish.cvd.config.FetchH\x00R\x05fetch\x88\x01\x01\x12=\n" + "\ametrics\x18\x02 \x01(\v2\x1e.cuttlefish.cvd.config.MetricsH\x01R\ametrics\x88\x01\x01\x12:\n" + @@ -2013,14 +2067,16 @@ const file_cuttlefish_host_commands_cvd_cli_parser_load_config_proto_rawDesc = " "\n" + "netsim_uwb\x18\x05 \x01(\bH\x04R\tnetsimUwb\x88\x01\x01\x12\x1f\n" + "\vnetsim_args\x18\x06 \x03(\tR\n" + - "netsimArgsB\b\n" + + "netsimArgs\x12&\n" + + "\fnetsim_modem\x18\a \x01(\bH\x05R\vnetsimModem\x88\x01\x01B\b\n" + "\x06_fetchB\n" + "\n" + "\b_metricsB\t\n" + "\a_commonB\f\n" + "\n" + "_netsim_btB\r\n" + - "\v_netsim_uwb\"t\n" + + "\v_netsim_uwbB\x0f\n" + + "\r_netsim_modem\"t\n" + "\x06Common\x12\"\n" + "\n" + "group_name\x18\x01 \x01(\tH\x00R\tgroupName\x88\x01\x01\x12&\n" + @@ -2152,7 +2208,7 @@ const file_cuttlefish_host_commands_cvd_cli_parser_load_config_proto_rawDesc = " "\tV4l2Proxy\x12$\n" + "\vdevice_path\x18\x01 \x01(\tH\x00R\n" + "devicePath\x88\x01\x01B\x0e\n" + - "\f_device_path\"\xb7\x04\n" + + "\f_device_path\"\xed\x04\n" + "\x02Vm\x12\x17\n" + "\x04cpus\x18\x01 \x01(\rH\x01R\x04cpus\x88\x01\x01\x12 \n" + "\tmemory_mb\x18\x02 \x01(\rH\x02R\bmemoryMb\x88\x01\x01\x12\"\n" + @@ -2165,7 +2221,9 @@ const file_cuttlefish_host_commands_cvd_cli_parser_load_config_proto_rawDesc = " "\x04qemu\x18\b \x01(\v2\x1b.cuttlefish.cvd.config.QemuH\x00R\x04qemu\x12J\n" + "\x0ecustom_actions\x18\t \x03(\v2#.cuttlefish.cvd.config.CustomActionR\rcustomActions\x12E\n" + "\tpage_size\x18\n" + - " \x01(\x0e2#.cuttlefish.cvd.config.UserPageSizeH\x06R\bpageSize\x88\x01\x01B\x05\n" + + " \x01(\x0e2#.cuttlefish.cvd.config.UserPageSizeH\x06R\bpageSize\x88\x01\x01\x12$\n" + + "\venable_pkvm\x18\v \x01(\bH\aR\n" + + "enablePkvm\x88\x01\x01B\x05\n" + "\x03vmmB\a\n" + "\x05_cpusB\f\n" + "\n" + @@ -2174,7 +2232,8 @@ const file_cuttlefish_host_commands_cvd_cli_parser_load_config_proto_rawDesc = " "\x11_setupwizard_modeB\a\n" + "\x05_uuidB\f\n" + "\n" + - "_page_size\"\x8b\x02\n" + + "_page_sizeB\x0e\n" + + "\f_enable_pkvm\"\x8b\x02\n" + "\x06Crosvm\x12*\n" + "\x0eenable_sandbox\x18\x01 \x01(\bH\x00R\renableSandbox\x88\x01\x01\x123\n" + "\x13simple_media_device\x18\x02 \x01(\bH\x01R\x11simpleMediaDevice\x88\x01\x01\x12\"\n" + diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_media_configs.cpp b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_media_configs.cpp index 94e6994d597..71b99a0fd20 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_media_configs.cpp +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_media_configs.cpp @@ -41,16 +41,16 @@ Result> GenerateMediaFlags( for (const auto& device : instance.media().devices()) { std::string flag = "--media="; if (device.has_v4l2_emulated_camera_splane()) { - flag += "type=v4l2_emulated_camera_splane"; + flag += "v4l2_emulated_camera_splane"; } else if (device.has_v4l2_emulated_camera_mplane()) { - flag += "type=v4l2_emulated_camera_mplane"; + flag += "v4l2_emulated_camera_mplane"; } else if (device.has_v4l2_proxy()) { // TODO(b/520114678): Use device.v4l2_proxy.device_path when // supported. - flag += "type=v4l2_proxy"; + flag += "v4l2_proxy"; } if (device.has_lens_facing()) { - flag += ",lens_facing=" + device.lens_facing(); + flag += ":lens_facing=" + device.lens_facing(); } flags.push_back(flag); } diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_vm_configs.cpp b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_vm_configs.cpp index d161f3d3ed2..fbd3cee10bf 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_vm_configs.cpp +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_vm_configs.cpp @@ -50,6 +50,7 @@ inline constexpr char kFlagCrosvmSimpleMediaDevice[] = "crosvm_simple_media_device"; inline constexpr char kFlagCrosvmV4l2Proxy[] = "crosvm_v4l2_proxy"; inline constexpr char kFlagVhostUserVsock[] = "vhost_user_vsock"; +inline constexpr char kFlagEnablePkvm[] = "enable_pkvm"; std::set GatherFlagNamesUsedInInstanceConfig(const Instance& ins) { std::set names; @@ -87,6 +88,9 @@ std::set GatherFlagNamesUsedInInstanceConfig(const Instance& ins) { ins.vm().crosvm().has_vhost_user_vsock()) { names.insert(kFlagVhostUserVsock); } + if (ins.vm().has_enable_pkvm()) { + names.insert(kFlagEnablePkvm); + } return names; } @@ -187,6 +191,11 @@ static std::string VhostUserVsock(const Instance& instance) { : default_val; } +static bool EnablePkvm(const Instance& instance) { + const auto& vm = instance.vm(); + return vm.has_enable_pkvm() ? vm.enable_pkvm() : CF_DEFAULTS_ENABLE_PKVM; +} + static std::vector UserPageSize( const EnvironmentSpecification& cfg) { std::vector ret; @@ -276,6 +285,9 @@ Result> GenerateVmFlags( flags.emplace_back( GenerateInstanceFlag(kFlagVhostUserVsock, cfg, VhostUserVsock)); } + if (used_names.contains(kFlagEnablePkvm)) { + flags.emplace_back(GenerateInstanceFlag(kFlagEnablePkvm, cfg, EnablePkvm)); + } flags = MergeResults(std::move(flags), CF_EXPECT(CustomConfigsFlags(cfg))); flags = MergeResults(std::move(flags), UserPageSize(cfg)); diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/launch_cvd_parser.cpp b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/launch_cvd_parser.cpp index ab5697933a0..9428c7a872a 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/launch_cvd_parser.cpp +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/launch_cvd_parser.cpp @@ -66,6 +66,10 @@ Result> GenerateCfFlags( flags.emplace_back(GenerateFlag("netsim_uwb", launch.netsim_uwb())); } + if (launch.has_netsim_modem()) { + flags.emplace_back(GenerateFlag("netsim_modem", launch.netsim_modem())); + } + if (!launch.netsim_args().empty()) { for (const auto& arg : launch.netsim_args()) { CF_EXPECTF(arg.find_first_of(" \t\n\v\f\r") == std::string::npos, diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/load_config.proto b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/load_config.proto index 25ac3c436fb..10a9308b3de 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/load_config.proto +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/load_config.proto @@ -52,6 +52,7 @@ message EnvironmentSpecification { optional bool netsim_bt = 5; optional bool netsim_uwb = 6; repeated string netsim_args = 7; + optional bool netsim_modem = 8; } // TODO: chadreynolds - when we can make breaking changes and update, use this @@ -63,6 +64,7 @@ message EnvironmentOptions { optional bool netsim_bt = 4; optional bool netsim_uwb = 5; repeated string netsim_args = 6; + optional bool netsim_modem = 7; } message Common { @@ -93,6 +95,7 @@ message Instance { // TODO: b/337089452 - handle outside of proto logic optional string import_template = 9 [json_name = "@import"]; optional Media media = 10; + optional string config = 11; } message Boot { @@ -190,6 +193,7 @@ message Vm { repeated CustomAction custom_actions = 9; // Desired user space page size. optional UserPageSize page_size = 10; + optional bool enable_pkvm = 11; } message Crosvm { diff --git a/base/cvd/cuttlefish/host/commands/cvd/fetch/BUILD.bazel b/base/cvd/cuttlefish/host/commands/cvd/fetch/BUILD.bazel index 5db93be1685..97e7db26f57 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/fetch/BUILD.bazel +++ b/base/cvd/cuttlefish/host/commands/cvd/fetch/BUILD.bazel @@ -40,11 +40,11 @@ cf_cc_library( "//cuttlefish/common/libs/utils:files", "//cuttlefish/common/libs/utils:json", "//cuttlefish/host/commands/cvd/fetch:build_api_flags", - "//cuttlefish/host/commands/cvd/fetch:credential_flags", "//cuttlefish/host/libs/web:credential_source", "//cuttlefish/host/libs/web/http_client", "//cuttlefish/result", "@abseil-cpp//absl/log", + "@jsoncpp", ], ) @@ -59,7 +59,6 @@ cf_cc_library( "//cuttlefish/host/libs/web:android_build_url", "//cuttlefish/host/libs/web/cas:cas_flags", "//cuttlefish/result", - "//libbase", "@abseil-cpp//absl/strings", ], ) @@ -127,9 +126,7 @@ cf_cc_library( "//cuttlefish/common/libs/utils:environment", "//cuttlefish/host/commands/cvd/fetch:build_api_credentials", "//cuttlefish/host/commands/cvd/fetch:build_api_flags", - "//cuttlefish/host/commands/cvd/fetch:fetch_cvd_parser", "//cuttlefish/host/libs/web:android_build_api", - "//cuttlefish/host/libs/web:android_build_api_key", "//cuttlefish/host/libs/web:android_build_url", "//cuttlefish/host/libs/web:build_api", "//cuttlefish/host/libs/web:caching_build_api", @@ -173,7 +170,6 @@ cf_cc_library( srcs = ["fetch_context.cc"], hdrs = ["fetch_context.h"], deps = [ - "//cuttlefish/common/libs/utils:archive", "//cuttlefish/common/libs/utils:files", "//cuttlefish/host/commands/cvd/fetch:builds", "//cuttlefish/host/commands/cvd/fetch:de_android_sparse", @@ -187,7 +183,6 @@ cf_cc_library( "//cuttlefish/host/libs/web:build_api_zip", "//cuttlefish/host/libs/zip:zip_file", "//cuttlefish/host/libs/zip/libzip_cc:archive", - "//cuttlefish/posix:strerror", "//cuttlefish/result", "//libbase", "@abseil-cpp//absl/strings", @@ -214,7 +209,6 @@ cf_cc_library( "//cuttlefish/host/commands/cvd/fetch:fetch_tracer", "//cuttlefish/host/commands/cvd/fetch:host_package", "//cuttlefish/host/commands/cvd/fetch:host_tools_target", - "//cuttlefish/host/commands/cvd/fetch:substitute", "//cuttlefish/host/commands/cvd/fetch:target_directories", "//cuttlefish/host/commands/cvd/utils:common", "//cuttlefish/host/libs/config:fetcher_config", @@ -226,7 +220,6 @@ cf_cc_library( "//cuttlefish/host/libs/web:chrome_os_build_string", "//cuttlefish/host/libs/web:luci_build_api", "//cuttlefish/host/libs/web/http_client:curl_global_init", - "//cuttlefish/host/libs/zip:zip_string", "//cuttlefish/host/libs/zip/libzip_cc:archive", "//cuttlefish/io", "//cuttlefish/io:string", diff --git a/base/cvd/cuttlefish/host/commands/cvd/fetch/build_api_credentials.cc b/base/cvd/cuttlefish/host/commands/cvd/fetch/build_api_credentials.cc index 48a174b2f54..053258b871a 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/fetch/build_api_credentials.cc +++ b/base/cvd/cuttlefish/host/commands/cvd/fetch/build_api_credentials.cc @@ -20,6 +20,8 @@ #include #include "absl/log/log.h" +#include "json/reader.h" +#include "json/value.h" #include "cuttlefish/common/libs/utils/environment.h" #include "cuttlefish/common/libs/utils/files.h" diff --git a/base/cvd/cuttlefish/host/commands/cvd/fetch/build_api_flags.cc b/base/cvd/cuttlefish/host/commands/cvd/fetch/build_api_flags.cc index 07efbaadc3d..990a823ef6f 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/fetch/build_api_flags.cc +++ b/base/cvd/cuttlefish/host/commands/cvd/fetch/build_api_flags.cc @@ -16,7 +16,6 @@ #include "cuttlefish/host/commands/cvd/fetch/build_api_flags.h" #include -#include #include #include diff --git a/base/cvd/cuttlefish/host/commands/cvd/fetch/downloaders.cc b/base/cvd/cuttlefish/host/commands/cvd/fetch/downloaders.cc index 2c421186284..be7bd1d77c7 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/fetch/downloaders.cc +++ b/base/cvd/cuttlefish/host/commands/cvd/fetch/downloaders.cc @@ -23,7 +23,6 @@ #include "cuttlefish/common/libs/utils/environment.h" #include "cuttlefish/host/commands/cvd/fetch/build_api_credentials.h" #include "cuttlefish/host/commands/cvd/fetch/build_api_flags.h" -#include "cuttlefish/host/commands/cvd/fetch/fetch_cvd_parser.h" #include "cuttlefish/host/libs/web/android_build_api.h" #include "cuttlefish/host/libs/web/android_build_url.h" #include "cuttlefish/host/libs/web/build_api.h" diff --git a/base/cvd/cuttlefish/host/commands/cvd/fetch/downloaders.h b/base/cvd/cuttlefish/host/commands/cvd/fetch/downloaders.h index 83df7bfad2c..2d34c98d68f 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/fetch/downloaders.h +++ b/base/cvd/cuttlefish/host/commands/cvd/fetch/downloaders.h @@ -18,7 +18,7 @@ #include #include -#include "cuttlefish/host/commands/cvd/fetch/fetch_cvd_parser.h" +#include "cuttlefish/host/commands/cvd/fetch/build_api_flags.h" #include "cuttlefish/host/libs/web/build_api.h" #include "cuttlefish/host/libs/web/luci_build_api.h" #include "cuttlefish/result/result.h" diff --git a/base/cvd/cuttlefish/host/commands/cvd/fetch/substitute.cc b/base/cvd/cuttlefish/host/commands/cvd/fetch/substitute.cc index 8ccb474b900..e2f80a1cddd 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/fetch/substitute.cc +++ b/base/cvd/cuttlefish/host/commands/cvd/fetch/substitute.cc @@ -17,8 +17,10 @@ #include +#include #include #include +#include #include #include #include @@ -29,6 +31,7 @@ #include #include "absl/log/log.h" #include "absl/strings/match.h" +#include "fmt/format.h" #include "cuttlefish/common/libs/utils/environment.h" #include "cuttlefish/common/libs/utils/files.h" @@ -64,10 +67,27 @@ Result Substitute(const std::string& target, CF_EXPECT(EnsureDirectoryExists(android::base::Dirname(full_link_name))); - int unlink_res = unlink(full_link_name.c_str()); - CF_EXPECTF(unlink_res == 0 || errno == ENOENT, "{}", StrError(errno)); + std::random_device rd; + std::string tmp_link_name; + constexpr int kMaxRetries = 10; + for (int attempt = 0; attempt < kMaxRetries; ++attempt) { + tmp_link_name = fmt::format("{}.tmp_{}_{}", full_link_name, getpid(), rd()); + auto symlink_res = Symlink(target, tmp_link_name); + if (symlink_res.has_value()) { + break; + } + if (errno == EEXIST && attempt + 1 < kMaxRetries) { + continue; + } + return symlink_res; + } + + if (rename(tmp_link_name.c_str(), full_link_name.c_str()) != 0) { + const int err = errno; + unlink(tmp_link_name.c_str()); + return CF_ERRF("Failed to rename symlink: {}", StrError(err)); + } - CF_EXPECT(Symlink(target, full_link_name)); return {}; } diff --git a/base/cvd/cuttlefish/host/commands/cvd/instances/status_fetcher.cpp b/base/cvd/cuttlefish/host/commands/cvd/instances/status_fetcher.cpp index 0751f6930a0..cdaff46797a 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/instances/status_fetcher.cpp +++ b/base/cvd/cuttlefish/host/commands/cvd/instances/status_fetcher.cpp @@ -115,11 +115,34 @@ Result FetchInstanceStatus(LocalInstance& instance, std::string stdout_str, stderr_str; RunWithManagedStdio(std::move(help_cmd), nullptr, &stdout_str, &stderr_str); - std::vector internal_flags = - CF_EXPECT(ParseGflagsXmlHelp(stdout_str)); - bool has_print = std::any_of( - internal_flags.begin(), internal_flags.end(), - [](const GflagDescription& desc) { return desc.name == "print"; }); + bool has_print = false; + Result> internal_flags = + ParseGflagsXmlHelp(stdout_str); + if (internal_flags.has_value()) { + has_print = std::any_of( + internal_flags->begin(), internal_flags->end(), + [](const GflagDescription& desc) { return desc.name == "print"; }); + } else { + LOG(INFO) << bin << " does not support --helpxml. Falling back to --help."; + ConstructCommandParam fallback_help_cmd_param{ + .bin_path = bin_path, + .home = home, + .args = {"--help"}, + .envs = envs, + .working_dir = working_dir, + .command_name = bin, + }; + if (Result fallback_cmd = + ConstructCommand(fallback_help_cmd_param); + fallback_cmd.has_value()) { + stdout_str.clear(); + stderr_str.clear(); + RunWithManagedStdio(std::move(*fallback_cmd), nullptr, &stdout_str, + &stderr_str); + has_print = stdout_str.find("--print") != std::string::npos || + stderr_str.find("--print") != std::string::npos; + } + } std::vector args{"--wait_for_launcher", std::to_string(timeout.count())}; diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/call_service.cpp b/base/cvd/cuttlefish/host/commands/modem_simulator/call_service.cpp index 554878d4ff0..248ca45b5c6 100644 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/call_service.cpp +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/call_service.cpp @@ -400,8 +400,8 @@ void CallService::HandleHangup(const Client& client, CommandParser cmd(command); cmd.SkipPrefix(); - std::string action(*cmd); - int n = std::stoi(action.substr(0, 1)); + const std::string action(*cmd); + const int n = action.empty() ? -1 : (action[0] - '0'); int index = -1; if (cmd->length() > 1) { index = std::stoi(action.substr(1)); diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/channel_monitor.cpp b/base/cvd/cuttlefish/host/commands/modem_simulator/channel_monitor.cpp index b8f64818680..5a68a543993 100644 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/channel_monitor.cpp +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/channel_monitor.cpp @@ -81,8 +81,8 @@ void ChannelMonitor::AcceptIncomingConnection() { } void ChannelMonitor::ReadCommand(Client& client) { - std::vector buffer(kMaxCommandLength); - auto bytes_read = client.client_read_fd_->Read(buffer.data(), buffer.size()); + char buffer[kMaxCommandLength]; + auto bytes_read = client.client_read_fd_->Read(buffer, kMaxCommandLength); if (bytes_read <= 0) { if (errno == EAGAIN && client.type == Client::REMOTE && client.first_read_command_) { @@ -105,13 +105,9 @@ void ChannelMonitor::ReadCommand(Client& client) { return; } - std::string& incomplete_command = client.incomplete_command; - // Add the incomplete command from the last read - auto commands = std::string{incomplete_command.data()}; - commands.append(buffer.data()); - - incomplete_command.clear(); + std::string commands(std::move(client.incomplete_command)); + commands.append(buffer, bytes_read); // Replacing '\n' with '\r' absl::StrReplaceAll({{"\n", "\r"}}, &commands); @@ -132,8 +128,8 @@ void ChannelMonitor::ReadCommand(Client& client) { } pos = r_pos + 1; // Skip '\r' } else if (pos < commands.length()) { // Incomplete command - incomplete_command = commands.substr(pos); - VLOG(1) << "incomplete command: " << incomplete_command; + client.incomplete_command = commands.substr(pos); + VLOG(1) << "incomplete command: " << client.incomplete_command; } } } diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0.xml b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0.xml index 655c37a967a..25ad8495b39 100755 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0.xml +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0.xml @@ -177,5 +177,9 @@ - + + + 3F979580BFFE8210428031A073BE211797 + 89049032000001000000000254806852 + diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml index b4363da9308..1b65eb621a4 100755 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml @@ -203,5 +203,9 @@ - + + + 3F979580BFFE8210428031A073BE211797 + 89049032000001000000000254806852 + diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim1.xml b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim1.xml new file mode 100755 index 00000000000..f1f77b54035 --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim1.xml @@ -0,0 +1,185 @@ + + + + 144,0,621A8205422100300483022F008A01058B032F0601800200C08801F0 + 144,0,61184F10A0000003431002FF86FF0389FFFFFFFF50044353494DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,61184F10A0000000871002FF86FF0389FFFFFFFF50045553494DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,62178202412183022FE28A01058B032F06038002000A880110 + 144,0,98683081462002318389 + + 89860318640220133898 + + + 144,0,62178202412183022F058A01058B032F060280020004880128 + 144,0,FFFFFFFF + + + + + + 144,0,62198205422100400283024F308A01058B036F0606800200808800 + 144,0,A81EC0034F3A01C1034F3306C5034F0902C4034F1104C6034F2503C9034F3107A905CA034F5008AA0FC2034F4A09C7034F4B0AC8034F4C0BFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621A8205422100140A83024F4C8A01058B036F060E800200C8880158 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621A82054221001C1483024F3A8A01058B036F060E80020230880108 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621A820542210001FA83024F338A01058B036F060E800200FA880130 + + + 144,0,621A820542210002FA83024F098A01058B036F060E800201F4880110 + + + 144,0,621A82054221000FFA83024F118A01058B036F060E80020EA6880120 + + + + + + + + 311740123456790 + + + 144,0,621982054221001C0283026F408A01058B036F0605800200388800 + 144,0,000000000000000000000000000007915155214365F7FFFFFFFFFFFF + + + 144,0,62198205422100050183026FC98A01058B036F0602800200058800 + 144,0,0100000000 + + + 144,0,621982054221001C0283026F408A01058B036F06058002003E8800 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07915155674523F1FFFFFFFFFFFF + + + 144,0,62178202412183026FAD8A01058B036F060180020004880118 + 144,0,00000003 + + + 144,0,62198205422100050183026FCA8A01058B036F060E800200058800 + 144,0,0000000000 + + + 106,130 + + + 144,0,621C8202412183026F7BA5038001718A01058B036F06038002001E880168 + 144,0,64F00064F02064F04064F07064F080FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621982054221001C0A83026F3B8A01058B036F0605800201188800 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + + + + + 4,6124 + 76,62228202412183025031A503C001408A01058B066F0601010001800200108102002288009000 + 36,A706300404024401A5063004040244029000 + + + + 6,019000 + 4,6b00 + + + + + + + + PINSTATE_UNKNOWN + 1234 + 12345678 + 3 + 10 + 1234 + 12345678 + 3 + 10 + + + + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + + + + + + + + + + + + + + + + + + + + + 3F979580BFFE8210428031A073BE211797 + 89049032000001000000000254806853 + + diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml new file mode 100755 index 00000000000..c3e6db772bc --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/etc/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml @@ -0,0 +1,211 @@ + + + + 144,0,621A8205422100300483022F008A01058B032F0601800200C08801F0 + 144,0,61184F10A0000003431002FF86FF0389FFFFFFFF50044353494DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,61184F10A0000000871002FF86FF0389FFFFFFFF50045553494DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,62178202412183022FE28A01058B032F06038002000A880110 + 144,0,98683081462002318389 + + 89860318640220133898 + + + 144,0,62178202412183022F058A01058B032F060280020004880128 + 144,0,FFFFFFFF + + + + + + 144,0,62198205422100400283024F308A01058B036F0606800200808800 + 144,0,A81EC0034F3A01C1034F3306C5034F0902C4034F1104C6034F2503C9034F3107A905CA034F5008AA0FC2034F4A09C7034F4B0AC8034F4C0BFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621A8205422100140A83024F4C8A01058B036F060E800200C8880158 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621A82054221001C1483024F3A8A01058B036F060E80020230880108 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621A820542210001FA83024F338A01058B036F060E800200FA880130 + + + 144,0,621A820542210002FA83024F098A01058B036F060E800201F4880110 + + + 144,0,621A82054221000FFA83024F118A01058B036F060E80020EA6880120 + + + + + + + + 311740123456790 + + + 144,0,621982054221001C0283026F408A01058B036F0605800200388800 + 144,0,00000000000000000000000000000891688118109844F0FFFFFFFFFF + + + 144,0,62258205422100040183026FC9A503C001408A01058B066F060103000080020004810200188800 + 144,0,01000000 + + + 144,0,62178202412183026FAD8A01058B036F060180020004880118 + 144,0,00000002 + + + 144,0,62258205422100260483026FC7A503C001408A01058B066F060103000080020098810200AC8800 + 144,0 + 144,0 + 144,0 + + + 144,0,62198205422100050183026FCA8A01058B036F060E800200058800 + 144,0,0000000000 + + + 106,130 + + + 144,0,621C8202412183026F7BA5038001718A01058B036F06038002001E880168 + 144,0,64F00064F02064F04064F07064F080FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + 144,0,621982054221001C0A83026F3B8A01058B036F0605800201188800 + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + + + + + + 76,62228202412183024300A503C001408A01058B066F0601010001800201DC810201EE88009000 + 516,30088200300404024310301AA0120410A000000476416E64726F696443545340300404024311301AA0120410A000000476416E64726F696443545341300404024312301AA0120410A000000476416E64726F696443545342300404024313301AA0120410A000000476416E64726F696443545343300404024314301AA0120410A000000476416E64726F696443545344300404024315301AA0120410A000000476416E64726F696443545345300404024316301AA0120410A000000476416E64726F6964435453463004040243173010A0080406FFFFFFFFFFFF300404024318301AA0120410A000000476416E64726F696443545347300404024313301AA0129000 + + + 76,62228202412183024318A503C001408A01058B066F0601010001800200188102002A88009000 + 124,3016041461ED377E85D386A8DFEE6B864BD85B0BFAA5AF8130220420CE7B2B47AE2B7552C8F92CC29124279883041FB623A5F194A82C9BF15D492AA09000 + + + + + 6,019000 + 110,62338202782183023F00A50C80016187010183040007DBF08A01058B062F0601020002C60C90016083010183010A83010D8102FFFF9000 + 6,019000 + 4,9000 + 4,6C35 + 4,6B00 + 4,9000 + 4,6D00 + 4,6B00 + 4,6A82 + 4,6A81 + 4,6E00 + 4,9000 + 24,983311111111111111029000 + 78,622382054221004A1283022F06A503C001408A01058B062F060101000080020534810205489000 + + + + + 6,019000 + 4,6b00 + + + + + + + + PINSTATE_UNKNOWN + 1234 + 12345678 + 3 + 10 + 1234 + 12345678 + 3 + 10 + + + + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + DISABLE + + + + + + + + + + + + + + + + + + + + + 3F979580BFFE8210428031A073BE211797 + 89049032000001000000000254806853 + + diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/main.cpp b/base/cvd/cuttlefish/host/commands/modem_simulator/main.cpp index 3bfc1ec2e99..040b5cce472 100644 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/main.cpp +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/main.cpp @@ -89,7 +89,9 @@ int ModemSimulatorMain(int argc, char** argv) { NvramConfig::InitNvramConfigService(server_fds.size(), FLAGS_sim_type); // Don't get a SIGPIPE from the clients - if (sigaction(SIGPIPE, nullptr, nullptr) != 0) { + struct sigaction sa{}; + sa.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &sa, nullptr) != 0) { LOG(ERROR) << "Failed to set SIGPIPE to be ignored: " << strerror(errno); } diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/modem_service.cpp b/base/cvd/cuttlefish/host/commands/modem_simulator/modem_service.cpp index 54151593be2..e5806c32371 100644 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/modem_service.cpp +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/modem_service.cpp @@ -34,14 +34,13 @@ CommandHandler::CommandHandler(const std::string& command, p_func handler) p_command_handler(handler) {} int CommandHandler::Compare(const std::string& command) const { - int result = -1; - if (match_mode == PARTIAL_MATCH) { - result = - command.compare(2, command_prefix.size(), command_prefix); // skip "AT" - } else { - result = command.compare(2, command.size(), command_prefix); + if (command.size() < 2) { // the "AT" prefix + return -1; } - return result; + + return (match_mode == PARTIAL_MATCH) + ? command.compare(2, command_prefix.size(), command_prefix) + : command.compare(2, command.size(), command_prefix); } void CommandHandler::HandleCommand(const Client& client, diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.cpp b/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.cpp index 0d5f7646fd4..46f4d802781 100644 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.cpp +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.cpp @@ -313,6 +313,12 @@ std::vector SimService::InitializeCommandHandlers() { CommandHandler( "+CICCID", [this](const Client& client) { this->HandleGetIccId(client); }), + CommandHandler( + "+CEID", + [this](const Client& client) { this->HandleGetEid(client); }), + CommandHandler( + "+CATR", + [this](const Client& client) { this->HandleGetAtr(client); }), CommandHandler("+CLCK=", [this](const Client& client, std::string& cmd) { this->HandleFacilityLock(client, cmd); @@ -1323,6 +1329,58 @@ void SimService::HandleGetIccId(const Client& client) { client.SendCommandResponse(responses); } +void SimService::HandleGetEid(const Client& client) { + std::vector responses; + + XMLElement* root = sim_file_system_.GetRootElement(); + if (!root) { + client.SendCommandResponse(kCmeErrorOperationNotAllowed); + return; + } + + XMLElement* card_profile = root->FirstChildElement("CardProfile"); + if (!card_profile) { + client.SendCommandResponse(kCmeErrorNotFound); + return; + } + + XMLElement* final = card_profile->FirstChildElement("EID"); + if (!final) { + client.SendCommandResponse(kCmeErrorNotFound); + return; + } + + responses.push_back("+CEID: " + std::string(final->GetText())); + responses.push_back("OK"); + client.SendCommandResponse(responses); +} + +void SimService::HandleGetAtr(const Client& client) { + std::vector responses; + + XMLElement* root = sim_file_system_.GetRootElement(); + if (!root) { + client.SendCommandResponse(kCmeErrorOperationNotAllowed); + return; + } + + XMLElement* card_profile = root->FirstChildElement("CardProfile"); + if (!card_profile) { + client.SendCommandResponse(kCmeErrorNotFound); + return; + } + + XMLElement* final = card_profile->FirstChildElement("ATR"); + if (!final) { + client.SendCommandResponse(kCmeErrorNotFound); + return; + } + + responses.push_back("+CATR: " + std::string(final->GetText())); + responses.push_back("OK"); + client.SendCommandResponse(responses); +} + /* * AT+CLCK * Execute command is used to lock, unlock or interrogate a MT or a network diff --git a/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.h b/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.h index 198aa697bfb..e3695f7791f 100644 --- a/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.h +++ b/base/cvd/cuttlefish/host/commands/modem_simulator/sim_service.h @@ -42,6 +42,8 @@ class SimService : public ModemService, public std::enable_shared_from_thisPost( makeSafeCallback( this, - [&sms_pdu](SmsService* me) { me->HandleReceiveSMS(sms_pdu); }), + [sms_pdu](SmsService* me) { me->HandleReceiveSMS(sms_pdu); }), std::chrono::seconds(1)); } else { // Send SMS to remote host port SendSmsToRemote(remote_host_port, sms_pdu); diff --git a/base/cvd/cuttlefish/host/commands/run_cvd/launch/BUILD.bazel b/base/cvd/cuttlefish/host/commands/run_cvd/launch/BUILD.bazel index dc908fc4290..d86c9eed9a3 100644 --- a/base/cvd/cuttlefish/host/commands/run_cvd/launch/BUILD.bazel +++ b/base/cvd/cuttlefish/host/commands/run_cvd/launch/BUILD.bazel @@ -281,11 +281,13 @@ cf_cc_library( "//cuttlefish/common/libs/fs", "//cuttlefish/common/libs/utils:files", "//cuttlefish/common/libs/utils:subprocess", + "//cuttlefish/host/commands/run_cvd/launch:grpc_socket_creator", "//cuttlefish/host/libs/config:config_utils", "//cuttlefish/host/libs/config:cuttlefish_config", "//cuttlefish/host/libs/config:known_paths", "//cuttlefish/host/libs/feature", "//cuttlefish/result", + "//libbase", "@fruit", ], ) diff --git a/base/cvd/cuttlefish/host/commands/run_cvd/launch/netsim_server.cpp b/base/cvd/cuttlefish/host/commands/run_cvd/launch/netsim_server.cpp index f684cf87238..43af27e9587 100644 --- a/base/cvd/cuttlefish/host/commands/run_cvd/launch/netsim_server.cpp +++ b/base/cvd/cuttlefish/host/commands/run_cvd/launch/netsim_server.cpp @@ -15,6 +15,11 @@ #include "cuttlefish/host/commands/run_cvd/launch/netsim_server.h" +#include +#include + +#include +#include #include #include #include @@ -27,6 +32,7 @@ #include "cuttlefish/common/libs/fs/shared_fd.h" #include "cuttlefish/common/libs/utils/files.h" #include "cuttlefish/common/libs/utils/subprocess.h" +#include "cuttlefish/host/commands/run_cvd/launch/grpc_socket_creator.h" #include "cuttlefish/host/libs/config/config_utils.h" #include "cuttlefish/host/libs/config/cuttlefish_config.h" #include "cuttlefish/host/libs/config/known_paths.h" @@ -56,13 +62,24 @@ class Chip { public: SharedFD fd_in; SharedFD fd_out; + SharedFD vsock_fd; + int sim_type = 1; Chip(std::string kind) : kind_(kind) {} // Append the chip information as Json to the command. void Append(Command& c) const { - c.AppendToLastParameter(R"({"kind":")", kind_, R"(","fdIn":)", fd_in, - R"(,"fdOut":)", fd_out, "}"); + if (vsock_fd->IsOpen()) { + c.AppendToLastParameter(R"({"kind":")", kind_, R"(","vsockFd":)", + vsock_fd); + } else { + c.AppendToLastParameter(R"({"kind":")", kind_, R"(","fdIn":)", fd_in, + R"(,"fdOut":)", fd_out); + } + if (kind_ == "CELLULAR") { + c.AppendToLastParameter(R"(,"simType":)", std::to_string(sim_type)); + } + c.AppendToLastParameter("}"); } private: @@ -94,8 +111,9 @@ class Device { class NetsimServer : public CommandSource { public: INJECT(NetsimServer(const CuttlefishConfig& config, - const CuttlefishConfig::InstanceSpecific& instance)) - : config_(config), instance_(instance) {} + const CuttlefishConfig::InstanceSpecific& instance, + GrpcSocketCreator& grpc_socket)) + : config_(config), instance_(instance), grpc_socket_(grpc_socket) {} // CommandSource Result> Commands() override { @@ -107,6 +125,11 @@ class NetsimServer : public CommandSource { // Port configuration. netsimd.AddParameter("--hci_port=", config_.rootcanal_hci_port()); + if (EnableNetsimNfc(config_)) { + netsimd.AddParameter("--grpc_uds_path=", grpc_socket_.CreateGrpcSocket( + "NetsimControlServer")); + } + // When no connector is requested, add the instance number if (config_.netsim_connector_instance_num() == config_.netsim_instance_num()) { @@ -122,8 +145,16 @@ class NetsimServer : public CommandSource { } // Add parameters from passthrough option --netsim-args. - for (auto const& arg : config_.netsim_args()) { - netsimd.AddParameter(arg); + // NETSIM_GRPC_PORT is extracted and injected as an environment variable; + // all other options are passed as command-line arguments. + for (const std::string& arg : config_.netsim_args()) { + if (arg.starts_with("NETSIM_GRPC_PORT=")) { + const std::string::size_type equals_pos = arg.find('='); + netsimd.AddEnvironmentVariable(arg.substr(0, equals_pos), + arg.substr(equals_pos + 1)); + } else { + netsimd.AddParameter(arg); + } } // Add command for forwarding the HCI port to a vsock server. @@ -137,6 +168,9 @@ class NetsimServer : public CommandSource { hci_vsock_proxy.AddParameter("--client_tcp_host=127.0.0.1"); hci_vsock_proxy.AddParameter("--client_tcp_port=", config_.rootcanal_hci_port()); + if (instance_.vhost_user_vsock()) { + hci_vsock_proxy.AddParameter("--vhost_user_vsock=true"); + } // Add command for forwarding the test port to a vsock server. Command test_vsock_proxy(SocketVsockProxyBinary()); @@ -149,6 +183,9 @@ class NetsimServer : public CommandSource { test_vsock_proxy.AddParameter("--client_tcp_host=127.0.0.1"); test_vsock_proxy.AddParameter("--client_tcp_port=", config_.rootcanal_test_port()); + if (instance_.vhost_user_vsock()) { + test_vsock_proxy.AddParameter("--vhost_user_vsock=true"); + } std::vector commands; commands.emplace_back(std::move(netsimd)); @@ -186,27 +223,59 @@ class NetsimServer : public CommandSource { for (const auto& instance : config_.Instances()) { Device device(instance.adb_ip_and_port()); // Add bluetooth chip if enabled - if (config_.netsim_radio_enabled( - CuttlefishConfig::NetsimRadio::Bluetooth)) { + if (instance.has_bluetooth() && + !instance.enable_host_bluetooth_connector()) { Chip chip("BLUETOOTH"); chip.fd_in = CF_EXPECT(MakeFifo(instance, "bt_fifo_vm.in")); chip.fd_out = CF_EXPECT(MakeFifo(instance, "bt_fifo_vm.out")); device.chips.emplace_back(chip); } // Add uwb chip if enabled - if (config_.netsim_radio_enabled(CuttlefishConfig::NetsimRadio::Uwb)) { + if (config_.enable_host_uwb() && !instance.enable_host_uwb_connector()) { Chip chip("UWB"); chip.fd_in = CF_EXPECT(MakeFifo(instance, "uwb_fifo_vm.in")); chip.fd_out = CF_EXPECT(MakeFifo(instance, "uwb_fifo_vm.out")); device.chips.emplace_back(chip); } // Add nfc chip if enabled - if (config_.netsim_radio_enabled(CuttlefishConfig::NetsimRadio::Nfc)) { + if (EnableNetsimNfc(config_)) { Chip chip("NFC"); chip.fd_in = CF_EXPECT(MakeFifo(instance, "nfc_fifo_vm.in")); chip.fd_out = CF_EXPECT(MakeFifo(instance, "nfc_fifo_vm.out")); device.chips.emplace_back(chip); } + // Add modem chip if enabled + if (instance.enable_modem_netsim()) { + // modem_count is the number of modems configured for this VM instance + int modem_count = instance.modem_simulator_instance_number(); + CF_EXPECT( + modem_count >= 0 && modem_count < 4, + "Modem simulator instance number should range between 0 and 3"); + auto port_strings = + android::base::Split(instance.modem_simulator_ports(), ","); + for (size_t i = 0; + i < static_cast(modem_count) && i < port_strings.size(); + ++i) { + int port = 0; + CF_EXPECT( + android::base::ParseInt(port_strings[i], &port), + "Failed to parse modem simulator port: " << port_strings[i]); + + auto vsock = SharedFD::VsockServer( + port, SOCK_STREAM, + instance.vhost_user_vsock() + ? std::make_optional(instance.vsock_guest_cid()) + : std::nullopt); + CF_EXPECT(vsock->IsOpen(), vsock->StrError() + << " (try `cvd reset`, or `pkill " + "run_cvd` and `pkill crosvm`)"); + + Chip chip("CELLULAR"); + chip.vsock_fd = vsock; + chip.sim_type = instance.modem_simulator_sim_type(); + device.chips.emplace_back(chip); + } + } // Add other chips if enabled devices_.emplace_back(device); } @@ -223,12 +292,14 @@ class NetsimServer : public CommandSource { std::vector devices_; const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific instance_; + GrpcSocketCreator& grpc_socket_; }; } // namespace fruit::Component> + const CuttlefishConfig::InstanceSpecific, + GrpcSocketCreator>> NetsimServerComponent() { return fruit::createComponent() .addMultibinding() diff --git a/base/cvd/cuttlefish/host/commands/run_cvd/launch/netsim_server.h b/base/cvd/cuttlefish/host/commands/run_cvd/launch/netsim_server.h index 65c0640ffcd..9dcf3e4a54b 100644 --- a/base/cvd/cuttlefish/host/commands/run_cvd/launch/netsim_server.h +++ b/base/cvd/cuttlefish/host/commands/run_cvd/launch/netsim_server.h @@ -17,12 +17,14 @@ #include "fruit/fruit.h" +#include "cuttlefish/host/commands/run_cvd/launch/grpc_socket_creator.h" #include "cuttlefish/host/libs/config/cuttlefish_config.h" namespace cuttlefish { fruit::Component> + const CuttlefishConfig::InstanceSpecific, + GrpcSocketCreator>> NetsimServerComponent(); } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/run_cvd/launch/streamer.cpp b/base/cvd/cuttlefish/host/commands/run_cvd/launch/streamer.cpp index 5c22c264dd4..619f30a5c43 100644 --- a/base/cvd/cuttlefish/host/commands/run_cvd/launch/streamer.cpp +++ b/base/cvd/cuttlefish/host/commands/run_cvd/launch/streamer.cpp @@ -150,7 +150,7 @@ class StreamerSockets : public virtual SetupFeature { // SetupFeature std::string Name() const override { return "StreamerSockets"; } bool Enabled() const override { - bool is_accelerated = instance_.gpu_mode() != GpuMode::GuestSwiftshader; + bool is_accelerated = !IsGuestRenderingMode(instance_.gpu_mode()); return !(VmManagerIsQemu(config_) && is_accelerated); } diff --git a/base/cvd/cuttlefish/host/commands/run_cvd/launch/uwb_connector.cpp b/base/cvd/cuttlefish/host/commands/run_cvd/launch/uwb_connector.cpp index ed03df4292f..721255ba414 100644 --- a/base/cvd/cuttlefish/host/commands/run_cvd/launch/uwb_connector.cpp +++ b/base/cvd/cuttlefish/host/commands/run_cvd/launch/uwb_connector.cpp @@ -40,7 +40,7 @@ namespace cuttlefish { Result> UwbConnector( const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance) { - if (!config.enable_host_uwb()) { + if (!instance.enable_host_uwb_connector()) { return {}; } std::vector fifo_paths = { @@ -51,9 +51,6 @@ Result> UwbConnector( for (const auto& path : fifo_paths) { fifos.push_back(CF_EXPECT(CreateOrReuseAndDrainFifo(path, 0660))); } - if (!instance.enable_host_uwb_connector()) { - return {}; - } return Command(HostBinaryPath("tcp_connector")) .AddParameter("-fifo_out=", fifos[0]) .AddParameter("-fifo_in=", fifos[1]) diff --git a/base/cvd/cuttlefish/host/commands/secure_env/secure_env_only_oemlock.cpp b/base/cvd/cuttlefish/host/commands/secure_env/secure_env_only_oemlock.cpp index 6d61eb97b0a..975539a907c 100644 --- a/base/cvd/cuttlefish/host/commands/secure_env/secure_env_only_oemlock.cpp +++ b/base/cvd/cuttlefish/host/commands/secure_env/secure_env_only_oemlock.cpp @@ -187,6 +187,24 @@ Result SecureEnvMain(int argc, char** argv) { } }); + std::vector stub_snapshot_handlers = { + rust_snapshot_socket2, + keymaster_snapshot_socket2, + gatekeeper_snapshot_socket2, + weaver_snapshot_socket2, + }; + for (SharedFD snapshot_socket : stub_snapshot_handlers) { + threads.emplace_back([snapshot_socket]() { + while (true) { + // infinite loop that returns if resetting responder is needed + auto result = secure_env_impl::WorkerStubLoop(snapshot_socket); + if (!result.ok()) { + LOG(FATAL) << "stub worker failed: " << result.error().Trace(); + } + } + }); + } + auto kernel_events_fd = DupFdFlag(FLAGS_kernel_events_fd); threads.emplace_back(StartKernelEventMonitor(kernel_events_fd, oemlock_lock)); diff --git a/base/cvd/cuttlefish/host/commands/secure_env/worker_thread_loop_body.cpp b/base/cvd/cuttlefish/host/commands/secure_env/worker_thread_loop_body.cpp index 7827cc4b315..25ea922156a 100644 --- a/base/cvd/cuttlefish/host/commands/secure_env/worker_thread_loop_body.cpp +++ b/base/cvd/cuttlefish/host/commands/secure_env/worker_thread_loop_body.cpp @@ -23,6 +23,32 @@ namespace cuttlefish { namespace secure_env_impl { +namespace { + +Result StubHandleSuspendRequest(SharedFD snapshot_socket) { + // Read the suspend request. + SnapshotSocketMessage suspend_request; + CF_EXPECT_EQ( + sizeof(suspend_request), + snapshot_socket->Read(&suspend_request, sizeof(suspend_request)), + "socket read failed: " << snapshot_socket->StrError()); + CF_EXPECT_EQ(SnapshotSocketMessage::kSuspend, suspend_request); + // Send the ACK response. + const SnapshotSocketMessage ack_response = SnapshotSocketMessage::kSuspendAck; + CF_EXPECT_EQ(sizeof(ack_response), + snapshot_socket->Write(&ack_response, sizeof(ack_response)), + "socket write failed: " << snapshot_socket->StrError()); + // Block until resumed. + SnapshotSocketMessage resume_request; + CF_EXPECT_EQ( + sizeof(resume_request), + snapshot_socket->Read(&resume_request, sizeof(resume_request)), + "socket read failed: " << snapshot_socket->StrError()); + CF_EXPECT_EQ(SnapshotSocketMessage::kResume, resume_request); + return {}; +} + +} // namespace Result WorkerInnerLoop(std::function process_callback, SharedFD read_fd, SharedFD snapshot_socket) { @@ -50,26 +76,26 @@ Result WorkerInnerLoop(std::function process_callback, } if (readable_fds.IsSet(snapshot_socket)) { - // Read the suspend request. - SnapshotSocketMessage suspend_request; - CF_EXPECT_EQ( - sizeof(suspend_request), - snapshot_socket->Read(&suspend_request, sizeof(suspend_request)), - "socket read failed: " << snapshot_socket->StrError()); - CF_EXPECT_EQ(SnapshotSocketMessage::kSuspend, suspend_request); - // Send the ACK response. - const SnapshotSocketMessage ack_response = - SnapshotSocketMessage::kSuspendAck; - CF_EXPECT_EQ(sizeof(ack_response), - snapshot_socket->Write(&ack_response, sizeof(ack_response)), - "socket write failed: " << snapshot_socket->StrError()); - // Block until resumed. - SnapshotSocketMessage resume_request; - CF_EXPECT_EQ( - sizeof(resume_request), - snapshot_socket->Read(&resume_request, sizeof(resume_request)), - "socket read failed: " << snapshot_socket->StrError()); - CF_EXPECT_EQ(SnapshotSocketMessage::kResume, resume_request); + CF_EXPECT(StubHandleSuspendRequest(snapshot_socket)); + } + } + + return {}; +} + +Result WorkerStubLoop(SharedFD snapshot_socket) { + for (;;) { + SharedFDSet readable_fds; + readable_fds.Set(snapshot_socket); + + int num_fds = Select(&readable_fds, nullptr, nullptr, nullptr); + if (num_fds < 0) { + LOG(FATAL) << "Select() returned a negative value: " << num_fds + << StrError(errno); + } + + if (readable_fds.IsSet(snapshot_socket)) { + CF_EXPECT(StubHandleSuspendRequest(snapshot_socket)); } } diff --git a/base/cvd/cuttlefish/host/commands/secure_env/worker_thread_loop_body.h b/base/cvd/cuttlefish/host/commands/secure_env/worker_thread_loop_body.h index efe4e167c24..95a9104de89 100644 --- a/base/cvd/cuttlefish/host/commands/secure_env/worker_thread_loop_body.h +++ b/base/cvd/cuttlefish/host/commands/secure_env/worker_thread_loop_body.h @@ -26,5 +26,7 @@ namespace secure_env_impl { Result WorkerInnerLoop(std::function process_callback, SharedFD read_fd, SharedFD snapshot_socket); +Result WorkerStubLoop(SharedFD snapshot_socket); + } // namespace secure_env_impl } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/start/main.cc b/base/cvd/cuttlefish/host/commands/start/main.cc index 3b92236e948..67f1eea7fee 100644 --- a/base/cvd/cuttlefish/host/commands/start/main.cc +++ b/base/cvd/cuttlefish/host/commands/start/main.cc @@ -155,6 +155,7 @@ const std::unordered_set& BoolFlags() { "enable_kernel_log", "enable_minimal_mode", "enable_modem_simulator", + "enable_pkvm", "enable_sandbox", "enable_usb", "enable_virtiofs", diff --git a/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_mplane/src/device.rs b/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_mplane/src/device.rs index 09fed58e8e7..6986837e048 100644 --- a/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_mplane/src/device.rs +++ b/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_mplane/src/device.rs @@ -30,6 +30,11 @@ use v4l2r::bindings::v4l2_requestbuffers; use v4l2r::ioctl::BufferCapabilities; use v4l2r::ioctl::BufferField; use v4l2r::ioctl::BufferFlags; +use v4l2r::ioctl::CtrlId; +use v4l2r::ioctl::CtrlWhich; +use v4l2r::ioctl::EventType as V4l2EventType; +use v4l2r::ioctl::QueryCtrlFlags; +use v4l2r::ioctl::SubscribeEventFlags; use v4l2r::ioctl::V4l2Buffer; use v4l2r::ioctl::V4l2PlanesWithBackingMut; use v4l2r::memory::MemoryType; @@ -45,10 +50,84 @@ use virtio_media::ioctl::virtio_media_dispatch_ioctl; use virtio_media::memfd::MemFdBuffer; use virtio_media::mmap::MmapMappingManager; use virtio_media::protocol::DequeueBufferEvent; +use virtio_media::protocol::SessionEvent; use virtio_media::protocol::SgEntry; use virtio_media::protocol::V4l2Event; use virtio_media::protocol::V4l2Ioctl; use virtio_media::protocol::VIRTIO_MEDIA_MMAP_FLAG_RW; +use std::str::FromStr; + +/// Rust equivalent of the V4L2_CTRL_ID2WHICH C preprocessor macro. +/// Extracts the control class ID from a control ID by masking out the lower 16 bits +/// and any reserved top bits (uses mask 0x0fff0000). +/// See: https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html +const fn v4l2_ctrl_id2which(id: u32) -> u32 { + id & 0x0fff0000 +} + +/// https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#LENS_FACING_FRONT +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LensFacing { + Front = 0, + Back = 1, + External = 2, +} + +impl FromStr for LensFacing { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "FRONT" => Ok(LensFacing::Front), + "BACK" => Ok(LensFacing::Back), + "EXTERNAL" => Ok(LensFacing::External), + _ => Err(format!("Invalid lens facing: {}. Expected FRONT, BACK, or EXTERNAL", s)), + } + } +} + +/// Encapsulates the camera gain value. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Gain(i32); + +impl Gain { + pub const MIN: i32 = 100; + pub const MAX: i32 = 1600; + pub const DEFAULT: i32 = 100; + + pub fn new(value: i32) -> Result { + if value < Self::MIN || value > Self::MAX { + Err(libc::ERANGE) + } else { + Ok(Gain(value)) + } + } + + pub fn value(&self) -> i32 { + self.0 + } +} + +impl Default for Gain { + fn default() -> Self { + Gain(Self::DEFAULT) + } +} + +/// State of all camera controls. +struct CameraControls { + lens_facing: LensFacing, + gain: Gain, +} + +impl CameraControls { + fn new(lens_facing: LensFacing) -> Self { + Self { + lens_facing, + gain: Gain::default(), + } + } +} /// Current status of a buffer. #[derive(Debug, PartialEq, Eq)] @@ -123,9 +202,17 @@ impl Buffer { } } self.v4l2_buffer.set_sequence(sequence); + let mut ts = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + // SAFETY: clock_gettime is a standard POSIX libc call with a valid pointer. + unsafe { + libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts); + } self.v4l2_buffer.set_timestamp(bindings::timeval { - tv_sec: (sequence + 1) as bindings::__time_t / 1000, - tv_usec: (sequence + 1) as bindings::__time_t % 1000, + tv_sec: ts.tv_sec as bindings::__time_t, + tv_usec: (ts.tv_nsec / 1000) as bindings::__time_t, }); flags &= !BufferFlags::QUEUED; } @@ -158,6 +245,7 @@ impl VirtioMediaDeviceSession for EmulatedCameraSession { impl EmulatedCameraSession { fn write_pattern( iteration: u64, + controls: &CameraControls, mut sink_y: WY, mut sink_u: WU, mut sink_v: WV, @@ -165,7 +253,12 @@ impl EmulatedCameraSession { let mut writer_y = BufWriter::new(&mut sink_y); let mut writer_u = BufWriter::new(&mut sink_u); let mut writer_v = BufWriter::new(&mut sink_v); - let y = (iteration % 256) as u8; + // The base Y (luma) value changes over iterations to create a moving pattern. + let base_y = (iteration % 256) as u8; + // Apply gain to the luma channel. + // Gain::MIN (100) represents 1.0x gain. Higher values scale the brightness. + // We clamp the result to 255.0 to avoid overflow. + let y = ((base_y as f32) * (controls.gain.value() as f32 / Gain::MIN as f32)).min(255.0) as u8; let u = ((iteration + 64) % 256) as u8; let v = ((iteration + 128) % 256) as u8; for _ in 0..(WIDTH * HEIGHT) { @@ -184,6 +277,7 @@ impl EmulatedCameraSession { fn process_queued_buffers( &mut self, evt_queue: &mut Q, + controls: &CameraControls, ) -> IoctlResult<()> { while let Some(buf_id) = self.queued_buffers.pop_front() { let iteration = self.iteration; @@ -199,6 +293,7 @@ impl EmulatedCameraSession { Self::write_pattern( iteration, + controls, buffer.planes[0].fd.as_file(), buffer.planes[1].fd.as_file(), buffer.planes[2].fd.as_file(), @@ -235,6 +330,8 @@ pub struct EmulatedCamera, + /// Camera controls. + controls: CameraControls, } impl EmulatedCamera @@ -242,11 +339,78 @@ where Q: VirtioMediaEventQueue, HM: VirtioMediaHostMemoryMapper, { - pub fn new(evt_queue: Q, mapper: HM) -> Self { + pub fn new(evt_queue: Q, mapper: HM, lens_facing: LensFacing) -> Self { Self { evt_queue, mmap_manager: MmapMappingManager::from(mapper), active_session: None, + controls: CameraControls::new(lens_facing), + } + } + + fn lens_facing_query_ext_ctrl(&self) -> bindings::v4l2_query_ext_ctrl { + let name_str = "LENS_FACING"; + let mut name = [0u8; 32]; + name[0..name_str.len()].copy_from_slice(name_str.as_bytes()); + bindings::v4l2_query_ext_ctrl { + id: CID_LENS_FACING, + type_: bindings::v4l2_ctrl_type_V4L2_CTRL_TYPE_INTEGER, + name: name.map(|b| b as i8), + minimum: LensFacing::Front as i64, + maximum: LensFacing::External as i64, + step: 1, + default_value: self.controls.lens_facing as i64, + flags: bindings::V4L2_CTRL_FLAG_READ_ONLY, + elems: 1, + elem_size: std::mem::size_of::() as u32, + ..Default::default() + } + } + + fn gain_query_ext_ctrl(&self) -> bindings::v4l2_query_ext_ctrl { + let name_str = "Gain"; + let mut name = [0u8; 32]; + name[0..name_str.len()].copy_from_slice(name_str.as_bytes()); + bindings::v4l2_query_ext_ctrl { + id: bindings::V4L2_CID_GAIN, + type_: bindings::v4l2_ctrl_type_V4L2_CTRL_TYPE_INTEGER, + name: name.map(|b| b as i8), + minimum: Gain::MIN as i64, + maximum: Gain::MAX as i64, + step: 1, + default_value: Gain::DEFAULT as i64, + flags: 0, + elems: 1, + elem_size: std::mem::size_of::() as u32, + ..Default::default() + } + } + + fn user_class_query_ext_ctrl(&self) -> bindings::v4l2_query_ext_ctrl { + let name_str = "User Controls"; + let mut name = [0u8; 32]; + name[0..name_str.len()].copy_from_slice(name_str.as_bytes()); + bindings::v4l2_query_ext_ctrl { + id: bindings::V4L2_CID_USER_CLASS, + type_: bindings::v4l2_ctrl_type_V4L2_CTRL_TYPE_CTRL_CLASS, + name: name.map(|b| b as i8), + // V4L2 standard requires control class headers to be marked as both RO and WO. + flags: bindings::V4L2_CTRL_FLAG_READ_ONLY | bindings::V4L2_CTRL_FLAG_WRITE_ONLY, + ..Default::default() + } + } + + fn camera_class_query_ext_ctrl(&self) -> bindings::v4l2_query_ext_ctrl { + let name_str = "Camera Controls"; + let mut name = [0u8; 32]; + name[0..name_str.len()].copy_from_slice(name_str.as_bytes()); + bindings::v4l2_query_ext_ctrl { + id: bindings::V4L2_CID_CAMERA_CLASS, + type_: bindings::v4l2_ctrl_type_V4L2_CTRL_TYPE_CTRL_CLASS, + name: name.map(|b| b as i8), + // V4L2 standard requires control class headers to be marked as both RO and WO. + flags: bindings::V4L2_CTRL_FLAG_READ_ONLY | bindings::V4L2_CTRL_FLAG_WRITE_ONLY, + ..Default::default() } } } @@ -329,6 +493,10 @@ where } } +// Use an offset for virtio-media custom camera class control id values. +const CID_OFFSET: u32 = bindings::V4L2_CID_CAMERA_CLASS_BASE + 0x100; +const CID_LENS_FACING: u32 = CID_OFFSET + 1; + const PIXELFORMAT: u32 = PixelFormat::from_fourcc(b"YM12").to_u32(); const WIDTH: u32 = 640; const HEIGHT: u32 = 480; @@ -662,7 +830,7 @@ where let buffer = host_buffer.v4l2_buffer.clone(); if session.streaming { - session.process_queued_buffers(&mut self.evt_queue)?; + session.process_queued_buffers(&mut self.evt_queue, &self.controls)?; } Ok(buffer) @@ -674,7 +842,7 @@ where } session.streaming = true; - session.process_queued_buffers(&mut self.evt_queue)?; + session.process_queued_buffers(&mut self.evt_queue, &self.controls)?; Ok(()) } @@ -768,4 +936,280 @@ where ..Default::default() }) } + + /// https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-queryctrl.html#control-flags + fn query_ext_ctrl( + &mut self, + _session: &Self::Session, + id: CtrlId, + flags: QueryCtrlFlags, + ) -> IoctlResult { + let id: u32 = unsafe { std::mem::transmute(id) }; + if flags.contains(QueryCtrlFlags::NEXT) { + if id < bindings::V4L2_CID_USER_CLASS { + return Ok(self.user_class_query_ext_ctrl()); + } else if id < bindings::V4L2_CID_GAIN { + return Ok(self.gain_query_ext_ctrl()); + } else if id < bindings::V4L2_CID_CAMERA_CLASS { + return Ok(self.camera_class_query_ext_ctrl()); + } else if id < CID_LENS_FACING { + return Ok(self.lens_facing_query_ext_ctrl()); + } + } else { + if id == bindings::V4L2_CID_USER_CLASS { + return Ok(self.user_class_query_ext_ctrl()); + } else if id == bindings::V4L2_CID_GAIN { + return Ok(self.gain_query_ext_ctrl()); + } else if id == bindings::V4L2_CID_CAMERA_CLASS { + return Ok(self.camera_class_query_ext_ctrl()); + } else if id == CID_LENS_FACING { + return Ok(self.lens_facing_query_ext_ctrl()); + } + } + return Err(libc::EINVAL); + } + + fn g_ext_ctrls( + &mut self, + _session: &Self::Session, + which: CtrlWhich, + ctrls: &mut bindings::v4l2_ext_controls, + ctrl_array: &mut Vec, + _user_regions: Vec>, + ) -> IoctlResult<()> { + // Validate control class. Also handles class support queries when count == 0. + match which { + CtrlWhich::Current | CtrlWhich::Default => {} + CtrlWhich::Class(class) => { + if class != bindings::V4L2_CTRL_CLASS_USER && class != bindings::V4L2_CTRL_CLASS_CAMERA { + ctrls.error_idx = ctrls.count; + return Err(libc::EINVAL); + } + } + _ => { + ctrls.error_idx = ctrls.count; + return Err(libc::EINVAL); + } + } + + // Ensure all requested controls belong to the selected class. + if let CtrlWhich::Class(class_id) = which { + for (idx, ctrl) in ctrl_array.iter().enumerate() { + if v4l2_ctrl_id2which(ctrl.id) != class_id { + ctrls.error_idx = ctrls.count; + return Err(libc::EINVAL); + } + } + } + + // Process controls. Class controls are write-only headers and must fail on read. + for (idx, ctrl) in ctrl_array.iter_mut().enumerate() { + match ctrl.id { + bindings::V4L2_CID_USER_CLASS | bindings::V4L2_CID_CAMERA_CLASS => { + ctrls.error_idx = ctrls.count; + return Err(libc::EACCES); + } + bindings::V4L2_CID_GAIN => { + ctrl.__bindgen_anon_1.value = match which { + CtrlWhich::Default => Gain::DEFAULT, + _ => self.controls.gain.value(), + }; + } + CID_LENS_FACING => { + ctrl.__bindgen_anon_1.value = self.controls.lens_facing as i32; + } + _ => { + ctrls.error_idx = ctrls.count; + return Err(libc::EINVAL); + } + } + } + ctrls.error_idx = ctrls.count; + Ok(()) + } + + fn try_ext_ctrls( + &mut self, + _session: &Self::Session, + which: CtrlWhich, + ctrls: &mut bindings::v4l2_ext_controls, + ctrl_array: &mut Vec, + _user_regions: Vec>, + ) -> IoctlResult<()> { + // Validate control class. Setting defaults is not allowed for TRY/SET. + match which { + CtrlWhich::Current => {} + CtrlWhich::Class(class) => { + if class != bindings::V4L2_CTRL_CLASS_USER && class != bindings::V4L2_CTRL_CLASS_CAMERA { + ctrls.error_idx = ctrls.count; + return Err(libc::EINVAL); + } + } + _ => { + ctrls.error_idx = ctrls.count; + return Err(libc::EINVAL); + } + } + + // Ensure all requested controls belong to the selected class. + if let CtrlWhich::Class(class_id) = which { + for (idx, ctrl) in ctrl_array.iter().enumerate() { + if v4l2_ctrl_id2which(ctrl.id) != class_id { + ctrls.error_idx = idx as u32; + return Err(libc::EINVAL); + } + } + } + + // Validate control values. Class controls are read-only headers and must fail on write/try. + for (idx, ctrl) in ctrl_array.iter_mut().enumerate() { + match ctrl.id { + bindings::V4L2_CID_USER_CLASS | bindings::V4L2_CID_CAMERA_CLASS => { + ctrls.error_idx = idx as u32; + return Err(libc::EACCES); + } + bindings::V4L2_CID_GAIN => { + let value = unsafe { ctrl.__bindgen_anon_1.value }; + if let Err(err) = Gain::new(value) { + ctrls.error_idx = idx as u32; + return Err(err); + } + } + CID_LENS_FACING => { + ctrls.error_idx = idx as u32; + return Err(libc::EACCES); + } + _ => { + ctrls.error_idx = idx as u32; + return Err(libc::EINVAL); + } + } + } + ctrls.error_idx = ctrls.count; + Ok(()) + } + + fn s_ext_ctrls( + &mut self, + session: &mut Self::Session, + which: CtrlWhich, + ctrls: &mut bindings::v4l2_ext_controls, + ctrl_array: &mut Vec, + _user_regions: Vec>, + ) -> IoctlResult<()> { + // Validate control class. Setting defaults is not allowed for TRY/SET. + match which { + CtrlWhich::Current => {} + CtrlWhich::Class(class) => { + if class != bindings::V4L2_CTRL_CLASS_USER && class != bindings::V4L2_CTRL_CLASS_CAMERA { + ctrls.error_idx = ctrls.count; + return Err(libc::EINVAL); + } + } + _ => { + ctrls.error_idx = ctrls.count; + return Err(libc::EINVAL); + } + } + + // Ensure all requested controls belong to the selected class. + if let CtrlWhich::Class(class_id) = which { + for (idx, ctrl) in ctrl_array.iter().enumerate() { + if v4l2_ctrl_id2which(ctrl.id) != class_id { + ctrls.error_idx = ctrls.count; + return Err(libc::EINVAL); + } + } + } + + // Apply control values. Class controls are read-only headers and must fail on write/try. + for (idx, ctrl) in ctrl_array.iter_mut().enumerate() { + match ctrl.id { + bindings::V4L2_CID_USER_CLASS | bindings::V4L2_CID_CAMERA_CLASS => { + ctrls.error_idx = ctrls.count; + return Err(libc::EACCES); + } + bindings::V4L2_CID_GAIN => { + let value = unsafe { ctrl.__bindgen_anon_1.value }; + match Gain::new(value) { + Ok(gain) => { + if self.controls.gain != gain { + self.controls.gain = gain; + let ctrl_event = bindings::v4l2_event { + type_: bindings::V4L2_EVENT_CTRL, + id: bindings::V4L2_CID_GAIN, + ..Default::default() + }; + self.evt_queue.send_event(V4l2Event::Event(SessionEvent::new( + session.id, ctrl_event, + ))); + } + } + Err(err) => { + ctrls.error_idx = ctrls.count; + return Err(err); + } + } + } + CID_LENS_FACING => { + ctrls.error_idx = ctrls.count; + return Err(libc::EACCES); + } + _ => { + ctrls.error_idx = ctrls.count; + return Err(libc::EINVAL); + } + } + } + ctrls.error_idx = ctrls.count; + Ok(()) + } + + fn subscribe_event( + &mut self, + session: &mut Self::Session, + event: V4l2EventType, + flags: SubscribeEventFlags, + ) -> IoctlResult<()> { + if !flags.contains(SubscribeEventFlags::SEND_INITIAL) { + return Err(libc::EINVAL); + } + match event { + V4l2EventType::Ctrl(id) => match id { + CID_LENS_FACING | bindings::V4L2_CID_GAIN => { + let ctrl_event = bindings::v4l2_event { + type_: bindings::V4L2_EVENT_CTRL, + id, + ..Default::default() + }; + self.evt_queue + .send_event(V4l2Event::Event(SessionEvent::new(session.id, ctrl_event))); + Ok(()) + } + bindings::V4L2_CID_USER_CLASS | bindings::V4L2_CID_CAMERA_CLASS => { + // Subscription succeeds, but we do not send any initial event. + Ok(()) + } + _ => Err(libc::EINVAL), + }, + _ => Err(libc::EINVAL), + } + } + + fn unsubscribe_event( + &mut self, + _session: &mut Self::Session, + event: bindings::v4l2_event_subscription, + ) -> IoctlResult<()> { + return if event.type_ == bindings::V4L2_EVENT_CTRL + && (event.id == CID_LENS_FACING + || event.id == bindings::V4L2_CID_GAIN + || event.id == bindings::V4L2_CID_USER_CLASS + || event.id == bindings::V4L2_CID_CAMERA_CLASS) + { + Ok(()) + } else { + Err(libc::EINVAL) + }; + } } diff --git a/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_mplane/src/main.rs b/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_mplane/src/main.rs index 5ad797021df..1ed6ad21c4d 100644 --- a/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_mplane/src/main.rs +++ b/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_mplane/src/main.rs @@ -24,9 +24,12 @@ use virtio_media::protocol::VirtioMediaDeviceConfig; use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; mod device; +use device::LensFacing; #[derive(Debug, Error)] pub(crate) enum Error { + #[error("Invalid argument: {0}")] + InvalidArgument(String), #[error("Could not create daemon: {0}")] CouldNotCreateDaemon(vhost_user_backend::Error), #[error("Fatal error: {0}")] @@ -44,19 +47,26 @@ struct CmdLineArgs { /// Log verbosity, one of Off, Error, Warning, Info, Debug, Trace. #[clap(short, long, default_value_t = log::LevelFilter::Debug)] verbosity: log::LevelFilter, + /// Lens facing configuration: FRONT, BACK, or EXTERNAL. + #[clap(long, value_name = "LENS_FACING", default_value = "EXTERNAL")] + lens_facing: String, } #[derive(PartialEq, Debug)] struct Config { socket_path: PathBuf, + lens_facing: LensFacing, } impl TryFrom for Config { type Error = Error; fn try_from(args: CmdLineArgs) -> Result { + let lens_facing = args.lens_facing.parse::() + .map_err(Error::InvalidArgument)?; Ok(Config { socket_path: args.socket_path, + lens_facing, }) } } @@ -80,14 +90,17 @@ fn start_backend(config: Config) -> Result<()> { // across VMs restarts rather than having to manually start the binary again. loop { use virtio_media::v4l2r::ioctl::Capabilities; - let config = VirtioMediaDeviceConfig { + let device_config = VirtioMediaDeviceConfig { device_caps: (Capabilities::VIDEO_CAPTURE_MPLANE | Capabilities::STREAMING).bits(), device_type: VFL_TYPE_VIDEO, card, }; + let lens_facing = config.lens_facing; let backend = Arc::new(RwLock::new(VhuMediaBackend::new( - config, - |event_queue, host_mapper| crate::device::EmulatedCamera::new(event_queue, host_mapper), + device_config, + move |event_queue, host_mapper| { + crate::device::EmulatedCamera::new(event_queue, host_mapper, lens_facing) + }, ))); let mut daemon = VhostUserDaemon::new( String::from("vhost-user-media-backend"), diff --git a/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_splane/src/device.rs b/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_splane/src/device.rs index 69133e21ddb..eae3f807dac 100644 --- a/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_splane/src/device.rs +++ b/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_splane/src/device.rs @@ -137,9 +137,17 @@ impl Buffer { } BufferState::Outgoing { sequence } => { self.v4l2_buffer.set_sequence(sequence); + let mut ts = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + // SAFETY: clock_gettime is a standard POSIX libc call with a valid pointer. + unsafe { + libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts); + } self.v4l2_buffer.set_timestamp(bindings::timeval { - tv_sec: (sequence + 1) as bindings::__time_t / 1000, - tv_usec: (sequence + 1) as bindings::__time_t % 1000, + tv_sec: ts.tv_sec as bindings::__time_t, + tv_usec: (ts.tv_nsec / 1000) as bindings::__time_t, }); Self::unset_flag(&mut flags, BufferFlags::QUEUED); } diff --git a/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_splane/src/main.rs b/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_splane/src/main.rs index 175d8dc61a6..76834e66b99 100644 --- a/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_splane/src/main.rs +++ b/base/cvd/cuttlefish/host/commands/vhost_user_media/emulated_camera_splane/src/main.rs @@ -37,7 +37,7 @@ struct CmdLineArgs { #[clap(short, long, default_value_t = log::LevelFilter::Debug)] verbosity: log::LevelFilter, /// Lens facing configuration: FRONT, BACK, or EXTERNAL. - #[clap(long, value_name = "LENS_FACING", default_value = "BACK")] + #[clap(long, value_name = "LENS_FACING", default_value = "EXTERNAL")] lens_facing: String, } diff --git a/base/cvd/cuttlefish/host/frontend/webrtc/main.cpp b/base/cvd/cuttlefish/host/frontend/webrtc/main.cpp index 6056860968c..934ba045de9 100644 --- a/base/cvd/cuttlefish/host/frontend/webrtc/main.cpp +++ b/base/cvd/cuttlefish/host/frontend/webrtc/main.cpp @@ -472,7 +472,9 @@ int CuttlefishMain() { std::to_string(instance.memory_mb()) + " mb"); std::string user_friendly_gpu_mode; - if (instance.gpu_mode() == GpuMode::GuestSwiftshader) { + if (instance.gpu_mode() == GpuMode::GuestLavapipe) { + user_friendly_gpu_mode = "Lavapipe (Guest GPU Rendering)"; + } else if (instance.gpu_mode() == GpuMode::GuestSwiftshader) { user_friendly_gpu_mode = "SwiftShader (Guest CPU Rendering)"; } else if (instance.gpu_mode() == GpuMode::DrmVirgl) { user_friendly_gpu_mode = diff --git a/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.cpp b/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.cpp index 3e545b1cb96..cb868aff4e4 100644 --- a/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.cpp +++ b/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.cpp @@ -269,21 +269,6 @@ int CuttlefishConfig::casimir_rf_port() const { return (*dictionary_)[kCasimirRfPort].asInt(); } -static constexpr char kNetsimRadios[] = "netsim_radios"; - -void CuttlefishConfig::netsim_radio_enable(NetsimRadio flag) { - if (dictionary_->isMember(kNetsimRadios)) { - // OR the radio to current set of radios - (*dictionary_)[kNetsimRadios] = (*dictionary_)[kNetsimRadios].asInt() | flag; - } else { - (*dictionary_)[kNetsimRadios] = flag; - } -} - -bool CuttlefishConfig::netsim_radio_enabled(NetsimRadio flag) const { - return (*dictionary_)[kNetsimRadios].asInt() & flag; -} - static constexpr char kNetsimInstanceNum[] = "netsim_instance_num"; int CuttlefishConfig::netsim_instance_num() const { return (*dictionary_)[kNetsimInstanceNum].asInt(); @@ -707,6 +692,10 @@ std::vector CuttlefishConfig::environment_dirs() const { return result; } +bool EnableNetsimNfc(const CuttlefishConfig& config) { + return config.enable_host_nfc() && !config.enable_host_nfc_connector(); +} + bool VmManagerIsCrosvm(const CuttlefishConfig& config) { return VmManagerIsCrosvm(config.vm_manager()); } diff --git a/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.h b/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.h index 0a667ab4473..fd5f4f91c99 100644 --- a/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.h +++ b/base/cvd/cuttlefish/host/libs/config/cuttlefish_config.h @@ -155,16 +155,6 @@ class CuttlefishConfig { void set_casimir_rf_port(int port); int casimir_rf_port() const; - // Flags for the set of radios that are connected to netsim - enum NetsimRadio { - Bluetooth = 0b00000001, - Wifi = 0b00000010, - Uwb = 0b00000100, - Nfc = 0b00001000, - }; - - void netsim_radio_enable(NetsimRadio flag); - bool netsim_radio_enabled(NetsimRadio flag) const; void set_netsim_instance_num(int netsim_instance_num); int netsim_instance_num() const; // Netsim has a built-in connector to forward packets to another daemon based @@ -414,6 +404,7 @@ class CuttlefishConfig { bool crosvm_simple_media_device() const; std::string crosvm_v4l2_proxy() const; bool use_pmem() const; + bool enable_pkvm() const; // Wifi MAC address inside the guest int wifi_mac_prefix() const; @@ -512,6 +503,7 @@ class CuttlefishConfig { // Configuration flags for a minimal device bool enable_minimal_mode() const; bool enable_modem_simulator() const; + bool enable_modem_netsim() const; int modem_simulator_instance_number() const; int modem_simulator_sim_type() const; @@ -536,6 +528,10 @@ class CuttlefishConfig { bool enable_gpu_external_blob() const; bool enable_gpu_system_blob() const; + bool has_vulkan_gfxstream_apex() const; + bool has_vulkan_lavapipe_apex() const; + bool has_vulkan_swiftshader_apex() const; + std::string hwcomposer() const; bool restart_subprocesses() const; @@ -661,6 +657,7 @@ class CuttlefishConfig { void set_crosvm_simple_media_device(const bool simple_media_device); void set_crosvm_v4l2_proxy(const std::string v4l2_proxy); void set_use_pmem(const bool use_pmem); + void set_enable_pkvm(bool enable_pkvm); // Wifi MAC address inside the guest void set_wifi_mac_prefix(const int wifi_mac_prefix); // Gnss grpc proxy server port inside the host @@ -747,6 +744,7 @@ class CuttlefishConfig { // Configuration flags for a minimal device void set_enable_minimal_mode(bool enable_minimal_mode); void set_enable_modem_simulator(bool enable_modem_simulator); + void set_enable_modem_netsim(bool enable); void set_modem_simulator_instance_number(int instance_numbers); void set_modem_simulator_sim_type(int sim_type); @@ -769,6 +767,10 @@ class CuttlefishConfig { void set_enable_gpu_external_blob(const bool enable_gpu_external_blob); void set_enable_gpu_system_blob(const bool enable_gpu_system_blob); + void set_has_vulkan_gfxstream_apex(const bool has_apex); + void set_has_vulkan_lavapipe_apex(const bool has_apex); + void set_has_vulkan_swiftshader_apex(const bool has_apex); + void set_hwcomposer(const std::string&); void set_restart_subprocesses(bool restart_subprocesses); @@ -940,4 +942,6 @@ bool VmManagerIsCrosvm(const CuttlefishConfig&); bool VmManagerIsQemu(const CuttlefishConfig&); bool VmManagerIsGem5(const CuttlefishConfig&); +bool EnableNetsimNfc(const CuttlefishConfig& config); + } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/libs/config/cuttlefish_config_instance.cpp b/base/cvd/cuttlefish/host/libs/config/cuttlefish_config_instance.cpp index 83d7dda6cb0..bafd2577e9b 100644 --- a/base/cvd/cuttlefish/host/libs/config/cuttlefish_config_instance.cpp +++ b/base/cvd/cuttlefish/host/libs/config/cuttlefish_config_instance.cpp @@ -859,6 +859,34 @@ bool CuttlefishConfig::InstanceSpecific::enable_gpu_system_blob() const { return (*Dictionary())[kEnableGpuSystemBlob].asBool(); } +static constexpr char kHasVulkanGfxstreamApex[] = "has_vulkan_gfxstream_apex"; +void CuttlefishConfig::MutableInstanceSpecific::set_has_vulkan_gfxstream_apex( + const bool has_apex) { + (*Dictionary())[kHasVulkanGfxstreamApex] = has_apex; +} +bool CuttlefishConfig::InstanceSpecific::has_vulkan_gfxstream_apex() const { + return (*Dictionary())[kHasVulkanGfxstreamApex].asBool(); +} + +static constexpr char kHasVulkanLavapipeApex[] = "has_vulkan_lavapipe_apex"; +void CuttlefishConfig::MutableInstanceSpecific::set_has_vulkan_lavapipe_apex( + const bool has_apex) { + (*Dictionary())[kHasVulkanLavapipeApex] = has_apex; +} +bool CuttlefishConfig::InstanceSpecific::has_vulkan_lavapipe_apex() const { + return (*Dictionary())[kHasVulkanLavapipeApex].asBool(); +} + +static constexpr char kHasVulkanSwiftshaderApex[] = + "has_vulkan_swiftshader_apex"; +void CuttlefishConfig::MutableInstanceSpecific::set_has_vulkan_swiftshader_apex( + const bool has_apex) { + (*Dictionary())[kHasVulkanSwiftshaderApex] = has_apex; +} +bool CuttlefishConfig::InstanceSpecific::has_vulkan_swiftshader_apex() const { + return (*Dictionary())[kHasVulkanSwiftshaderApex].asBool(); +} + static constexpr char kEnableAudio[] = "enable_audio"; void CuttlefishConfig::MutableInstanceSpecific::set_enable_audio(bool enable) { (*Dictionary())[kEnableAudio] = enable; @@ -1351,6 +1379,15 @@ void CuttlefishConfig::MutableInstanceSpecific::set_modem_simulator_ports( (*Dictionary())[kModemSimulatorPorts] = modem_simulator_ports; } +static constexpr char kEnableModemNetsim[] = "enable_modem_netsim"; +bool CuttlefishConfig::InstanceSpecific::enable_modem_netsim() const { + return (*Dictionary())[kEnableModemNetsim].asBool(); +} +void CuttlefishConfig::MutableInstanceSpecific::set_enable_modem_netsim( + bool enable) { + (*Dictionary())[kEnableModemNetsim] = enable; +} + std::string CuttlefishConfig::InstanceSpecific::launcher_log_path() const { return AbsolutePath(PerInstanceLogPath(kLogNameLauncher)); } @@ -1834,6 +1871,15 @@ bool CuttlefishConfig::InstanceSpecific::use_pmem() const { return (*Dictionary())[kCrosvmUsePmem].asBool(); } +static constexpr char kEnablePkvm[] = "enable_pkvm"; +void CuttlefishConfig::MutableInstanceSpecific::set_enable_pkvm( + bool enable_pkvm) { + (*Dictionary())[kEnablePkvm] = enable_pkvm; +} +bool CuttlefishConfig::InstanceSpecific::enable_pkvm() const { + return (*Dictionary())[kEnablePkvm].asBool(); +} + static constexpr char kEnableTapDevices[] = "enable_tap_devices"; void CuttlefishConfig::MutableInstanceSpecific::set_enable_tap_devices( const bool enable_tap_devices) { diff --git a/base/cvd/cuttlefish/host/libs/config/gpu_mode.cc b/base/cvd/cuttlefish/host/libs/config/gpu_mode.cc index 21f0af7974a..5a14b8c0291 100644 --- a/base/cvd/cuttlefish/host/libs/config/gpu_mode.cc +++ b/base/cvd/cuttlefish/host/libs/config/gpu_mode.cc @@ -40,6 +40,8 @@ Result GpuModeFromString(std::string_view mode) { return GpuMode::GfxstreamGuestAngleHostSwiftshader; } else if (mode == kGpuModeGfxstreamGuestAngleHostLavapipe) { return GpuMode::GfxstreamGuestAngleHostLavapipe; + } else if (mode == kGpuModeGuestLavapipe) { + return GpuMode::GuestLavapipe; } else if (mode == kGpuModeGuestSwiftshader) { return GpuMode::GuestSwiftshader; } else if (mode == kGpuModeNone) { @@ -73,6 +75,9 @@ std::string_view format_as(GpuMode mode) { case GpuMode::GfxstreamGuestAngleHostSwiftshader: return kGpuModeGfxstreamGuestAngleHostSwiftshader; break; + case GpuMode::GuestLavapipe: + return kGpuModeGuestLavapipe; + break; case GpuMode::GuestSwiftshader: return kGpuModeGuestSwiftshader; break; @@ -93,4 +98,8 @@ bool IsGfxstreamGuestAngleMode(GpuMode mode) { mode == GpuMode::GfxstreamGuestAngleHostSwiftshader; } +bool IsGuestRenderingMode(GpuMode mode) { + return mode == GpuMode::GuestLavapipe || mode == GpuMode::GuestSwiftshader; +} + } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/libs/config/gpu_mode.h b/base/cvd/cuttlefish/host/libs/config/gpu_mode.h index 2461a1aaf02..3fe38c52f57 100644 --- a/base/cvd/cuttlefish/host/libs/config/gpu_mode.h +++ b/base/cvd/cuttlefish/host/libs/config/gpu_mode.h @@ -31,6 +31,7 @@ enum class GpuMode { GfxstreamGuestAngle, GfxstreamGuestAngleHostLavapipe, GfxstreamGuestAngleHostSwiftshader, + GuestLavapipe, GuestSwiftshader, None, }; @@ -45,6 +46,7 @@ inline constexpr std::string_view kGpuModeGfxstreamGuestAngleHostLavapipe = "gfxstream_guest_angle_host_lavapipe"; inline constexpr std::string_view kGpuModeGfxstreamGuestAngleHostSwiftshader = "gfxstream_guest_angle_host_swiftshader"; +inline constexpr std::string_view kGpuModeGuestLavapipe = "guest_lavapipe"; inline constexpr std::string_view kGpuModeGuestSwiftshader = "guest_swiftshader"; inline constexpr std::string_view kGpuModeNone = "none"; @@ -73,6 +75,9 @@ void AbslStringify(Sink& sink, GpuMode mode) { case GpuMode::GfxstreamGuestAngleHostSwiftshader: sink.Append(kGpuModeGfxstreamGuestAngleHostSwiftshader); break; + case GpuMode::GuestLavapipe: + sink.Append(kGpuModeGuestLavapipe); + break; case GpuMode::GuestSwiftshader: sink.Append(kGpuModeGuestSwiftshader); break; @@ -89,4 +94,6 @@ std::string_view format_as(GpuMode mode); // For libfmt bool IsGfxstreamMode(GpuMode mode); bool IsGfxstreamGuestAngleMode(GpuMode mode); +bool IsGuestRenderingMode(GpuMode mode); + } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/libs/config/kernel_args.cpp b/base/cvd/cuttlefish/host/libs/config/kernel_args.cpp index 26561270951..342a4e1437a 100644 --- a/base/cvd/cuttlefish/host/libs/config/kernel_args.cpp +++ b/base/cvd/cuttlefish/host/libs/config/kernel_args.cpp @@ -109,6 +109,11 @@ std::vector KernelCommandLineFromConfig( const CuttlefishConfig::InstanceSpecific& instance) { std::vector kernel_cmdline; AppendVector(&kernel_cmdline, VmManagerKernelCmdline(config, instance)); + if (instance.enable_pkvm() && instance.target_arch() == Arch::Arm64) { + kernel_cmdline.push_back("kvm-arm.mode=protected"); + // Makes the guest kernel's early loader modprobe the pKVM IOMMU driver. + kernel_cmdline.push_back("kvm-arm.protected_modules=pkvm_iommu_temp"); + } AppendVector(&kernel_cmdline, config.extra_kernel_cmdline()); return kernel_cmdline; } diff --git a/base/cvd/cuttlefish/host/libs/config/media.cpp b/base/cvd/cuttlefish/host/libs/config/media.cpp index 7adbadaf61c..5ac4103b2e8 100644 --- a/base/cvd/cuttlefish/host/libs/config/media.cpp +++ b/base/cvd/cuttlefish/host/libs/config/media.cpp @@ -16,6 +16,7 @@ #include "cuttlefish/host/libs/config/media.h" +#include #include #include #include @@ -38,30 +39,30 @@ static constexpr char kMediaTypeV4l2Proxy[] = "v4l2_proxy"; Result> ParseMediaConfig( const std::string& flag) { - std::unordered_map props; - if (!flag.empty()) { - const std::vector pairs = absl::StrSplit(flag, ","); - for (const std::string& pair : pairs) { - const std::vector keyvalue = absl::StrSplit(pair, "="); - CF_EXPECT_EQ(keyvalue.size(), 2, - "Invalid media flag key-value: \"" << flag << "\""); - const std::string& prop_key = keyvalue[0]; - const std::string& prop_val = keyvalue[1]; - props[prop_key] = prop_val; - } - } + const std::vector parts = absl::StrSplit(flag, ":"); + CF_EXPECT(!parts.empty(), "Invalid media flag: \"" << flag << "\""); - auto type_it = props.find("type"); - CF_EXPECT(type_it != props.end(), "Missing media type"); + const std::string& type_str = parts[0]; CuttlefishConfig::MediaType type{CuttlefishConfig::MediaType::kUnknown}; - if (type_it->second == kMediaTypeV4l2EmulatedCameraSPlane) { + if (type_str == kMediaTypeV4l2EmulatedCameraSPlane) { type = CuttlefishConfig::MediaType::kV4l2EmulatedCameraSPlane; - } else if (type_it->second == kMediaTypeV4l2EmulatedCameraMPlane) { + } else if (type_str == kMediaTypeV4l2EmulatedCameraMPlane) { type = CuttlefishConfig::MediaType::kV4l2EmulatedCameraMPlane; - } else if (type_it->second == kMediaTypeV4l2Proxy) { + } else if (type_str == kMediaTypeV4l2Proxy) { type = CuttlefishConfig::MediaType::kV4l2Proxy; } else { - return CF_ERRF("Unknown media type value: \"{}\"", type_it->second); + return CF_ERRF("Unknown media type value: \"{}\"", type_str); + } + + std::unordered_map props; + for (size_t i = 1; i < parts.size(); ++i) { + const std::vector keyvalue = absl::StrSplit(parts[i], "="); + CF_EXPECT_EQ(keyvalue.size(), 2, + "Invalid media flag key-value: \"" << parts[i] << "\" in \"" + << flag << "\""); + const std::string& prop_key = keyvalue[0]; + const std::string& prop_val = keyvalue[1]; + props[prop_key] = prop_val; } std::string lens_facing = ""; diff --git a/base/cvd/cuttlefish/host/libs/config/media.h b/base/cvd/cuttlefish/host/libs/config/media.h index 946cebf64fc..fd329ce2021 100644 --- a/base/cvd/cuttlefish/host/libs/config/media.h +++ b/base/cvd/cuttlefish/host/libs/config/media.h @@ -24,15 +24,18 @@ namespace cuttlefish { constexpr const char kMediaFlag[] = "media"; constexpr const char kMediaHelp[] = - "Comma separated key=value pairs of media device properties. Supported " - "properties:\n" - " 'type': optional, defaults to 'v4l2_emulated_camera_splane', supported values:\n" - " 'v4l2_emulated_camera_splane': emulated media capture device (single-plane)\n" - " 'v4l2_emulated_camera_mplane': emulated media capture device (multi-plane)\n" + "Colon separated media device properties: " + "\"[type]:[key1]=[val1]:[key2]=[val2]\". " + "Supported types:\n" + " 'v4l2_emulated_camera_splane': emulated media capture device " + "(single-plane)\n" + " 'v4l2_emulated_camera_mplane': emulated media capture device " + "(multi-plane)\n" " 'v4l2_proxy': proxy a host V4L2 device into the guest\n" + "Supported keys:\n" " 'lens_facing': optional, supported values: 'FRONT', 'BACK', 'EXTERNAL'\n" "Example usage:\n" - " --media=type=v4l2_emulated_camera_splane,lens_facing=BACK\n"; + " --media=v4l2_emulated_camera_mplane:lens_facing=BACK\n"; Result> ParseMediaConfig( const std::string& flag); diff --git a/base/cvd/cuttlefish/host/libs/metrics/metrics_conversion.cc b/base/cvd/cuttlefish/host/libs/metrics/metrics_conversion.cc index 8dd2d76ad84..3fd691a3808 100644 --- a/base/cvd/cuttlefish/host/libs/metrics/metrics_conversion.cc +++ b/base/cvd/cuttlefish/host/libs/metrics/metrics_conversion.cc @@ -120,6 +120,9 @@ CuttlefishFlags_GpuMode ConvertGpuMode(GpuMode mode) { case GpuMode::GuestSwiftshader: return CuttlefishFlags_GpuMode:: CuttlefishFlags_GpuMode_CUTTLEFISH_FLAGS_GPU_MODE_GUEST_SWIFTSHADER; + case GpuMode::GuestLavapipe: + return CuttlefishFlags_GpuMode:: + CuttlefishFlags_GpuMode_CUTTLEFISH_FLAGS_GPU_MODE_GUEST_LAVAPIPE; case GpuMode::None: return CuttlefishFlags_GpuMode:: CuttlefishFlags_GpuMode_CUTTLEFISH_FLAGS_GPU_MODE_NONE; diff --git a/base/cvd/cuttlefish/host/libs/screen_connector/screen_connector.h b/base/cvd/cuttlefish/host/libs/screen_connector/screen_connector.h index 0dda9795330..d9e0f41985b 100644 --- a/base/cvd/cuttlefish/host/libs/screen_connector/screen_connector.h +++ b/base/cvd/cuttlefish/host/libs/screen_connector/screen_connector.h @@ -72,6 +72,7 @@ class ScreenConnector : public ScreenConnectorFrameRenderer { GpuMode::GfxstreamGuestAngle, GpuMode::GfxstreamGuestAngleHostSwiftshader, GpuMode::GfxstreamGuestAngleHostLavapipe, + GpuMode::GuestLavapipe, GpuMode::GuestSwiftshader, }; if (!Contains(valid_gpu_modes, instance.gpu_mode())) { diff --git a/base/cvd/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp b/base/cvd/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp index 36eee2741df..c18f93b453d 100644 --- a/base/cvd/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp +++ b/base/cvd/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp @@ -76,7 +76,18 @@ CrosvmManager::ConfigureGraphics( std::unordered_map bootconfig_args; - if (instance.gpu_mode() == GpuMode::GuestSwiftshader) { + if (instance.gpu_mode() == GpuMode::GuestLavapipe) { + bootconfig_args = { + {"androidboot.cpuvulkan.version", std::to_string(VK_API_VERSION_1_4)}, + {"androidboot.hardware.gralloc", "minigbm"}, + {"androidboot.hardware.hwcomposer", instance.hwcomposer()}, + {"androidboot.hardware.hwcomposer.mode", "client"}, + {"androidboot.hardware.hwcomposer.display_finder_mode", "drm"}, + {"androidboot.hardware.egl", "angle"}, + {"androidboot.hardware.vulkan", "lvp"}, + {"androidboot.opengles.version", "196609"}, // OpenGL ES 3.1 + }; + } else if (instance.gpu_mode() == GpuMode::GuestSwiftshader) { bootconfig_args = { {"androidboot.cpuvulkan.version", std::to_string(VK_API_VERSION_1_3)}, {"androidboot.hardware.gralloc", "minigbm"}, @@ -323,7 +334,7 @@ Result BuildVhostUserGpu( gpu_device_cmd.Cmd().AddParameter("gpu"); const GpuMode gpu_mode = instance.gpu_mode(); - CF_EXPECT(IsGfxstreamMode(gpu_mode) || gpu_mode == GpuMode::GuestSwiftshader, + CF_EXPECT(IsGfxstreamMode(gpu_mode) || IsGuestRenderingMode(gpu_mode), "GPU mode " << GpuModeString(gpu_mode) << " not yet supported with vhost user gpu."); @@ -333,7 +344,7 @@ Result BuildVhostUserGpu( // Why does this need JSON instead of just following the normal flags style... Json::Value gpu_params_json; gpu_params_json["pci-address"] = gpu_pci_address; - if (gpu_mode == GpuMode::GuestSwiftshader) { + if (IsGuestRenderingMode(gpu_mode)) { gpu_params_json["backend"] = "2D"; } else if (gpu_mode == GpuMode::Gfxstream) { gpu_params_json["context-types"] = "gfxstream-gles:gfxstream-vulkan"; @@ -479,7 +490,7 @@ Result ConfigureGpu(const CuttlefishConfig& config, Command* crosvm_cmd) { crosvm_cmd->AddParameter("--wayland-sock=", instance.frames_socket_path()); } - if (gpu_mode == GpuMode::GuestSwiftshader) { + if (IsGuestRenderingMode(gpu_mode)) { crosvm_cmd->AddParameter("--gpu=", gpu_displays_string, "backend=2D", gpu_common_string); } else if (gpu_mode == GpuMode::DrmVirgl) { @@ -563,6 +574,12 @@ Result> CrosvmManager::StartCommands( crosvm_cmd.AddKvmPath(config.kvm_path()); } + // A pkvm guest needs to boot at virtual EL2; crosvm verifies host support + // (KVM_CAP_ARM_EL2, GICv3 irqchip) and fails to start otherwise. + if (instance.enable_pkvm()) { + crosvm_cmd.Cmd().AddParameter("--nested=on"); + } + if (!instance.smt()) { crosvm_cmd.Cmd().AddParameter("--no-smt"); } @@ -618,7 +635,14 @@ Result> CrosvmManager::StartCommands( } if (instance.hwcomposer() != kHwComposerNone) { - const bool pmem_disabled = instance.mte() || !instance.use_pmem(); + // pmem is disabled for pkvm guests: the pmem backing files are mmap'd + // MAP_SHARED, and host writeback of storage-backed pages triggers + // mmu-notifiers that arm64 KVM currently handles by tearing down the + // guest's entire shadow stage-2 instead of a range-scoped invalidation + // (see the nested_mmu reverse-mapping TODO in arch/arm64/kvm/mmu.c), + // which makes the guest extremely slow. + const bool pmem_disabled = + instance.mte() || instance.enable_pkvm() || !instance.use_pmem(); const std::string pmem_path = HwcomposerPmemPath(instance); if (!pmem_disabled && FileExists(pmem_path)) { crosvm_cmd.Cmd().AddParameter("--pmem=path=", pmem_path); @@ -705,7 +729,8 @@ Result> CrosvmManager::StartCommands( } #endif - const bool pmem_disabled = instance.mte() || !instance.use_pmem(); + const bool pmem_disabled = + instance.mte() || instance.enable_pkvm() || !instance.use_pmem(); const std::string access_kregistry = AccessKregistryPath(instance); if (!pmem_disabled && FileExists(access_kregistry)) { crosvm_cmd.Cmd().AddParameter("--pmem=path=", access_kregistry); diff --git a/base/cvd/cuttlefish/host/libs/vm_manager/gem5_manager.cpp b/base/cvd/cuttlefish/host/libs/vm_manager/gem5_manager.cpp index eb55a0eeff0..2d38b61701a 100644 --- a/base/cvd/cuttlefish/host/libs/vm_manager/gem5_manager.cpp +++ b/base/cvd/cuttlefish/host/libs/vm_manager/gem5_manager.cpp @@ -220,8 +220,20 @@ Gem5Manager::ConfigureGraphics( std::unordered_map bootconfig_args; - if (instance.gpu_mode() == GpuMode::GuestSwiftshader) { - LOG(INFO) << "We are in SwiftShader mode"; + if (instance.gpu_mode() == GpuMode::GuestLavapipe) { + VLOG(0) << "We are in Lavapipe mode"; + bootconfig_args = { + {"androidboot.cpuvulkan.version", std::to_string(VK_API_VERSION_1_1)}, + {"androidboot.hardware.gralloc", "minigbm"}, + {"androidboot.hardware.hwcomposer", "ranchu"}, + {"androidboot.hardware.hwcomposer.mode", "noop"}, + {"androidboot.hardware.hwcomposer.display_finder_mode", "gem5"}, + {"androidboot.hardware.egl", "angle"}, + {"androidboot.hardware.vulkan", "lvp"}, + {"androidboot.opengles.version", "196609"}, // OpenGL ES 3.1 + }; + } else if (instance.gpu_mode() == GpuMode::GuestSwiftshader) { + VLOG(0) << "We are in SwiftShader mode"; bootconfig_args = { {"androidboot.cpuvulkan.version", std::to_string(VK_API_VERSION_1_1)}, {"androidboot.hardware.gralloc", "minigbm"}, diff --git a/base/cvd/cuttlefish/host/libs/vm_manager/qemu_manager.cpp b/base/cvd/cuttlefish/host/libs/vm_manager/qemu_manager.cpp index c85c364d7ab..fa1eedbb504 100644 --- a/base/cvd/cuttlefish/host/libs/vm_manager/qemu_manager.cpp +++ b/base/cvd/cuttlefish/host/libs/vm_manager/qemu_manager.cpp @@ -129,7 +129,18 @@ QemuManager::ConfigureGraphics( std::unordered_map bootconfig_args; const GpuMode gpu_mode = instance.gpu_mode(); - if (gpu_mode == GpuMode::GuestSwiftshader) { + if (gpu_mode == GpuMode::GuestLavapipe) { + bootconfig_args = { + {"androidboot.cpuvulkan.version", std::to_string(VK_API_VERSION_1_4)}, + {"androidboot.hardware.gralloc", "minigbm"}, + {"androidboot.hardware.hwcomposer", instance.hwcomposer()}, + {"androidboot.hardware.hwcomposer.mode", "client"}, + {"androidboot.hardware.egl", "angle"}, + {"androidboot.hardware.vulkan", "lvp"}, + // OpenGL ES 3.1 + {"androidboot.opengles.version", "196609"}, + }; + } else if (gpu_mode == GpuMode::GuestSwiftshader) { bootconfig_args = { {"androidboot.cpuvulkan.version", std::to_string(VK_API_VERSION_1_2)}, {"androidboot.hardware.gralloc", "minigbm"}, @@ -446,8 +457,7 @@ Result> QemuManager::StartCommands( qemu_cmd.AddParameter("-vnc"); qemu_cmd.AddParameter("127.0.0.1:", instance.qemu_vnc_server_port()); - } else if (gpu_mode == GpuMode::GuestSwiftshader || - IsGfxstreamMode(gpu_mode)) { + } else if (IsGuestRenderingMode(gpu_mode) || IsGfxstreamMode(gpu_mode)) { qemu_cmd.AddParameter("-vnc"); qemu_cmd.AddParameter("127.0.0.1:", instance.qemu_vnc_server_port()); } else { diff --git a/base/cvd/cuttlefish/host/libs/web/BUILD.bazel b/base/cvd/cuttlefish/host/libs/web/BUILD.bazel index 77782ea71cc..2c6584d5ffc 100644 --- a/base/cvd/cuttlefish/host/libs/web/BUILD.bazel +++ b/base/cvd/cuttlefish/host/libs/web/BUILD.bazel @@ -32,6 +32,7 @@ cf_cc_library( "//cuttlefish/host/libs/web:android_build_url", "//cuttlefish/host/libs/web:build_api", "//cuttlefish/host/libs/web:credential_source", + "//cuttlefish/host/libs/web:parse_time", "//cuttlefish/host/libs/web/cas:cas_downloader", "//cuttlefish/host/libs/web/http_client", "//cuttlefish/host/libs/web/http_client:http_file", @@ -248,3 +249,22 @@ cf_cc_library( "@zlib", ], ) + +cf_cc_library( + name = "parse_time", + srcs = ["parse_time.cc"], + hdrs = ["parse_time.h"], + deps = [ + "//cuttlefish/result:expect", + "//cuttlefish/result:result_type", + ], +) + +cf_cc_test( + name = "parse_time_test", + srcs = ["parse_time_test.cc"], + deps = [ + "//cuttlefish/host/libs/web:parse_time", + "//cuttlefish/result:result_matchers", + ], +) diff --git a/base/cvd/cuttlefish/host/libs/web/android_build_api.cpp b/base/cvd/cuttlefish/host/libs/web/android_build_api.cpp index 878b3470388..8ca9c004b99 100644 --- a/base/cvd/cuttlefish/host/libs/web/android_build_api.cpp +++ b/base/cvd/cuttlefish/host/libs/web/android_build_api.cpp @@ -16,17 +16,14 @@ #include "cuttlefish/host/libs/web/android_build_api.h" #include -#include #include #include #include -#include #include #include #include #include -#include #include #include #include @@ -53,6 +50,7 @@ #include "cuttlefish/host/libs/web/http_client/http_client.h" #include "cuttlefish/host/libs/web/http_client/http_file.h" #include "cuttlefish/host/libs/web/http_client/http_json.h" +#include "cuttlefish/host/libs/web/parse_time.h" #include "cuttlefish/host/libs/zip/libzip_cc/seekable_source.h" #include "cuttlefish/host/libs/zip/libzip_cc/writable_source.h" #include "cuttlefish/host/libs/zip/remote_zip.h" @@ -90,15 +88,6 @@ Result GetResponseJson(const HttpResponse& response, return response.data; } -Result ParseTime(std::string_view str) { - std::stringstream stream = std::stringstream(std::string(str)); - tm time_tm; - stream >> std::get_time(&time_tm, "%Y-%m-%dT%H:%M:%S."); - CF_EXPECTF(!!stream, "Failed to parse time '{}'", str); - - return std::chrono::system_clock::from_time_t(mktime(&time_tm)); -} - } // namespace AndroidBuildApi::AndroidBuildApi(HttpClient& http_client, diff --git a/base/cvd/cuttlefish/host/libs/web/parse_time.cc b/base/cvd/cuttlefish/host/libs/web/parse_time.cc new file mode 100644 index 00000000000..d53a0322012 --- /dev/null +++ b/base/cvd/cuttlefish/host/libs/web/parse_time.cc @@ -0,0 +1,40 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cuttlefish/host/libs/web/parse_time.h" + +#include + +#include +#include +#include +#include +#include + +#include "cuttlefish/result/expect.h" +#include "cuttlefish/result/result_type.h" + +namespace cuttlefish { + +Result ParseTime(std::string_view str) { + std::stringstream stream = std::stringstream(std::string(str)); + tm time_tm; + stream >> std::get_time(&time_tm, "%Y-%m-%dT%H:%M:%S"); + CF_EXPECTF(!!stream, "Failed to parse time '{}'", str); + + return std::chrono::system_clock::from_time_t(mktime(&time_tm)); +} + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/libs/web/parse_time.h b/base/cvd/cuttlefish/host/libs/web/parse_time.h new file mode 100644 index 00000000000..4c9c13bac14 --- /dev/null +++ b/base/cvd/cuttlefish/host/libs/web/parse_time.h @@ -0,0 +1,28 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include + +#include "cuttlefish/result/result_type.h" + +namespace cuttlefish { + +Result ParseTime(std::string_view str); + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/libs/web/parse_time_test.cc b/base/cvd/cuttlefish/host/libs/web/parse_time_test.cc new file mode 100644 index 00000000000..44c54f4de32 --- /dev/null +++ b/base/cvd/cuttlefish/host/libs/web/parse_time_test.cc @@ -0,0 +1,35 @@ +// +// Copyright (C) 2026 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cuttlefish/host/libs/web/parse_time.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "cuttlefish/result/result_matchers.h" + +namespace cuttlefish { +namespace { + +TEST(ParseTimeTest, WithMilliseconds) { + EXPECT_THAT(ParseTime("2026-08-03T11:56:20.100Z"), IsOk()); +} + +TEST(ParseTimeTest, WithoutMilliseconds) { + EXPECT_THAT(ParseTime("2026-08-03T11:56:20Z"), IsOk()); +} + +} // namespace +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/package/BUILD.bazel b/base/cvd/cuttlefish/package/BUILD.bazel index cbc54adb2b1..2fb8a9eb2c6 100644 --- a/base/cvd/cuttlefish/package/BUILD.bazel +++ b/base/cvd/cuttlefish/package/BUILD.bazel @@ -89,7 +89,7 @@ package_files( "cuttlefish-common/bin/rootcanal": "@rootcanal", "cuttlefish-common/bin/run_cvd": "//cuttlefish/host/commands/run_cvd", "cuttlefish-common/bin/screen_recording_server": "//cuttlefish/host/commands/screen_recording_server", - # "cuttlefish-common/bin/secure_env": "//cuttlefish/host/commands/secure_env", # TODO: schuffelen - make this more complete + "cuttlefish-common/bin/secure_env": "//cuttlefish/host/commands/secure_env", # TODO: schuffelen - make this more complete "cuttlefish-common/bin/sefcontext_compile": "@selinux//:sefcontext_compile", "cuttlefish-common/bin/sensors_simulator": "//cuttlefish/host/commands/sensors_simulator", "cuttlefish-common/bin/simg2img": "@android_system_core//:simg2img", @@ -117,6 +117,8 @@ package_files( "cuttlefish-common/etc/cvd_custom_action_config/cuttlefish_example_action_config.json": "//cuttlefish/host/example_custom_actions:custom_action_config.json", "cuttlefish-common/etc/modem_simulator/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml": "//cuttlefish/host/commands/modem_simulator:etc/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml", "cuttlefish-common/etc/modem_simulator/files/iccprofile_for_sim0.xml": "//cuttlefish/host/commands/modem_simulator:etc/files/iccprofile_for_sim0.xml", + "cuttlefish-common/etc/modem_simulator/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml": "//cuttlefish/host/commands/modem_simulator:etc/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml", + "cuttlefish-common/etc/modem_simulator/files/iccprofile_for_sim1.xml": "//cuttlefish/host/commands/modem_simulator:etc/files/iccprofile_for_sim1.xml", "cuttlefish-common/etc/modem_simulator/files/numeric_operator.xml": "//cuttlefish/host/commands/modem_simulator:etc/files/numeric_operator.xml", # "cuttlefish-common/bin/crosvm": "@crosvm_bin//:crosvm__crosvm", # TODO: b/402274999 - currently requires --enable_sandbox=false "cuttlefish-common/usr/share/webrtc/assets/client.html": "//cuttlefish/host/frontend/webrtc/html_client:client.html", diff --git a/base/cvd/external_proto/cf_flags.proto b/base/cvd/external_proto/cf_flags.proto index b7c8df9dd3a..177fde4b1a4 100644 --- a/base/cvd/external_proto/cf_flags.proto +++ b/base/cvd/external_proto/cf_flags.proto @@ -82,7 +82,7 @@ message CuttlefishFlags { string extra_kernel_cmdline = 9; - // Next index: 10 + // Next index: 11 enum GpuMode { CUTTLEFISH_FLAGS_GPU_MODE_UNSPECIFIED = 0; CUTTLEFISH_FLAGS_GPU_MODE_AUTO = 1; @@ -94,6 +94,7 @@ message CuttlefishFlags { CUTTLEFISH_FLAGS_GPU_MODE_GUEST_GFXSTREAM_GUEST_ANGLE_HOST_LAVAPIPE = 7; CUTTLEFISH_FLAGS_GPU_MODE_GUEST_VIRGL_RENDERER = 8; CUTTLEFISH_FLAGS_GPU_MODE_NONE = 9; + CUTTLEFISH_FLAGS_GPU_MODE_GUEST_LAVAPIPE = 10; } // The graphics backend that the user asked for through flags. diff --git a/cloud-init-image-for-cuttlefish/user-data b/cloud-init-image-for-cuttlefish/user-data index 460a0f1b03f..d590591fb23 100644 --- a/cloud-init-image-for-cuttlefish/user-data +++ b/cloud-init-image-for-cuttlefish/user-data @@ -22,6 +22,13 @@ growpart: - /dev/sda1 write_files: + - path: /etc/apt/sources.list.d/debian-snapshot.list + permissions: '0644' + owner: root:root + content: | + # Frozen snapshot: pins the kernel and NVIDIA versions installed by + # after_install_1.sh; bump the timestamp and those versions together. + deb [check-valid-until=no] https://snapshot.debian.org/archive/debian/20260315T022902Z/ trixie-backports main contrib non-free non-free-firmware - path: /root/after_install_1.sh permissions: '0755' owner: root:root @@ -55,38 +62,34 @@ write_files: # Install kernel DEBIAN_DISTRIBUTION="$(lsb_release -c -s)" - #apt-get install -y '^linux-image-6.1.*aosp14-linaro.*' '^linux-headers-6.1.*aosp14-linaro.*' - has_backports=$(apt-cache policy | grep "${DEBIAN_DISTRIBUTION}-backports") - if [ x"$has_backports" != x"" ]; then - apt install -y -t "${DEBIAN_DISTRIBUTION}-backports" "linux-headers-${DEBIAN_ARCH}" - apt install -y -t "${DEBIAN_DISTRIBUTION}-backports" "linux-image-${DEBIAN_ARCH}" - fi + KERNEL_ABI="6.18.15+deb13-arm64" + KERNEL_DEB_VERSION="6.18.15-1~bpo13+1" + NVIDIA_DEB_VERSION="550.163.01-4~bpo13+1" + apt-get -o Acquire::Retries=5 update + apt-get install -y -o Acquire::Retries=5 \ + "linux-image-${KERNEL_ABI}=${KERNEL_DEB_VERSION}" \ + "linux-headers-${KERNEL_ABI}=${KERNEL_DEB_VERSION}" || exit 1 # Install nVidia or AMD GPU driver nvidia_gpu=$(lspci | grep -i nvidia) amd_gpu=$(lspci | grep VGA | grep AMD) if [ x"$amd_gpu" != x"" ]; then # # Install amd firmware - # if [ x"$has_backports" != x"" ]; then - # apt-get install -y -t ${DEBIAN_DISTRIBUTION}-backports firmware-amd-graphics - # else - # apt-get install -y firmware-amd-graphics - # fi + # apt-get install -y firmware-amd-graphics # sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT=\"\(.*\)\"/GRUB_CMDLINE_LINUX_DEFAULT=\"\1 amdgpu.runpm=0 amdgpu.dc=0\"/' /etc/default/grub # dpkg-reconfigure -fnoninteractive "grub-efi-${DEBIAN_ARCH}" echo "AMD GPU detected — skipping GPU driver installation." elif [ x"$nvidia_gpu" != x"" ]; then - # Install nvidia driver - if [ x"$has_backports" != x"" ]; then - DEBIAN_FRONTEND=noninteractive apt-get install -y -t "${DEBIAN_DISTRIBUTION}-backports" -q --force-yes nvidia-kernel-dkms - DEBIAN_FRONTEND=noninteractive apt-get install -y -t "${DEBIAN_DISTRIBUTION}-backports" -q --force-yes nvidia-driver - DEBIAN_FRONTEND=noninteractive apt-get install -y -t "${DEBIAN_DISTRIBUTION}-backports" -q --force-yes firmware-misc-nonfree - else - DEBIAN_FRONTEND=noninteractive apt-get install -y -q --force-yes nvidia-open-kernel-dkms - DEBIAN_FRONTEND=noninteractive apt-get install -y -q --force-yes nvidia-driver - DEBIAN_FRONTEND=noninteractive apt-get install -y -q --force-yes firmware-misc-nonfree - fi + # Install nvidia driver, pinned to the version validated against the + # pinned kernel. + DEBIAN_FRONTEND=noninteractive apt-get install -y -q -o Acquire::Retries=5 \ + -t "${DEBIAN_DISTRIBUTION}-backports" \ + "nvidia-open-kernel-dkms=${NVIDIA_DEB_VERSION}" \ + "nvidia-driver=${NVIDIA_DEB_VERSION}" || exit 1 + DEBIAN_FRONTEND=noninteractive apt-get install -y -q firmware-misc-nonfree + apt-mark hold nvidia-open-kernel-dkms nvidia-driver fi + rm -f /etc/apt/sources.list.d/debian-snapshot.list # End of Install kernel # Install android cuttlefish packages diff --git a/container/image/Containerfile b/container/image/Containerfile index 9b6eb58922b..bcc74670df1 100644 --- a/container/image/Containerfile +++ b/container/image/Containerfile @@ -32,6 +32,18 @@ RUN apt install -y --no-install-recommends \ sudo RUN update-ca-certificates +# When setting `auto` value, nginx spawns worker processes as the number of CPU +# cores. It's discouraged in any multiple container scenarios, as it spawns too +# much worker processes in huge machines, but not actually required. +RUN sed -i 's/worker_processes auto;/worker_processes 2;/' \ + /etc/nginx/nginx.conf +# When setting `auto` value, nginx select which CPU core executing worker +# processes. It's discouraged in any multiple container scenarios, as all +# containers may use CPU core `0`-`N` when `worker_processes` is set as `N+1`. +# When setting no values, OS scheduler manages it instead. +RUN sed -i 's/worker_cpu_affinity auto;/# worker_cpu_affinity auto;/' \ + /etc/nginx/nginx.conf + COPY ./container/image/guest/run_services.sh /root/ RUN chmod +x /root/run_services.sh diff --git a/e2etests/cvd/common/common.go b/e2etests/cvd/common/common.go index 7c4ff4312ed..c827da9a8e9 100644 --- a/e2etests/cvd/common/common.go +++ b/e2etests/cvd/common/common.go @@ -629,7 +629,7 @@ func RunXts(t *testing.T, cuttlefishArgs FetchAndCreateArgs, xtsArgs XtsArgs) { testname := fmt.Sprintf("%s#%s", xtsTestCase.Name, xtsTest.Name) t.Run(testname, func(t *testing.T) { log.Printf("%s result: %s", testname, xtsTest.Result) - if xtsTest.Result == "failed" { + if strings.HasPrefix(xtsTest.Result, "fail") { t.Error("XTS test failed, see logs") } }) diff --git a/e2etests/cvd/media_tests/main_test.go b/e2etests/cvd/media_tests/main_test.go index eb14d63fb39..0e0d1440379 100644 --- a/e2etests/cvd/media_tests/main_test.go +++ b/e2etests/cvd/media_tests/main_test.go @@ -44,7 +44,7 @@ func TestEmulatedCameraV4l2Compliance(t *testing.T) { t.Fatal(err) } - if err := c.CVDCreate(e2etests.CreateArgs{Args: []string{"--media=type=v4l2_emulated_camera_splane"}}); err != nil { + if err := c.CVDCreate(e2etests.CreateArgs{Args: []string{"--media=v4l2_emulated_camera_splane"}}); err != nil { t.Fatal(err) } diff --git a/e2etests/debian_substitution_marker b/e2etests/debian_substitution_marker index 83c485fcbfb..67931cfb2e4 100644 --- a/e2etests/debian_substitution_marker +++ b/e2etests/debian_substitution_marker @@ -275,6 +275,16 @@ symlinks: { link_name: "etc/modem_simulator/files/iccprofile_for_sim0.xml" } +symlinks: { + target: "/usr/lib/cuttlefish-common/etc/modem_simulator/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml" + link_name: "etc/modem_simulator/files/iccprofile_for_sim1_for_CtsCarrierApiTestCases.xml" +} + +symlinks: { + target: "/usr/lib/cuttlefish-common/etc/modem_simulator/files/iccprofile_for_sim1.xml" + link_name: "etc/modem_simulator/files/iccprofile_for_sim1.xml" +} + symlinks: { target: "/usr/lib/cuttlefish-common/etc/modem_simulator/files/numeric_operator.xml" link_name: "etc/modem_simulator/files/numeric_operator.xml" diff --git a/frontend/src/host_orchestrator/orchestrator/BUILD.bazel b/frontend/src/host_orchestrator/orchestrator/BUILD.bazel index 40e16b69a9f..7fb7dfcdfe1 100644 --- a/frontend/src/host_orchestrator/orchestrator/BUILD.bazel +++ b/frontend/src/host_orchestrator/orchestrator/BUILD.bazel @@ -12,7 +12,6 @@ go_library( "displayremoveaction.go", "displayscreenshotaction.go", "execcvdcommandaction.go", - "fetchartifactsaction.go", "getscreenrecordingaction.go", "imagedirectories.go", "instancemanager.go", @@ -43,9 +42,7 @@ go_test( name = "orchestrator_test", srcs = [ "controller_test.go", - "createcvdaction_test.go", "imagedirectories_test.go", - "instancemanager_test.go", "listcvdsaction_test.go", "operation_test.go", "userartifacts_test.go", @@ -56,7 +53,6 @@ go_test( "//src/host_orchestrator/api/v1:api", "//src/host_orchestrator/orchestrator/debug", "//src/host_orchestrator/orchestrator/testing", - "//src/liboperator/operator", "@com_github_google_btree//:btree", "@com_github_google_go_cmp//cmp", "@com_github_gorilla_mux//:mux", diff --git a/frontend/src/host_orchestrator/orchestrator/userartifacts.go b/frontend/src/host_orchestrator/orchestrator/userartifacts.go index 20611b87d90..6d791445f1b 100644 --- a/frontend/src/host_orchestrator/orchestrator/userartifacts.go +++ b/frontend/src/host_orchestrator/orchestrator/userartifacts.go @@ -312,6 +312,13 @@ func extractFile(dst string, src string) error { return nil } +// isSafeToExtract reports whether target (already joined onto dst) stays within +// dst, rejecting paths that escape via ".." components or an absolute path +func isSafeToExtract(dst string, target string) bool { + rel, err := filepath.Rel(dst, target) + return err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(os.PathSeparator)) +} + func untar(dst string, src string) error { r, err := os.Open(src) if err != nil { @@ -336,6 +343,9 @@ func untar(dst string, src string) error { continue } target := filepath.Join(dst, header.Name) + if !isSafeToExtract(dst, target) { + return fmt.Errorf("archive entry %q escapes the extraction directory", header.Name) + } switch header.Typeflag { case tar.TypeDir: if _, err := os.Stat(target); err != nil { @@ -356,6 +366,13 @@ func untar(dst string, src string) error { } f.Close() case tar.TypeSymlink: + linkTarget := header.Linkname + if !filepath.IsAbs(linkTarget) { + linkTarget = filepath.Join(filepath.Dir(target), linkTarget) + } + if !isSafeToExtract(dst, linkTarget) { + return fmt.Errorf("symlink entry %q targets outside the extraction directory", header.Name) + } if err := os.Symlink(header.Linkname, target); err != nil { return err } @@ -391,7 +408,11 @@ func unzip(dstDir string, src string) error { if f.Mode().IsDir() { continue } - if err := extractTo(filepath.Join(dstDir, f.Name), f); err != nil { + target := filepath.Join(dstDir, f.Name) + if !isSafeToExtract(dstDir, target) { + return fmt.Errorf("archive entry %q escapes the extraction directory", f.Name) + } + if err := extractTo(target, f); err != nil { return err } } diff --git a/frontend/src/host_orchestrator/orchestrator/userartifacts_test.go b/frontend/src/host_orchestrator/orchestrator/userartifacts_test.go index e9e82bf8896..2fa26154abf 100644 --- a/frontend/src/host_orchestrator/orchestrator/userartifacts_test.go +++ b/frontend/src/host_orchestrator/orchestrator/userartifacts_test.go @@ -238,45 +238,24 @@ func TestStatArtifactSucceeds(t *testing.T) { } func TestExtractArtifactSucceedsWithZipFormat(t *testing.T) { - rootDir := orchtesting.TempDir(t) - defer orchtesting.RemoveDir(t, rootDir) - opts := UserArtifactsManagerOpts{RootDir: rootDir} - uam, err := NewUserArtifactsManagerImpl(opts) - if err != nil { - t.Fatal(err) - } + tempDir := orchtesting.TempDir(t) + defer orchtesting.RemoveDir(t, tempDir) zipContents := map[string]string{ "alpha.txt": "This is alpha.\n", "bravo.txt": "This is bravo.\n", "charlie.txt": "This is charlie.\n", "delta.txt": "This is delta.\n", } - tempDir := orchtesting.TempDir(t) - defer orchtesting.RemoveDir(t, tempDir) zipFile, err := createZip(tempDir, zipContents) if err != nil { t.Fatal(err) } - data, err := ioutil.ReadFile(zipFile) - if err != nil { - t.Fatal(err) - } - checksum := getSha256Sum(data) - chunk := UserArtifactChunk{ - Name: filepath.Base(zipFile), - OffsetBytes: 0, - SizeBytes: int64(len(data)), - FileSizeBytes: int64(len(data)), - File: bytes.NewReader(data), - } - if err := uam.UpdateArtifact(checksum, chunk); err != nil { - t.Fatal(err) - } - if err := uam.ExtractArtifact(checksum); err != nil { + extractedDir, err := updateAndExtractArtifact(t, zipFile) + if err != nil { t.Fatal(err) } - if got, err := getContents(filepath.Join(rootDir, fmt.Sprintf("%s_extracted", checksum))); err != nil { + if got, err := getContents(extractedDir); err != nil { t.Fatal(err) } else if diff := cmp.Diff(zipContents, got); diff != "" { t.Fatalf("content mismatch (-want +got):\n%s", diff) @@ -284,77 +263,65 @@ func TestExtractArtifactSucceedsWithZipFormat(t *testing.T) { } func TestExtractArtifactSucceedsWithTarGzFormat(t *testing.T) { - rootDir := orchtesting.TempDir(t) - defer orchtesting.RemoveDir(t, rootDir) - opts := UserArtifactsManagerOpts{RootDir: rootDir} - uam, err := NewUserArtifactsManagerImpl(opts) - if err != nil { - t.Fatal(err) - } - tarContents := map[string]string{ - "foo/alpha.txt": "This is alpha.\n", - "foo/bravo.txt": "This is bravo.\n", - "bar/charlie.txt": "This is charlie.\n", - "delta.txt": "This is delta.\n", - } tempDir := orchtesting.TempDir(t) defer orchtesting.RemoveDir(t, tempDir) + tarContents := map[string]archiveEntry{ + "foo/alpha.txt": {Content: "This is alpha.\n"}, + "foo/bravo.txt": {Content: "This is bravo.\n"}, + "bar/charlie.txt": {Content: "This is charlie.\n"}, + "delta.txt": {Content: "This is delta.\n"}, + } tarFile, err := createTarGz(tempDir, tarContents) if err != nil { t.Fatal(err) } - data, err := ioutil.ReadFile(tarFile) + + extractedDir, err := updateAndExtractArtifact(t, tarFile) if err != nil { t.Fatal(err) } - checksum := getSha256Sum(data) - chunk := UserArtifactChunk{ - Name: filepath.Base(tarFile), - OffsetBytes: 0, - SizeBytes: int64(len(data)), - FileSizeBytes: int64(len(data)), - File: bytes.NewReader(data), + tarWant := map[string]string{} + for name, entry := range tarContents { + tarWant[name] = entry.Content } - if err := uam.UpdateArtifact(checksum, chunk); err != nil { + if got, err := getContents(extractedDir); err != nil { t.Fatal(err) + } else if diff := cmp.Diff(tarWant, got); diff != "" { + t.Fatalf("content mismatch (-want +got):\n%s", diff) } +} - if err := uam.ExtractArtifact(checksum); err != nil { +func TestExtractArtifactFailsWithInvalidFileFormat(t *testing.T) { + tempDir := orchtesting.TempDir(t) + defer orchtesting.RemoveDir(t, tempDir) + archive := filepath.Join(tempDir, testFileName) + if err := ioutil.WriteFile(archive, []byte(testFileData), 0644); err != nil { t.Fatal(err) } - if got, err := getContents(filepath.Join(rootDir, fmt.Sprintf("%s_extracted", checksum))); err != nil { - t.Fatal(err) - } else if diff := cmp.Diff(tarContents, got); diff != "" { - t.Fatalf("content mismatch (-want +got):\n%s", diff) + if _, err := updateAndExtractArtifact(t, archive); err == nil { + t.Fatal("Expected an error") } } -func TestExtractArtifactFailsWithInvalidFileFormat(t *testing.T) { - rootDir := orchtesting.TempDir(t) - defer orchtesting.RemoveDir(t, rootDir) - opts := UserArtifactsManagerOpts{RootDir: rootDir} - uam, err := NewUserArtifactsManagerImpl(opts) +func TestExtractArtifactAfterArtifactIsFullyExtractedFails(t *testing.T) { + tempDir := orchtesting.TempDir(t) + defer orchtesting.RemoveDir(t, tempDir) + archive, err := createTarGz(tempDir, map[string]archiveEntry{"file": {Content: "content\n"}}) if err != nil { t.Fatal(err) } - checksum := getSha256Sum([]byte(testFileData)) - chunk := UserArtifactChunk{ - Name: testFileName, - OffsetBytes: 0, - SizeBytes: int64(len(testFileData)), - FileSizeBytes: int64(len(testFileData)), - File: strings.NewReader(testFileData), - } - if err := uam.UpdateArtifact(checksum, chunk); err != nil { + // Extract twice on the same manager: the first extraction must succeed and + // the second must fail because the artifact is already extracted. + uam, checksum := updateArtifact(t, archive) + if err := uam.ExtractArtifact(checksum); err != nil { t.Fatal(err) } - if err := uam.ExtractArtifact(checksum); err == nil { t.Fatal("Expected an error") } } -func TestExtractArtifactAfterArtifactIsFullyExtractedFails(t *testing.T) { +func TestExtractArtifactFailsArtifactNotFound(t *testing.T) { rootDir := orchtesting.TempDir(t) defer orchtesting.RemoveDir(t, rootDir) opts := UserArtifactsManagerOpts{RootDir: rootDir} @@ -362,46 +329,44 @@ func TestExtractArtifactAfterArtifactIsFullyExtractedFails(t *testing.T) { if err != nil { t.Fatal(err) } + + if err := uam.ExtractArtifact("foo"); err == nil { + t.Fatal("Expected an error") + } +} + +func TestExtractArtifactFailsWithTarPathTraversal(t *testing.T) { tempDir := orchtesting.TempDir(t) defer orchtesting.RemoveDir(t, tempDir) - archive, err := createTarGz(tempDir, map[string]string{"file": "content\n"}) + archive, err := createTarGz(tempDir, map[string]archiveEntry{"../../../../../../PWNED": {Content: "owned"}}) if err != nil { t.Fatal(err) } - data, err := ioutil.ReadFile(archive) - if err != nil { - t.Fatal(err) - } - checksum := getSha256Sum(data) - chunk := UserArtifactChunk{ - Name: filepath.Base(archive), - OffsetBytes: 0, - SizeBytes: int64(len(data)), - FileSizeBytes: int64(len(data)), - File: bytes.NewReader(data), - } - if err := uam.UpdateArtifact(checksum, chunk); err != nil { - t.Fatal(err) + if _, err := updateAndExtractArtifact(t, archive); err == nil { + t.Fatal("Expected an error") } - if err := uam.ExtractArtifact(checksum); err != nil { +} + +func TestExtractArtifactFailsWithZipPathTraversal(t *testing.T) { + tempDir := orchtesting.TempDir(t) + defer orchtesting.RemoveDir(t, tempDir) + archive, err := createZip(tempDir, map[string]string{"../../../../../../PWNED": "owned"}) + if err != nil { t.Fatal(err) } - - if err := uam.ExtractArtifact(checksum); err == nil { + if _, err := updateAndExtractArtifact(t, archive); err == nil { t.Fatal("Expected an error") } } -func TestExtractArtifactFailsArtifactNotFound(t *testing.T) { - rootDir := orchtesting.TempDir(t) - defer orchtesting.RemoveDir(t, rootDir) - opts := UserArtifactsManagerOpts{RootDir: rootDir} - uam, err := NewUserArtifactsManagerImpl(opts) +func TestExtractArtifactFailsWithTarSymlinkEscape(t *testing.T) { + tempDir := orchtesting.TempDir(t) + defer orchtesting.RemoveDir(t, tempDir) + archive, err := createTarGz(tempDir, map[string]archiveEntry{"sneak": {Symlink: "../../../../../../etc"}}) if err != nil { t.Fatal(err) } - - if err := uam.ExtractArtifact("foo"); err == nil { + if _, err := updateAndExtractArtifact(t, archive); err == nil { t.Fatal("Expected an error") } } @@ -650,7 +615,15 @@ func getSubdirs(path string) []string { return subdirs } -func createTarGz(dir string, contents map[string]string) (string, error) { +// archiveEntry describes an entry to add to an archive built by createTarGz: a +// regular file holding Content, or, when Symlink is non-empty, a symbolic link +// pointing at Symlink (used to exercise the symlink-escape guard). +type archiveEntry struct { + Content string + Symlink string +} + +func createTarGz(dir string, contents map[string]archiveEntry) (string, error) { tarFile, err := ioutil.TempFile(dir, "*.tar.gz") if err != nil { return "", err @@ -665,7 +638,19 @@ func createTarGz(dir string, contents map[string]string) (string, error) { directories := map[string]struct{}{} - for name, content := range contents { + for name, entry := range contents { + if entry.Symlink != "" { + header := tar.Header{ + Name: name, + Typeflag: tar.TypeSymlink, + Linkname: entry.Symlink, + Mode: 0777, + } + if err := tarWriter.WriteHeader(&header); err != nil { + return "", err + } + continue + } dir, _ := filepath.Split(name) dirPaths := getSubdirs(dir) for _, dp := range dirPaths { @@ -684,15 +669,15 @@ func createTarGz(dir string, contents map[string]string) (string, error) { } header := tar.Header{ Name: name, - Size: int64(len(content)), + Size: int64(len(entry.Content)), Mode: 0555, } if err := tarWriter.WriteHeader(&header); err != nil { return "", err } - if n, err := tarWriter.Write([]byte(content)); err != nil { + if n, err := tarWriter.Write([]byte(entry.Content)); err != nil { return "", err - } else if n != len(content) { + } else if n != len(entry.Content) { return "", fmt.Errorf("Failed to write entire file: %s", name) } } @@ -707,3 +692,38 @@ func getChunkStateItemList(cs *chunkState) []chunkStateItem { }) return items } + +// updateArtifact uploads the archive at archivePath as a single chunk, returning +// the manager and the artifact checksum so callers can extract it. +func updateArtifact(t *testing.T, archivePath string) (*UserArtifactsManagerImpl, string) { + rootDir := orchtesting.TempDir(t) + t.Cleanup(func() { orchtesting.RemoveDir(t, rootDir) }) + uam, err := NewUserArtifactsManagerImpl(UserArtifactsManagerOpts{RootDir: rootDir}) + if err != nil { + t.Fatal(err) + } + data, err := ioutil.ReadFile(archivePath) + if err != nil { + t.Fatal(err) + } + checksum := getSha256Sum(data) + chunk := UserArtifactChunk{ + Name: filepath.Base(archivePath), + OffsetBytes: 0, + SizeBytes: int64(len(data)), + FileSizeBytes: int64(len(data)), + File: bytes.NewReader(data), + } + if err := uam.UpdateArtifact(checksum, chunk); err != nil { + t.Fatal(err) + } + return uam, checksum +} + +// updateAndExtractArtifact uploads the archive at archivePath as a single chunk and +// extracts it, returning the directory the artifact was extracted into so +// callers can inspect the result. +func updateAndExtractArtifact(t *testing.T, archivePath string) (string, error) { + uam, checksum := updateArtifact(t, archivePath) + return uam.ExtractedArtifactPath(checksum), uam.ExtractArtifact(checksum) +} diff --git a/gigabyte-ampere-cuttlefish-installer/addpreseed.sh b/gigabyte-ampere-cuttlefish-installer/addpreseed.sh index c0bc88a2cb1..b46c0d67ccb 100755 --- a/gigabyte-ampere-cuttlefish-installer/addpreseed.sh +++ b/gigabyte-ampere-cuttlefish-installer/addpreseed.sh @@ -11,6 +11,7 @@ new_iso=preseed-mini.iso PRESEEDFILE=$(realpath "${BASEDIR}"/preseed/preseed.cfg) AFTERINSTALLSCRIPT=$(realpath "${BASEDIR}"/preseed/after_install_1.sh) +DEBIANSNAPSHOTLIST=$(realpath "${BASEDIR}"/preseed/debian-snapshot.list) part_img_ready=1 if test "$auto_extract_efi" = 1; then @@ -34,6 +35,7 @@ bsdtar -C ${new_files} -xf "$orig_iso" cd ${new_files} cp -f "${PRESEEDFILE}" preseed.cfg cp -f "${AFTERINSTALLSCRIPT}" after_install_1.sh +cp -f "${DEBIANSNAPSHOTLIST}" debian-snapshot.list chmod a+rx after_install_1.sh # add preseed to console based installer @@ -41,6 +43,7 @@ chmod ug+w initrd.gz gzip -d -f initrd.gz echo preseed.cfg | cpio -H newc -o -A -F initrd echo after_install_1.sh | cpio -H newc -o -A -F initrd +echo debian-snapshot.list | cpio -H newc -o -A -F initrd gzip -9 initrd chmod a-w initrd.gz # add preseed to GTK based installer @@ -50,11 +53,13 @@ chmod ug+w initrd.gz gzip -d -f initrd.gz cp -f ../preseed.cfg . cp -f ../after_install_1.sh . +cp -f ../debian-snapshot.list . echo preseed.cfg | cpio -H newc -o -A -F initrd echo after_install_1.sh | cpio -H newc -o -A -F initrd +echo debian-snapshot.list | cpio -H newc -o -A -F initrd gzip -9 initrd chmod a-w initrd.gz -rm -f preseed.cfg after_install_1.sh +rm -f preseed.cfg after_install_1.sh debian-snapshot.list cd .. chmod a-w gtk # modify Graphical installer to use tty1 diff --git a/gigabyte-ampere-cuttlefish-installer/preseed/after_install_1.sh b/gigabyte-ampere-cuttlefish-installer/preseed/after_install_1.sh index f16d67d61e2..8ccdc129d90 100755 --- a/gigabyte-ampere-cuttlefish-installer/preseed/after_install_1.sh +++ b/gigabyte-ampere-cuttlefish-installer/preseed/after_install_1.sh @@ -20,23 +20,29 @@ adduser vsoc-01 kvm adduser vsoc-01 render adduser vsoc-01 video -# Detect distribution -DEBIAN_DISTRIBUTION="$(lsb_release -c -s)" -DEBIAN_ARCH="$(dpkg --print-architecture)" - -apt -o Apt::Get::Assume-Yes=true -o APT::Color=0 -o DPkgPM::Progress-Fancy=0 \ - update +apt -o Apt::Get::Assume-Yes=true -o APT::Color=0 -o DPkgPM::Progress-Fancy=0 update # Install kernel DEBIAN_DISTRIBUTION="$(lsb_release -c -s)" +DEBIAN_SNAPSHOT_DIR="/root/debian-snapshot" #apt-get install -y '^linux-image-6.1.*aosp14-linaro.*' '^linux-headers-6.1.*aosp14-linaro.*' has_backports=$(apt-cache policy | grep "${DEBIAN_DISTRIBUTION}-backports") +# Enable ARM64 nested virtualization (KVM NV2). +# +# Note that any VHE host honors the flag, FEAT_NV2 or not. Without FEAT_NV2 KVM +# falls back to normal VHE operation. Without a GICv3-class vGIC the flag is +# fatal - KVM initialization fails and /dev/kvm never appears. +sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT=\"\(.*\)\"/GRUB_CMDLINE_LINUX_DEFAULT=\"\1 kvm-arm.mode=nested\"/' /etc/default/grub if [ x"$has_backports" != x"" ]; then apt install -y -t "${DEBIAN_DISTRIBUTION}-backports" linux-headers-arm64 apt install -y -t "${DEBIAN_DISTRIBUTION}-backports" linux-image-arm64 else - apt install -y linux-headers-arm64 - apt install -y linux-image-arm64 + KERNEL_ABI="6.18.15+deb13-arm64" + KERNEL_DEB_VERSION="6.18.15-1~bpo13+1" + apt-get -o Dir::Etc::SourceParts="${DEBIAN_SNAPSHOT_DIR}" -o Acquire::Retries=5 update + apt-get install -y -o Dir::Etc::SourceParts="${DEBIAN_SNAPSHOT_DIR}" -o Acquire::Retries=5 \ + "linux-image-${KERNEL_ABI}=${KERNEL_DEB_VERSION}" \ + "linux-headers-${KERNEL_ABI}=${KERNEL_DEB_VERSION}" || exit 1 fi # Install nVidia or AMD GPU driver @@ -59,13 +65,25 @@ elif [ x"$nvidia_gpu" != x"" ]; then DEBIAN_FRONTEND=noninteractive apt-get install -y -t "${DEBIAN_DISTRIBUTION}-backports" -q --force-yes nvidia-driver DEBIAN_FRONTEND=noninteractive apt-get install -y -t "${DEBIAN_DISTRIBUTION}-backports" -q --force-yes firmware-misc-nonfree else - DEBIAN_FRONTEND=noninteractive apt-get install -y -q --force-yes nvidia-open-kernel-dkms - DEBIAN_FRONTEND=noninteractive apt-get install -y -q --force-yes nvidia-driver - DEBIAN_FRONTEND=noninteractive apt-get install -y -q --force-yes firmware-misc-nonfree + NVIDIA_DEB_VERSION="550.163.01-4~bpo13+1" + apt-get -o Dir::Etc::SourceParts="${DEBIAN_SNAPSHOT_DIR}" -o Acquire::Retries=5 update + DEBIAN_FRONTEND=noninteractive apt-get install -y -q -o Dir::Etc::SourceParts="${DEBIAN_SNAPSHOT_DIR}" \ + -o Acquire::Retries=5 -t ${DEBIAN_DISTRIBUTION}-backports \ + "nvidia-open-kernel-dkms=${NVIDIA_DEB_VERSION}" \ + "nvidia-driver=${NVIDIA_DEB_VERSION}" || exit 1 + DEBIAN_FRONTEND=noninteractive apt-get install -y -q firmware-misc-nonfree + apt-mark hold nvidia-open-kernel-dkms nvidia-driver fi fi # End of Install kernel +# Regenerate grub.cfg: the kernel postinst's update-grub hook only runs when a +# kernel package is actually configured, so a no-op install above would leave +# the nested-virt cmdline out of the boot menu. +if [ -e /boot/grub/grub.cfg ]; then + update-grub || exit 1 +fi + # Install android cuttlefish packages #DEPLOY_CHANNEL= #DEPLOY_VERSION= diff --git a/gigabyte-ampere-cuttlefish-installer/preseed/debian-snapshot.list b/gigabyte-ampere-cuttlefish-installer/preseed/debian-snapshot.list new file mode 100644 index 00000000000..76d2a73bffb --- /dev/null +++ b/gigabyte-ampere-cuttlefish-installer/preseed/debian-snapshot.list @@ -0,0 +1 @@ +deb [check-valid-until=no] https://snapshot.debian.org/archive/debian/20260315T022902Z/ trixie-backports main contrib non-free non-free-firmware diff --git a/gigabyte-ampere-cuttlefish-installer/preseed/preseed.cfg b/gigabyte-ampere-cuttlefish-installer/preseed/preseed.cfg index 83458ad8d03..206967eb647 100644 --- a/gigabyte-ampere-cuttlefish-installer/preseed/preseed.cfg +++ b/gigabyte-ampere-cuttlefish-installer/preseed/preseed.cfg @@ -201,7 +201,7 @@ d-i partman-auto/expert_recipe_linaro_lvm_nopv string \ method{ efi } format{ } \ label { esp } \ . \ - 256 2048 4096 ext4 \ + 512 2048 4096 ext4 \ $primary{ } \ $bootable{ } \ method{ format } \ @@ -322,6 +322,8 @@ d-i grub-installer/update-nvram boolean false d-i finish-install/reboot_in_progress note d-i preseed/late_command string \ - cp -f /after_install_1.sh /target/root; \ + mkdir -p /target/root/debian-snapshot && \ + cp -f /debian-snapshot.list /target/root/debian-snapshot/ && \ + cp -f /after_install_1.sh /target/root && \ in-target /bin/sh /root/after_install_1.sh; diff --git a/gigabyte-ampere-cuttlefish-installer/utils/download-ci-cf.sh b/gigabyte-ampere-cuttlefish-installer/utils/download-ci-cf.sh index 22f5162b01a..62d1594b175 100755 --- a/gigabyte-ampere-cuttlefish-installer/utils/download-ci-cf.sh +++ b/gigabyte-ampere-cuttlefish-installer/utils/download-ci-cf.sh @@ -4,13 +4,24 @@ set -o errexit -URL=https://ci.android.com/builds/latest/branches/aosp-android-latest-release/targets/aosp_cf_arm64_only_phone-userdebug/view/BUILD_INFO -RURL=$(curl -Ls -o /dev/null -w %{url_effective} ${URL}) -echo $RURL +BRANCH=aosp-android-latest-release +TARGET=aosp_cf_arm64_only_phone-userdebug +BUILD_ID=$(curl -fsS \ + "https://ci.android.com/builds/branches/${BRANCH}/targets/${TARGET}/status.json" \ + | sed -n 's/.*[{,[:space:]]"last_known_good_build"[[:space:]]*:[[:space:]]*"\([0-9][0-9]*\)".*/\1/p') +echo "BUILD_ID = ${BUILD_ID}" -FILENAME=$(wget -nv -O - ${RURL%/view/BUILD_INFO}/ | grep aosp_cf_arm64_only_phone-img- | sed 's/.*\(aosp_cf_arm64_only_phone-img-[0-9]*[.]zip\).*/\1/g') +if [[ -z "${BUILD_ID}" ]]; then + echo "Error: BUILD_ID empty." + exit 1 +fi -wget -nv -c ${RURL%/view/BUILD_INFO}/raw/${FILENAME} -wget -nv -c ${RURL%/view/BUILD_INFO}/raw/cvd-host_package.tar.gz +FILENAME="aosp_cf_arm64_only_phone-img-${BUILD_ID}.zip" +echo "FILENAME = ${FILENAME}" + +RAWURL="https://ci.android.com/builds/submitted/${BUILD_ID}/${TARGET}/latest/raw" + +wget -nv -c ${RAWURL}/${FILENAME} +wget -nv -c ${RAWURL}/cvd-host_package.tar.gz exit 0 diff --git a/tools/baseimage/pkg/gce/scripts/create_base_image_main.sh b/tools/baseimage/pkg/gce/scripts/create_base_image_main.sh index ebb9e35c825..82828d4d66b 100644 --- a/tools/baseimage/pkg/gce/scripts/create_base_image_main.sh +++ b/tools/baseimage/pkg/gce/scripts/create_base_image_main.sh @@ -26,10 +26,13 @@ sudo apt-get upgrade -y # Avoids blocking "Default mirror not found" popup prompt when pbuilder is installed. echo "pbuilder pbuilder/mirrorsite string https://deb.debian.org/debian" | sudo debconf-set-selections -kmodver_begin=$(sudo chroot /mnt/image/ /usr/bin/dpkg -s linux-image-cloud-${arch} | grep ^Depends: | \ - cut -d: -f2 | cut -d" " -f2 | sed 's/linux-image-//') +kmodver_begin=$(sudo chroot /mnt/image/ /usr/bin/dpkg -l | grep '^ii' | \ + awk '{print $2}' | grep '^linux-image-[0-9]' | head -n1 | sed 's/linux-image-//') echo "IMAGE STARTS WITH KERNEL: ${kmodver_begin}" +# Make sure we don't update the kernel image or headers during this process +sudo chroot /mnt/image /usr/bin/apt-mark hold linux-image-cloud-${arch} linux-headers-cloud-${arch} + sudo chroot /mnt/image /usr/bin/apt update sudo chroot /mnt/image /usr/bin/apt upgrade -y diff --git a/tools/baseimage/pkg/gce/scripts/install_cuttlefish_debs.sh b/tools/baseimage/pkg/gce/scripts/install_cuttlefish_debs.sh index 105df368c23..12a86fb54ac 100644 --- a/tools/baseimage/pkg/gce/scripts/install_cuttlefish_debs.sh +++ b/tools/baseimage/pkg/gce/scripts/install_cuttlefish_debs.sh @@ -25,10 +25,13 @@ arch=$(uname -m) [ "${arch}" = "x86_64" ] && arch=amd64 [ "${arch}" = "aarch64" ] && arch=arm64 -kmodver_begin=$(sudo chroot /mnt/image/ /usr/bin/dpkg -s linux-image-cloud-${arch} | grep ^Depends: | \ - cut -d: -f2 | cut -d" " -f2 | sed 's/linux-image-//') +kmodver_begin=$(sudo chroot /mnt/image/ /usr/bin/dpkg -l | grep '^ii' | \ + awk '{print $2}' | grep '^linux-image-[0-9]' | head -n1 | sed 's/linux-image-//') echo "IMAGE STARTS WITH KERNEL: ${kmodver_begin}" +# Make sure we don't update the kernel image or headers during this process +sudo chroot /mnt/image /usr/bin/apt-mark hold linux-image-cloud-${arch} linux-headers-cloud-${arch} + sudo chroot /mnt/image /usr/bin/apt update sudo chroot /mnt/image /usr/bin/apt upgrade -y diff --git a/tools/baseimage/pkg/gce/scripts/install_nvidia.sh b/tools/baseimage/pkg/gce/scripts/install_nvidia.sh index 00841ac1cd8..8a28a6efdca 100644 --- a/tools/baseimage/pkg/gce/scripts/install_nvidia.sh +++ b/tools/baseimage/pkg/gce/scripts/install_nvidia.sh @@ -27,12 +27,9 @@ if [ ! -d /dev/fd ]; then ln -s /proc/self/fd /dev/fd fi -# Using "Depends:" is more reliable than "Version:", because it works for -# backported ("bpo") kernels as well. NOTE: "Package" can be used instead -# if we don't install the metapackage ("linux-image-cloud-${arch}") but a -# specific version in the future -kmodver=$(dpkg -s linux-image-cloud-${arch} | grep ^Depends: | \ - cut -d: -f2 | cut -d" " -f2 | sed 's/linux-image-//') +# Query the installed concrete kernel package version on disk rather than the metapackage +kmodver=$(dpkg -l | grep '^ii' | awk '{print $2}' | \ + grep '^linux-image-[0-9]' | head -n1 | sed 's/linux-image-//') apt-get install -y wget