Showing preview only (3,515K chars total). Download the full file or copy to clipboard to get everything.
Repository: oxidecomputer/propolis
Branch: master
Commit: 3b21bdc2aaf3
Files: 383
Total size: 3.3 MB
Directory structure:
gitextract_ra4peprf/
├── .cargo/
│ └── config.toml
├── .git-blame-ignore-revs
├── .github/
│ ├── buildomat/
│ │ ├── config.toml
│ │ ├── jobs/
│ │ │ ├── check-headers.sh
│ │ │ ├── falcon-build.sh
│ │ │ ├── image.sh
│ │ │ ├── phd-build.sh
│ │ │ ├── phd-run-migrate-from-base.sh
│ │ │ ├── phd-run.sh
│ │ │ └── test-gimlet.sh
│ │ └── phd-run-with-args.sh
│ └── workflows/
│ └── rust.yml
├── .gitignore
├── .licenserc.yaml
├── Cargo.toml
├── LICENSE
├── README.md
├── bin/
│ ├── dropshot-apis/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── main.rs
│ ├── mock-server/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib/
│ │ │ ├── api_types.rs
│ │ │ └── lib.rs
│ │ └── main.rs
│ ├── propolis-cli/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ └── main.rs
│ ├── propolis-server/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── lib/
│ │ │ ├── config.rs
│ │ │ ├── initializer.rs
│ │ │ ├── lib.rs
│ │ │ ├── migrate/
│ │ │ │ ├── codec.rs
│ │ │ │ ├── destination.rs
│ │ │ │ ├── memx.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── preamble.rs
│ │ │ │ ├── protocol.rs
│ │ │ │ └── source.rs
│ │ │ ├── serial/
│ │ │ │ ├── history_buffer.rs
│ │ │ │ └── mod.rs
│ │ │ ├── server.rs
│ │ │ ├── spec/
│ │ │ │ ├── api_spec_v0.rs
│ │ │ │ ├── builder.rs
│ │ │ │ └── mod.rs
│ │ │ ├── stats/
│ │ │ │ ├── mod.rs
│ │ │ │ ├── network_interface.rs
│ │ │ │ ├── pvpanic.rs
│ │ │ │ ├── virtual_disk.rs
│ │ │ │ └── virtual_machine.rs
│ │ │ ├── vcpu_tasks.rs
│ │ │ ├── vm/
│ │ │ │ ├── active.rs
│ │ │ │ ├── ensure.rs
│ │ │ │ ├── guest_event.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── objects.rs
│ │ │ │ ├── request_queue.rs
│ │ │ │ ├── services.rs
│ │ │ │ ├── state_driver.rs
│ │ │ │ └── state_publisher.rs
│ │ │ └── vnc.rs
│ │ ├── main.rs
│ │ └── proptest-regressions/
│ │ └── vm/
│ │ └── request_queue.txt
│ ├── propolis-standalone/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── cidata.rs
│ │ ├── config.rs
│ │ ├── main.rs
│ │ └── snapshot.rs
│ └── propolis-utils/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ └── bin/
│ ├── cpuid-gen.rs
│ └── rsrvrctl.rs
├── crates/
│ ├── bhyve-api/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── header-check/
│ │ │ ├── Cargo.toml
│ │ │ ├── README.md
│ │ │ ├── build.rs
│ │ │ └── test/
│ │ │ └── main.rs
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── sys/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── enums.rs
│ │ ├── ioctls.rs
│ │ ├── lib.rs
│ │ ├── structs.rs
│ │ └── vmm_data.rs
│ ├── cpuid-profile-config/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── cpuid-utils/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── bits.rs
│ │ ├── host.rs
│ │ ├── instance_spec.rs
│ │ └── lib.rs
│ ├── dladm/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ └── sys.rs
│ ├── nvpair/
│ │ ├── Cargo.toml
│ │ ├── header-check/
│ │ │ ├── Cargo.toml
│ │ │ ├── build.rs
│ │ │ └── test/
│ │ │ └── main.rs
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── sys/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── pbind/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── propolis-api-types/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── disk.rs
│ │ ├── instance.rs
│ │ ├── instance_spec/
│ │ │ ├── components/
│ │ │ │ ├── backends.rs
│ │ │ │ ├── board.rs
│ │ │ │ ├── devices.rs
│ │ │ │ └── mod.rs
│ │ │ └── mod.rs
│ │ ├── lib.rs
│ │ ├── migration.rs
│ │ └── serial.rs
│ ├── propolis-api-types-versions/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── add_vsock/
│ │ │ ├── api.rs
│ │ │ ├── components/
│ │ │ │ ├── devices.rs
│ │ │ │ └── mod.rs
│ │ │ ├── instance_spec.rs
│ │ │ └── mod.rs
│ │ ├── crucible_volume_info/
│ │ │ ├── disk.rs
│ │ │ └── mod.rs
│ │ ├── impls/
│ │ │ ├── instance.rs
│ │ │ ├── instance_spec.rs
│ │ │ └── mod.rs
│ │ ├── initial/
│ │ │ ├── components/
│ │ │ │ ├── backends.rs
│ │ │ │ ├── board.rs
│ │ │ │ ├── devices.rs
│ │ │ │ └── mod.rs
│ │ │ ├── disk.rs
│ │ │ ├── instance.rs
│ │ │ ├── instance_spec.rs
│ │ │ ├── migration.rs
│ │ │ ├── mod.rs
│ │ │ └── serial.rs
│ │ ├── latest.rs
│ │ ├── lib.rs
│ │ └── programmable_smbios/
│ │ ├── api.rs
│ │ ├── instance_spec.rs
│ │ └── mod.rs
│ ├── propolis-config-toml/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ └── spec.rs
│ ├── propolis-server-api/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── propolis-types/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── rfb/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── examples/
│ │ │ ├── shared.rs
│ │ │ ├── socket.rs
│ │ │ └── websock.rs
│ │ └── src/
│ │ ├── encodings.rs
│ │ ├── keysym.rs
│ │ ├── lib.rs
│ │ ├── proto.rs
│ │ ├── server.rs
│ │ └── tungstenite.rs
│ ├── rgb-frame/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ └── viona-api/
│ ├── Cargo.toml
│ ├── header-check/
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ └── test/
│ │ └── main.rs
│ └── src/
│ ├── ffi.rs
│ └── lib.rs
├── docs/
│ ├── lifecycle.md
│ ├── migrate-with-crucible.md
│ ├── server-send-vcr.md
│ └── standalone-with-crucible.md
├── lib/
│ ├── propolis/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── accessors.rs
│ │ ├── api_version.rs
│ │ ├── attestation/
│ │ │ ├── boot_digest/
│ │ │ │ ├── crucible.rs
│ │ │ │ └── mod.rs
│ │ │ ├── mod.rs
│ │ │ └── server.rs
│ │ ├── block/
│ │ │ ├── attachment.rs
│ │ │ ├── crucible.rs
│ │ │ ├── file.rs
│ │ │ ├── id.rs
│ │ │ ├── in_memory.rs
│ │ │ ├── mem_async.rs
│ │ │ ├── minder.rs
│ │ │ └── mod.rs
│ │ ├── chardev/
│ │ │ ├── file_out.rs
│ │ │ ├── mod.rs
│ │ │ ├── pollers.rs
│ │ │ └── sock.rs
│ │ ├── common.rs
│ │ ├── cpuid.rs
│ │ ├── enlightenment/
│ │ │ ├── bhyve.rs
│ │ │ ├── hyperv/
│ │ │ │ ├── bits.rs
│ │ │ │ ├── hypercall.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── overlay.rs
│ │ │ │ └── tsc.rs
│ │ │ └── mod.rs
│ │ ├── exits.rs
│ │ ├── firmware/
│ │ │ ├── mod.rs
│ │ │ └── smbios/
│ │ │ ├── bits.rs
│ │ │ ├── mod.rs
│ │ │ └── table.rs
│ │ ├── hw/
│ │ │ ├── bhyve/
│ │ │ │ ├── atpic.rs
│ │ │ │ ├── atpit.rs
│ │ │ │ ├── hpet.rs
│ │ │ │ ├── ioapic.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── pmtimer.rs
│ │ │ │ └── rtc.rs
│ │ │ ├── chipset/
│ │ │ │ ├── i440fx.rs
│ │ │ │ └── mod.rs
│ │ │ ├── ibmpc.rs
│ │ │ ├── ids.rs
│ │ │ ├── mod.rs
│ │ │ ├── nvme/
│ │ │ │ ├── admin.rs
│ │ │ │ ├── bits.rs
│ │ │ │ ├── cmds.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── queue.rs
│ │ │ │ └── requests.rs
│ │ │ ├── pci/
│ │ │ │ ├── bar.rs
│ │ │ │ ├── bits.rs
│ │ │ │ ├── bridge.rs
│ │ │ │ ├── bus.rs
│ │ │ │ ├── cfgspace.rs
│ │ │ │ ├── device.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── test.rs
│ │ │ │ └── topology.rs
│ │ │ ├── ps2/
│ │ │ │ ├── ctrl.rs
│ │ │ │ ├── keyboard/
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ ├── scan_code_1.rs
│ │ │ │ │ └── scan_code_2.rs
│ │ │ │ └── mod.rs
│ │ │ ├── qemu/
│ │ │ │ ├── debug.rs
│ │ │ │ ├── fwcfg.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── pvpanic.rs
│ │ │ │ └── ramfb.rs
│ │ │ ├── testdev.rs
│ │ │ ├── uart/
│ │ │ │ ├── lpc.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── uart16550.rs
│ │ │ └── virtio/
│ │ │ ├── bits.rs
│ │ │ ├── block.rs
│ │ │ ├── mod.rs
│ │ │ ├── p9fs.rs
│ │ │ ├── pci.rs
│ │ │ ├── queue.rs
│ │ │ ├── softnpu.rs
│ │ │ ├── testutil.rs
│ │ │ ├── viona.rs
│ │ │ └── vsock.rs
│ │ ├── intr_pins.rs
│ │ ├── lib.rs
│ │ ├── lifecycle.rs
│ │ ├── migrate.rs
│ │ ├── mmio.rs
│ │ ├── msr.rs
│ │ ├── pio.rs
│ │ ├── tasks.rs
│ │ ├── util/
│ │ │ ├── aspace.rs
│ │ │ ├── id.rs
│ │ │ ├── mod.rs
│ │ │ └── regmap.rs
│ │ ├── vcpu.rs
│ │ ├── vmm/
│ │ │ ├── hdl.rs
│ │ │ ├── machine.rs
│ │ │ ├── mem.rs
│ │ │ ├── mod.rs
│ │ │ └── time.rs
│ │ └── vsock/
│ │ ├── buffer.rs
│ │ ├── mod.rs
│ │ ├── packet.rs
│ │ ├── poller.rs
│ │ ├── poller_stub.rs
│ │ └── proxy.rs
│ └── propolis-client/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ └── support.rs
├── openapi/
│ └── propolis-server/
│ ├── propolis-server-1.0.0-833484.json.gitstub
│ ├── propolis-server-2.0.0-d68a9f.json.gitstub
│ ├── propolis-server-3.0.0-10da2b.json.gitstub
│ ├── propolis-server-4.0.0-5ce09a.json.gitstub
│ └── propolis-server-5.0.0-0c6dd9.json
├── packaging/
│ ├── package-manifest.toml
│ ├── propolis-package/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ └── main.rs
│ └── smf/
│ ├── method_script.sh
│ └── propolis-server/
│ └── manifest.xml
├── phd-tests/
│ ├── .gitignore
│ ├── README.md
│ ├── artifacts.toml
│ ├── framework/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── artifacts/
│ │ │ ├── buildomat.rs
│ │ │ ├── manifest.rs
│ │ │ ├── mod.rs
│ │ │ └── store.rs
│ │ ├── disk/
│ │ │ ├── crucible.rs
│ │ │ ├── fat.rs
│ │ │ ├── file.rs
│ │ │ ├── in_memory.rs
│ │ │ └── mod.rs
│ │ ├── guest_os/
│ │ │ ├── alpine.rs
│ │ │ ├── debian11_nocloud.rs
│ │ │ ├── linux.rs
│ │ │ ├── mod.rs
│ │ │ ├── shell_commands.rs
│ │ │ ├── ubuntu22_04.rs
│ │ │ ├── windows.rs
│ │ │ ├── windows_server_2016.rs
│ │ │ ├── windows_server_2019.rs
│ │ │ └── windows_server_2022.rs
│ │ ├── host_api/
│ │ │ ├── kvm.rs
│ │ │ ├── mod.rs
│ │ │ └── stubs.rs
│ │ ├── lib.rs
│ │ ├── lifecycle.rs
│ │ ├── log_config.rs
│ │ ├── port_allocator.rs
│ │ ├── serial/
│ │ │ ├── mod.rs
│ │ │ ├── raw_buffer.rs
│ │ │ └── vt80x24.rs
│ │ ├── test_vm/
│ │ │ ├── config.rs
│ │ │ ├── environment.rs
│ │ │ ├── metrics.rs
│ │ │ ├── mod.rs
│ │ │ ├── server.rs
│ │ │ └── spec.rs
│ │ └── zfs.rs
│ ├── quickstart.sh
│ ├── runner/
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ └── src/
│ │ ├── config.rs
│ │ ├── execute.rs
│ │ ├── fixtures.rs
│ │ └── main.rs
│ ├── testcase/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── testcase_macro/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ └── tests/
│ ├── Cargo.toml
│ ├── src/
│ │ ├── boot_order/
│ │ │ └── efi_utils.rs
│ │ ├── boot_order.rs
│ │ ├── cpuid.rs
│ │ ├── crucible/
│ │ │ ├── migrate.rs
│ │ │ ├── mod.rs
│ │ │ └── smoke.rs
│ │ ├── disk.rs
│ │ ├── framework.rs
│ │ ├── hw.rs
│ │ ├── hyperv.rs
│ │ ├── lib.rs
│ │ ├── migrate.rs
│ │ ├── server_state_machine.rs
│ │ ├── smoke.rs
│ │ ├── stats.rs
│ │ └── vsock.rs
│ └── testdata/
│ └── dirt.sh
├── rust-toolchain.toml
├── rustfmt.toml
├── scripts/
│ ├── README.md
│ ├── cpuid-queries.d
│ ├── live-migration-times.d
│ ├── nvme-trace.d
│ ├── time-adjustments.d
│ ├── viona.d
│ └── vm-exit-codes.d
├── tools/
│ ├── check_headers
│ └── install_builder_prerequisites.sh
└── xtask/
├── Cargo.toml
└── src/
├── external.rs
├── main.rs
├── task_clippy.rs
├── task_fmt.rs
├── task_license.rs
├── task_phd.rs
├── task_prepush.rs
├── task_style.rs
└── util.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .cargo/config.toml
================================================
[alias]
xtask = "run --package xtask --quiet --"
[env]
# Currently required by Falcon due to
# https://github.com/rust-lang/cargo/issues/3946#issuecomment-973132993
CARGO_WORKSPACE_DIR = { value = "", relative = true }
[build]
# Tokio's unstable features are required by `tokio-dtrace` probes, and for
# disabling the LIFO slot optimization.
#
# See here for details:
# https://github.com/oxidecomputer/oxide-tokio-rt/blob/main/README.md#enabling-tokio_unstable-features
rustflags = ["--cfg", "tokio_unstable"]
================================================
FILE: .git-blame-ignore-revs
================================================
# Whitespace-only changes
da65a727cceee1b386ded8609309015c366cca2a
================================================
FILE: .github/buildomat/config.toml
================================================
#
# This file, with this flag, must be present in the default branch in order for
# the buildomat integration to create check suites.
#
enable = true
#
# Require approval for pull requests made by users outside our organisation.
#
org_only = true
#
# Allow jobs on pull requests from automated services specified here.
#
allow_users = [
"dependabot[bot]",
]
================================================
FILE: .github/buildomat/jobs/check-headers.sh
================================================
#!/bin/bash
#:
#: name = "header-check"
#: variety = "basic"
#: target = "helios-2.0"
#: rust_toolchain = true
#:
# Run the various `header-check` tests across Propolis' crates.
#
# These tests are run on an illumos target for best fidelity: while the
# immediate struct and function definitions could in theory be analyzed
# anywhere, they may contain definitions that vary across target OSes. We must
# ensure, at a minimum, that FFI definitions are correct w.r.t these headers'
# interpretation on illumos. Anywhere else is just a convenience.
set -e
GATE_REF="$(./tools/check_headers gate_ref)"
# TODO: `--branch` is overly restrictive, but it's what we've got. In git 2.49
# the --revision flag was added to `git-clone`, and can clone an arbitrary
# revision, which is more appropriate here. We might be tracking an arbitrary
# commit with some changes in illumos-gate that isn't yet merged, after all.
git clone --depth 1 --branch "$GATE_REF" \
https://code.oxide.computer/illumos-gate ./gate_src
./tools/check_headers run ./gate_src
================================================
FILE: .github/buildomat/jobs/falcon-build.sh
================================================
#!/bin/bash
#:
#: name = "falcon"
#: variety = "basic"
#: target = "helios-2.0"
#: rust_toolchain = "stable"
#: output_rules = [
#: "/work/release/*",
#: ]
#:
#: [[publish]]
#: series = "falcon"
#: name = "propolis-server"
#: from_output = "/work/release/propolis-server"
#:
#: [[publish]]
#: series = "falcon"
#: name = "propolis-server.sha256.txt"
#: from_output = "/work/release/propolis-server.sha256.txt"
#:
#: [[publish]]
#: series = "falcon"
#: name = "propolis-cli"
#: from_output = "/work/release/propolis-cli"
#:
#: [[publish]]
#: series = "falcon"
#: name = "propolis-cli.sha256.txt"
#: from_output = "/work/release/propolis-cli.sha256.txt"
set -o errexit
set -o pipefail
set -o xtrace
cargo --version
rustc --version
banner prerequisites
ptime -m ./tools/install_builder_prerequisites.sh -y
banner check
ptime -m cargo check --features falcon
ptime -m cargo clippy --features falcon --all-targets
banner build
ptime -m cargo build --features falcon --release \
-p propolis-server -p propolis-cli
OUTDIR=/work/release
mkdir -p $OUTDIR
cp target/release/propolis-cli $OUTDIR/propolis-cli
cp target/release/propolis-server $OUTDIR/propolis-server
cd $OUTDIR
digest -a sha256 propolis-cli > propolis-cli.sha256.txt
digest -a sha256 propolis-server > propolis-server.sha256.txt
================================================
FILE: .github/buildomat/jobs/image.sh
================================================
#!/bin/bash
#:
#: name = "image"
#: variety = "basic"
#: target = "helios-2.0"
#: rust_toolchain = "stable"
#: output_rules = [
#: "/out/*",
#: ]
#:
#: [[publish]]
#: series = "image"
#: name = "propolis-server.tar.gz"
#: from_output = "/out/propolis-server.tar.gz"
#:
#: [[publish]]
#: series = "image"
#: name = "propolis-server.sha256.txt"
#: from_output = "/out/propolis-server.sha256.txt"
#:
set -o errexit
set -o pipefail
set -o xtrace
cargo --version
rustc --version
banner prerequisites
ptime -m ./tools/install_builder_prerequisites.sh -y
banner build
# Enable the "omicron-build" feature to indicate this is an artifact destined
# for production use on an appropriately configured Oxide machine
#
# The 'release' profile is configured for abort-on-panic, so we get an
# immediate coredump rather than unwinding in the case of an error.
ptime -m cargo build --release --verbose -p propolis-server --features omicron-build
banner image
ptime -m cargo run -p propolis-package
banner contents
tar tvfz out/propolis-server.tar.gz
banner copy
pfexec mkdir -p /out
pfexec chown "$UID" /out
mv out/propolis-server.tar.gz /out/propolis-server.tar.gz
cd /out
digest -a sha256 propolis-server.tar.gz > propolis-server.sha256.txt
================================================
FILE: .github/buildomat/jobs/phd-build.sh
================================================
#!/bin/bash
#:
#: name = "phd-build"
#: variety = "basic"
#: target = "helios-2.0"
#: rust_toolchain = "stable"
#: output_rules = [
#: "/out/*",
#: ]
#:
#: [[publish]]
#: series = "phd_build"
#: name = "propolis-server.tar.gz"
#: from_output = "/out/propolis-server-debug.tar.gz"
#:
#: [[publish]]
#: series = "phd_build"
#: name = "propolis-server.sha256.txt"
#: from_output = "/out/propolis-server-debug.sha256.txt"
#:
#: [[publish]]
#: series = "propolis_tests"
#: name = "propolis-tests.tar.gz"
#: from_output = "/out/propolis-tests-debug.tar.gz"
set -o errexit
set -o pipefail
set -o xtrace
outdir="/out"
cargo --version
rustc --version
banner prerequisites
ptime -m ./tools/install_builder_prerequisites.sh -y
# Build the Propolis server binary with 'dev' profile to enable assertions that
# should fire during tests.
banner build-propolis
# We'll do a few cargo builds, keeping features the same means we reuse build
# artifacts from crates these configure.
TEST_FEATURES="omicron-build,failure-injection"
# Compile propolis-server so that it allows development features to be used even
# though the `omicron-build` feature is enabled. This should be a relatively
# small incremental step after building and running tests with the same
# features, above.
export PHD_BUILD="true"
ptime -m cargo build --verbose -p propolis-server \
--features "$TEST_FEATURES"
# Build Propolis test binaries, but they won't run here. The path to get here
# is unfortunate:
# * we don't have `git` on a Gimlet target.
# * or `pkg`.
# * `uname -m` is "oxide", which confuses rustup too.
# * some tests need a VMM, but this job is probably run inside a VM.
#
# Doing the build on a bare gimlet host is "possible", but it's much easier to
# just build the test binaries here and squirrel them off to *run* on a Gimlet.
# So do that.
ptime -m cargo build --tests --verbose --features "$TEST_FEATURES"
# The PHD runner requires unwind-on-panic to catch certain test failures, so
# build it with the 'dev' profile which is so configured.
banner build-phd
ptime -m cargo build --verbose -p phd-runner
banner contents
tar -czvf target/debug/propolis-server-debug.tar.gz \
-C target/debug propolis-server
tar -czvf target/debug/phd-runner.tar.gz \
-C target/debug phd-runner \
-C phd-tests artifacts.toml
tar -czvf target/debug/propolis-tests-debug.tar.gz \
$(find target/debug/deps/ -perm -111 -type f -name 'propolis-*')
banner copy
pfexec mkdir -p $outdir
pfexec chown "$UID" $outdir
cp .github/buildomat/phd-run-with-args.sh $outdir/phd-run-with-args.sh
mv target/debug/propolis-server-debug.tar.gz \
$outdir/propolis-server-debug.tar.gz
mv target/debug/phd-runner.tar.gz $outdir/phd-runner.tar.gz
mv target/debug/propolis-tests-debug.tar.gz $outdir/propolis-tests-debug.tar.gz
cd $outdir
digest -a sha256 propolis-server-debug.tar.gz > \
propolis-server-debug.sha256.txt
digest -a sha256 phd-runner.tar.gz > phd-runner.sha256.txt
================================================
FILE: .github/buildomat/jobs/phd-run-migrate-from-base.sh
================================================
#!/bin/bash
#:
#: name = "phd-run-migrate-from-base"
#: variety = "basic"
#: target = "lab-2.0-gimlet"
#: output_rules = [
#: "/tmp/phd-runner.log",
#: "/tmp/phd-tmp-files.tar.gz",
#: ]
#: skip_clone = true
#:
#: [dependencies.phd-build]
#: job = "phd-build"
#:
# This job runs the PHD migrate-from-base tests, which test upgrading from the
# current mainline propolis-server to the propolis-server version under test.
#
# PHD always uses the propolis-client from the commit under test, so these tests
# will fail if there is a breaking change to the Propolis API. They'll also fail
# if there's a breaking change to the migration protocol. These changes may be
# expected, in which case this run will fail. However, the "regular" phd-run
# job should always be green before merging new PRs.
#
# This job will be removed once API breaking changes are no longer allowed.
cp /input/phd-build/out/phd-run-with-args.sh /tmp/phd-run-with-args.sh
chmod a+x /tmp/phd-run-with-args.sh
exec /tmp/phd-run-with-args.sh \
--include-filter "phd_tests::migrate::from_base" \
--base-propolis-branch master
================================================
FILE: .github/buildomat/jobs/phd-run.sh
================================================
#!/bin/bash
#:
#: name = "phd-run"
#: variety = "basic"
#: target = "lab-2.0-gimlet"
#: output_rules = [
#: "/tmp/phd-runner.log",
#: "/tmp/phd-tmp-files.tar.gz",
#: ]
#: skip_clone = true
#:
#: [dependencies.phd-build]
#: job = "phd-build"
#:
# This job runs all the PHD test cases that don't involve upgrading from an
# earlier version of Propolis.
#
# These tests should always pass even in the presence of breaking changes to the
# Propolis API or live migration protocol.
cp /input/phd-build/out/phd-run-with-args.sh /tmp/phd-run-with-args.sh
chmod a+x /tmp/phd-run-with-args.sh
exec /tmp/phd-run-with-args.sh --exclude-filter "phd_tests::migrate::from_base"
================================================
FILE: .github/buildomat/jobs/test-gimlet.sh
================================================
#!/bin/bash
#:
#: name = "test-gimlet"
#: variety = "basic"
#: target = "lab-2.0-gimlet"
#: rust_toolchain = false
#: skip_clone = true
#:
#: [dependencies.phd-build]
#: job = "phd-build"
#
# That buildomat frontmatter is kinda absurd. We're going to run Propolis
# tests, but not build them? And `lab-2.0-gimlet` specifically?
#
# # The Target
#
# Propolis has a handful of tests that would like to create and destroy real
# VMMs. In particular the tests involving virtio-nic set up a viona device
# which itself requires an actual VMM.
#
# This means we have effectively the same constraints as the `phd-run` jobs to
# be able to run *all* of the Propolis tests.
#
# # The Skips
#
# The Gimlet target doesn't have git, or pkg to get git. Installing the Rust
# toolchain by hand is doable, but pretty funky. Instead, we *build* Propolis
# tests in the `phd-build` job, and only *run* them here. We've built most of
# the dependency tree there already anyway, so we're not going too far out of
# our way for this.
set -o errexit
set -o pipefail
set -o xtrace
banner prepare
TEST_TAR="propolis-tests-debug.tar.gz"
cp /input/phd-build/out/"$TEST_TAR" .
tar xvf "$TEST_TAR"
banner test-propolis
# Set up an etherstub to use for VNICs in virtio-nic tests. We might want the
# tests to run on a real link one day to do actual networking, but we don't need
# that yet!
TEST_DEV="prop_viona_test0"
pfexec dladm create-etherstub "$TEST_DEV"
for testbin in ./target/debug/deps/propolis-*; do
VIONA_TEST_NIC="$TEST_DEV" pfexec ptime -m "$testbin"
done
================================================
FILE: .github/buildomat/phd-run-with-args.sh
================================================
#!/bin/bash
# Unpacks and executes the PHD runner in the Buildomat environment, passing
# through to the runner any arguments that were passed to this script.
set -o errexit
set -o pipefail
set -o xtrace
indir="/input"
indir_suffix="phd-build/out/*.tar.gz"
phddir="$PWD/phd-test"
# Put artifacts on the runner's SSDs (the /work ramdisk is small by design, too
# small for images of any appreciable size).
# Find usable disks to make a zpool on. Note that this only works on Oxide
# compute sled runners such as `lab-2.0-gimlet`.
disks=( $(pilot local disk list -H -o type,disk | grep -v 'M.2' | cut -f 2) )
pfexec zpool create -f phd-artifacts ${disks[@]}
artifactdir="/phd-artifacts"
banner 'Inputs'
find $indir -ls
rm -rf "$phddir"
mkdir "$phddir"
for p in $indir/$indir_suffix; do
tar xzvf $p -C $phddir
for f in $(tar tf "$p"); do
chmod +x "$phddir/$f"
done
done
ls $phddir
banner 'Setup'
tmpdir="/tmp/propolis-phd"
if [ ! -d "$tmpdir" ]; then
mkdir $tmpdir
fi
# We'll be using the reservoir, so set it to something higher than the default
# 0MiB. Most tests would only need 512MiB (the default PHD guest VM memory
# size), some tests want to run multiple VMs concurrently (particularly around
# migration). 4096MiB is an arbitrary number intended to support the above and
# that we might want to run those tests concurrently at some point.
#
# Currently the lab host these tests will run on is well-known and has much
# more memory than this. Hopefully we won't have Propolis CI running on a
# machine with ~4GiB of memory, but this number could be tuned down if the need
# arises.
pfexec /usr/lib/rsrvrctl -s 4096
banner 'Tests'
runner="$phddir/phd-runner"
artifacts="$phddir/artifacts.toml"
propolis="$phddir/propolis-server"
ls $runner
ls $artifacts
ls $propolis
args=(
$runner '--emit-bunyan' 'run'
'--propolis-server-cmd' $propolis
'--crucible-downstairs-commit' 'auto'
'--artifact-toml-path' $artifacts
'--tmp-directory' $tmpdir
'--artifact-directory' $artifactdir
$@
)
# Disable errexit so that we still upload logs on failure
set +e
(RUST_BACKTRACE=1 RUST_LOG="info,phd=debug" ptime -m pfexec "${args[@]}" | \
tee /tmp/phd-runner.log)
failcount=$?
set -e
tar -czvf /tmp/phd-tmp-files.tar.gz \
-C "$tmpdir" .
exitcode=0
if [ $failcount -eq 0 ]; then
echo
echo "ALL TESTS PASSED"
echo
else
echo
echo "SOME TESTS FAILED"
echo
exitcode=1
fi
if find /dev/vmm -mindepth 1 -maxdepth 1 | read ; then
echo "VMM handles leaked:"
find /dev/vmm -type c -exec basename {} \;
exitcode=2
fi
exit $exitcode
================================================
FILE: .github/workflows/rust.yml
================================================
name: Rust
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
CARGO_TERM_COLOR: always
jobs:
check-style:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Report rustfmt version
run: cargo fmt -- --version
- name: Check style
run: cargo fmt -- --check
- name: Check misc. style
run: cargo xtask style
check-clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Report clippy version
run: cargo clippy -- --version
- name: Check clippy
run: cargo xtask clippy --strict
check-license:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Check license headers
uses: apache/skywalking-eyes/header@5c5b974209f0de5d905f37deb69369068ebfc15c # v0.7.0
build-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Test build documentation
run: cargo doc --workspace --no-deps
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
# see https://github.com/oxidecomputer/omicron/issues/4461
# (by default the action picks a merge commit with the target branch
# rather than the actual PR tip)
ref: ${{ github.event.pull_request.head.sha }}
# `test_apis_up_to_date` needs a full history for the gitstubs
fetch-depth: 0
- name: Build
run: cargo build --verbose
- name: Build mock-only server
run: cargo build -p propolis-mock-server --verbose
- name: Test Libraries
run: cargo test --lib --verbose
- name: Test everything
run: cargo test --locked
# Build and test propolis-the-library on its own; `cargo test --lib` as used
# above builds the entire workspace, meaning propolis-server default features
# are used to build and run propolis-lib tests. Instead, this check uses
# `cargo build -p propolis` to only operate with that one crate.
build-and-test-propolis-lib:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Build
run: cargo build -p propolis --verbose
- name: Test Propolis-lib
run: cargo test -p propolis --verbose
================================================
FILE: .gitignore
================================================
/target
/crates/*/header-check/target
/crates/*/header-check/Cargo.lock
debug.out
core
out/
================================================
FILE: .licenserc.yaml
================================================
header:
license:
spdx-id: MPL-2.0
content: |
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
paths:
- '**/*.rs'
paths-ignore:
- 'target/**/*.rs'
comment: on-failure
================================================
FILE: Cargo.toml
================================================
[workspace]
resolver = "2"
members = [
"crates/*",
"crates/*/sys",
"bin/*",
"lib/*",
"packaging/propolis-package",
"phd-tests/*",
"xtask",
]
default-members = [
"crates/*",
"crates/*/sys",
"lib/*",
"bin/dropshot-apis",
"bin/propolis-cli",
"bin/propolis-server",
"bin/propolis-standalone",
"xtask",
]
# `header-check` crates are excluded because they require an external checkout
# of illumos-gate (perhaps even to a specific revision) to run. So, to support
# more Propolis-local development, exclude these and leave them for more
# specific tools (see `tools/check_headers`, which is used to run these in CI)
exclude = [
"crates/bhyve-api/header-check",
"crates/nvpair/header-check",
"crates/viona-api/header-check",
]
# If one wants the 'dev' profile, but with "panic = abort" semantics, they
# should opt in with this profile. Unwinding is required by PHD and
# should_abort cargo tests, and so remains the default for the 'dev' profile.
[profile.dev-abort]
inherits = "dev"
panic = "abort"
# Building for 'release' implies running on a real illumos system, where we
# certainly want (rust) panics to cause an immediate abort and coredump.
[profile.release]
panic = "abort"
[workspace.dependencies]
# Internal crates
bhyve_api = { path = "crates/bhyve-api" }
bhyve_api_sys = { path = "crates/bhyve-api/sys" }
cpuid_utils = { path = "crates/cpuid-utils" }
cpuid_profile_config = { path = "crates/cpuid-profile-config" }
dladm = { path = "crates/dladm" }
nvpair = { path = "crates/nvpair" }
nvpair_sys = { path = "crates/nvpair/sys" }
pbind = { path = "crates/pbind" }
propolis-config-toml = { path = "crates/propolis-config-toml" }
propolis_api_types = { path = "crates/propolis-api-types" }
propolis-api-types-versions = { path = "crates/propolis-api-types-versions" }
propolis-server-api = { path = "crates/propolis-server-api" }
propolis_types = { path = "crates/propolis-types" }
rfb = { path = "crates/rfb" }
rgb_frame = { path = "crates/rgb-frame" }
viona_api = { path = "crates/viona-api" }
# PHD testing framework
phd-framework = { path = "phd-tests/framework" }
phd-testcase = { path = "phd-tests/testcase" }
phd-testcase-macros = { path = "phd-tests/testcase_macro" }
phd-tests = { path = "phd-tests/tests" }
# Public library crates
propolis = { path = "lib/propolis", default-features = false }
propolis-client = { path = "lib/propolis-client" }
# Propolis cfg(feature = "falcon")
dlpi = { git = "https://github.com/oxidecomputer/dlpi-sys", branch = "main" }
ispf = { git = "https://github.com/oxidecomputer/ispf" }
libloading = "0.7"
p9ds = { git = "https://github.com/oxidecomputer/p9fs" }
softnpu = { git = "https://github.com/oxidecomputer/softnpu" }
# Omicron-related
internal-dns-resolver = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
internal-dns-types = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
nexus-client = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
omicron-common = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
omicron-zone-package = "0.12.2"
oximeter-instruments = { git = "https://github.com/oxidecomputer/omicron", branch = "main", default-features = false, features = ["kstat"] }
oximeter-producer = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
oximeter = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
sled-agent-client = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
# Crucible
crucible = { git = "https://github.com/oxidecomputer/crucible", rev = "3c1708d86e10f0370807388a1efe092edd99d431" }
crucible-client-types = { git = "https://github.com/oxidecomputer/crucible", rev = "3c1708d86e10f0370807388a1efe092edd99d431" }
# Attestation
dice-verifier = { git = "https://github.com/oxidecomputer/dice-util", rev = "1d3084b514389847e8e0f5d966d2be4f18d02d32", features = ["sled-agent"] }
vm-attest = { git = "https://github.com/oxidecomputer/vm-attest", rev = "2cdd17580a4fc6c871d24797016af8dbaac9421d", default-features = false }
# External dependencies
anyhow = "1.0"
async-trait = "0.1.88"
atty = "0.2.14"
backoff = "0.4.0"
backtrace = "0.3.66"
base64 = "0.21"
bit_field = "0.10.1"
bitflags = "2.4"
bitstruct = "0.1"
bitvec = "1.0"
byteorder = "1"
bytes = "1.7.1"
camino = "1.1.6"
cargo_metadata = "0.18.1"
cc = "1.0.73"
cfg-if = "1.0.0"
chrono = "0.4.19"
clap = "4.2"
const_format = "0.2"
crossbeam-channel = "0.5"
ctrlc = "3.2"
dropshot = "0.17.0"
dropshot-api-manager = "0.7.1"
dropshot-api-manager-types = "0.7.1"
erased-serde = "0.4"
errno = "0.2.8"
escargot = "0.5.8"
expectorate = "1.0.5"
fatfs = "0.3.6"
futures = "0.3"
futures-util = "0.3.21"
flate2 = "1.0.28"
heck = "0.5.0"
hex = "0.4.3"
http = "1.1.0"
hyper = "1.0"
linkme = "0.3.33"
iddqd = "0.3"
itertools = "0.13.0"
kstat-rs = "0.2.4"
lazy_static = "1.4"
libc = "0.2"
mockall = "0.12"
newtype_derive = "0.1.6"
newtype-uuid = { version = "1.0.1", features = [ "v4" ] }
# "feature" for `utsname`, "poll" for `PollFlags`
nix = { version = "0.31", features = [ "feature", "poll" ] }
owo-colors = "4"
oxide-tokio-rt = "0.1.2"
paste = "1.0.15"
pin-project-lite = "0.2.13"
proc-macro2 = "1.0"
proc-macro-error = "1"
progenitor = "0.14.0"
progenitor-client = "0.14.0"
proptest = "1.5.0"
quote = "1.0"
rand = "0.9.1"
reqwest = { version = "0.13", default-features = false }
ring = "0.17"
ron = "0.8"
schemars = "0.8.10"
semver = "1.0"
serde = "1.0"
serde_arrays = "0.1"
serde_derive = "1.0"
serde_json = "1.0"
serde_test = "1.0.138"
sha2 = "0.10.9"
slog = "2.7"
slog-async = "2.8"
slog-bunyan = "2.4.0"
slog-dtrace = "0.3"
slog-term = "2.8"
strum = "0.26"
syn = "1.0"
tar = "0.4"
tempfile = "3.2"
termwiz = "0.20"
thiserror = "1.0"
tokio = "1"
tokio-tungstenite = "0.21"
tokio-util = "0.7"
toml = "0.7.8"
tracing = "0.1.35"
tracing-appender = "0.2.2"
tracing-bunyan-formatter = "0.3.3"
tracing-subscriber = "0.3.14"
usdt = { version = "0.6", default-features = false }
uuid = "1.3.2"
zerocopy = "0.8.25"
#
# It's common during development to use a local copy of various complex
# dependencies. If you want to use those, uncomment one of these blocks.
#
# [patch."https://github.com/oxidecomputer/omicron"]
# internal-dns = { path = "../omicron/internal-dns" }
# nexus-client = { path = "../omicron/clients/nexus-client" }
# omicron-common = { path = "../omicron/common" }
# oximeter-instruments = { path = "../omicron/oximeter/instruments" }
# oximeter-producer = { path = "../omicron/oximeter/producer" }
# oximeter = { path = "../omicron/oximeter/oximeter" }
# [patch."https://github.com/oxidecomputer/crucible"]
# crucible = { path = "../crucible/upstairs" }
# crucible-client-types = { path = "../crucible/crucible-client-types" }
================================================
FILE: LICENSE
================================================
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
================================================
FILE: README.md
================================================
# Propolis
Propolis VMM userspace for use with illumos bhyve.
## Prerequisites
Given the current tight coupling of the `bhyve-api` component to the ioctl
interface presented by the bhyve kernel component, running on recent illumos
bits is required.
Propolis works best (and its CI tests run) on AMD hosts, but it can also be used
to run VMs on Intel hosts. Live migration is primarily supported on AMD hosts
but may work on Intel hosts as well.
## Components
Programs:
- [propolis-server](bin/propolis-server): Run a Propolis VM instance, operated
via REST API calls (typically by
[omicron](https://github.com/oxidecomputer/omicron))
- [propolis-cli](bin/propolis-cli): CLI wrapper interface for `propolis-server`
API calls
- [propolis-standalone](bin/propolis-standalone): Simple standalone program to
run a Propolis VM instance, operated via a local config file
Libraries:
- [propolis-client](lib/propolis-client): Rust crate for `propolis-server` API
- [propolis](lib/propolis): Represents the bulk of the emulation logic required
to implement a userspace VMM. Both `propolis-server` and
`propolis-standalone` are built around this.
## Internal Crates
These are not meant as committed public interfaces, but rather internal
implementation details, consumed by Propolis components.
- bhyve-api: API (ioctls & structs) for the illumos bhyve kernel VMM
- dladm: Some thin wrappers around `dladm` queries
- propolis-server-config: Type definitions for `propolis-server` config file
- propolis-types: Publically exposed (via `propolis-server`) types, intergral
to the `propolis` library
- viona-api: API (ioctls & structs) for the illumos viona driver
## xtasks
Propolis uses the `cargo xtask` pattern in order to conveniently expose certain
tasks to developers.
- `clippy`: Run suite of clippy checks. This performs more than a simple
`cargo clippy`, since there are several combinations of feature flags which
must be checked.
- `fmt`: Check style according to `rustfmt`
- `license`: Check (crudely) that files bear appropriate license headers
- `phd`: Run the PHD test suite
- `style`: Perform miscellaneous style checks
- `prepush`: Preform pre-push checks (`clippy`, `fmt`, `license`, `style`) in a
manner which resembles (but does not exactly match) how they are run in CI.
Running tests (unit, integration, or phd) are not included and are left to
the user.
It is recommended that developers run the `prepush` test before pushing a
branch which will be subsequently checked by CI. Doing so currently requires
an x86\_64 UNIX/Linux machine.
## License
Unless otherwise noted, all components are licensed under the [Mozilla Public
License Version 2.0](LICENSE).
================================================
FILE: bin/dropshot-apis/Cargo.toml
================================================
[package]
name = "propolis-dropshot-apis"
version = "0.1.0"
edition = "2024"
license = "MPL-2.0"
[dependencies]
anyhow.workspace = true
camino.workspace = true
clap.workspace = true
dropshot-api-manager-types.workspace = true
dropshot-api-manager.workspace = true
propolis-server-api.workspace = true
semver.workspace = true
================================================
FILE: bin/dropshot-apis/src/main.rs
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::process::ExitCode;
use anyhow::Context;
use camino::Utf8PathBuf;
use clap::Parser;
use dropshot_api_manager::{Environment, ManagedApiConfig, ManagedApis};
use dropshot_api_manager_types::{ManagedApiMetadata, Versions};
use propolis_server_api::*;
pub fn environment() -> anyhow::Result<Environment> {
// The workspace root is two levels up from this crate's directory.
let workspace_root = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.parent()
.unwrap()
.to_path_buf();
let env = Environment::new(
// This is the command used to run the OpenAPI manager.
"cargo xtask openapi",
workspace_root,
// This is the location within the workspace root where the OpenAPI
// documents are stored.
"openapi",
)?
.with_default_git_branch("origin/master".to_owned());
Ok(env)
}
/// The list of APIs managed by the OpenAPI manager.
pub fn all_apis() -> anyhow::Result<ManagedApis> {
let apis = vec![ManagedApiConfig {
ident: "propolis-server",
versions: Versions::Versioned {
supported_versions: propolis_server_api::supported_versions(),
},
title: "Oxide Propolis Server API",
metadata: ManagedApiMetadata {
description: Some(
"API for interacting with the Propolis hypervisor frontend.",
),
contact_url: Some("https://oxide.computer"),
contact_email: Some("api@oxide.computer"),
..Default::default()
},
api_description: propolis_server_api_mod::stub_api_description,
}];
let apis = ManagedApis::new(apis)
.context("error creating ManagedApis")?
.with_git_stub_storage();
Ok(apis)
}
fn main() -> anyhow::Result<ExitCode> {
let app = dropshot_api_manager::App::parse();
let env = environment()?;
let apis = all_apis()?;
Ok(app.exec(&env, &apis))
}
#[cfg(test)]
mod test {
use dropshot_api_manager::test_util::check_apis_up_to_date;
use super::*;
// Also recommended: a test which ensures documents are up-to-date. The
// OpenAPI manager comes with a helper function for this, called
// `check_apis_up_to_date`.
#[test]
fn test_apis_up_to_date() -> anyhow::Result<ExitCode> {
let env = environment()?;
let apis = all_apis()?;
let result = check_apis_up_to_date(&env, &apis)?;
Ok(result.to_exit_code())
}
}
================================================
FILE: bin/mock-server/Cargo.toml
================================================
[package]
name = "propolis-mock-server"
version = "0.0.0"
license = "MPL-2.0"
edition = "2021"
[lib]
name = "propolis_mock_server"
path = "src/lib/lib.rs"
doc = false
doctest = false
test = false
[[bin]]
name = "propolis-mock-server"
path = "src/main.rs"
doc = false
doctest = false
test = false
[dependencies]
atty.workspace = true
anyhow.workspace = true
clap = { workspace = true, features = ["derive"] }
base64.workspace = true
dropshot = { workspace = true }
futures.workspace = true
hyper.workspace = true
serde.workspace = true
propolis_api_types.workspace = true
propolis-api-types-versions.workspace = true
propolis_types.workspace = true
semver.workspace = true
serde_json.workspace = true
slog.workspace = true
slog-async.workspace = true
slog-dtrace.workspace = true
slog-term.workspace = true
slog-bunyan.workspace = true
thiserror.workspace = true
tokio = { workspace = true, features = ["full"] }
tokio-tungstenite.workspace = true
# Progenitor is used to instantiate copies of the API types without exposing
# internal dependencies (like crucible-client-types). Although we are not
# using the client itself, its deps (reqwest, etc) are required.
progenitor.workspace = true
reqwest.workspace = true
schemars.workspace = true
rand.workspace = true
uuid.workspace = true
================================================
FILE: bin/mock-server/src/lib/api_types.rs
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
progenitor::generate_api!(
spec = "../../openapi/propolis-server/propolis-server-latest.json",
derives = [schemars::JsonSchema],
replace = {
SpecKey = propolis_api_types_versions::latest::instance_spec::SpecKey,
},
patch = {
InstanceMetadata = { derives = [Clone, Eq, PartialEq] },
InstanceProperties = { derives = [ Clone, Eq, PartialEq ] },
Slot = { derives = [Copy] },
},
);
impl TryFrom<types::PciPath> for propolis_types::PciPath {
type Error = String;
fn try_from(value: types::PciPath) -> Result<Self, Self::Error> {
propolis_types::PciPath::new(value.bus, value.device, value.function)
.map_err(|e| e.to_string())
}
}
// Duplicate the parameter types for the endpoints related to the serial console
#[derive(JsonSchema, Serialize, Deserialize)]
pub struct InstanceSerialParams {
/// Character index in the serial buffer from which to read, counting the bytes output since
/// instance start. If this is provided, `most_recent` must *not* be provided.
pub from_start: Option<u64>,
/// Character index in the serial buffer from which to read, counting *backward* from the most
/// recently buffered data retrieved from the instance. (See note on `from_start` about mutual
/// exclusivity)
pub most_recent: Option<u64>,
}
#[derive(JsonSchema, Serialize, Deserialize)]
pub struct InstanceSerialHistoryParams {
/// Character index in the serial buffer from which to read, counting the bytes output since
/// instance start. If this is not provided, `most_recent` must be provided, and if this *is*
/// provided, `most_recent` must *not* be provided.
pub from_start: Option<u64>,
/// Character index in the serial buffer from which to read, counting *backward* from the most
/// recently buffered data retrieved from the instance. (See note on `from_start` about mutual
/// exclusivity)
pub most_recent: Option<u64>,
/// Maximum number of bytes of buffered serial console contents to return. If the requested
/// range runs to the end of the available buffer, the data returned will be shorter than
/// `max_bytes`.
pub max_bytes: Option<u64>,
}
#[derive(
Copy, Clone, Debug, PartialEq, Eq, JsonSchema, Serialize, Deserialize,
)]
pub enum MockMode {
/// The mock server should run freely, advancing the state every time the
/// instance_state_monitor endpoint is requested while new state
/// transitions are queued.
Run,
/// The mock server should only advance the current state when the
/// /mock/step endpoint is requested.
SingleStep,
}
================================================
FILE: bin/mock-server/src/lib/lib.rs
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Implementation of a mock Propolis server
use std::sync::Arc;
use dropshot::{
channel, endpoint, ApiDescription, HttpError, HttpResponseCreated,
HttpResponseOk, HttpResponseUpdatedNoContent, Query, RequestContext,
TypedBody, WebsocketConnection,
};
use futures::SinkExt;
use slog::{error, o, Logger};
use std::collections::BTreeMap;
use thiserror::Error;
use tokio::sync::{watch, Mutex};
use tokio_tungstenite::tungstenite::protocol::{Role, WebSocketConfig};
use tokio_tungstenite::tungstenite::Message;
use tokio_tungstenite::WebSocketStream;
mod api_types;
use api_types::types::{self as api, InstanceEnsureRequest};
pub use api_types::MockMode;
#[derive(Debug, Eq, PartialEq, Error)]
pub enum Error {
#[error("Failed to send simulated state change update through channel")]
TransitionSendFail,
#[error("Cannot request any new mock instance state once it is stopped/destroyed/failed")]
TerminalState,
#[error("Cannot transition to {requested:?} from {current:?}")]
InvalidTransition {
current: api::InstanceState,
requested: api::InstanceStateRequested,
},
}
/// simulated instance properties
pub struct InstanceContext {
/// The instance's current generation last observed by the
/// `instance-state-monitor` endpoint.
curr_gen: u64,
pub properties: api::InstanceProperties,
serial: Arc<serial::Serial>,
serial_task: serial::SerialTask,
state_watcher_rx: watch::Receiver<MockState>,
state_watcher_tx: watch::Sender<MockState>,
}
struct MockState {
queue: BTreeMap<u64, api::InstanceStateMonitorResponse>,
/// The next generation to use when inserting new state(s) into the queue.
next_queue_gen: u64,
/// Current generation when single-stepping.
///
/// This is set when setting the single-step mock mode, and unset if not in
/// that mode.
single_step_gen: Option<u64>,
}
impl InstanceContext {
pub fn new(properties: api::InstanceProperties, _log: &Logger) -> Self {
let (state_watcher_tx, state_watcher_rx) = {
let mut queue = BTreeMap::new();
queue.insert(
0,
api::InstanceStateMonitorResponse {
gen_: 0,
state: api::InstanceState::Creating,
migration: api::InstanceMigrateStatusResponse {
migration_in: None,
migration_out: None,
},
},
);
watch::channel(MockState {
queue,
single_step_gen: None,
next_queue_gen: 1,
})
};
let serial = serial::Serial::new(&properties.name);
let serial_task = serial::SerialTask::spawn();
Self {
curr_gen: 0,
properties,
serial,
serial_task,
state_watcher_rx,
state_watcher_tx,
}
}
/// Updates the state of the mock instance.
///
/// Returns an error if the state transition is invalid.
pub async fn set_target_state(
&mut self,
log: &Logger,
target: api::InstanceStateRequested,
) -> Result<(), Error> {
match (self.current_state(), target) {
(
api::InstanceState::Stopped
| api::InstanceState::Destroyed
| api::InstanceState::Failed,
_,
) => {
// Cannot request any state once the target is halt/destroy
Err(Error::TerminalState)
}
(
api::InstanceState::Rebooting,
api::InstanceStateRequested::Run,
) => {
// Requesting a run when already on the road to reboot is an
// immediate success.
Ok(())
}
(api::InstanceState::Running, api::InstanceStateRequested::Run) => {
Ok(())
}
(
api::InstanceState::Running,
api::InstanceStateRequested::Reboot,
) => {
self.queue_states(
log,
&[
api::InstanceState::Rebooting,
api::InstanceState::Running,
],
)
.await;
Ok(())
}
(current, api::InstanceStateRequested::Reboot) => {
Err(Error::InvalidTransition {
current,
requested: api::InstanceStateRequested::Reboot,
})
}
(_, api::InstanceStateRequested::Run) => {
self.queue_states(log, &[api::InstanceState::Running]).await;
Ok(())
}
(
api::InstanceState::Stopping,
api::InstanceStateRequested::Stop,
) => Ok(()),
(_, api::InstanceStateRequested::Stop) => {
self.queue_states(
log,
&[
api::InstanceState::Stopping,
api::InstanceState::Stopped,
],
)
.await;
self.serial_task.shutdown().await;
Ok(())
}
}
}
fn current_state(&self) -> api::InstanceState {
self.state_watcher_rx.borrow().queue
.get(&self.curr_gen)
.expect("current generation must be in the queue, this is weird 'n' bad")
.state
}
async fn queue_states(
&mut self,
log: &Logger,
states: &[api::InstanceState],
) {
self.state_watcher_tx.send_modify(|mock_state| {
for &state in states {
let generation = mock_state.next_queue_gen;
mock_state.next_queue_gen += 1;
mock_state.queue.insert(
generation,
api::InstanceStateMonitorResponse {
gen_: generation,
migration: api::InstanceMigrateStatusResponse {
migration_in: None,
migration_out: None,
},
state,
},
);
slog::info!(
log,
"queued instance state transition";
"state" => ?state,
"gen" => ?generation,
);
}
})
}
}
/// Contextual information accessible from mock HTTP callbacks.
pub struct Context {
instance: Mutex<Option<InstanceContext>>,
log: Logger,
}
impl Context {
pub fn new(log: Logger) -> Self {
Context { instance: Mutex::new(None), log }
}
}
#[endpoint {
method = PUT,
path = "/instance",
}]
async fn instance_ensure(
rqctx: RequestContext<Arc<Context>>,
request: TypedBody<api::InstanceEnsureRequest>,
) -> Result<HttpResponseCreated<api::InstanceEnsureResponse>, HttpError> {
let server_context = rqctx.context();
let request = request.into_inner();
let InstanceEnsureRequest { properties, .. } = request;
// Handle an already-initialized instance
let mut instance = server_context.instance.lock().await;
if let Some(instance) = &*instance {
if instance.properties != properties {
return Err(HttpError::for_internal_error(
"Cannot update running server".to_string(),
));
}
return Ok(HttpResponseCreated(api::InstanceEnsureResponse {
migrate: None,
}));
}
*instance = Some(InstanceContext::new(properties, &server_context.log));
Ok(HttpResponseCreated(api::InstanceEnsureResponse { migrate: None }))
}
#[endpoint {
method = GET,
path = "/instance",
}]
async fn instance_get(
rqctx: RequestContext<Arc<Context>>,
) -> Result<HttpResponseOk<api::InstanceGetResponse>, HttpError> {
let instance = rqctx.context().instance.lock().await;
let instance = instance.as_ref().ok_or_else(|| {
HttpError::for_internal_error(
"Server not initialized (no instance)".to_string(),
)
})?;
let instance_info = api::Instance {
properties: instance.properties.clone(),
state: instance.current_state(),
};
Ok(HttpResponseOk(api::InstanceGetResponse { instance: instance_info }))
}
#[endpoint {
method = GET,
path = "/instance/state-monitor",
}]
async fn instance_state_monitor(
rqctx: RequestContext<Arc<Context>>,
request: TypedBody<api::InstanceStateMonitorRequest>,
) -> Result<HttpResponseOk<api::InstanceStateMonitorResponse>, HttpError> {
let (mut state_watcher, gen) = {
let instance = rqctx.context().instance.lock().await;
let instance = instance.as_ref().ok_or_else(|| {
HttpError::for_internal_error(
"Server not initialized (no instance)".to_string(),
)
})?;
let gen = request.into_inner().gen_;
let state_watcher = instance.state_watcher_rx.clone();
(state_watcher, gen)
};
slog::debug!(
rqctx.log,
"instance state monitor request";
"request_gen" => gen,
);
loop {
let state = {
let mock_state = state_watcher.borrow_and_update();
match mock_state.single_step_gen {
// We are single-stepping, and have not yet reached the
// requested generation. Keep waiting until single-stepped to
// where we need to be.
Some(g) if gen > g => {
slog::info!(
rqctx.log,
"instance state monitor: wait for single step...";
"request_gen" => gen,
"current_gen" => g,
);
None
}
// Otherwise, if we have stepped to the requested generation, or
// if we are not in single-step mode, just return the current
// thing.
_ => mock_state.queue.get(&gen).cloned(),
}
};
if let Some(state) = state {
slog::info!(
rqctx.log,
"instance state monitor";
"request_gen" => gen,
"state" => ?state.state,
);
// Advance to the state with the generation we showed to the
// watcher, for use in `instance_get` and when determining what
// state transitions are valid.
rqctx
.context()
.instance
.lock()
.await
.as_mut()
.expect("if we didn't have an instance, we shouldn't have gotten here")
.curr_gen = gen;
return Ok(HttpResponseOk(state));
}
state_watcher.changed().await.unwrap();
}
}
#[endpoint {
method = PUT,
path = "/instance/state",
}]
async fn instance_state_put(
rqctx: RequestContext<Arc<Context>>,
request: TypedBody<api::InstanceStateRequested>,
) -> Result<HttpResponseUpdatedNoContent, HttpError> {
let mut instance = rqctx.context().instance.lock().await;
let instance = instance.as_mut().ok_or_else(|| {
HttpError::for_internal_error(
"Server not initialized (no instance)".to_string(),
)
})?;
let requested_state = request.into_inner();
instance.set_target_state(&rqctx.log, requested_state).await.map_err(
|err| {
HttpError::for_internal_error(format!(
"Failed to transition: {err}"
))
},
)?;
Ok(HttpResponseUpdatedNoContent {})
}
// TODO: mock the "Serial" struct itself instead?
#[channel {
protocol = WEBSOCKETS,
path = "/instance/serial",
}]
async fn instance_serial(
rqctx: RequestContext<Arc<Context>>,
query: Query<api_types::InstanceSerialParams>,
websock: WebsocketConnection,
) -> dropshot::WebsocketChannelResult {
let config = WebSocketConfig::default();
let mut ws_stream = WebSocketStream::from_raw_socket(
websock.into_inner(),
Role::Server,
Some(config),
)
.await;
match rqctx.context().instance.lock().await.as_ref() {
None => {
ws_stream.send(Message::Close(None)).await?;
Err("Instance not yet created!".into())
}
Some(instance_ctx)
if instance_ctx.current_state() != api::InstanceState::Running =>
{
ws_stream.send(Message::Close(None)).await?;
Err(format!(
"Instance isn't Running! ({:?})",
instance_ctx.current_state()
)
.into())
}
Some(instance_ctx) => {
let serial = instance_ctx.serial.clone();
let query_params = query.into_inner();
let history_query = serial::HistoryQuery::from_query(
query_params.from_start,
query_params.most_recent,
);
if let Some(mut hq) = history_query {
loop {
let (data, offset) = serial.history_vec(hq, None).await?;
if data.is_empty() {
break;
}
ws_stream.send(Message::Binary(data)).await?;
hq = serial::HistoryQuery::FromStart(offset);
}
}
instance_ctx.serial_task.new_conn(ws_stream).await;
Ok(())
}
}
}
#[endpoint {
method = GET,
path = "/instance/serial/history",
}]
async fn instance_serial_history_get(
rqctx: RequestContext<Arc<Context>>,
query: Query<api_types::InstanceSerialHistoryParams>,
) -> Result<HttpResponseOk<api::InstanceSerialConsoleHistoryResponse>, HttpError>
{
let query_params = query.into_inner();
let history_query = serial::HistoryQuery::from_query(
query_params.from_start,
query_params.most_recent,
)
.ok_or_else(|| {
HttpError::for_bad_request(
None,
"Exactly one of 'from_start' or 'most_recent' must be specified."
.to_string(),
)
})?;
let max_bytes = query_params.max_bytes.map(|x| x as usize);
let ctx = rqctx.context();
let (data, end) = ctx
.instance
.lock()
.await
.as_ref()
.ok_or(HttpError::for_internal_error(
"No mock instance instantiated".to_string(),
))?
.serial
.history_vec(history_query, max_bytes)
.await
.map_err(|e| HttpError::for_bad_request(None, e.to_string()))?;
Ok(HttpResponseOk(api::InstanceSerialConsoleHistoryResponse {
data,
last_byte_offset: end as u64,
}))
}
#[endpoint {
method = GET,
path = "/mock/mode"
}]
async fn mock_mode_get(
rqctx: RequestContext<Arc<Context>>,
) -> Result<HttpResponseOk<MockMode>, HttpError> {
let instance = rqctx.context().instance.lock().await;
let instance = instance.as_ref().ok_or_else(|| {
HttpError::for_internal_error(
"Server not initialized (no instance)".to_string(),
)
})?;
let mode = if instance.state_watcher_rx.borrow().single_step_gen.is_some() {
MockMode::SingleStep
} else {
MockMode::Run
};
Ok(HttpResponseOk(mode))
}
#[endpoint {
method = PUT,
path = "/mock/mode"
}]
async fn mock_mode_set(
rqctx: RequestContext<Arc<Context>>,
request: TypedBody<MockMode>,
) -> Result<HttpResponseUpdatedNoContent, HttpError> {
let instance = rqctx.context().instance.lock().await;
let instance = instance.as_ref().ok_or_else(|| {
HttpError::for_internal_error(
"Server not initialized (no instance)".to_string(),
)
})?;
let mode = request.into_inner();
instance.state_watcher_tx.send_if_modified(|mock_state| {
match mode {
MockMode::Run => {
mock_state.single_step_gen = None;
true
}
// If we're already in single-step mode, don't clobber the existing
// single-step generation.
MockMode::SingleStep if mock_state.single_step_gen.is_some() => {
false
}
// Otherwise, start single-stepping from the current generation.
MockMode::SingleStep => {
mock_state.single_step_gen = Some(instance.curr_gen);
true
}
}
});
Ok(HttpResponseUpdatedNoContent())
}
#[endpoint {
method = PUT,
path = "/mock/step"
}]
async fn mock_step(
rqctx: RequestContext<Arc<Context>>,
) -> Result<HttpResponseUpdatedNoContent, HttpError> {
let instance = rqctx.context().instance.lock().await;
let instance = instance.as_ref().ok_or_else(|| {
HttpError::for_internal_error(
"Server not initialized (no instance)".to_string(),
)
})?;
if instance.state_watcher_rx.borrow().single_step_gen.is_none() {
return Err(HttpError::for_bad_request(
None,
"not in single-step mode".to_string(),
));
}
instance.state_watcher_tx.send_modify(|state| {
let g = state
.single_step_gen
.as_mut()
.expect("we just checked that it's set");
*g += 1;
slog::info!(
rqctx.log,
"instance state stepped to generation {g}";
"gen" => *g,
);
});
Ok(HttpResponseUpdatedNoContent())
}
mod serial {
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use dropshot::WebsocketConnectionRaw;
use futures::StreamExt;
use tokio::sync::{mpsc, Notify};
use tokio_tungstenite::tungstenite::protocol::{
frame::coding::CloseCode, CloseFrame,
};
use tokio_tungstenite::WebSocketStream;
type WsConn = WebSocketStream<WebsocketConnectionRaw>;
const DEFAULT_MAX_LEN: usize = 1024;
pub(crate) enum HistoryQuery {
FromStart(usize),
MostRecent(usize),
}
impl HistoryQuery {
pub(crate) const fn from_query(
from_start: Option<u64>,
most_recent: Option<u64>,
) -> Option<Self> {
match (from_start, most_recent) {
(Some(from_start), None) => {
Some(Self::FromStart(from_start as usize))
}
(None, Some(most_recent)) => {
Some(Self::MostRecent(most_recent as usize))
}
_ => None,
}
}
}
/// Fake serial task
pub(crate) struct SerialTask {
chan_ctrl: mpsc::Sender<()>,
chan_ws: mpsc::Sender<WsConn>,
is_shutdown: AtomicBool,
}
impl SerialTask {
pub fn spawn() -> Self {
let (ctrl_send, ctrl_recv) = mpsc::channel(1);
let (ws_send, ws_recv) = mpsc::channel::<WsConn>(1);
tokio::spawn(async move {
Self::serial_task_work(ctrl_recv, ws_recv).await
});
Self {
chan_ctrl: ctrl_send,
chan_ws: ws_send,
is_shutdown: AtomicBool::new(false),
}
}
/// Drive client connections to the UART websocket
///
/// At this time, there is no real data being emitted from the mock
/// instance besides what's made up in the [`Serial`] below. Because of
/// that, the serial task has little to do besides holding the websocket
/// connections open until the mock instance enters shutdown.
async fn serial_task_work(
mut chan_ctrl: mpsc::Receiver<()>,
mut chan_ws: mpsc::Receiver<WsConn>,
) {
let bail = Notify::new();
let mut connections = futures::stream::FuturesUnordered::new();
let mut is_shutdown = false;
/// Send appropriate shutdown notice
async fn close_for_shutdown(mut conn: WsConn) {
let _ = conn
.close(Some(CloseFrame {
code: CloseCode::Away,
reason: "VM stopped".into(),
}))
.await;
}
/// Wait for a client connection to close (while discarding any
/// input from it), or a signal that the VM is shutting down.
async fn wait_for_close(
mut conn: WsConn,
bail: &Notify,
) -> Option<WsConn> {
let mut pconn = std::pin::Pin::new(&mut conn);
loop {
tokio::select! {
msg = pconn.next() => {
// Discard input (if any) and keep truckin'
msg.as_ref()?;
},
_ = bail.notified() => {
return Some(conn);
}
}
}
}
loop {
tokio::select! {
_vm_shutdown = chan_ctrl.recv() => {
// We've been signaled that the VM is shutdown
bail.notify_waiters();
chan_ws.close();
is_shutdown = true;
if connections.is_empty() {
return;
}
}
conn = chan_ws.recv() => {
// A new client connection has been passed to us
if conn.is_none() {
continue;
}
let conn = conn.unwrap();
if is_shutdown {
close_for_shutdown(conn).await;
continue;
}
connections
.push(async { wait_for_close(conn, &bail).await });
}
disconnect = connections.next(), if !connections.is_empty() => {
match disconnect {
None => {
// last open client
assert!(connections.is_empty());
if is_shutdown {
return;
}
}
Some(Some(conn)) => {
// client needs disconnect due to shutdown
close_for_shutdown(conn).await;
}
_ => {
// client disconnected itself
continue
}
}
}
}
}
}
pub async fn new_conn(&self, ws: WsConn) {
if let Err(mut ws) = self.chan_ws.send(ws).await.map_err(|e| e.0) {
let _ = ws
.close(Some(CloseFrame {
code: CloseCode::Away,
reason: "VM stopped".into(),
}))
.await;
}
}
pub async fn shutdown(&self) {
if !self.is_shutdown.swap(true, Ordering::Relaxed) {
self.chan_ctrl.send(()).await.unwrap();
}
}
}
/// Mock source of UART data from the guest, including history
pub(crate) struct Serial {
mock_data: Vec<u8>,
}
impl Serial {
pub(super) fn new(name: &str) -> Arc<Self> {
Arc::new(Self { mock_data: Self::mock_data(name) })
}
// Conjure up some fake console output
fn mock_data(name: &str) -> Vec<u8> {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut buf = Vec::with_capacity(1024);
#[rustfmt::skip]
let gerunds = [
"Loading", "Reloading", "Advancing", "Reticulating",
"Defeating", "Spoiling", "Cooking", "Destroying", "Resenting",
"Introducing", "Reiterating", "Blasting", "Tolling",
"Delivering", "Engendering", "Establishing",
];
#[rustfmt::skip]
let nouns = [
"canon", "browsers", "meta", "splines", "villains", "plot",
"books", "evidence", "decisions", "chaos", "points",
"processors", "bells", "value", "gender", "shots",
];
let mut hasher = DefaultHasher::new();
name.hash(&mut hasher);
let mut entropy = hasher.finish();
buf.extend(
format!(
"This is simulated serial console output for {name}.\r\n"
)
.as_bytes(),
);
while entropy != 0 {
let gerund = gerunds[entropy as usize % gerunds.len()];
entropy /= gerunds.len() as u64;
let noun = nouns[entropy as usize % nouns.len()];
entropy /= nouns.len() as u64;
buf.extend(
format!(
"{} {}... {}[\x1b[92m 0K \x1b[m]\r\n",
gerund,
noun,
" ".repeat(40 - gerund.len() - noun.len())
)
.as_bytes(),
);
}
buf.extend(
format!(
"\x1b[2J\x1b[HOS/478 ({name}) (ttyl)\r\n\r\n{name} login: "
)
.as_bytes(),
);
buf
}
pub async fn history_vec(
&self,
query: HistoryQuery,
max_bytes: Option<usize>,
) -> Result<(Vec<u8>, usize), &'static str> {
let end = self.mock_data.len();
let byte_limit = max_bytes.unwrap_or(DEFAULT_MAX_LEN);
match query {
HistoryQuery::FromStart(n) => {
if n > self.mock_data.len() {
Err("requesting data beyond history")
} else {
let data = &self.mock_data[n..];
let truncated = &data[..(data.len().min(byte_limit))];
Ok((truncated.to_vec(), end))
}
}
HistoryQuery::MostRecent(n) => {
let clamped = n.min(self.mock_data.len());
let data =
&self.mock_data[(self.mock_data.len() - clamped)..];
let truncated = &data[..(data.len().min(byte_limit))];
Ok((truncated.to_vec(), end))
}
}
}
}
}
/// Returns a Dropshot [`ApiDescription`] object to launch a mock Propolis
/// server.
///
/// This function should be avoided in favor of `start()` because using this
/// function requires that the consumer and Propolis update Dropshot
/// dependencies in lockstep due to the sharing of various types.
pub fn api() -> ApiDescription<Arc<Context>> {
let mut api = ApiDescription::new();
api.register(instance_ensure).unwrap();
api.register(instance_get).unwrap();
api.register(instance_state_monitor).unwrap();
api.register(instance_state_put).unwrap();
api.register(instance_serial).unwrap();
api.register(instance_serial_history_get).unwrap();
api.register(mock_mode_get).unwrap();
api.register(mock_mode_set).unwrap();
api.register(mock_step).unwrap();
api
}
// These types need to be exposed so that consumers have names for them without
// having to maintain a dropshot dependency in lockstep with their dependency on
// this crate.
/// configuration for the dropshot server
pub type Config = dropshot::ConfigDropshot;
/// the dropshot server itself
pub type Server = dropshot::HttpServer<Arc<Context>>;
/// errors returned from attempting to start a dropshot server
// Dropshot should expose this, but it's going to be removed anyway.
pub type ServerStartError = Box<dyn std::error::Error + Send + Sync>;
/// Starts a Propolis mock server
pub fn start(config: Config, log: Logger) -> Result<Server, ServerStartError> {
let propolis_log = log.new(o!("component" => "propolis-server-mock"));
let dropshot_log = log.new(o!("component" => "dropshot"));
let private = Arc::new(Context::new(propolis_log));
let starter = dropshot::HttpServerStarter::new(
&config,
api(),
private,
&dropshot_log,
)?;
Ok(starter.start())
}
================================================
FILE: bin/mock-server/src/main.rs
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::net::SocketAddr;
use std::path::PathBuf;
use std::sync::Arc;
use anyhow::anyhow;
use clap::Parser;
use dropshot::{
CompressionConfig, ConfigDropshot, HandlerTaskMode, HttpServerStarter,
};
use slog::{info, Drain};
#[derive(Debug, Parser)]
#[clap(about, version)]
/// An HTTP server providing access to Propolis
enum Args {
/// Generates the OpenAPI specification.
OpenApi,
/// Runs the Propolis server.
Run {
#[clap(action)]
cfg: PathBuf,
#[clap(name = "PROPOLIS_IP:PORT", action)]
propolis_addr: SocketAddr,
/// IP:Port for the Oximeter register address
#[clap(long, action)]
metric_addr: Option<SocketAddr>,
},
}
fn build_logger() -> slog::Logger {
let main_drain = if atty::is(atty::Stream::Stdout) {
let decorator = slog_term::TermDecorator::new().build();
let drain = slog_term::FullFormat::new(decorator).build().fuse();
slog_async::Async::new(drain)
.overflow_strategy(slog_async::OverflowStrategy::Block)
.build_no_guard()
} else {
let drain =
slog_bunyan::with_name("propolis-server", std::io::stdout())
.build()
.fuse();
slog_async::Async::new(drain)
.overflow_strategy(slog_async::OverflowStrategy::Block)
.build_no_guard()
};
let (dtrace_drain, probe_reg) = slog_dtrace::Dtrace::new();
let filtered_main = slog::LevelFilter::new(main_drain, slog::Level::Info);
let log = slog::Logger::root(
slog::Duplicate::new(filtered_main.fuse(), dtrace_drain.fuse()).fuse(),
slog::o!(),
);
if let slog_dtrace::ProbeRegistration::Failed(err) = probe_reg {
slog::error!(&log, "Error registering slog-dtrace probes: {:?}", err);
}
log
}
pub fn run_openapi() -> Result<(), String> {
propolis_mock_server::api()
.openapi("Oxide Propolis Server API", semver::Version::new(0, 0, 1))
.description(
"API for interacting with the Propolis hypervisor frontend.",
)
.contact_url("https://oxide.computer")
.contact_email("api@oxide.computer")
.write(&mut std::io::stdout())
.map_err(|e| e.to_string())
}
async fn run_server(
config_dropshot: dropshot::ConfigDropshot,
_metrics_addr: Option<SocketAddr>,
log: slog::Logger,
) -> anyhow::Result<()> {
let context = propolis_mock_server::Context::new(log.new(slog::o!()));
info!(log, "Starting server...");
let server = HttpServerStarter::new(
&config_dropshot,
propolis_mock_server::api(),
Arc::new(context),
&log,
)
.map_err(|error| anyhow!("Failed to start server: {error}"))?
.start();
let server_res = server.await;
server_res.map_err(|e| anyhow!("Server exited with an error: {e}"))
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Command line arguments.
let args = Args::parse();
match args {
Args::OpenApi => run_openapi()
.map_err(|e| anyhow!("Cannot generate OpenAPI spec: {e}")),
Args::Run { cfg: _cfg, propolis_addr, metric_addr } => {
// Dropshot configuration.
let config_dropshot = ConfigDropshot {
bind_address: propolis_addr,
default_request_body_max_bytes: 1024 * 1024, // 1M for ISO bytes
default_handler_task_mode: HandlerTaskMode::Detached,
log_headers: vec![],
compression: CompressionConfig::None,
};
let log = build_logger();
run_server(config_dropshot, metric_addr, log).await
}
}
}
================================================
FILE: bin/propolis-cli/Cargo.toml
================================================
[package]
name = "propolis-cli"
version = "0.1.0"
license = "MPL-2.0"
edition = "2021"
[dependencies]
anyhow.workspace = true
clap = { workspace = true, features = ["derive"] }
crucible-client-types.workspace = true
futures.workspace = true
libc.workspace = true
newtype-uuid.workspace = true
propolis-client.workspace = true
propolis-config-toml.workspace = true
slog.workspace = true
slog-async.workspace = true
slog-term.workspace = true
tokio = { workspace = true, features = ["full"] }
tokio-tungstenite.workspace = true
uuid.workspace = true
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
base64.workspace = true
================================================
FILE: bin/propolis-cli/README.md
================================================
# Propolis CLI
The `propolis-cli` utility provides a user-friendly frontend to the
[`propolis-server`](../propolis-server) REST API.
## Getting started
The easiest way to launch a VM via the CLI is to write a TOML file describing
the VM's configuration. An example of such a file might be the following:
```toml
[block_dev.alpine_iso]
type = "file"
path = "/path/to/alpine-extended-3.12.0-x86_64.iso"
[dev.block0]
driver = "pci-virtio-block"
block_dev = "alpine_iso"
pci-path = "0.4.0"
[dev.net0]
driver = "pci-virtio-viona"
vnic = "vnic_name"
pci-path = "0.5.0"
```
To create and run a Propolis VM using this configuration:
```
# propolis-cli -s <server ip> -p <port> new --config-toml <path> <VM name>
# propolis-cli -s <server ip> -p <port> state run
```
To connect to the VM's serial console:
```
# propolis-cli -s <server ip> -p <port> serial
```
Run `propolis-cli --help` to see the full list of supported commands and their
arguments.
================================================
FILE: bin/propolis-cli/src/main.rs
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::{
net::{IpAddr, SocketAddr, ToSocketAddrs},
os::unix::prelude::AsRawFd,
time::Duration,
};
use anyhow::{anyhow, Context};
use clap::{Args, Parser, Subcommand};
use futures::{future, SinkExt};
use newtype_uuid::{GenericUuid, TypedUuid, TypedUuidKind, TypedUuidTag};
use propolis_client::instance_spec::{
BlobStorageBackend, Board, Chipset, Component, CrucibleStorageBackend,
GuestHypervisorInterface, HyperVFeatureFlag, I440Fx, InstanceMetadata,
InstanceProperties, InstanceSpec, InstanceSpecGetResponse, NvmeDisk,
PciPath, QemuPvpanic, ReplacementComponent, SerialPort, SerialPortNumber,
SpecKey, VirtioDisk,
};
use propolis_client::support::nvme_serial_from_str;
use propolis_client::types::{
InstanceEnsureRequest, InstanceInitializationMethod,
};
use propolis_config_toml::spec::toml_cpuid_to_spec_cpuid;
use propolis_config_toml::spec::SpecConfig;
use serde::{Deserialize, Serialize};
use slog::{o, Drain, Level, Logger};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio_tungstenite::tungstenite::{
protocol::{frame::coding::CloseCode, CloseFrame},
Message,
};
use uuid::Uuid;
use propolis_client::{
support::{InstanceSerialConsoleHelper, WSClientOffset},
types::{InstanceStateRequested, InstanceVcrReplace, MigrationState},
Client,
};
#[derive(Debug, Parser)]
#[clap(about, version)]
/// A simple CLI tool to manipulate propolis-server
struct Opt {
/// propolis-server address
#[clap(short, long, value_parser = resolve_host)]
server: IpAddr,
/// propolis-server port
#[clap(short, long, default_value = "12400", action)]
port: u16,
/// Enable debugging
#[clap(short, long, action)]
debug: bool,
#[clap(subcommand)]
cmd: Command,
}
// `New`, via `VmConfig`, is large enough to trip this lint. This enum is
// created exactly once, so we don't need to be picky about the layout..
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Subcommand)]
enum Command {
/// Create a new propolis instance
New {
/// Instance name
#[clap(action)]
name: String,
/// Instance uuid (if specified)
#[clap(short = 'u', action)]
uuid: Option<Uuid>,
#[clap(flatten)]
config: VmConfig,
/// A UUID to use for the instance's silo, attached to instance metrics.
#[clap(long)]
silo_id: Option<TypedUuid<SiloKind>>,
/// A UUID to use for the instance's project, attached to instance metrics.
#[clap(long)]
project_id: Option<TypedUuid<ProjectKind>>,
/// A UUID to use for the instance's hosting sled, attached to instance
/// metrics.
#[clap(long)]
sled_id: Option<TypedUuid<SledKind>>,
/// A model number to use for the instance's hosting sled, attached to
/// instance metrics.
#[clap(long, default_value_t = String::from("fake-gimlet"))]
sled_model: String,
/// A revision number to use for the instance's hosting sled, attached to
/// instance metrics.
#[clap(long, default_value_t = 1)]
sled_revision: u32,
/// A serial number to use for the instance's hosting sled, attached to
/// instance metrics.
#[clap(long, default_value_t = String::from("fake-serial"))]
sled_serial: String,
},
/// Get the properties of a propolis instance
Get,
/// Transition the instance to a new state
State {
/// The requested state
#[clap(value_parser = parse_state)]
state: InstanceStateRequested,
},
/// Drop to a Serial console connected to the instance
Serial {
/// The offset since boot (or if negative, the current end of the
/// buffered data) from which to retrieve output history.
/// Defaults to the most recent 16 KiB of console output (-16384).
#[clap(long, short)]
byte_offset: Option<i64>,
},
/// Migrate instance to new propolis-server
Migrate {
/// Destination propolis-server address
#[clap(value_parser = resolve_host)]
dst_server: IpAddr,
/// Destination propolis-server port
#[clap(short = 'p', default_value = "12400", action)]
dst_port: u16,
/// Uuid for the destination instance
#[clap(short = 'u', action)]
dst_uuid: Option<Uuid>,
/// File with a JSON array of DiskRequest structs
#[clap(long, action)]
crucible_disks: Option<PathBuf>,
},
/// Monitor an instance's state in real time
Monitor,
/// Inject an NMI into the instance
InjectNmi,
/// Call the VolumeConstructionRequest replace endpoint
Vcr {
/// Uuid for the disk
#[clap(short = 'd', action)]
disk_id: String,
/// File with a JSON InstanceVcrReplace struct
#[clap(long, action)]
vcr_replace: PathBuf,
},
}
#[derive(Args, Clone, Debug)]
struct VmConfig {
/// A path to a file containing a JSON-formatted instance spec
#[clap(short = 's', long, action, group = "spec_group")]
spec: Option<PathBuf>,
/// Number of vCPUs allocated to instance
#[clap(short = 'c', default_value = "4", action, requires = "config_toml")]
vcpus: u8,
/// Memory allocated to instance (MiB)
#[clap(short, default_value = "1024", action, requires = "config_toml")]
memory: u64,
/// CPUID profile to use.
///
/// The named profile must be defined in `config_toml`.
#[clap(long, requires = "config_toml")]
cpuid_profile: Option<String>,
/// A path to a file containing a config TOML
#[clap(short = 't', long, action, group = "config_group", requires_all = ["vcpus", "memory"])]
config_toml: Option<PathBuf>,
/// File with a JSON array of DiskRequest structs
#[clap(long, action, conflicts_with = "spec")]
crucible_disks: Option<PathBuf>,
// cloud_init ISO file
#[clap(long, action, conflicts_with = "spec")]
cloud_init: Option<PathBuf>,
/// enable Hyper-V compatible enlightenments for this VM
#[clap(long, action)]
hyperv: bool,
}
fn add_component_to_spec(
spec: &mut InstanceSpec,
id: SpecKey,
component: Component,
) -> anyhow::Result<()> {
use std::collections::btree_map::Entry;
match spec.components.entry(id) {
Entry::Vacant(vacant_entry) => {
vacant_entry.insert(component);
Ok(())
}
Entry::Occupied(occupied_entry) => Err(anyhow::anyhow!(
"duplicate component ID {:?}",
occupied_entry.key()
)),
}
}
/// A legacy Propolis API disk request, preserved here for compatibility with
/// the `--crucible-disks` option.
#[derive(Clone, Debug, Deserialize, Serialize)]
struct DiskRequest {
name: String,
slot: u8,
read_only: bool,
device: String,
volume_construction_request: propolis_client::VolumeConstructionRequest,
}
#[derive(Clone, Debug)]
struct ParsedDiskRequest {
device_id: SpecKey,
device_spec: Component,
backend_id: SpecKey,
backend_spec: CrucibleStorageBackend,
}
impl DiskRequest {
fn parse(&self) -> anyhow::Result<ParsedDiskRequest> {
// Preserve compatibility with the old Propolis API by adding 16 to the
// slot number, which must be between 0 and 7 inclusive.
if !(0..8).contains(&self.slot) {
anyhow::bail!("disk request slots must be in [0..7]");
}
let slot = self.slot + 0x10;
let backend_id = SpecKey::Name(format!("{}-backend", self.name));
let pci_path = PciPath::new(0, slot, 0).with_context(|| {
format!("processing disk request {:?}", self.name)
})?;
let device_spec = match self.device.as_ref() {
"virtio" => Component::VirtioDisk(VirtioDisk {
backend_id: backend_id.clone(),
pci_path,
}),
"nvme" => Component::NvmeDisk(NvmeDisk {
backend_id: backend_id.clone(),
pci_path,
serial_number: nvme_serial_from_str(&self.name, b' '),
}),
_ => anyhow::bail!(
"invalid device type in disk request: {:?}",
self.device
),
};
let backend_spec = CrucibleStorageBackend {
readonly: self.read_only,
request_json: serde_json::to_string(
&self.volume_construction_request,
)?,
};
Ok(ParsedDiskRequest {
device_id: SpecKey::Name(self.name.clone()),
device_spec,
backend_id,
backend_spec,
})
}
}
impl VmConfig {
fn instance_spec(&self) -> anyhow::Result<InstanceSpec> {
// If the configuration specifies an instance spec path, just read the
// spec from that path and return it. Otherwise, construct a spec from
// this configuration's component parts.
if let Some(path) = &self.spec {
return parse_json_file(path);
}
let parsed_toml = self
.config_toml
.as_ref()
.map(propolis_config_toml::parse)
.transpose()?;
let from_toml =
parsed_toml.as_ref().map(SpecConfig::try_from).transpose()?;
let enable_pcie =
from_toml.as_ref().map(|cfg| cfg.enable_pcie).unwrap_or(false);
let cpuid_profile = parsed_toml
.as_ref()
.and_then(|cfg| {
self.cpuid_profile.as_ref().map(|profile| {
let profile =
cfg.cpuid_profiles.get(profile).ok_or_else(|| {
anyhow!(
"CPUID profile not defined in {}: {profile}",
self.config_toml.as_ref().unwrap().display()
)
})?;
toml_cpuid_to_spec_cpuid(profile)
.map_err(Into::<anyhow::Error>::into)
})
})
.transpose()?;
let mut spec = InstanceSpec {
board: Board {
chipset: Chipset::I440Fx(I440Fx { enable_pcie }),
cpuid: cpuid_profile,
cpus: self.vcpus,
memory_mb: self.memory,
guest_hv_interface: if self.hyperv {
GuestHypervisorInterface::HyperV {
features: [HyperVFeatureFlag::ReferenceTsc]
.into_iter()
.collect(),
}
} else {
Default::default()
},
},
components: Default::default(),
smbios: None,
};
if let Some(from_toml) = from_toml {
for (id, component) in from_toml.components.iter() {
add_component_to_spec(
&mut spec,
id.clone(),
component.clone(),
)?;
}
}
for disk_request in self
.crucible_disks
.as_ref()
.map(|path| parse_json_file::<Vec<DiskRequest>>(path))
.transpose()?
.iter()
.flatten()
{
let ParsedDiskRequest {
device_id,
device_spec,
backend_id,
backend_spec,
} = disk_request.parse()?;
add_component_to_spec(&mut spec, device_id, device_spec)?;
add_component_to_spec(
&mut spec,
backend_id,
Component::CrucibleStorageBackend(backend_spec),
)?;
}
if let Some(cloud_init) = self.cloud_init.as_ref() {
let bytes = base64::Engine::encode(
&base64::engine::general_purpose::STANDARD,
std::fs::read(cloud_init)?,
);
const CLOUD_INIT_NAME: &str = "cloud-init";
const CLOUD_INIT_BACKEND_NAME: &str = "cloud-init-backend";
add_component_to_spec(
&mut spec,
SpecKey::Name(CLOUD_INIT_NAME.to_owned()),
Component::VirtioDisk(VirtioDisk {
backend_id: SpecKey::Name(
CLOUD_INIT_BACKEND_NAME.to_owned(),
),
pci_path: PciPath::new(0, 0x18, 0).unwrap(),
}),
)?;
add_component_to_spec(
&mut spec,
SpecKey::Name(CLOUD_INIT_BACKEND_NAME.to_owned()),
Component::BlobStorageBackend(BlobStorageBackend {
base64: bytes,
readonly: true,
}),
)?;
}
for (name, port) in [
("com1", SerialPortNumber::Com1),
("com2", SerialPortNumber::Com2),
("com3", SerialPortNumber::Com3),
] {
add_component_to_spec(
&mut spec,
SpecKey::Name(name.to_owned()),
Component::SerialPort(SerialPort { num: port }),
)?;
}
// If there are no SoftNPU devices, also enable COM4.
if !spec
.components
.iter()
.any(|(_, c)| matches!(c, Component::SoftNpuPort(_)))
{
add_component_to_spec(
&mut spec,
SpecKey::Name("com4".to_owned()),
Component::SerialPort(SerialPort {
num: SerialPortNumber::Com4,
}),
)?;
}
add_component_to_spec(
&mut spec,
SpecKey::Name("pvpanic".to_owned()),
Component::QemuPvpanic(QemuPvpanic { enable_isa: true }),
)?;
Ok(spec)
}
}
fn parse_state(state: &str) -> anyhow::Result<InstanceStateRequested> {
match state.to_lowercase().as_str() {
"run" => Ok(InstanceStateRequested::Run),
"stop" => Ok(InstanceStateRequested::Stop),
"reboot" => Ok(InstanceStateRequested::Reboot),
_ => Err(anyhow!(
"invalid requested state, must be one of: 'run', 'stop', 'reboot"
)),
}
}
fn parse_json_file<T: serde::de::DeserializeOwned>(
path: &Path,
) -> anyhow::Result<T> {
let file = File::open(path)?;
let reader = BufReader::new(file);
serde_json::from_reader(reader).map_err(|e| e.into())
}
/// Given a string representing an host, attempts to resolve it to a specific IP address
fn resolve_host(server: &str) -> anyhow::Result<IpAddr> {
(server, 0)
.to_socket_addrs()?
.map(|sock_addr| sock_addr.ip())
.next()
.ok_or_else(|| anyhow!("failed to resolve server argument '{server}'"))
}
/// Create a top-level logger that outputs to stderr
fn create_logger(opt: &Opt) -> Logger {
let decorator = slog_term::TermDecorator::new().stderr().build();
let drain = slog_term::FullFormat::new(decorator).build().fuse();
let level = if opt.debug { Level::Debug } else { Level::Info };
let drain = slog::LevelFilter(drain, level).fuse();
let drain = slog_async::Async::new(drain).build().fuse();
Logger::root(drain, o!())
}
// Implement typed UUID wrappers for the project / silo IDs, to avoid conflating
// them.
enum ProjectKind {}
impl TypedUuidKind for ProjectKind {
fn tag() -> TypedUuidTag {
const TAG: TypedUuidTag = TypedUuidTag::new("project");
TAG
}
}
enum SiloKind {}
impl TypedUuidKind for SiloKind {
fn tag() -> TypedUuidTag {
const TAG: TypedUuidTag = TypedUuidTag::new("silo");
TAG
}
}
enum SledKind {}
impl TypedUuidKind for SledKind {
fn tag() -> TypedUuidTag {
const TAG: TypedUuidTag = TypedUuidTag::new("sled");
TAG
}
}
#[allow(clippy::too_many_arguments)]
async fn new_instance(
client: &Client,
name: String,
id: Uuid,
spec: InstanceSpec,
metadata: InstanceMetadata,
) -> anyhow::Result<()> {
let properties = InstanceProperties {
id,
name,
description: "propolis-cli generated instance".to_string(),
metadata,
};
let request = InstanceEnsureRequest {
properties,
init: InstanceInitializationMethod::Spec { spec },
};
// Try to create the instance
client
.instance_ensure()
.body(request)
.send()
.await
.with_context(|| anyhow!("failed to create instance"))?;
Ok(())
}
async fn replace_vcr(
client: &Client,
id: String,
vcr_replace: InstanceVcrReplace,
) -> anyhow::Result<()> {
// Try to call the endpoint
client
.instance_issue_crucible_vcr_request()
.id(id)
.body(vcr_replace)
.send()
.await
.with_context(|| anyhow!("failed to issue vcr request"))?;
Ok(())
}
async fn get_instance(client: &Client) -> anyhow::Result<()> {
let res = client
.instance_get()
.send()
.await
.with_context(|| anyhow!("failed to get instance properties"))?;
println!("{:#?}", res.instance);
Ok(())
}
async fn put_instance(
client: &Client,
state: InstanceStateRequested,
) -> anyhow::Result<()> {
client
.instance_state_put()
.body(state)
.send()
.await
.with_context(|| anyhow!("failed to set instance state"))?;
Ok(())
}
async fn stdin_to_websockets_task(
mut stdinrx: tokio::sync::mpsc::Receiver<Vec<u8>>,
wstx: tokio::sync::mpsc::Sender<Vec<u8>>,
) {
// next_raw must live outside loop, because Ctrl-A should work across
// multiple inbuf reads.
let mut next_raw = false;
loop {
let inbuf = if let Some(inbuf) = stdinrx.recv().await {
inbuf
} else {
continue;
};
// Put bytes from inbuf to outbuf, but don't send Ctrl-A unless
// next_raw is true.
let mut outbuf = Vec::with_capacity(inbuf.len());
let mut exit = false;
for c in inbuf {
match c {
// Ctrl-A means send next one raw
b'\x01' => {
if next_raw {
// Ctrl-A Ctrl-A should be sent as Ctrl-A
outbuf.push(c);
next_raw = false;
} else {
next_raw = true;
}
}
b'\x03' => {
if !next_raw {
// Exit on non-raw Ctrl-C
exit = true;
break;
} else {
// Otherwise send Ctrl-C
outbuf.push(c);
next_raw = false;
}
}
_ => {
outbuf.push(c);
next_raw = false;
}
}
}
// Send what we have, even if there's a Ctrl-C at the end.
if !outbuf.is_empty() {
wstx.send(outbuf).await.unwrap();
}
if exit {
break;
}
}
}
async fn serial(
addr: SocketAddr,
byte_offset: Option<i64>,
log: Logger,
) -> anyhow::Result<()> {
let mut ws_console = serial_connect(addr, byte_offset, log).await?;
let _raw_guard = RawTermiosGuard::stdio_guard()
.with_context(|| anyhow!("failed to set raw mode"))?;
let mut stdout = tokio::io::stdout();
// https://docs.rs/tokio/latest/tokio/io/trait.AsyncReadExt.html#method.read_exact
// is not cancel safe! Meaning reads from tokio::io::stdin are not cancel
// safe. Spawn a separate task to read and put bytes onto this channel.
let (stdintx, stdinrx) = tokio::sync::mpsc::channel(16);
let (wstx, mut wsrx) = tokio::sync::mpsc::channel(16);
tokio::spawn(async move {
let mut stdin = tokio::io::stdin();
let mut inbuf = [0u8; 1024];
loop {
let n = match stdin.read(&mut inbuf).await {
Err(_) | Ok(0) => break,
Ok(n) => n,
};
stdintx.send(inbuf[0..n].to_vec()).await.unwrap();
}
});
tokio::spawn(async move { stdin_to_websockets_task(stdinrx, wstx).await });
loop {
tokio::select! {
c = wsrx.recv() => {
match c {
None => {
// channel is closed
break;
}
Some(c) => {
ws_console.send(Message::Binary(c)).await?;
},
}
}
msg = ws_console.recv() => {
match msg {
Some(Ok(msg)) => {
match msg.process().await {
Ok(Message::Binary(input)) => {
stdout.write_all(&input).await?;
stdout.flush().await?;
}
Ok(Message::Close(Some(CloseFrame {code, reason}))) => {
eprint!("\r\nConnection closed: {code:?}\r\n");
match code {
CloseCode::Abnormal
| CloseCode::Error
| CloseCode::Extension
| CloseCode::Invalid
| CloseCode::Policy
| CloseCode::Protocol
| CloseCode::Size
| CloseCode::Unsupported => {
anyhow::bail!("{reason}");
}
_ => break,
}
}
Ok(Message::Close(None)) => {
eprint!("\r\nConnection closed.\r\n");
break;
}
// note: migration events via Message::Text are
// already handled within ws_console.recv(), but
// would still be available to match here if we want
// to indicate that it happened to the user
_ => continue,
}
}
None => {
eprint!("\r\nConnection lost.\r\n");
break;
}
_ => continue,
}
}
}
}
Ok(())
}
async fn serial_connect(
addr: SocketAddr,
byte_offset: Option<i64>,
log: Logger,
) -> anyhow::Result<InstanceSerialConsoleHelper> {
let offset = match byte_offset {
Some(x) if x >= 0 => WSClientOffset::FromStart(x as u64),
Some(x) => WSClientOffset::MostRecent(-x as u64),
None => WSClientOffset::MostRecent(16384),
};
Ok(InstanceSerialConsoleHelper::new(addr, offset, Some(log)).await?)
}
async fn migrate_instance(
src_client: Client,
dst_client: Client,
src_addr: SocketAddr,
dst_uuid: Uuid,
disks: Vec<DiskRequest>,
) -> anyhow::Result<()> {
// Grab the instance details
let InstanceSpecGetResponse { mut properties, .. } = src_client
.instance_spec_get()
.send()
.await
.with_context(|| anyhow!("failed to get src instance properties"))?
.into_inner();
let src_uuid = properties.id;
properties.id = dst_uuid;
let mut replace_components = HashMap::new();
for disk in disks {
let ParsedDiskRequest { backend_id, backend_spec, .. } =
disk.parse()?;
let old = replace_components.insert(
backend_id.to_string(),
ReplacementComponent::CrucibleStorageBackend(backend_spec),
);
if old.is_some() {
anyhow::bail!(
"duplicate backend name {backend_id} in replacement disk \
list"
);
}
}
let request = InstanceEnsureRequest {
properties,
init: InstanceInitializationMethod::MigrationTarget {
migration_id: Uuid::new_v4(),
src_addr: src_addr.to_string(),
replace_components,
},
};
// Initiate the migration via the destination instance
let migration_res =
dst_client.instance_ensure().body(request).send().await?;
let migration_id = migration_res
.migrate
.as_ref()
.ok_or_else(|| anyhow!("no migrate id on response"))?
.migration_id;
// Wait for the migration to complete by polling both source and destination
// TODO: replace with into_iter method call after edition upgrade
let handles = IntoIterator::into_iter([
("src", src_client, src_uuid),
("dst", dst_client, dst_uuid),
])
.map(|(role, client, id)| {
tokio::spawn(async move {
loop {
let state =
client.instance_migrate_status().send().await?.into_inner();
let migration = if role == "src" {
state.migration_out
} else {
state.migration_in
};
// The destination should start reporting migration status as
// soon as the ensure request completes. The source may not
// have a migration status yet because the request from the
// destination needs to arrive first.
let Some(migration) = migration else {
if role == "dst" {
anyhow::bail!("dst instance's migration ID wasn't set");
} else {
println!("src hasn't received migration request yet");
tokio::time::sleep(Duration::from_secs(1)).await;
continue;
}
};
if migration.id != migration_id {
anyhow::bail!(
"{role} instance's migration ID is wrong: \
got {}, expected {migration_id}",
migration.id
);
}
let state = migration.state;
println!("{role}({id}) migration state={state:?}");
if state == MigrationState::Finish {
return Ok::<_, anyhow::Error>(());
} else if state == MigrationState::Error {
return Err(anyhow::anyhow!(
"{role} instance ran into error during migration"
));
}
tokio::time::sleep(Duration::from_secs(1)).await;
}
})
});
future::join_all(handles)
.await
// Hoist out any JoinErrors
.into_iter()
.collect::<Result<Vec<_>, _>>()?
// Then any errors from polling the source/destination
.into_iter()
.collect::<anyhow::Result<()>>()?;
Ok(())
}
async fn monitor(addr: SocketAddr) -> anyhow::Result<()> {
// We use a custom client builder here because the default progenitor
// one has a timeout of 15s but we want to be able to wait indefinitely.
let client = reqwest::ClientBuilder::new().build().unwrap();
let client = propolis_client::Client::new_with_client(
&format!("http://{addr}"),
client,
);
let mut gen = 0;
loop {
// State monitoring always returns the most recent state/gen pair
// known to Propolis.
let response = client
.instance_state_monitor()
.body(propolis_client::types::InstanceStateMonitorRequest {
gen_: gen,
})
.send()
.await
.with_context(|| anyhow!("failed to get new instance state"))?;
println!("InstanceState: {:?}", response.state);
if response.state == propolis_client::types::InstanceState::Destroyed {
return Ok(());
}
// Update the generation number we're asking for, to ensure the
// Propolis will only return more recent values.
gen = response.gen_ + 1;
}
}
async fn inject_nmi(client: &Client) -> anyhow::Result<()> {
client
.instance_issue_nmi()
.send()
.await
.with_context(|| anyhow!("failed to inject NMI"))?;
Ok(())
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let opt = Opt::parse();
let log = create_logger(&opt);
let addr = SocketAddr::new(opt.server, opt.port);
let client = Client::new(&format!("http://{addr}"));
match opt.cmd {
Command::New {
name,
uuid,
config,
silo_id,
project_id,
sled_id,
sled_model,
sled_revision,
sled_serial,
} => {
let metadata = InstanceMetadata {
project_id: project_id
.unwrap_or_else(TypedUuid::new_v4)
.into_untyped_uuid(),
silo_id: silo_id
.unwrap_or_else(TypedUuid::new_v4)
.into_untyped_uuid(),
sled_id: sled_id
.unwrap_or_else(TypedUuid::new_v4)
.into_untyped_uuid(),
sled_model,
sled_revision,
sled_serial,
};
new_instance(
&client,
name.to_string(),
uuid.unwrap_or_else(Uuid::new_v4),
config.instance_spec()?,
metadata,
)
.await?
}
Command::Get => get_instance(&client).await?,
Command::State { state } => put_instance(&client, state).await?,
Command::Serial { byte_offset } => {
serial(addr, byte_offset, log).await?
}
Command::Migrate { dst_server, dst_port, dst_uuid, crucible_disks } => {
let dst_addr = SocketAddr::new(dst_server, dst_port);
let dst_client = Client::new(&format!("http://{dst_addr}"));
let dst_uuid = dst_uuid.unwrap_or_else(Uuid::new_v4);
let disks = if let Some(crucible_disks) = crucible_disks {
parse_json_file(&crucible_disks)?
} else {
vec![]
};
migrate_instance(client, dst_client, addr, dst_uuid, disks).await?
}
Command::Monitor => monitor(addr).await?,
Command::InjectNmi => inject_nmi(&client).await?,
Command::Vcr { disk_id, vcr_replace } => {
let replace: InstanceVcrReplace = parse_json_file(&vcr_replace)?;
replace_vcr(&client, disk_id, replace).await?
}
}
Ok(())
}
/// Guard object that will set the terminal to raw mode and restore it
/// to its previous state when it's dropped
struct RawTermiosGuard(libc::c_int, libc::termios);
impl RawTermiosGuard {
fn stdio_guard() -> Result<RawTermiosGuard, std::io::Error> {
let fd = std::io::stdout().as_raw_fd();
let termios = unsafe {
let mut curr_termios = std::mem::zeroed();
let r = libc::tcgetattr(fd, &mut curr_termios);
if r == -1 {
return Err(std::io::Error::last_os_error());
}
curr_termios
};
let guard = RawTermiosGuard(fd, termios);
unsafe {
let mut raw_termios = termios;
libc::cfmakeraw(&mut raw_termios);
let r = libc::tcsetattr(fd, libc::TCSAFLUSH, &raw_termios);
if r == -1 {
return Err(std::io::Error::last_os_error());
}
}
Ok(guard)
}
}
impl Drop for RawTermiosGuard {
fn drop(&mut self) {
let r = unsafe { libc::tcsetattr(self.0, libc::TCSADRAIN, &self.1) };
if r == -1 {
panic!("{:?}", std::io::Error::last_os_error());
}
}
}
#[cfg(test)]
mod test {
use super::stdin_to_websockets_task;
#[tokio::test]
async fn test_stdin_to_websockets_task() {
use tokio::sync::mpsc::error::TryRecvError;
let (stdintx, stdinrx) = tokio::sync::mpsc::channel(16);
let (wstx, mut wsrx) = tokio::sync::mpsc::channel(16);
tokio::spawn(
async move { stdin_to_websockets_task(stdinrx, wstx).await },
);
// send characters, receive characters
stdintx
.send("test post please ignore".chars().map(|c| c as u8).collect())
.await
.unwrap();
let actual = wsrx.recv().await.unwrap();
assert_eq!(
String::from_utf8(actual).unwrap(),
"test post please ignore"
);
// don't send ctrl-a
stdintx.send("\x01".chars().map(|c| c as u8).collect()).await.unwrap();
assert_eq!(wsrx.try_recv(), Err(TryRecvError::Empty));
// the "t" here is sent "raw" because of last ctrl-a but that doesn't change anything
stdintx.send("test".chars().map(|c| c as u8).collect()).await.unwrap();
let actual = wsrx.recv().await.unwrap();
assert_eq!(String::from_utf8(actual).unwrap(), "test");
// ctrl-a ctrl-c = only ctrl-c sent
stdintx
.send("\x01\x03".chars().map(|c| c as u8).collect())
.await
.unwrap();
let actual = wsrx.recv().await.unwrap();
assert_eq!(String::from_utf8(actual).unwrap(), "\x03");
// same as above, across two messages
stdintx.send("\x01".chars().map(|c| c as u8).collect()).await.unwrap();
stdintx.send("\x03".chars().map(|c| c as u8).collect()).await.unwrap();
assert_eq!(wsrx.try_recv(), Err(TryRecvError::Empty));
let actual = wsrx.recv().await.unwrap();
assert_eq!(String::from_utf8(actual).unwrap(), "\x03");
// ctrl-a ctrl-a = only ctrl-a sent
stdintx
.send("\x01\x01".chars().map(|c| c as u8).collect())
.await
.unwrap();
let actual = wsrx.recv().await.unwrap();
assert_eq!(String::from_utf8(actual).unwrap(), "\x01");
// ctrl-c on its own means exit
stdintx.send("\x03".chars().map(|c| c as u8).collect()).await.unwrap();
assert_eq!(wsrx.try_recv(), Err(TryRecvError::Empty));
// channel is closed
assert!(wsrx.recv().await.is_none());
}
}
================================================
FILE: bin/propolis-server/Cargo.toml
================================================
[package]
name = "propolis-server"
version = "0.1.0"
license = "MPL-2.0"
edition = "2021"
[lib]
name = "propolis_server"
path = "src/lib/lib.rs"
doctest = false
[[bin]]
name = "propolis-server"
path = "src/main.rs"
doc = false
doctest = false
test = false
[dependencies]
atty.workspace = true
anyhow.workspace = true
async-trait.workspace = true
bit_field.workspace = true
bitvec.workspace = true
bytes.workspace = true
chrono = { workspace = true, features = [ "serde" ] }
clap = { workspace = true, features = ["derive"] }
const_format.workspace = true
cpuid_utils = { workspace = true, features = ["instance-spec"] }
crucible-client-types.workspace = true
dropshot = { workspace = true, features = ["usdt-probes"] }
erased-serde.workspace = true
futures.workspace = true
hyper.workspace = true
internal-dns-resolver.workspace = true
internal-dns-types.workspace = true
itertools.workspace = true
kstat-rs.workspace = true
lazy_static.workspace = true
nexus-client.workspace = true
omicron-common.workspace = true
oxide-tokio-rt.workspace = true
oximeter-instruments.workspace = true
oximeter-producer.workspace = true
oximeter.workspace = true
pbind.workspace = true
ron.workspace = true
thiserror.workspace = true
tokio = { workspace = true, features = ["full"] }
tokio-tungstenite.workspace = true
tokio-util = { workspace = true, features = ["codec"] }
toml.workspace = true
semver.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
slog.workspace = true
slog-async.workspace = true
slog-bunyan.workspace = true
slog-dtrace.workspace = true
slog-term.workspace = true
strum = { workspace = true, features = ["derive"] }
propolis = { workspace = true, features = ["crucible-full", "oximeter"] }
propolis_api_types = { workspace = true }
propolis-api-types-versions.workspace = true
propolis-server-api.workspace = true
propolis_types.workspace = true
rgb_frame.workspace = true
rfb = { workspace = true, features = ["tungstenite"] }
uuid.workspace = true
usdt.workspace = true
vm-attest.workspace = true
base64.workspace = true
schemars = { workspace = true, features = ["chrono", "uuid1"] }
[dev-dependencies]
hex.workspace = true
reqwest = { workspace = true, features = ["rustls"] }
ring.workspace = true
slog = { workspace = true, features = [ "max_level_trace", "release_max_level_debug" ] }
expectorate.workspace = true
mockall.workspace = true
proptest.workspace = true
[features]
default = []
# When building to be packaged for inclusion in the production ramdisk
# (nominally an Omicron package), certain code is compiled in or out.
omicron-build = ["propolis/omicron-build"]
# Falcon builds require corresponding bits turned on in the dependency libs
falcon = ["propolis/falcon"]
# Testing necessitates injecting failures which should hopefully be rare or even
# never occur on real otherwise-unperturbed systems. We conditionally compile
# code supporting failure injection to avoid the risk of somehow injecting
# failures into a real system not under test.
failure-injection = []
================================================
FILE: bin/propolis-server/README.md
================================================
# Propolis Server
This binary provides a REST API to create and manage a Propolis VM. It typically
runs in the context of a complete Oxide control plane deployment, but it can
also be run as a freestanding binary for ad hoc testing of Propolis VMs.
## Running
The server requires a path to a [guest bootrom
image](../propolis-standalone#guest-bootrom) on the local filesystem. It also
must be run with privileges sufficient to create bhyve virtual machines. The
`pfexec(1)` utility can help enable these privileges.
To build and run the server:
```bash
cargo build --bin propolis-server
pfexec target/debug/propolis-server <path_to_bootrom> <ip:port> <vnc_ip:port>
```
The API will be served on `ip:port`. The easiest way to interact with the server
is to use [`propolis-cli`](../propolis-cli), but you can also use tools like
cURL to interact with the API directly. The server's OpenAPI specification is
[checked into the repo](../../openapi/propolis-server).
================================================
FILE: bin/propolis-server/src/lib/config.rs
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Describes a server config which may be parsed from a TOML file.
#[cfg(not(feature = "omicron-build"))]
pub fn reservoir_decide(log: &slog::Logger) -> bool {
// Automatically enable use of the memory reservoir (rather than transient
// allocations) for guest memory if it meets some arbitrary size threshold.
const RESERVOIR_THRESH_MB: usize = 512;
match propolis::vmm::query_reservoir() {
Err(e) => {
slog::error!(log, "could not query reservoir {:?}", e);
false
}
Ok(size) => {
let size_in_play =
(size.vrq_alloc_sz + size.vrq_free_sz) / (1024 * 1024);
if size_in_play > RESERVOIR_THRESH_MB {
slog::info!(
log,
"allocating from reservoir ({}MiB) for guest memory",
size_in_play
);
true
} else {
slog::info!(
log,
"reservoir too small ({}MiB) to use for guest memory",
size_in_play
);
false
}
}
}
}
#[cfg(feature = "omicron-build")]
pub fn reservoir_decide(_log: &slog::Logger) -> bool {
// Always use the reservoir in production
true
}
================================================
FILE: bin/propolis-server/src/lib/initializer.rs
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::convert::TryInto;
use std::fs::File;
use std::num::{NonZeroU8, NonZeroUsize};
use std::os::unix::fs::FileTypeExt;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::serial::Serial;
use crate::spec::{self, Spec, StorageBackend, StorageDevice};
use crate::stats::{
track_network_interface_kstats, track_vcpu_kstats, BlockMetrics,
VirtualDisk, VirtualMachine,
};
use crate::vm::{
BlockBackendMap, CrucibleBackendMap, DeviceMap, NetworkInterfaceIds,
};
use anyhow::Context;
use cpuid_utils::CpuidValues;
use crucible_client_types::VolumeConstructionRequest;
pub use nexus_client::Client as NexusClient;
use oximeter::types::ProducerRegistry;
use oximeter_instruments::kstat::KstatSampler;
use propolis::attestation;
use propolis::attestation::server::AttestationServerConfig;
use propolis::attestation::server::AttestationSock;
use propolis::block;
use propolis::chardev::{self, BlockingSource, Source};
use propolis::common::{Lifecycle, GB, MB, PAGE_SIZE};
use propolis::cpuid::TopoKind;
use propolis::enlightenment::Enlightenment;
use propolis::firmware::smbios;
use propolis::hw::bhyve::BhyveHpet;
use propolis::hw::chipset::{i440fx, Chipset};
use propolis::hw::ibmpc;
use propolis::hw::pci;
use propolis::hw::pci::topology::PciTopologyError;
use propolis::hw::ps2::ctrl::PS2Ctrl;
use propolis::hw::qemu::pvpanic::QemuPvpanic;
use propolis::hw::qemu::{
debug::QemuDebugPort,
fwcfg::{self, Entry},
ramfb,
};
use propolis::hw::uart::LpcUart;
use propolis::hw::{nvme, virtio};
use propolis::intr_pins;
use propolis::vmm::{self, Builder, Machine};
use propolis::vsock::GuestCid;
use propolis_api_types::instance::InstanceProperties;
use propolis_api_types::instance_spec::components::devices::SerialPortNumber;
use propolis_api_types::instance_spec::{self, SpecKey};
use propolis_types::{CpuidIdent, CpuidVendor};
use slog::info;
use strum::IntoEnumIterator;
use thiserror::Error;
// XXX: completely arb for now
const MAX_FILE_WORKERS: usize = 32;
const DEFAULT_WORKER_COUNT: usize = 8;
/// An error that can arise while initializing a new machine.
#[derive(Debug, Error)]
pub enum MachineInitError {
/// Catch-all for `anyhow` errors.
///
/// The machine initializer calls many bhyve functions that return a
/// [`std::io::Error`]. Instead of forcing each such call site to define its
/// own error type, this type allows callers to attach an
/// [`anyhow::Context`] and convert it to this error variant without losing
/// information about the interior I/O error.
#[error(transparent)]
GenericError(#[from] anyhow::Error),
#[error("bootrom {path:?} length {length:x} not aligned to {align:x}")]
BootromNotAligned { path: String, length: u64, align: u64 },
#[error(
"bootrom read truncated: expected {rom_len} bytes, read {nread} bytes"
)]
BootromReadTruncated { rom_len: usize, nread: usize },
#[error(transparent)]
PciTopologyError(#[from] PciTopologyError),
#[error("failed to deserialize volume construction request")]
VcrDeserializationFailed(#[from] serde_json::Error),
#[error("failed to decode in-memory storage backend contents")]
InMemoryBackendDecodeFailed(#[from] base64::DecodeError),
#[error("multiple Crucible disks with backend ID {0}")]
DuplicateCrucibleBackendId(SpecKey),
#[error("boot order entry {0:?} does not refer to an attached disk")]
BootOrderEntryWithoutDevice(SpecKey),
#[error(
"disk device {device_id:?} refers to a \
non-existent block backend {backend_id:?}"
)]
DeviceWithoutBlockBackend { device_id: SpecKey, backend_id: SpecKey },
#[error("boot entry {0:?} refers to a device on non-zero PCI bus {1}")]
BootDeviceOnDownstreamPciBus(SpecKey, u8),
#[error("failed to insert {0} fwcfg entry")]
FwcfgInsertFailed(&'static str, #[source] fwcfg::InsertError),
#[error("failed to specialize CPUID for vcpu {0}")]
CpuidSpecializationFailed(i32, #[source] propolis::cpuid::SpecializeError),
#[error("failed to start attestation server")]
AttestationServer(#[source] std::io::Error),
#[cfg(feature = "falcon")]
#[error("softnpu p9 device missing")]
SoftNpuP9Missing,
}
/// Arbitrary ROM limit for now
const MAX_ROM_SIZE: usize = 0x20_0000;
fn get_spec_guest_ram_limits(spec: &Spec) -> (usize, usize) {
let memsize = spec.board.memory_mb as usize * MB;
let lowmem = memsize.min(3 * GB);
let highmem = memsize.saturating_sub(3 * GB);
(lowmem, highmem)
}
pub fn build_instance(
name: &str,
spec: &Spec,
use_reservoir: bool,
guest_hv_interface: Arc<dyn Enlightenment>,
_log: slog::Logger,
) -> Result<Machine, MachineInitError> {
let (lowmem, highmem) = get_spec_guest_ram_limits(spec);
let create_opts = propolis::vmm::CreateOpts {
force: true,
use_reservoir,
track_dirty: true,
};
let mut builder = Builder::new(name, create_opts)
.context("failed to create kernel vmm builder")?
.max_cpus(spec.board.cpus)
.context("failed to set max cpus")?
.guest_hypervisor_interface(guest_hv_interface)
.add_mem_region(0, lowmem, "lowmem")
.context("failed to add low memory region")?
.add_rom_region(0x1_0000_0000 - MAX_ROM_SIZE, MAX_ROM_SIZE, "bootrom")
.context("failed to add bootrom region")?
.add_mmio_region(0xc000_0000_usize, 0x2000_0000_usize, "dev32")
.context("failed to add low device MMIO region")?
.add_mmio_region(0xe000_0000_usize, 0x1000_0000_usize, "pcicfg")
.context("failed to add PCI config region")?;
let highmem_start = 0x1_0000_0000;
if highmem > 0 {
builder = builder
.add_mem_region(highmem_start, highmem, "highmem")
.context("failed to add high memory region")?;
}
let dev64_start = highmem_start + highmem;
builder = builder
.add_mmio_region(dev64_start, vmm::MAX_PHYSMEM - dev64_start, "dev64")
.context("failed to add high device MMIO region")?;
Ok(builder.finalize().context("failed to finalize kernel vmm")?)
}
pub struct RegisteredChipset {
chipset: Arc<dyn Chipset>,
isa: Arc<i440fx::Piix3Lpc>,
}
impl RegisteredChipset {
pub fn pci_attach(&self, bdf: pci::Bdf, dev: Arc<dyn pci::Endpoint>) {
self.chipset.pci_attach(bdf, dev, self.isa.route_lintr(bdf));
}
pub fn irq_pin(&self, irq: u8) -> Option<Box<dyn intr_pins::IntrPin>> {
self.isa.irq_pin(irq)
}
fn reset_pin(&self) -> Arc<dyn intr_pins::IntrPin> {
self.chipset.reset_pin()
}
}
struct StorageBackendInstance {
be: Arc<dyn block::Backend>,
crucible: Option<Arc<block::CrucibleBackend>>,
}
#[derive(Default)]
pub struct MachineInitializerState {
rom_size_bytes: Option<usize>,
}
pub struct MachineInitializer<'a> {
pub(crate) log: slog::Logger,
pub(crate) machine: &'a Machine,
pub(crate) devices: DeviceMap,
pub(crate) block_backends: BlockBackendMap,
pub(crate) crucible_backends: CrucibleBackendMap,
pub(crate) spec: &'a Spec,
pub(crate) properties: &'a InstanceProperties,
pub(crate) producer_registry: Option<ProducerRegistry>,
pub(crate) state: MachineInitializerState,
pub(crate) kstat_sampler: Option<KstatSampler>,
pub(crate) stats_vm: crate::stats::VirtualMachine,
}
impl MachineInitializer<'_> {
pub fn initialize_rom(
&mut self,
path: &std::path::Path,
) -> Result<(), MachineInitError> {
fn open_bootrom(
path: &std::path::Path,
) -> Result<(File, usize), MachineInitError> {
let fp = File::open(path)
.with_context(|| format!("failed to open bootrom {path:?}"))?;
let len = fp
.metadata()
.with_context(|| {
format!("failed to query metadata for bootrom {path:?}")
})?
.len();
if len % (PAGE_SIZE as u64) != 0 {
Err(MachineInitError::BootromNotAligned {
path: path.to_string_lossy().to_string(),
length: len,
align: PAGE_SIZE as u64,
})
} else {
Ok((fp, len as usize))
}
}
let (romfp, rom_len) = open_bootrom(path)
.unwrap_or_else(|e| panic!("Cannot open bootrom: {e}"));
let mem = self.machine.acc_mem.access().unwrap();
let mapping = mem
.direct_writable_region_by_name("bootrom")
.context("failed to map guest bootrom region")?;
let offset = mapping.len() - rom_len;
let submapping = mapping.subregion(offset, rom_len).unwrap();
let nread =
submapping.pread(&romfp, rom_len, 0).with_context(|| {
format!(
"failed to read bootrom {path:?} into guest memory mapping"
)
})?;
if nread != rom_len {
return Err(MachineInitError::BootromReadTruncated {
rom_len,
nread,
});
}
self.state.rom_size_bytes = Some(rom_len);
Ok(())
}
pub fn initialize_rtc(
&self,
chipset: &RegisteredChipset,
) -> Result<(), MachineInitError> {
let (lowmem, highmem) = get_spec_guest_ram_limits(self.spec);
let rtc = chipset.isa.rtc.as_ref();
rtc.memsize_to_nvram(lowmem as u32, highmem as u64)
.context("failed to write guest memory size to RTC NVRAM")?;
rtc.set_time(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time precedes UNIX epoch"),
)
.context("failed to set guest real-time clock")?;
Ok(())
}
pub fn initialize_hpet(&mut self) {
let hpet = BhyveHpet::create(self.machine.hdl.clone());
self.devices
.insert(SpecKey::Name(hpet.type_name().into()), hpet.clone());
}
pub fn initialize_chipset(
&mut self,
event_handler: &Arc<dyn super::vm::guest_event::ChipsetEventHandler>,
) -> Result<RegisteredChipset, MachineInitError> {
let mut pci_builder = pci::topology::Builder::new();
for bridge in self.spec.pci_pci_bridges.values() {
let desc = pci::topology::BridgeDescription::new(
pci::topology::LogicalBusId(bridge.downstream_bus),
bridge.pci_path.into(),
);
pci_builder.add_bridge(desc)?;
}
let pci::topology::FinishedTopology { topology: pci_topology, bridges } =
pci_builder.finish(self.machine)?;
match self.spec.board.chipset {
instance_spec::components::board::Chipset::I440Fx(i440fx) => {
let power_ref = Arc::downgrade(event_handler);
let reset_ref = Arc::downgrade(event_handler);
let power_pin = Arc::new(propolis::intr_pins::FuncPin::new(
Box::new(move |rising| {
if rising {
if let Some(handler) = power_ref.upgrade() {
handler.chipset_halt();
}
}
}),
));
let reset_pin = Arc::new(propolis::intr_pins::FuncPin::new(
Box::new(move |rising| {
if rising {
if let Some(handler) = reset_ref.upgrade() {
handler.chipset_reset();
}
}
}),
));
let chipset_hb = i440fx::I440FxHostBridge::create(
pci_topology,
i440fx::Opts {
power_pin: Some(power_pin),
reset_pin: Some(reset_pin),
enable_pcie: i440fx.enable_pcie,
},
);
let chipset_lpc =
i440fx::Piix3Lpc::create(self.machine.hdl.clone());
let chipset_pm = i440fx::Piix3PM::create(
self.machine.hdl.clone(),
chipset_hb.power_pin(),
self.log.new(slog::o!("device" => "piix3pm")),
);
let do_pci_attach = |bdf, dev: Arc<dyn pci::Endpoint>| {
chipset_hb.pci_attach(
bdf,
dev,
chipset_lpc.route_lintr(bdf),
);
};
// Attach chipset devices to PCI and buses
do_pci_attach(i440fx::DEFAULT_HB_BDF, chipset_hb.clone());
chipset_hb.attach(self.machine);
do_pci_attach(i440fx::DEFAULT_LPC_BDF, chipset_lpc.clone());
chipset_lpc.attach(&self.machine.bus_pio);
do_pci_attach(i440fx::DEFAULT_PM_BDF, chipset_pm.clone());
chipset_pm.attach(&self.machine.bus_pio);
self.devices.insert(
SpecKey::Name(chipset_hb.type_name().into()),
chipset_hb.clone(),
);
self.devices.insert(
SpecKey::Name(chipset_lpc.type_name().into()),
chipset_lpc.clone(),
);
self.devices.insert(
SpecKey::Name(chipset_pm.type_name().into()),
chipset_pm,
);
// Record attachment for any bridges in PCI topology too
for (bdf, bridge) in bridges {
let spec_element = self
.spec
.pci_pci_bridges
.iter()
.find(|(_, spec_bridge)| {
bdf == spec_bridge.pci_path.into()
})
.expect("all PCI bridges are in the topology");
self.devices.insert(spec_element.0.clone(), bridge);
}
Ok(RegisteredChipset { chipset: chipset_hb, isa: chipset_lpc })
}
}
}
pub fn initialize_uart(
&mut self,
chipset: &RegisteredChipset,
) -> Serial<LpcUart> {
let mut com1 = None;
for (name, desc) in self.spec.serial.iter() {
if desc.device != spec::SerialPortDevice::Uart {
continue;
}
let (irq, port) = match desc.num {
SerialPortNumber::Com1 => (ibmpc::IRQ_COM1, ibmpc::PORT_COM1),
SerialPortNumber::Com2 => (ibmpc::IRQ_COM2, ibmpc::PORT_COM2),
SerialPortNumber::Com3 => (ibmpc::IRQ_COM3, ibmpc::PORT_COM3),
SerialPortNumber::Com4 => (ibmpc::IRQ_COM4, ibmpc::PORT_COM4),
};
let dev = LpcUart::new(chipset.irq_pin(irq).unwrap());
dev.set_autodiscard(true);
LpcUart::attach(&dev, &self.machine.bus_pio, port);
self.devices.insert(name.to_owned(), dev.clone());
if desc.num == SerialPortNumber::Com1 {
assert!(com1.is_none());
com1 = Some(dev);
}
}
let sink_size = NonZeroUsize::new(64).unwrap();
let source_size = NonZeroUsize::new(1024).unwrap();
Serial::new(com1.unwrap(), sink_size, source_size)
}
pub fn initialize_ps2(
&mut self,
chipset: &RegisteredChipset,
) -> Arc<PS2Ctrl> {
let ps2_ctrl = PS2Ctrl::create();
ps2_ctrl.attach(
&self.machine.bus_pio,
chipset.irq_pin(ibmpc::IRQ_PS2_PRI).unwrap(),
chipset.irq_pin(ibmpc::IRQ_PS2_AUX).unwrap(),
chipset.reset_pin(),
);
self.devices.insert(
SpecKey::Name(ps2_ctrl.type_name().into()),
ps2_ctrl.clone(),
);
ps2_ctrl
}
pub fn initialize_qemu_debug_port(
&mut self,
) -> Result<(), MachineInitError> {
let dbg = QemuDebugPort::create(&self.machine.bus_pio);
let debug_file = std::fs::File::create("debug.out")
.context("failed to create firmware debug port logfile")?;
let poller = chardev::BlockingFileOutput::new(debug_file);
poller.attach(Arc::clone(&dbg) as Arc<dyn BlockingSource>);
self.devices.insert(SpecKey::Name(dbg.type_name().into()), dbg);
Ok(())
}
pub fn initialize_qemu_pvpanic(
&mut self,
virtual_machine: VirtualMachine,
) -> Result<(), MachineInitError> {
if let Some(pvpanic) = &self.spec.pvpanic {
if pvpanic.spec.enable_isa {
let device = QemuPvpanic::create(
self.log.new(slog::o!("dev" => "qemu-pvpanic")),
);
device.attach_pio(&self.machine.bus_pio);
self.devices.insert(pvpanic.id.clone(), device.clone());
if let Some(ref registry) = self.producer_registry {
let producer = crate::stats::PvpanicProducer::new(
virtual_machine,
device,
);
registry.register_producer(producer).context(
"failed to register PVPANIC Oximeter producer",
)?;
}
}
}
Ok(())
}
pub async fn initialize_vsock(
&mut self,
chipset: &RegisteredChipset,
attest_cfg: Option<AttestationServerConfig>,
) -> Result<Option<AttestationSock>, MachineInitError> {
use propolis::vsock::proxy::VsockPortMapping;
if let Some(vsock) = &self.spec.vsock {
let bdf: pci::Bdf = vsock.spec.pci_path.into();
let mappings = vec![VsockPortMapping::new(
attestation::ATTESTATION_PORT.into(),
attestation::ATTESTATION_ADDR,
)];
let guest_cid = GuestCid::try_from(vsock.spec.guest_cid)
.context("could not parse guest cid")?;
// While the spec does not recommend how large the virtio descriptor
// table should be, we sized this appropriately in testing, so
// that the guest is able to move vsock packets at a reasonable
// throughput without the need to be much larger.
let num_queues = 256;
let device = virtio::PciVirtioSock::new(
num_queues,
guest_cid,
self.log.new(slog::o!("dev" => "virtio-socket")),
mappings,
);
self.devices.insert(vsock.id.clone(), device.clone());
chipset.pci_attach(bdf, device);
// Spawn attestation server that will go over the vsock device
if let Some(cfg) = attest_cfg {
let attest = AttestationSock::new(
self.log.new(slog::o!("component" => "attestation-server")),
cfg.sled_agent_addr,
)
.await
.map_err(MachineInitError::AttestationServer)?;
return Ok(Some(attest));
}
} else {
info!(self.log, "no vsock device in instance spec");
return Ok(None);
}
Ok(None)
}
async fn create_storage_backend_from_spec(
&mut self,
backend_spec: &StorageBackend,
backend_id: &SpecKey,
nexus_client: &Option<NexusClient>,
wanted_heap: &mut usize,
) -> Result<StorageBackendInstance, MachineInitError> {
match backend_spec {
StorageBackend::Crucible(spec) => {
info!(self.log, "Creating Crucible disk";
"backend_id" => %backend_id);
let vcr: VolumeConstructionRequest =
serde_json::from_str(&spec.request_json)
.map_err(MachineInitError::VcrDeserializationFailed)?;
let cru_id = match vcr {
VolumeConstructionRequest::Volume { id, .. } => {
id.to_string()
}
VolumeConstructionRequest::File { id, .. } => {
id.to_string()
}
VolumeConstructionRequest::Url { id, .. } => id.to_string(),
VolumeConstructionRequest::Region { .. } => {
"Region".to_string()
}
};
// Wild guess: we might collect up to 1MB (assuming we're
// limited by NVMe MDTS) of data in each Crucible worker. That
// is accumulated into a BytesMut, which is backed by a
// Vec::with_capacity. With a power of two capacity it's
// *probably* not rounded up further.
const PER_WORKER_HEAP: usize = MB;
// And Crucible workers are not currently tunable, so this is
// how many there are
// (see propolis::block::crucible::Crucible::WORKER_COUNT)
*wanted_heap += 8 * PER_WORKER_HEAP;
let be = propolis::block::CrucibleBackend::create(
vcr,
propolis::block::BackendOpts {
read_only: Some(spec.readonly),
..Default::default()
},
self.producer_registry.clone(),
nexus_client.clone(),
self.log.new(
slog::o!("component" => format!("crucible-{cru_id}")),
),
)
.await
.context("failed to create Crucible backend")?;
let crucible = Some(be.clone());
Ok(StorageBackendInstance { be, crucible })
}
StorageBackend::File(spec) => {
info!(self.log, "Creating file disk backend";
"path" => &spec.path);
// Check if raw device is being used and gripe if it isn't
let meta =
std::fs::metadata(&spec.path).with_context(|| {
format!(
"failed to read file backend metadata for {:?}",
spec.path
)
})?;
if meta.file_type().is_block_device() {
slog::warn!(
self.log,
"Block backend using standard device rather than raw";
"path" => &spec.path
);
}
let nworkers: NonZeroUsize = match spec.workers {
Some(workers) => {
if workers.get() <= MAX_FILE_WORKERS {
workers
} else {
slog::warn!(
self.log,
"workers must be between 1 and {} \
Using default value of {}.",
MAX_FILE_WORKERS,
DEFAULT_WORKER_COUNT
);
NonZeroUsize::new(DEFAULT_WORKER_COUNT).unwrap()
}
}
None => NonZeroUsize::new(DEFAULT_WORKER_COUNT).unwrap(),
};
// Similar to Crucible backends above: we might collect up to
// 1MB (assuming we're limited by NVMe MDTS) of data in each
// worker. This is a hack in its own right, see Propolis#985.
const PER_WORKER_HEAP: usize = MB;
*wanted_heap += nworkers.get() * PER_WORKER_HEAP;
let be = propolis::block::FileBackend::create(
&spec.path,
propolis::block::BackendOpts {
read_only: Some(spec.readonly),
block_size: Some(spec.block_size),
..Default::default()
},
nworkers,
self.log.clone(),
)
.with_context(|| {
format!(
"failed to create file backend for file {:?}",
spec.path
)
})?;
Ok(StorageBackendInstance { be, crucible: None })
}
StorageBackend::Blob(spec) => {
let bytes = base64::Engine::decode(
&base64::engine::general_purpose::STANDARD,
&spec.base64,
)?;
info!(self.log, "Creating in-memory disk backend";
"len" => bytes.len());
let nworkers = NonZeroUsize::new(8).unwrap();
let be = propolis::block::InMemoryBackend::create(
bytes,
propolis::block::BackendOpts {
block_size: Some(512),
read_only: Some(spec.readonly),
..Default::default()
},
nworkers,
)
.context("failed to create in-memory storage backend")?;
// In-memory backends need to be registered for lifecycle
// notifications so that they can export/import changes to the
// backing disk across migrations.
self.devices.insert(backend_id.clone(), be.clone());
Ok(StorageBackendInstance { be, crucible: None })
}
}
}
/// Collect the necessary information out of the VM under construction into
/// the provided `AttestationSocketInit`. This is expected to populate
/// `attest_init` with information so the caller can spawn off
/// `AttestationSockInit::run`.
pub fn prepare_rot_initializer(
&self,
vm_rot: &mut AttestationSock,
) -> Result<(), MachineInitError> {
let uuid = self.properties.id;
// The first boot entry is a key into `self.spec.disks`, which is how
// we'll get to a Crucible volume backing this boot option.
let boot_disk_entry =
self.spec.boot_settings.as_ref().and_then(|settings| {
if settings.order.len() >= 2 {
// In a rack we only configure propolis-server with zero or
// one boot disks. It's possible to provide a fuller list,
// and in the future the product may actually expose such a
// capability. At that time, we'll need to have a reckoning
// for what "boot disk measurement" from the RoT actually
// means; it probably "should" be "the measurement of the
// disk that EDK2 decided to boot into", but that
// communication to and from the guest is a little more
// complicated than we want or need to build out today.
//
// Since as the system exists we either have no specific
// boot disk (and don't know where the guest is expected to
// end up), or one boot disk (and can determine which disk
// to collect a measurement of before even running guest
// firmware), we encode this expectation up front. If the
// product has changed such that this assert is reached,
// "that's exciting!" and "sorry for crashing your
// Propolis".
panic!(
"Unsupported VM RoT configuration: \
more than one boot disk"
);
}
settings.order.first()
});
let boot_backend = if let Some(entry) = boot_disk_entry {
let disk_dev =
self.spec.disks.get(&entry.device_id).ok_or_else(|| {
MachineInitError::BootOrderEntryWithoutDevice(
entry.device_id.clone(),
)
})?;
let backend_id = match &disk_dev.device_spec {
spec::StorageDevice::Virtio(disk) => &disk.backend_id,
spec::StorageDevice::Nvme(disk) => &disk.backend_id,
};
let Some(block_backend) = self.block_backends.get(backend_id)
else {
return Err(MachineInitError::DeviceWithoutBlockBackend {
device_id: entry.device_id.to_owned(),
backend_id: backend_id.to_owned(),
});
};
if let Some(backend) =
block_backend.as_any().downcast_ref::<block::CrucibleBackend>()
{
if backend.is_read_only() {
Some(attestation::boot_digest::Backend::Crucible(
backend.clone_volume(),
))
} else {
// Disk must be read-only to be used for attestation.
slog::info!(
self.log,
"boot disk is not read-only (and will not be used for attestations)",
);
None
}
} else {
// Probably fine, just not handled right now.
slog::warn!(
self.log,
"VM RoT ignoring boot disk: not a Crucible volume"
);
None
}
} else {
None
};
vm_rot.prepare_instance_conf(uuid, boot_backend);
Ok(())
}
/// Initializes the storage devices and backends listed in this
/// initializer's instance spec.
///
/// On success, returns a map from Crucible backend IDs to Crucible
/// backends.
pub async fn initialize_storage_devices(
&mut self,
chipset: &RegisteredChipset,
nexus_client: Option<NexusClient>,
) -> Result<usize, MachineInitError> {
let mut wanted_heap = 0usize;
enum DeviceInterface {
Virtio,
Nvme,
}
for (device_id, disk) in &self.spec.disks {
info!(
self.log,
"Creating storage device";
"device_id" => %device_id,
"spec" => ?disk.device_spec
);
let (device_interface, backend_id, pci_path) = match &disk
.device_spec
{
spec::StorageDevice::Virtio(disk) => {
(DeviceInterface::Virtio, &disk.backend_id, disk.pci_path)
}
spec::StorageDevice::Nvme(disk) => {
(DeviceInterface::Nvme, &disk.backend_id, disk.pci_path)
}
};
// For all storage devices we'll have a QueueMinder connecting
// each emulated device queue to storage backends. The minder and
// structures in its supporting logic don't have much state, but may
// do some dynamic allocation. Assume they won't need more than 1KiB
// of state (`in_flight` has at most nworkers entries currently and
// will need to grow only once or twice to a small capacity. The
// number of outstanding boxed requests and responses is at most
// nworkers. Might be more, but not much).
//
// 64 * 1K is a wild over-estimate while we support 1-15 queues
// across virtio-block and nvme.
wanted_heap += 64 * 1024;
let bdf: pci::Bdf = pci_path.into();
let StorageBackendInstance { be: backend, crucible } = self
.create_storage_backend_from_spec(
&disk.backend_spec,
backend_id,
&nexus_client,
&mut wanted_heap,
)
.await?;
info!(
self.log,
"raised balloon size";
"ballon_size" => wanted_heap
);
self.block_backends.insert(backend_id.clone(), backend.clone());
let block_dev: Arc<dyn block::Device> = match device_interface {
DeviceInterface::Virtio => {
let vioblk = virtio::PciVirtioBlock::new(0x100);
self.devices.insert(device_id.clone(), vioblk.clone());
block::attach(&vioblk.block_attach, backend.attachment())
.unwrap();
chipset.pci_attach(bdf, vioblk.clone());
vioblk
}
DeviceInterface::Nvme => {
let spec::StorageDevice::Nvme(nvme_spec) =
&disk.device_spec
else {
unreachable!("disk is known to be an NVMe disk");
};
// Limit data transfers to 1MiB (2^8 * 4k) in size
let mdts = Some(8);
let component = format!("nvme-{device_id}");
let nvme = nvme::PciNvme::create(
&nvme_spec.serial_number,
mdts,
self.log.new(slog::o!("component" => component)),
);
self.devices.insert(device_id.clone(), nvme.clone());
block::attach(&nvme.block_attach, backend.attachment())
.unwrap();
chipset.pci_attach(bdf, nvme.clone());
nvme
}
};
if let Some(crucible) = crucible {
let crucible =
match self.crucible_backends.entry(backend_id.clone()) {
std::collections::btree_map::Entry::Occupied(_) => {
return Err(
MachineInitError::DuplicateCrucibleBackendId(
backend_id.clone(),
),
);
}
std::collections::btree_map::Entry::Vacant(e) => {
e.insert(crucible)
}
};
let Some(block_size) = crucible.block_size().await else {
slog::error!(
self.log,
"Could not get Crucible backend block size, \
virtual disk metrics can't be reported for it";
"disk_id" => %backend_id,
);
continue;
};
let Ok(volume_id) = crucible.get_uuid().await else {
slog::error!(
self.log,
"Could not get Crucible volume ID, \
virtual disk metrics can't be reported for it";
"disk_id" => %backend_id,
);
continue;
};
if let Some(registry) = &self.producer_registry {
let block_metrics = BlockMetrics::new(
VirtualDisk {
attached_instance_id: self.properties.id,
block_size,
disk_id: volume_id,
project_id: self.properties.metadata.project_id,
silo_id: self.properties.metadata.silo_id,
},
block_dev.attachment().max_queues(),
);
if let Err(e) =
registry.register_producer(block_metrics.producer())
{
slog::error!(
self.log,
"Could not register virtual disk producer, \
metrics will not be produced";
"disk_id" => %backend_id,
"volume_id" => %volume_id,
"error" => ?e,
);
continue;
};
block_dev.attachment().set_metric_consumer(block_metrics);
};
}
}
Ok(wanted_heap)
}
/// Initialize network devices, add them to the device map, and attach them
/// to the chipset.
///
/// If a KstatSampler is provided, this function will also track network
/// interface statistics.
pub async fn initialize_network_devices(
&mut self,
chipset: &RegisteredChipset,
) -> Result<(), MachineInitError> {
// Only create the vector if the kstat_sampler exists.
let mut interface_ids: Option<NetworkInterfaceIds> =
self.kstat_sampler.as_ref().map(|_| Vec::new());
for (device_name, nic) in &self.spec.nics {
info!(self.log, "Creating vNIC {}", device_name);
let bdf: pci::Bdf = nic.device_spec.pci_path.into();
// Set viona device parameters. The parameters here (copy_data and
// header_pad) require `viona::ApiVersion::V3`, below Propolis'
// minimum of V6, so we can always set them.
//
// The values chosen here are tuned to maximize performance when
// Propolis is used with OPTE in a full Oxide rack deployment,
// although they should not negatively impact use outside those
// conditions. These parameters and their effects (save for
// performance delta) are not guest-visible.
let params = Some(virtio::viona::DeviceParams {
// Loan guest packet data, rather than allocating fresh
// buffers and copying it.
copy_data: false,
// Leave room for underlay encapsulation:
// - ethernet: 14
// - IPv6: 40
// - UDP: 8
// - Geneve: 8–16 (due to options)
// - (and then round up to nearest 8)
header_pad: 80,
});
let viona = virtio::PciVirtioViona::new(
&nic.backend_spec.vnic_name,
&self.machine.hdl,
params,
)
.with_context(|| {
format!("failed to create viona device {device_name:?}")
})?;
self.devices.insert(device_name.clone(), viona.clone());
// Only push to interface_ids if kstat_sampler exists
if let Some(ref mut ids) = interface_ids {
ids.push((
nic.device_spec.interface_id,
viona.instance_id().with_context(|| {
format!(
"failed to get viona instance ID for network \
device {device_name:?}"
)
})?,
));
}
chipset.pci_attach(bdf, viona);
}
if let Some(sampler) = self.kstat_sampler.as_ref() {
track_network_interface_kstats(
&self.log,
sampler,
&self.stats_vm,
interface_ids.unwrap(),
)
.await
}
Ok(())
}
#[cfg(feature = "failure-injection")]
pub fn initialize_test_devices(&mut self) {
use propolis::hw::testdev::{
MigrationFailureDevice, MigrationFailures,
};
if let Some(mig) = &self.spec.migration_failure {
if mig.spec.fail_exports == 0 && mig.spec.fail_imports == 0 {
info!(
self.log,
"migration failure device's failure counts are both 0";
"device_spec" => ?mig.spec
);
}
let dev = MigrationFailureDevice::create(
&self.log,
MigrationFailures {
exports: mig.spec.fail_exports as usize,
imports: mig.spec.fail_imports as usize,
},
);
self.devices.insert(mig.id.clone(), dev);
}
}
#[cfg(feature = "falcon")]
pub fn initialize_softnpu_ports(
&mut self,
chipset: &RegisteredChipset,
) -> Result<(), MachineInitError> {
let softnpu = &self.spec.softnpu;
// Check to make sure we actually have both a pci port and at least one
// regular SoftNpu port, otherwise just return.
let pci_port = match &softnpu.pci_port {
Some(tfp) => tfp,
None => return Ok(()),
};
if softnpu.ports.is_empty() {
return Ok(());
}
// Get a Vec of references to the ports which will then be sorted by
// port name.
let mut ports: Vec<_> = softnpu.ports.iter().collect();
// SoftNpu ports are named <topology>_<node>_vnic<N> by falcon, where
// <N> indicates the intended order.
ports.sort_by_key(|p| p.0);
let data_links = ports
.iter()
.map(|port| port.1.backend_spec.vnic_name.clone())
.collect();
// Set up an LPC uart for ASIC management comms from the guest.
//
// NOTE: SoftNpu squats on com4.
let uart = LpcUart::new(chipset.irq_pin(ibmpc::IRQ_COM4).unwrap());
uart.set_autodiscard(true);
LpcUart::attach(&uart, &self.machine.bus_pio, ibmpc::PORT_COM4);
self.devices
.insert(SpecKey::Name("softnpu-uart".to_string()), uart.clone());
// Start with no pipeline. The guest must load the initial P4 program.
let pipeline = Arc::new(std::sync::Mutex::new(None));
// Set up the p9fs device for guest programs to load P4 programs
// through.
let p9_handler = virtio::softnpu::SoftNpuP9Handler::new(
"/dev/softnpufs".to_owned(),
"/dev/softnpufs".to_owned(),
ports.len() as u16,
pipeline.clone(),
self.log.clone(),
);
let vio9p =
virtio::p9fs::PciVirtio9pfs::new(0x40, Arc::new(p9_handler));
self.devices
.insert(SpecKey::Name("softnpu-p9fs".to_string()), vio9p.clone());
let bdf = softnpu
.p9_device
.as_ref()
.ok_or(MachineInitError::SoftNpuP9Missing)?
.pci_path
.into();
chipset.pci_attach(bdf, vio9p.clone());
// Create the SoftNpu device.
let queue_size = 0x8000;
let softnpu = virtio::softnpu::SoftNpu::new(
data_links,
queue_size,
uart,
vio9p,
pipeline,
self.log.clone(),
)
.context("failed to register softnpu")?;
self.devices
.insert(SpecKey::Name("softnpu-main".to_string()), softnpu.clone());
// Create the SoftNpu PCI port.
self.devices.insert(
SpecKey::Name("softnpu-pciport".to_string()),
softnpu.pci_port.clone(),
);
chipset.pci_attach(pci_port.pci_path.into(), softnpu.pci_port.clone());
Ok(())
}
#[cfg(feature = "falcon")]
pub fn initialize_9pfs(&mut self, chipset: &RegisteredChipset) {
let softnpu = &self.spec.softnpu;
// Check that there is actually a p9fs device to register, if not bail
// early.
let Some(p9fs) = &softnpu.p9fs else {
return;
};
let handler = virtio::p9fs::HostFSHandler::new(
p9fs.source.to_owned(),
p9fs.target.to_owned(),
p9fs.chunk_size,
self.log.clone(),
);
let vio9p = virtio::p9fs::PciVirtio9pfs::new(0x40, Arc::new(handler));
self.devices
.insert(SpecKey::Name("falcon-p9fs".to_string()), vio9p.clone());
chipset.pci_attach(p9fs.pci_path.into(), vio9p);
}
fn generate_smbios(
&self,
bootrom_version: &Option<String>,
) -> smbios::TableBytes {
use smbios::table::{type0, type1, type16, type4};
let rom_size =
self.state.rom_size_bytes.expect("ROM is already populated");
let bios_version = bootrom_version
.as_deref()
.unwrap_or("v0.8")
.try_into()
.expect("bootrom version string doesn't contain NUL bytes");
let smb_type0 = smbios::table::Type0 {
vendor: "Oxide".try_into().unwrap(),
bios_version,
bios_release_date: "The Aftermath 30, 3185 YOLD"
.try_into()
.unwrap(),
bios_rom_size: ((rom_size / (64 * 1024)) - 1) as u8,
bios_characteristics: type0::BiosCharacteristics::UNSUPPORTED,
bios_ext_characteristics: type0::BiosExtCharacteristics::ACPI
| type0::BiosExtCharacteristics::UEFI
| type0::BiosExtCharacteristics::IS_VM,
..Default::default()
};
// If `spec` contains smbios_type1_input then use it. Otherwise use
// defaults.
let mut smb_type1 = smbios::table::Type1::default();
if let Some(smbios) = self.spec.smbios_type1_input.clone() {
smb_type1.manufacturer =
smbios.manufacturer.try_into().unwrap_or_default();
smb_type1.product_name =
smbios.product_name.try_into().unwrap_or_default();
smb_type1.serial_number =
smbios.serial_number.try_into().unwrap_or_default();
smb_type1.version =
smbios.version.to_string().try_into().unwrap_or_default();
} else {
smb_type1.manufacturer = "Oxide".try_into().unwrap();
smb_type1.product_name = "OxVM".try_into().unwrap();
smb_type1.serial_number =
self.properties.id.to_string().try_into().unwrap_or_default();
};
smb_type1.uuid = self.properties.id.to_bytes_le();
smb_type1.wake_up_type = type1::WakeUpType::PowerSwitch;
// The processor vendor, family/model/stepping, and brand string should
// correspond to the values the guest will see if it queries CPUID.
//
// Note that all these values are `Option`s, because the spec may
// contain CPUID values that don't contain all of the input leaves.
let cpuid_vendor = self.spec.cpuid.get(CpuidIdent::leaf(0)).copied();
let cpuid_ident = self.spec.cpuid.get(CpuidIdent::leaf(1)).copied();
// Coerce the array-of-Options into an Option containing the array.
let cpuid_procname: Option<[CpuidValues; 3]> = [
self.spec.cpuid.get(CpuidIdent::leaf(0x8000_0002)).copied(),
self.spec.cpuid.get(CpuidIdent::leaf(0x8000_0003)).copied(),
self.spec.cpuid.get(CpuidIdent::leaf(0x8000_0004)).copied(),
]
.into_iter()
// This returns None if any of the input options were None (i.e. if any
// of the requested leaves weren't found). This implies that if the
// `collect` returns `Some`, there are necessarily three elements in the
// `Vec`, so `try_into::<[CpuidValues; 3]>` will always succeed.
.collect::<Option<Vec<_>>>()
.map(TryInto::try_into)
.transpose()
.expect("output array should always have three elements");
let family = cpuid_ident
.map(|ident| {
match ident.eax & 0xf00 {
// If family ID is 0xf, extended family is added to it
0xf00 => ((ident.eax >> 20) & 0xff) + 0xf,
// ... otherwise base family ID is used
base => base >> 8,
}
})
.unwrap_or(0);
let vendor = cpuid_vendor.map(CpuidVendor::try_from);
let proc_manufacturer = match vendor {
Some(Ok(CpuidVendor::Intel)) => "Intel",
Some(Ok(CpuidVendor::Amd)) => "Advanced Micro Devices, Inc.",
_ => "",
}
.try_into()
.unwrap();
let proc_family = match (vendor, family) {
// Explicitly match for Zen-based CPUs
//
// Although this family identifier is not valid in SMBIOS 2.7,
// having been defined in 3.x, we pass it through anyways.
(Some(Ok(CpuidVendor::Amd)), family) if family >= 0x17 => 0x6b,
// Emit Unknown for everything else
_ => 0x2,
};
let proc_id = cpuid_ident
.map(|id| u64::from(id.eax) | (u64::from(id.edx) << 32))
.unwrap_or(0);
let proc_version = cpuid_procname
.and_then(|vals| propolis::cpuid::parse_brand_string(vals).ok())
.unwrap_or_default();
let smb_type4 = smbios::table::Type4 {
proc_type: type4::ProcType::Central,
proc_family,
proc_manufacturer,
proc_id,
proc_version: proc_version.try_into().unwrap_or_default(),
status: type4::ProcStatus::Enabled,
// unknown
proc_upgrade: 0x2,
// make core and thread counts equal for now
core_count: self.spec.board.cpus,
core_enabled: self.spec.board.cpus,
thread_count: self.spec.board.cpus,
proc_characteristics: type4::Characteristics::IS_64_BIT
| type4::Characteristics::MULTI_CORE,
..Default::default()
};
let memsize_bytes = (self.spec.board.memory_mb as usize) * MB;
let mut smb_type16 = smbios::table::Type16 {
location: type16::Location::SystemBoard,
array_use: type16::ArrayUse::System,
error_correction: type16::ErrorCorrection::Unknown,
num_mem_devices: 1,
..Default::default()
};
smb_type16.set_max_capacity(memsize_bytes);
let phys_mem_array_handle = 0x1600.into();
let mut smb_type17 = smbios::table::Type17 {
phys_mem_array_handle,
// Unknown
form_factor: 0x2,
// Unknown
memory_type: 0x2,
..Default::default()
};
smb_type17.set_size(Some(memsize_bytes));
let smb_type32 = smbios::table::Type32::default();
// With "only" types 0, 1, 4, 16, 17, and 32, we are technically missing
// some (types 3, 7, 9, 19) of the data required by the 2.7 spec. The
// data provided here were what we determined was a reasonable
// collection to start with. Should further requirements arise, we may
// expand on it.
let mut smb_tables = smbios::Tables::new(0x7f00.into());
smb_tables.add(0x0000.into(), &smb_type0).unwrap();
smb_tables.add(0x0100.into(), &smb_type1).unwrap();
smb_tables.add(0x0300.into(), &smb_type4).unwrap();
smb_tables.add(phys_mem_array_handle, &smb_type16).unwrap();
smb_tables.add(0x1700.into(), &smb_type17).unwrap();
smb_tables.add(0x3200.into(), &smb_type32).unwrap();
smb_tables.commit()
}
fn generate_e820(&self) -> Result<Entry, MachineInitError> {
info!(self.log, "Generating E820 map for guest address space");
let mut e820_table = fwcfg::formats::E820Table::new();
for (addr, len, kind) in self.machine.map_physmem.mappings().into_iter()
{
let addr = addr.try_into().expect("usize should fit into u64");
let len = len.try_into().expect("usize should fit into u64");
match kind {
propolis::vmm::MapType::Dram => {
e820_table.add_mem(addr, len);
}
_ => {
e820_table.add_reserved(addr, len);
}
}
}
Ok(e820_table.finish())
}
fn generate_bootorder(&self) -> Result<Option<Entry>, MachineInitError> {
info!(
self.log,
"Generating bootorder with order: {:?}",
self.spec.boot_settings.as_ref()
);
let Some(boot_names) = self.spec.boot_settings.as_ref() else {
return Ok(None);
};
let mut order = fwcfg::formats::BootOrder::new();
for boot_entry in boot_names.order.iter() {
// Theoretically we could support booting from network devices by
// matching them here and adding their PCI paths, but exactly what
// would happen is ill-understood. So, only check disks here.
if let Some(spec) = self.spec.disks.get(&boot_entry.device_id) {
match &spec.device_spec {
StorageDevice::Virtio(disk) => {
let bdf: pci::Bdf = disk.pci_path.into();
if bdf.bus.get() != 0 {
return Err(
MachineInitError::BootDeviceOnDownstreamPciBus(
boot_entry.device_id.clone(),
bdf.bus.get(),
),
);
}
order.add_disk(bdf.location);
}
StorageDevice::Nvme(disk) => {
let bdf: pci::Bdf = disk.pci_path.into();
if bdf.bus.get() != 0 {
return Err(
MachineInitError::BootDeviceOnDownstreamPciBus(
boot_entry.device_id.clone(),
bdf.bus.get(),
),
);
}
// TODO: separately, propolis-standalone passes an eui64
// of 0, so do that here too. is that.. ok?
order.add_nvme(bdf.location, 0);
}
};
} else {
// This should be unreachable - we check that the boot disk is
// valid when constructing the spec we're initializing from.
return Err(MachineInitError::BootOrderEntryWithoutDevice(
boot_entry.device_id.clone(),
));
}
}
Ok(Some(order.finish()))
}
/// Initialize qemu `fw_cfg` device, and populate it with data including CPU
/// count, SMBIOS tables, and attached RAM-FB device.
///
/// Should not be called before [`Self::initialize_rom()`].
pub fn initialize_fwcfg(
&mut self,
cpus: u8,
bootrom_version: &Option<String>,
) -> Result<Arc<ramfb::RamFb>, MachineInitError> {
let fwcfg = fwcfg::FwCfg::new();
fwcfg
.insert_legacy(
fwcfg::LegacyId::SmpCpuCount,
fwcfg::Entry::fixed_u32(u32::from(cpus)),
)
.map_err(|e| MachineInitError::FwcfgInsertFailed("cpu count", e))?;
let smbios::TableBytes { entry_point, structure_table } =
self.generate_smbios(bootrom_version);
fwcfg
.insert_named(
"etc/smbios/smbios-tables",
fwcfg::Entry::Bytes(structure_table),
)
.map_err(|e| {
MachineInitError::FwcfgInsertFailed("smbios tables", e)
})?;
fwcfg
.insert_named(
"etc/smbios/smbios-anchor",
fwcfg::Entry::Bytes(entry_point),
)
.map_err(|e| {
MachineInitError::FwcfgInsertFailed("smbios anchor", e)
})?;
if let Some(boot_order) = self.generate_bootorder()? {
fwcfg.insert_named("bootorder", boot_order).map_err(|e| {
MachineInitError::FwcfgInsertFailed("bootorder", e)
})?;
}
let e820_entry = self.generate_e820()?;
fwcfg
.insert_named("etc/e820", e820_entry)
.map_err(|e| MachineInitError::FwcfgInsertFailed("e820", e))?;
let ramfb = ramfb::RamFb::create(
self.log.new(slog::o!("component" => "ramfb")),
);
ramfb.attach(&self.machine.acc_mem);
fwcfg
.insert_named(ramfb::RamFb::FWCFG_ENTRY_NAME, fwcfg::Entry::RamFb)
.map_err(|e| MachineInitError::FwcfgInsertFailed("ramfb", e))?;
fwcfg.attach_ramfb(Some(ramfb.clone()));
fwcfg.attach(&self.machine.bus_pio, &self.machine.acc_mem);
self.devices.insert(SpecKey::Name(fwcfg.type_name().into()), fwcfg);
self.devices
.insert(SpecKey::Name(ramfb.type_name().into()), ramfb.clone());
Ok(ramfb)
}
/// Initialize virtual CPUs by first setting their capabilities, inserting
/// them into the device map, and then, if a kstat sampler is provided,
/// tracking their kstats.
pub async fn initialize_cpus(&mut self) -> Result<(), MachineInitError> {
let hv_interface = self.machine.guest_hv_interface.as_ref();
for vcpu in self.machine.vcpus.iter() {
// Report that the guest is running on bhyve.
//
// The CPUID set in the spec is not allowed to contain any leaves in
// the hypervisor leaf region (enforced at spec generation time).
let mut set = self.spec.cpuid.clone();
hv_interface.add_cpuid(&mut set).expect(
"propolis_server::spec construction should deny direct \
requests to set hypervisor leaves",
);
// Instead of `TopoKind::supported`, we use an intentionally-reduced
// list of Intel-only leaves for the moment. This is because if we
// specialize leaves used by AMD (or just both vendors), we'll
// change the topology a guest sees.
//
// The initial CPU platform defined in Nexus (Omicron#8728) hews to
// the pre-specialization topology, which won't have leaf B at all.
// Before that is sent, though, we'll see the present-but-zero
// leaves from bhyve, which we would happily specialize into
// something reflecting the guest if requested here. Once
// Omicron#8728 lands and propolis-server receives explicit CPUID
// profiles, we can add AMD leaves here too.
let cpu_topo_leaves = [TopoKind::Std4];
let specialized = propolis::cpuid::Specializer::new()
.with_vcpu_count(
NonZeroU8::new(self.spec.board.cpus).unwrap(),
true,
)
.with_vcpuid(vcpu.id)
.with_cache_topo()
.clear_cpu_topo(TopoKind::iter())
.with_cpu_topo(cpu_topo_leaves.into_iter())
.execute(set)
.map_err(|e| {
MachineInitError::CpuidSpecializationFailed(vcpu.id, e)
})?;
info!(self.log, "setting CPUID for vCPU";
"vcpu" => vcpu.id,
"cpuid" => ?specialized);
vcpu.set_cpuid(specialized).with_context(|| {
format!("setting CPUID for vcpu {}", vcpu.id)
})?;
vcpu.set_default_capabs()
.context("failed to set vcpu capabilities")?;
// The vCPUs behave like devices, so add them to the list as well
self.devices.insert(
SpecKey::Name(format!("vcpu-{}", vcpu.id)),
vcpu.clone(),
);
}
if let Some(sampler) = self.kstat_sampler.as_ref() {
track_vcpu_kstats(&self.log, sampler, &self.stats_vm).await;
}
Ok(())
}
pub fn register_guest_hv_interface(
&mut self,
guest_hv_interface: Arc<dyn Lifecycle>,
) {
self.devices.insert(
SpecKey::Name("guest-hv-interface".to_string()),
guest_hv_interface,
);
}
}
================================================
FILE: bin/propolis-server/src/lib/lib.rs
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
pub mod config;
mod initializer;
mod migrate;
mod serial;
pub mod server;
mod spec;
mod stats;
mod vcpu_tasks;
mod vm;
pub mod vnc;
================================================
FILE: bin/propolis-server/src/lib/migrate/codec.rs
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Support for encoding messages in the propolis/bhyve live
//! migration protocol. Messages are serialized to binary and
//! wrapped in Binary websocket frames with a trailing byte
//! indicating the message type.
//!
//! As defined in RFD0071, most messages are either serialized
//! structures or blobs, while the structures involved in the
//! memory transfer phases of the protocols are directly serialized
//! binary structures. We represent each of these structures in a
//! dedicated message type; similarly with 4KiB "page" data, etc.
//! Serialized structures are assumed to be text.
//!
//! Several messages involved in memory transfer include bitmaps
//! that are nominally bounded by associated [start, end) address
//! ranges. However, the framing layer makes no effort to validate
//! the implied invariants: higher level software is responsible
//! for that.
use super::MigrateError;
use bytes::{Buf, BufMut, Bytes};
use slog::error;
use strum::FromRepr;
use thiserror::Error;
use tokio_tungstenite::tungstenite;
/// Migration protocol errors.
#[derive(Debug, Error)]
pub enum ProtocolError {
/// We received an unexpected message type
#[error("couldn't decode message type ({0})")]
InvalidMessageType(u8),
/// The message received on the wire wasn't the expected length
#[error("unexpected message length {1} for type {0:?}")]
UnexpectedMessageLen(u8, usize),
/// Encountered an I/O error on the transport
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
/// Failed to serialize or deserialize a message
#[error("serialization error: {0}")]
Ron(#[from] ron::Error),
/// Received non-UTF8 string
#[error("non-UTF8 string: {0}")]
Utf8(#[from] std::str::Utf8Error),
/// Nothing, not even a tag byte
#[error("received empty message with no discriminant")]
EmptyMessage,
/// An error occurred in the underlying websocket transport
#[error("error occurred in websocket layer: {0}")]
WebsocketError(Box<tokio_tungstenite::tungstenite::Error>),
/// All our codec's messages should be tungstenite::Message::Binary
#[error("received empty message with no discriminant")]
UnexpectedWebsocketMessage(tungstenite::Message),
}
impl From<ron::de::SpannedError> for ProtocolError {
fn from(value: ron::de::SpannedError) -> Self {
Self::Ron(value.code)
}
}
/// Message represents the different frame types for messages
/// exchanged in the live migration protocol. Most structured
/// data is serialized into a string, while blobs are uninterpreted
/// vectors of bytes and 4KiB pages (e.g. of RAM) are uninterpreted
/// fixed-sized arrays. The memory-related messages are nominally
/// structured, but given the overall volume of memory data exchanged,
/// we serialize and deserialize them directly.
#[derive(Debug)]
pub(crate) enum Message {
Okay,
Error(MigrateError),
Serialized(String),
Blob(Vec<u8>),
Page(Vec<u8>),
MemQuery(u64, u64),
MemOffer(u64, u64, Vec<u8>),
MemEnd(u64, u64),
MemFetch(u64, u64, Vec<u8>),
MemXfer(u64, u64, Vec<u8>),
MemDone,
}
/// MessageType represents tags that are used in the protocol for
/// identifying frame types. They are an implementation detail of
/// the wire format, and not used elsewhere. However, they must be
/// kept in bijection with Message, above.
#[derive(Debug, PartialEq, FromRepr)]
#[repr(u8)]
enum MessageType {
Okay,
Error,
Serialized,
Blob,
Page,
MemQuery,
MemOffer,
MemEnd,
MemFetch,
MemXfer,
MemDone,
}
/// By implementing `From<&Message>` on MessageType, we can translate
/// each message into its tag type, ensuring full coverage.
impl From<&Message> for MessageType {
fn from(m: &Message) -> MessageType {
match m {
Message::Okay => MessageType::Okay,
Message::Error(_) => MessageType::Error,
Message::Serialized(_) => MessageType::Serialized,
Message::Blob(_) => MessageType::Blob,
Message::Page(_) => MessageType::Page,
Message::MemQuery(_, _) => MessageType::MemQuery,
Message::MemOffer(_, _, _) => MessageType::MemOffer,
Message::MemEnd(_, _) => MessageType::MemEnd,
Message::MemFetch(_, _, _) => MessageType::MemFetch,
Message::MemXfer(_, _, _) => MessageType::MemXfer,
Message::MemDone => MessageType::MemDone,
}
}
}
impl std::convert::TryInto<tungstenite::Message> for Message {
type Error = ProtocolError;
fn try_into(self) -> Result<tungstenite::Message, ProtocolError> {
let mut dst = Vec::new();
let tag = MessageType::from(&self) as u8;
match self {
Message::Okay | Message::MemDone => {}
Message::Error(e) => {
let serialized = ron::ser::to_string(&e)?;
dst.extend(serialized.as_bytes());
}
Message::Serialized(s) => dst.put_slice(s.as_bytes()),
Message::Blob(bytes) | Message::Page(bytes) => {
dst.put_slice(&bytes);
}
Message::MemQuery(start, end) | Message::MemEnd(start, end) => {
dst.put_u64_le(start);
dst.put_u64_le(end);
}
Message::MemOffer(start, end, bitmap)
| Message::MemFetch(start, end, bitmap)
| Message::MemXfer(start, end, bitmap) => {
dst.put_u64_le(start);
dst.put_u64_le(end);
dst.put_slice(&bitmap);
}
}
// tag at the end so we can pop it later (& so u64's align nicely)
dst.push(tag);
Ok(tungstenite::Message::Binary(dst))
}
}
// Retrieves a (`start`, `end`) pair from the buffer, ensuring valid length.
fn get_start_end(
tag: MessageType,
src: &mut Bytes,
) -> Result<(u64, u64), ProtocolError> {
if src.len() < 16 {
return Err(ProtocolError::UnexpectedMessageLen(tag as u8, src.len()));
}
let start = src.get_u64_le();
let end = src.get_u64_le();
Ok((start, end))
}
impl std::convert::TryInto<Message> for tungstenite::Message {
type Error = ProtocolError;
fn try_into(self) -> Result<Message, ProtocolError> {
match self {
tungstenite::Message::Binary(mut v) => {
// If the tag byte is absent or invalid, don't bother looking at the message.
let tag_byte = v.pop().ok_or(ProtocolError::EmptyMessage)?;
let tag = MessageType::from_repr(tag_byte)
.ok_or(ProtocolError::InvalidMessageType(tag_byte))?;
let mut src = Bytes::from(v);
// At this point, we have a valid message of a known type, and
// the remaining bytes are the message contents.
// Attempt decode and return the received message.
let m = match tag {
MessageType::Okay => {
if !src.is_empty() {
return Err(ProtocolError::UnexpectedMessageLen(
tag as u8,
src.len(),
));
}
Message::Okay
}
MessageType::Error => {
let e = ron::de::from_str(std::str::from_utf8(&src)?)?;
Message::Error(e)
}
MessageType::Serialized => {
let s = std::str::from_utf8(&src)?.to_string();
Message::Serialized(s)
}
MessageType::Blob => Message::Blob(src.to_vec()),
MessageType::Page => {
if src.len() != 4096 {
return Err(ProtocolError::UnexpectedMessageLen(
tag as u8,
src.len(),
));
}
Message::Page(src.to_vec())
}
MessageType::MemQuery => {
let (start, end) = get_start_end(tag, &mut src)?;
Message::MemQuery(start, end)
}
MessageType::MemOffer => {
let (start, end) = get_start_end(tag, &mut src)?;
let bitmap = src.to_vec();
Message::MemOffer(start, end, bitmap)
}
MessageType::MemEnd => {
let (start, end) = get_start_end(tag, &mut src)?;
Message::MemEnd(start, end)
}
MessageType::MemFetch => {
let (start, end) = get_start_end(tag, &mut src)?;
let bitmap = src.to_vec();
Message::MemFetch(start, end, bitmap)
}
gitextract_ra4peprf/
├── .cargo/
│ └── config.toml
├── .git-blame-ignore-revs
├── .github/
│ ├── buildomat/
│ │ ├── config.toml
│ │ ├── jobs/
│ │ │ ├── check-headers.sh
│ │ │ ├── falcon-build.sh
│ │ │ ├── image.sh
│ │ │ ├── phd-build.sh
│ │ │ ├── phd-run-migrate-from-base.sh
│ │ │ ├── phd-run.sh
│ │ │ └── test-gimlet.sh
│ │ └── phd-run-with-args.sh
│ └── workflows/
│ └── rust.yml
├── .gitignore
├── .licenserc.yaml
├── Cargo.toml
├── LICENSE
├── README.md
├── bin/
│ ├── dropshot-apis/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── main.rs
│ ├── mock-server/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib/
│ │ │ ├── api_types.rs
│ │ │ └── lib.rs
│ │ └── main.rs
│ ├── propolis-cli/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ └── main.rs
│ ├── propolis-server/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── lib/
│ │ │ ├── config.rs
│ │ │ ├── initializer.rs
│ │ │ ├── lib.rs
│ │ │ ├── migrate/
│ │ │ │ ├── codec.rs
│ │ │ │ ├── destination.rs
│ │ │ │ ├── memx.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── preamble.rs
│ │ │ │ ├── protocol.rs
│ │ │ │ └── source.rs
│ │ │ ├── serial/
│ │ │ │ ├── history_buffer.rs
│ │ │ │ └── mod.rs
│ │ │ ├── server.rs
│ │ │ ├── spec/
│ │ │ │ ├── api_spec_v0.rs
│ │ │ │ ├── builder.rs
│ │ │ │ └── mod.rs
│ │ │ ├── stats/
│ │ │ │ ├── mod.rs
│ │ │ │ ├── network_interface.rs
│ │ │ │ ├── pvpanic.rs
│ │ │ │ ├── virtual_disk.rs
│ │ │ │ └── virtual_machine.rs
│ │ │ ├── vcpu_tasks.rs
│ │ │ ├── vm/
│ │ │ │ ├── active.rs
│ │ │ │ ├── ensure.rs
│ │ │ │ ├── guest_event.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── objects.rs
│ │ │ │ ├── request_queue.rs
│ │ │ │ ├── services.rs
│ │ │ │ ├── state_driver.rs
│ │ │ │ └── state_publisher.rs
│ │ │ └── vnc.rs
│ │ ├── main.rs
│ │ └── proptest-regressions/
│ │ └── vm/
│ │ └── request_queue.txt
│ ├── propolis-standalone/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── cidata.rs
│ │ ├── config.rs
│ │ ├── main.rs
│ │ └── snapshot.rs
│ └── propolis-utils/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ └── bin/
│ ├── cpuid-gen.rs
│ └── rsrvrctl.rs
├── crates/
│ ├── bhyve-api/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── header-check/
│ │ │ ├── Cargo.toml
│ │ │ ├── README.md
│ │ │ ├── build.rs
│ │ │ └── test/
│ │ │ └── main.rs
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── sys/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── enums.rs
│ │ ├── ioctls.rs
│ │ ├── lib.rs
│ │ ├── structs.rs
│ │ └── vmm_data.rs
│ ├── cpuid-profile-config/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── cpuid-utils/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── bits.rs
│ │ ├── host.rs
│ │ ├── instance_spec.rs
│ │ └── lib.rs
│ ├── dladm/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ └── sys.rs
│ ├── nvpair/
│ │ ├── Cargo.toml
│ │ ├── header-check/
│ │ │ ├── Cargo.toml
│ │ │ ├── build.rs
│ │ │ └── test/
│ │ │ └── main.rs
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── sys/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── pbind/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── propolis-api-types/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── disk.rs
│ │ ├── instance.rs
│ │ ├── instance_spec/
│ │ │ ├── components/
│ │ │ │ ├── backends.rs
│ │ │ │ ├── board.rs
│ │ │ │ ├── devices.rs
│ │ │ │ └── mod.rs
│ │ │ └── mod.rs
│ │ ├── lib.rs
│ │ ├── migration.rs
│ │ └── serial.rs
│ ├── propolis-api-types-versions/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── add_vsock/
│ │ │ ├── api.rs
│ │ │ ├── components/
│ │ │ │ ├── devices.rs
│ │ │ │ └── mod.rs
│ │ │ ├── instance_spec.rs
│ │ │ └── mod.rs
│ │ ├── crucible_volume_info/
│ │ │ ├── disk.rs
│ │ │ └── mod.rs
│ │ ├── impls/
│ │ │ ├── instance.rs
│ │ │ ├── instance_spec.rs
│ │ │ └── mod.rs
│ │ ├── initial/
│ │ │ ├── components/
│ │ │ │ ├── backends.rs
│ │ │ │ ├── board.rs
│ │ │ │ ├── devices.rs
│ │ │ │ └── mod.rs
│ │ │ ├── disk.rs
│ │ │ ├── instance.rs
│ │ │ ├── instance_spec.rs
│ │ │ ├── migration.rs
│ │ │ ├── mod.rs
│ │ │ └── serial.rs
│ │ ├── latest.rs
│ │ ├── lib.rs
│ │ └── programmable_smbios/
│ │ ├── api.rs
│ │ ├── instance_spec.rs
│ │ └── mod.rs
│ ├── propolis-config-toml/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ └── spec.rs
│ ├── propolis-server-api/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── propolis-types/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── rfb/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── examples/
│ │ │ ├── shared.rs
│ │ │ ├── socket.rs
│ │ │ └── websock.rs
│ │ └── src/
│ │ ├── encodings.rs
│ │ ├── keysym.rs
│ │ ├── lib.rs
│ │ ├── proto.rs
│ │ ├── server.rs
│ │ └── tungstenite.rs
│ ├── rgb-frame/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ └── viona-api/
│ ├── Cargo.toml
│ ├── header-check/
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ └── test/
│ │ └── main.rs
│ └── src/
│ ├── ffi.rs
│ └── lib.rs
├── docs/
│ ├── lifecycle.md
│ ├── migrate-with-crucible.md
│ ├── server-send-vcr.md
│ └── standalone-with-crucible.md
├── lib/
│ ├── propolis/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── accessors.rs
│ │ ├── api_version.rs
│ │ ├── attestation/
│ │ │ ├── boot_digest/
│ │ │ │ ├── crucible.rs
│ │ │ │ └── mod.rs
│ │ │ ├── mod.rs
│ │ │ └── server.rs
│ │ ├── block/
│ │ │ ├── attachment.rs
│ │ │ ├── crucible.rs
│ │ │ ├── file.rs
│ │ │ ├── id.rs
│ │ │ ├── in_memory.rs
│ │ │ ├── mem_async.rs
│ │ │ ├── minder.rs
│ │ │ └── mod.rs
│ │ ├── chardev/
│ │ │ ├── file_out.rs
│ │ │ ├── mod.rs
│ │ │ ├── pollers.rs
│ │ │ └── sock.rs
│ │ ├── common.rs
│ │ ├── cpuid.rs
│ │ ├── enlightenment/
│ │ │ ├── bhyve.rs
│ │ │ ├── hyperv/
│ │ │ │ ├── bits.rs
│ │ │ │ ├── hypercall.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── overlay.rs
│ │ │ │ └── tsc.rs
│ │ │ └── mod.rs
│ │ ├── exits.rs
│ │ ├── firmware/
│ │ │ ├── mod.rs
│ │ │ └── smbios/
│ │ │ ├── bits.rs
│ │ │ ├── mod.rs
│ │ │ └── table.rs
│ │ ├── hw/
│ │ │ ├── bhyve/
│ │ │ │ ├── atpic.rs
│ │ │ │ ├── atpit.rs
│ │ │ │ ├── hpet.rs
│ │ │ │ ├── ioapic.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── pmtimer.rs
│ │ │ │ └── rtc.rs
│ │ │ ├── chipset/
│ │ │ │ ├── i440fx.rs
│ │ │ │ └── mod.rs
│ │ │ ├── ibmpc.rs
│ │ │ ├── ids.rs
│ │ │ ├── mod.rs
│ │ │ ├── nvme/
│ │ │ │ ├── admin.rs
│ │ │ │ ├── bits.rs
│ │ │ │ ├── cmds.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── queue.rs
│ │ │ │ └── requests.rs
│ │ │ ├── pci/
│ │ │ │ ├── bar.rs
│ │ │ │ ├── bits.rs
│ │ │ │ ├── bridge.rs
│ │ │ │ ├── bus.rs
│ │ │ │ ├── cfgspace.rs
│ │ │ │ ├── device.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── test.rs
│ │ │ │ └── topology.rs
│ │ │ ├── ps2/
│ │ │ │ ├── ctrl.rs
│ │ │ │ ├── keyboard/
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ ├── scan_code_1.rs
│ │ │ │ │ └── scan_code_2.rs
│ │ │ │ └── mod.rs
│ │ │ ├── qemu/
│ │ │ │ ├── debug.rs
│ │ │ │ ├── fwcfg.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── pvpanic.rs
│ │ │ │ └── ramfb.rs
│ │ │ ├── testdev.rs
│ │ │ ├── uart/
│ │ │ │ ├── lpc.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── uart16550.rs
│ │ │ └── virtio/
│ │ │ ├── bits.rs
│ │ │ ├── block.rs
│ │ │ ├── mod.rs
│ │ │ ├── p9fs.rs
│ │ │ ├── pci.rs
│ │ │ ├── queue.rs
│ │ │ ├── softnpu.rs
│ │ │ ├── testutil.rs
│ │ │ ├── viona.rs
│ │ │ └── vsock.rs
│ │ ├── intr_pins.rs
│ │ ├── lib.rs
│ │ ├── lifecycle.rs
│ │ ├── migrate.rs
│ │ ├── mmio.rs
│ │ ├── msr.rs
│ │ ├── pio.rs
│ │ ├── tasks.rs
│ │ ├── util/
│ │ │ ├── aspace.rs
│ │ │ ├── id.rs
│ │ │ ├── mod.rs
│ │ │ └── regmap.rs
│ │ ├── vcpu.rs
│ │ ├── vmm/
│ │ │ ├── hdl.rs
│ │ │ ├── machine.rs
│ │ │ ├── mem.rs
│ │ │ ├── mod.rs
│ │ │ └── time.rs
│ │ └── vsock/
│ │ ├── buffer.rs
│ │ ├── mod.rs
│ │ ├── packet.rs
│ │ ├── poller.rs
│ │ ├── poller_stub.rs
│ │ └── proxy.rs
│ └── propolis-client/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ └── support.rs
├── openapi/
│ └── propolis-server/
│ ├── propolis-server-1.0.0-833484.json.gitstub
│ ├── propolis-server-2.0.0-d68a9f.json.gitstub
│ ├── propolis-server-3.0.0-10da2b.json.gitstub
│ ├── propolis-server-4.0.0-5ce09a.json.gitstub
│ └── propolis-server-5.0.0-0c6dd9.json
├── packaging/
│ ├── package-manifest.toml
│ ├── propolis-package/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ └── main.rs
│ └── smf/
│ ├── method_script.sh
│ └── propolis-server/
│ └── manifest.xml
├── phd-tests/
│ ├── .gitignore
│ ├── README.md
│ ├── artifacts.toml
│ ├── framework/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── artifacts/
│ │ │ ├── buildomat.rs
│ │ │ ├── manifest.rs
│ │ │ ├── mod.rs
│ │ │ └── store.rs
│ │ ├── disk/
│ │ │ ├── crucible.rs
│ │ │ ├── fat.rs
│ │ │ ├── file.rs
│ │ │ ├── in_memory.rs
│ │ │ └── mod.rs
│ │ ├── guest_os/
│ │ │ ├── alpine.rs
│ │ │ ├── debian11_nocloud.rs
│ │ │ ├── linux.rs
│ │ │ ├── mod.rs
│ │ │ ├── shell_commands.rs
│ │ │ ├── ubuntu22_04.rs
│ │ │ ├── windows.rs
│ │ │ ├── windows_server_2016.rs
│ │ │ ├── windows_server_2019.rs
│ │ │ └── windows_server_2022.rs
│ │ ├── host_api/
│ │ │ ├── kvm.rs
│ │ │ ├── mod.rs
│ │ │ └── stubs.rs
│ │ ├── lib.rs
│ │ ├── lifecycle.rs
│ │ ├── log_config.rs
│ │ ├── port_allocator.rs
│ │ ├── serial/
│ │ │ ├── mod.rs
│ │ │ ├── raw_buffer.rs
│ │ │ └── vt80x24.rs
│ │ ├── test_vm/
│ │ │ ├── config.rs
│ │ │ ├── environment.rs
│ │ │ ├── metrics.rs
│ │ │ ├── mod.rs
│ │ │ ├── server.rs
│ │ │ └── spec.rs
│ │ └── zfs.rs
│ ├── quickstart.sh
│ ├── runner/
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ └── src/
│ │ ├── config.rs
│ │ ├── execute.rs
│ │ ├── fixtures.rs
│ │ └── main.rs
│ ├── testcase/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── testcase_macro/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ └── tests/
│ ├── Cargo.toml
│ ├── src/
│ │ ├── boot_order/
│ │ │ └── efi_utils.rs
│ │ ├── boot_order.rs
│ │ ├── cpuid.rs
│ │ ├── crucible/
│ │ │ ├── migrate.rs
│ │ │ ├── mod.rs
│ │ │ └── smoke.rs
│ │ ├── disk.rs
│ │ ├── framework.rs
│ │ ├── hw.rs
│ │ ├── hyperv.rs
│ │ ├── lib.rs
│ │ ├── migrate.rs
│ │ ├── server_state_machine.rs
│ │ ├── smoke.rs
│ │ ├── stats.rs
│ │ └── vsock.rs
│ └── testdata/
│ └── dirt.sh
├── rust-toolchain.toml
├── rustfmt.toml
├── scripts/
│ ├── README.md
│ ├── cpuid-queries.d
│ ├── live-migration-times.d
│ ├── nvme-trace.d
│ ├── time-adjustments.d
│ ├── viona.d
│ └── vm-exit-codes.d
├── tools/
│ ├── check_headers
│ └── install_builder_prerequisites.sh
└── xtask/
├── Cargo.toml
└── src/
├── external.rs
├── main.rs
├── task_clippy.rs
├── task_fmt.rs
├── task_license.rs
├── task_phd.rs
├── task_prepush.rs
├── task_style.rs
└── util.rs
Showing preview only (469K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (6267 symbols across 259 files)
FILE: bin/dropshot-apis/src/main.rs
function environment (line 14) | pub fn environment() -> anyhow::Result<Environment> {
function all_apis (line 35) | pub fn all_apis() -> anyhow::Result<ManagedApis> {
function main (line 59) | fn main() -> anyhow::Result<ExitCode> {
function test_apis_up_to_date (line 77) | fn test_apis_up_to_date() -> anyhow::Result<ExitCode> {
FILE: bin/mock-server/src/lib/api_types.rs
type Error (line 22) | type Error = String;
function try_from (line 23) | fn try_from(value: types::PciPath) -> Result<Self, Self::Error> {
type InstanceSerialParams (line 32) | pub struct InstanceSerialParams {
type InstanceSerialHistoryParams (line 43) | pub struct InstanceSerialHistoryParams {
type MockMode (line 61) | pub enum MockMode {
FILE: bin/mock-server/src/lib/lib.rs
type Error (line 28) | pub enum Error {
type InstanceContext (line 41) | pub struct InstanceContext {
method new (line 64) | pub fn new(properties: api::InstanceProperties, _log: &Logger) -> Self {
method set_target_state (line 101) | pub async fn set_target_state(
method current_state (line 170) | fn current_state(&self) -> api::InstanceState {
method queue_states (line 177) | async fn queue_states(
type MockState (line 52) | struct MockState {
type Context (line 209) | pub struct Context {
method new (line 215) | pub fn new(log: Logger) -> Self {
function instance_ensure (line 224) | async fn instance_ensure(
function instance_get (line 252) | async fn instance_get(
function instance_state_monitor (line 272) | async fn instance_state_monitor(
function instance_state_put (line 345) | async fn instance_state_put(
function instance_serial (line 371) | async fn instance_serial(
function instance_serial_history_get (line 427) | async fn instance_serial_history_get(
function mock_mode_get (line 471) | async fn mock_mode_get(
function mock_mode_set (line 492) | async fn mock_mode_set(
function mock_step (line 528) | async fn mock_step(
type WsConn (line 571) | type WsConn = WebSocketStream<WebsocketConnectionRaw>;
constant DEFAULT_MAX_LEN (line 573) | const DEFAULT_MAX_LEN: usize = 1024;
type HistoryQuery (line 575) | pub(crate) enum HistoryQuery {
method from_query (line 580) | pub(crate) const fn from_query(
type SerialTask (line 597) | pub(crate) struct SerialTask {
method spawn (line 603) | pub fn spawn() -> Self {
method serial_task_work (line 623) | async fn serial_task_work(
method new_conn (line 709) | pub async fn new_conn(&self, ws: WsConn) {
method shutdown (line 720) | pub async fn shutdown(&self) {
type Serial (line 728) | pub(crate) struct Serial {
method new (line 732) | pub(super) fn new(name: &str) -> Arc<Self> {
method mock_data (line 737) | fn mock_data(name: &str) -> Vec<u8> {
method history_vec (line 788) | pub async fn history_vec(
function api (line 824) | pub fn api() -> ApiDescription<Arc<Context>> {
type Config (line 843) | pub type Config = dropshot::ConfigDropshot;
type Server (line 845) | pub type Server = dropshot::HttpServer<Arc<Context>>;
type ServerStartError (line 848) | pub type ServerStartError = Box<dyn std::error::Error + Send + Sync>;
function start (line 851) | pub fn start(config: Config, log: Logger) -> Result<Server, ServerStartE...
FILE: bin/mock-server/src/main.rs
type Args (line 19) | enum Args {
function build_logger (line 36) | fn build_logger() -> slog::Logger {
function run_openapi (line 69) | pub fn run_openapi() -> Result<(), String> {
function run_server (line 81) | async fn run_server(
function main (line 104) | async fn main() -> anyhow::Result<()> {
FILE: bin/propolis-cli/src/main.rs
type Opt (line 50) | struct Opt {
type Command (line 71) | enum Command {
type VmConfig (line 171) | struct VmConfig {
method instance_spec (line 290) | fn instance_spec(&self) -> anyhow::Result<InstanceSpec> {
function add_component_to_spec (line 207) | fn add_component_to_spec(
type DiskRequest (line 228) | struct DiskRequest {
method parse (line 245) | fn parse(&self) -> anyhow::Result<ParsedDiskRequest> {
type ParsedDiskRequest (line 237) | struct ParsedDiskRequest {
function parse_state (line 447) | fn parse_state(state: &str) -> anyhow::Result<InstanceStateRequested> {
function parse_json_file (line 458) | fn parse_json_file<T: serde::de::DeserializeOwned>(
function resolve_host (line 467) | fn resolve_host(server: &str) -> anyhow::Result<IpAddr> {
function create_logger (line 476) | fn create_logger(opt: &Opt) -> Logger {
type ProjectKind (line 488) | enum ProjectKind {}
method tag (line 491) | fn tag() -> TypedUuidTag {
type SiloKind (line 497) | enum SiloKind {}
method tag (line 500) | fn tag() -> TypedUuidTag {
type SledKind (line 506) | enum SledKind {}
method tag (line 509) | fn tag() -> TypedUuidTag {
function new_instance (line 516) | async fn new_instance(
function replace_vcr (line 546) | async fn replace_vcr(
function get_instance (line 563) | async fn get_instance(client: &Client) -> anyhow::Result<()> {
function put_instance (line 575) | async fn put_instance(
function stdin_to_websockets_task (line 589) | async fn stdin_to_websockets_task(
function serial (line 650) | async fn serial(
function serial_connect (line 745) | async fn serial_connect(
function migrate_instance (line 759) | async fn migrate_instance(
function monitor (line 878) | async fn monitor(addr: SocketAddr) -> anyhow::Result<()> {
function inject_nmi (line 911) | async fn inject_nmi(client: &Client) -> anyhow::Result<()> {
function main (line 921) | async fn main() -> anyhow::Result<()> {
type RawTermiosGuard (line 992) | struct RawTermiosGuard(libc::c_int, libc::termios);
method stdio_guard (line 995) | fn stdio_guard() -> Result<RawTermiosGuard, std::io::Error> {
method drop (line 1018) | fn drop(&mut self) {
function test_stdin_to_websockets_task (line 1031) | async fn test_stdin_to_websockets_task() {
FILE: bin/propolis-server/src/lib/config.rs
function reservoir_decide (line 8) | pub fn reservoir_decide(log: &slog::Logger) -> bool {
function reservoir_decide (line 41) | pub fn reservoir_decide(_log: &slog::Logger) -> bool {
FILE: bin/propolis-server/src/lib/initializer.rs
constant MAX_FILE_WORKERS (line 62) | const MAX_FILE_WORKERS: usize = 32;
constant DEFAULT_WORKER_COUNT (line 63) | const DEFAULT_WORKER_COUNT: usize = 8;
type MachineInitError (line 67) | pub enum MachineInitError {
constant MAX_ROM_SIZE (line 125) | const MAX_ROM_SIZE: usize = 0x20_0000;
function get_spec_guest_ram_limits (line 127) | fn get_spec_guest_ram_limits(spec: &Spec) -> (usize, usize) {
function build_instance (line 134) | pub fn build_instance(
type RegisteredChipset (line 177) | pub struct RegisteredChipset {
method pci_attach (line 182) | pub fn pci_attach(&self, bdf: pci::Bdf, dev: Arc<dyn pci::Endpoint>) {
method irq_pin (line 185) | pub fn irq_pin(&self, irq: u8) -> Option<Box<dyn intr_pins::IntrPin>> {
method reset_pin (line 188) | fn reset_pin(&self) -> Arc<dyn intr_pins::IntrPin> {
type StorageBackendInstance (line 193) | struct StorageBackendInstance {
type MachineInitializerState (line 199) | pub struct MachineInitializerState {
type MachineInitializer (line 203) | pub struct MachineInitializer<'a> {
function initialize_rom (line 218) | pub fn initialize_rom(
function initialize_rtc (line 269) | pub fn initialize_rtc(
function initialize_hpet (line 288) | pub fn initialize_hpet(&mut self) {
function initialize_chipset (line 294) | pub fn initialize_chipset(
function initialize_uart (line 399) | pub fn initialize_uart(
function initialize_ps2 (line 431) | pub fn initialize_ps2(
function initialize_qemu_debug_port (line 451) | pub fn initialize_qemu_debug_port(
function initialize_qemu_pvpanic (line 465) | pub fn initialize_qemu_pvpanic(
function initialize_vsock (line 492) | pub async fn initialize_vsock(
function create_storage_backend_from_spec (line 543) | async fn create_storage_backend_from_spec(
function prepare_rot_initializer (line 699) | pub fn prepare_rot_initializer(
function initialize_storage_devices (line 795) | pub async fn initialize_storage_devices(
function initialize_network_devices (line 962) | pub async fn initialize_network_devices(
function initialize_test_devices (line 1037) | pub fn initialize_test_devices(&mut self) {
function initialize_softnpu_ports (line 1064) | pub fn initialize_softnpu_ports(
function initialize_9pfs (line 1151) | pub fn initialize_9pfs(&mut self, chipset: &RegisteredChipset) {
function generate_smbios (line 1171) | fn generate_smbios(
function generate_e820 (line 1339) | fn generate_e820(&self) -> Result<Entry, MachineInitError> {
function generate_bootorder (line 1361) | fn generate_bootorder(&self) -> Result<Option<Entry>, MachineInitError> {
function initialize_fwcfg (line 1424) | pub fn initialize_fwcfg(
function initialize_cpus (line 1486) | pub async fn initialize_cpus(&mut self) -> Result<(), MachineInitError> {
function register_guest_hv_interface (line 1550) | pub fn register_guest_hv_interface(
FILE: bin/propolis-server/src/lib/migrate/codec.rs
type ProtocolError (line 33) | pub enum ProtocolError {
method from (line 67) | fn from(value: ron::de::SpannedError) -> Self {
type Message (line 80) | pub(crate) enum Message {
type Error (line 135) | type Error = ProtocolError;
method try_into (line 136) | fn try_into(self) -> Result<tungstenite::Message, ProtocolError> {
type MessageType (line 100) | enum MessageType {
method from (line 117) | fn from(m: &Message) -> MessageType {
function get_start_end (line 168) | fn get_start_end(
type Error (line 181) | type Error = ProtocolError;
function try_into (line 182) | fn try_into(self) -> Result<Message, ProtocolError> {
function encode (line 267) | fn encode(m: Message) -> Vec<u8> {
function encode_okay (line 276) | fn encode_okay() {
function encode_error (line 282) | fn encode_error() {
function encode_serialized (line 290) | fn encode_serialized() {
function encode_empty_blob (line 298) | fn encode_empty_blob() {
function encode_blob (line 305) | fn encode_blob() {
function encode_page (line 312) | fn encode_page() {
function encode_mem_query (line 320) | fn encode_mem_query() {
function encode_mem_offer (line 328) | fn encode_mem_offer() {
function encode_mem_end (line 337) | fn encode_mem_end() {
function encode_mem_fetch (line 345) | fn encode_mem_fetch() {
function encode_mem_xfer (line 354) | fn encode_mem_xfer() {
function encode_mem_done (line 363) | fn encode_mem_done() {
function get_start_end_ok (line 374) | fn get_start_end_ok() {
function get_start_end_err (line 384) | fn get_start_end_err() {
function decode_bad_tag_fails (line 400) | fn decode_bad_tag_fails() {
function decode_nonbinary_fails (line 408) | fn decode_nonbinary_fails() {
function decode_tagless_fails (line 415) | fn decode_tagless_fails() {
function decode_error (line 422) | fn decode_error() {
function decode_blob (line 431) | fn decode_blob() {
function decode_page (line 439) | fn decode_page() {
function decode_mem_query (line 448) | fn decode_mem_query() {
function decode_mem_offer (line 458) | fn decode_mem_offer() {
function decode_mem_offer_long_bitmap (line 469) | fn decode_mem_offer_long_bitmap() {
function decode_mem_end (line 483) | fn decode_mem_end() {
function decode_mem_fetch (line 493) | fn decode_mem_fetch() {
function decode_mem_xfer (line 504) | fn decode_mem_xfer() {
function decode_mem_done (line 515) | fn decode_mem_done() {
FILE: bin/propolis-server/src/lib/migrate/destination.rs
type MigrationTargetInfo (line 44) | pub(crate) struct MigrationTargetInfo {
type DestinationProtocol (line 56) | pub(crate) trait DestinationProtocol {
method run (line 60) | async fn run<'ensure>(
method run (line 198) | async fn run<'ensure>(
function initiate (line 70) | pub(crate) async fn initiate(
function migration_start_connect (line 151) | async fn migration_start_connect(
type RonV0 (line 180) | struct RonV0<T: MigrateConn> {
function new (line 269) | fn new(
function log (line 278) | fn log(&self) -> &slog::Logger {
function update_state (line 282) | fn update_state(
function run_sync_phases (line 296) | async fn run_sync_phases(
function run_import_phases (line 309) | async fn run_import_phases(
function run_import_phase (line 334) | async fn run_import_phase(
function sync (line 366) | async fn sync(
function ram_push (line 408) | async fn ram_push(
function query_ram (line 466) | async fn query_ram(
function xfer_ram (line 507) | async fn xfer_ram(
function device_state (line 522) | async fn device_state(
function time_data (line 563) | async fn time_data(
function import_device (line 663) | fn import_device(
function ram_pull (line 760) | async fn ram_pull(
function server_state (line 774) | async fn server_state(
function finish (line 804) | async fn finish(
function read_msg (line 823) | async fn read_msg(&mut self) -> Result<codec::Message, MigrateError> {
function read_ok (line 848) | async fn read_ok(&mut self) -> Result<(), MigrateError> {
function read_page (line 858) | async fn read_page(&mut self) -> Result<Vec<u8>, MigrateError> {
function send_msg (line 865) | async fn send_msg(
function write_guest_ram (line 872) | async fn write_guest_ram(
FILE: bin/propolis-server/src/lib/migrate/memx.rs
function validate_bitmap (line 30) | pub(crate) fn validate_bitmap(start: u64, end: u64, bits: &[u8]) -> bool {
function make_mem_offer (line 55) | pub(crate) fn make_mem_offer(
function make_mem_fetch (line 64) | pub(crate) fn make_mem_fetch(
function make_mem_xfer (line 73) | pub(crate) fn make_mem_xfer(
function make_mem_offer_simple (line 88) | fn make_mem_offer_simple() {
function make_mem_xfer_short (line 97) | fn make_mem_xfer_short() {
function make_mem_fetch_too_long_fails (line 107) | fn make_mem_fetch_too_long_fails() {
FILE: bin/propolis-server/src/lib/migrate/mod.rs
type MigrateConn (line 22) | pub(crate) trait MigrateConn:
type MigrateRole (line 31) | pub enum MigrateRole {
type MigratePhase (line 38) | enum MigratePhase {
method fmt (line 51) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type MigrateError (line 70) | pub enum MigrateError {
method from (line 157) | fn from(err: tokio_tungstenite::tungstenite::Error) -> MigrateError {
method from (line 163) | fn from(err: codec::ProtocolError) -> Self {
method from (line 169) | fn from(value: MigrateStateError) -> Self {
method from (line 175) | fn from(err: MigrateError) -> Self {
type Device (line 209) | struct Device {
type DevicePayload (line 217) | struct DevicePayload {
type PageIter (line 230) | struct PageIter<'a> {
function new (line 238) | pub fn new(start: u64, end: u64, bits: &'a [u8]) -> PageIter<'a> {
type Item (line 245) | type Item = u64;
method next (line 246) | fn next(&mut self) -> Option<Self::Item> {
function migrate_phase_begin (line 262) | fn migrate_phase_begin(step_desc: &str) {}
function migrate_phase_end (line 263) | fn migrate_phase_end(step_desc: &str) {}
function migrate_xfer_ram_region (line 264) | fn migrate_xfer_ram_region(pages: u64, size: u64, paused: u8) {}
function migrate_xfer_ram_page (line 265) | fn migrate_xfer_ram_page(addr: u64, size: u64) {}
function migrate_time_data_before (line 266) | fn migrate_time_data_before(
function migrate_time_data_after (line 272) | fn migrate_time_data_after(
FILE: bin/propolis-server/src/lib/migrate/preamble.rs
type Preamble (line 16) | pub(crate) struct Preamble {
method new (line 22) | pub fn new(
method amend_spec (line 36) | pub fn amend_spec(
FILE: bin/propolis-server/src/lib/migrate/protocol.rs
type Protocol (line 25) | pub enum Protocol {
method offer_string (line 32) | pub fn offer_string(&self) -> String {
type Error (line 38) | type Error = anyhow::Error;
method try_from (line 40) | fn try_from(value: ProtocolParts) -> Result<Self, Self::Error> {
constant PREFIX (line 58) | const PREFIX: &str = "propolis-migrate-";
constant ENCODING_VERSION_SEPARATOR (line 62) | const ENCODING_VERSION_SEPARATOR: char = '/';
constant DELIMITER (line 65) | const DELIMITER: char = ',';
type ProtocolParseError (line 69) | pub enum ProtocolParseError {
type Encoding (line 88) | enum Encoding {
method fmt (line 94) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Err (line 106) | type Err = ProtocolParseError;
method from_str (line 108) | fn from_str(s: &str) -> Result<Self, Self::Err> {
type ProtocolParts (line 123) | struct ProtocolParts {
method offer_string (line 129) | fn offer_string(&self) -> String {
method from (line 138) | fn from(value: Protocol) -> Self {
type Err (line 148) | type Err = ProtocolParseError;
method from_str (line 150) | fn from_str(s: &str) -> Result<Self, Self::Err> {
function make_protocol_offers_from_parts (line 177) | fn make_protocol_offers_from_parts<
function make_protocol_offer (line 194) | pub(super) fn make_protocol_offer() -> String {
function parse_protocol_offer (line 201) | fn parse_protocol_offer(
function select_compatible_protocol (line 223) | fn select_compatible_protocol(
function select_protocol_from_offer (line 246) | pub(super) fn select_protocol_from_offer(
constant PROTOCOLS_V1 (line 263) | const PROTOCOLS_V1: [ProtocolParts; 3] = [
constant PROTOCOLS_V2 (line 269) | const PROTOCOLS_V2: [ProtocolParts; 5] = [
function negotiation_selects_newest_version (line 278) | fn negotiation_selects_newest_version() {
function parse_sorts_offered_protocols (line 291) | fn parse_sorts_offered_protocols() {
function offer_string_round_trip (line 306) | fn offer_string_round_trip() {
function parse_failures (line 315) | fn parse_failures() {
FILE: bin/propolis-server/src/lib/migrate/source.rs
type RamOfferDiscipline (line 112) | enum RamOfferDiscipline {
type SourceProtocol (line 126) | pub(crate) trait SourceProtocol {
method run (line 133) | async fn run(
method run (line 318) | async fn run(
function initiate (line 144) | pub(crate) async fn initiate<T: MigrateConn>(
type PersistentState (line 238) | pub(crate) struct PersistentState {
type RonV0 (line 247) | struct RonV0<T: MigrateConn> {
constant PAGE_BITMAP_SIZE (line 279) | const PAGE_BITMAP_SIZE: usize = 4096;
type PageBitmap (line 280) | type PageBitmap = [u8; PAGE_BITMAP_SIZE];
function new (line 283) | async fn new(
type RonV0Runner (line 339) | struct RonV0Runner<'vm, T: MigrateConn> {
function log (line 351) | fn log(&self) -> &slog::Logger {
function update_state (line 355) | fn update_state(&mut self, state: MigrationState) {
function pause_vm (line 365) | async fn pause_vm(&mut self) {
function resume_vm (line 371) | async fn resume_vm(&mut self) {
function run_phase (line 377) | async fn run_phase(
function run (line 401) | async fn run(&mut self) -> Result<(), MigrateError> {
function sync (line 468) | async fn sync(&mut self) -> Result<(), MigrateError> {
function ram_push (line 481) | async fn ram_push(
function offer_ram (line 563) | async fn offer_ram(
function xfer_ram (line 652) | async fn xfer_ram(
function pause (line 672) | async fn pause(&mut self) -> Result<(), MigrateError> {
function device_state (line 681) | async fn device_state(&mut self) -> Result<(), MigrateError> {
function time_data (line 747) | async fn time_data(&mut self) -> Result<(), MigrateError> {
function ram_pull (line 765) | async fn ram_pull(&mut self) -> Result<(), MigrateError> {
function server_state (line 777) | async fn server_state(&mut self) -> Result<(), MigrateError> {
function finish (line 796) | async fn finish(&mut self) -> Result<(), MigrateError> {
function read_msg (line 839) | async fn read_msg(&mut self) -> Result<codec::Message, MigrateError> {
function read_ok (line 867) | async fn read_ok(&mut self) -> Result<(), MigrateError> {
function read_mem_query (line 877) | async fn read_mem_query(&mut self) -> Result<Range<u64>, MigrateError> {
function send_msg (line 894) | async fn send_msg(
function vmm_ram_bounds (line 901) | async fn vmm_ram_bounds(
function track_dirty (line 909) | async fn track_dirty(
function read_guest_mem (line 922) | async fn read_guest_mem(
FILE: bin/propolis-server/src/lib/serial/history_buffer.rs
type Error (line 17) | pub enum Error {
constant TTY_BUFFER_SIZE (line 28) | const TTY_BUFFER_SIZE: usize = 1024 * 1024;
constant DEFAULT_MAX_LENGTH (line 29) | const DEFAULT_MAX_LENGTH: isize = 16 * 1024;
type HistoryBuffer (line 35) | pub(crate) struct HistoryBuffer {
method new (line 100) | pub fn new(buffer_size: usize) -> Self {
method consume (line 110) | pub fn consume(&mut self, data: &[u8]) {
method contents_iter (line 126) | pub fn contents_iter(
method contents_vec (line 181) | pub fn contents_vec(
method offsets_from_start_and_end (line 194) | fn offsets_from_start_and_end(
method bytes_from_start (line 221) | pub fn bytes_from_start(&self) -> usize {
type SerialHistoryOffset (line 43) | pub(crate) enum SerialHistoryOffset {
type Error (line 51) | type Error = ();
method try_from (line 52) | fn try_from(req: &InstanceSerialConsoleStreamRequest) -> Result<Self, ...
type Error (line 68) | type Error = HttpError;
method try_from (line 70) | fn try_from(
method default (line 94) | fn default() -> Self {
function sugar (line 232) | fn sugar(
function test_continuous_buffer_range_abstraction (line 243) | fn test_continuous_buffer_range_abstraction() {
FILE: bin/propolis-server/src/lib/serial/mod.rs
function serial_close_recv (line 36) | fn serial_close_recv() {}
function serial_new_ws (line 37) | fn serial_new_ws() {}
function serial_uart_write (line 38) | fn serial_uart_write(n: usize) {}
function serial_uart_out (line 39) | fn serial_uart_out() {}
function serial_uart_read (line 40) | fn serial_uart_read(n: usize) {}
function serial_inject_uart (line 41) | fn serial_inject_uart() {}
function serial_ws_recv (line 42) | fn serial_ws_recv() {}
function serial_buffer_size (line 43) | fn serial_buffer_size(n: usize) {}
type SerialTaskError (line 48) | pub enum SerialTaskError {
type SerialTaskControlMessage (line 68) | pub enum SerialTaskControlMessage {
type SerialTask (line 73) | pub struct SerialTask {
function instance_serial_task (line 84) | pub async fn instance_serial_task<Device: Sink + Source>(
type Serial (line 288) | pub struct Serial<Device: Sink + Source> {
function new (line 309) | pub fn new(
function read_source (line 330) | pub async fn read_source(&self, buf: &mut [u8]) -> Option<usize> {
function write_sink (line 337) | pub async fn write_sink(&self, buf: &[u8]) -> Option<usize> {
function history_vec (line 342) | pub(crate) async fn history_vec(
function set_task_control_sender (line 355) | pub(crate) async fn set_task_control_sender(
function export_history (line 362) | pub(crate) async fn export_history(
function import (line 382) | pub(crate) async fn import(
method drop (line 398) | fn drop(&mut self) {
FILE: bin/propolis-server/src/lib/server.rs
type MetricsEndpointConfig (line 70) | pub struct MetricsEndpointConfig {
type StaticConfig (line 86) | pub struct StaticConfig {
type DropshotEndpointContext (line 106) | pub struct DropshotEndpointContext {
method new (line 115) | pub fn new(
type LazyNexusClientInner (line 149) | struct LazyNexusClientInner {
type LazyNexusClient (line 154) | pub struct LazyNexusClient {
method new (line 159) | pub fn new(log: Logger, addr: Ipv6Addr) -> Result<Self, ResolveError> {
method get_ip (line 168) | pub async fn get_ip(&self) -> Result<SocketAddrV6, ResolveError> {
method get (line 172) | pub async fn get(&self) -> Result<NexusClient, ResolveError> {
function find_local_nexus_client (line 184) | async fn find_local_nexus_client(
function instance_get (line 215) | async fn instance_get(
type PropolisServerImpl (line 222) | enum PropolisServerImpl {}
type Context (line 225) | type Context = Arc<DropshotEndpointContext>;
method instance_ensure (line 227) | async fn instance_ensure(
method instance_spec_get (line 315) | async fn instance_spec_get(
method instance_get (line 321) | async fn instance_get(
method instance_state_monitor (line 334) | async fn instance_state_monitor(
method instance_state_put (line 365) | async fn instance_state_put(
method instance_serial_history_get (line 408) | async fn instance_serial_history_get(
method instance_serial (line 432) | async fn instance_serial(
method instance_vnc (line 482) | async fn instance_vnc(
method instance_migrate_start (line 512) | async fn instance_migrate_start(
method instance_migrate_status (line 523) | async fn instance_migrate_status(
method instance_issue_crucible_snapshot_request (line 534) | async fn instance_issue_crucible_snapshot_request(
method disk_volume_status_v1 (line 561) | async fn disk_volume_status_v1(
method disk_volume_status (line 589) | async fn disk_volume_status(
method instance_issue_crucible_vcr_request (line 617) | async fn instance_issue_crucible_vcr_request(
method instance_issue_nmi (line 664) | async fn instance_issue_nmi(
function api (line 680) | pub fn api() -> ApiDescription<Arc<DropshotEndpointContext>> {
function not_created_error (line 687) | fn not_created_error() -> HttpError {
FILE: bin/propolis-server/src/lib/spec/api_spec_v0.rs
type ApiSpecError (line 37) | pub(crate) enum ApiSpecError {
function from (line 56) | fn from(val: Spec) -> Self {
type Error (line 242) | type Error = ApiSpecError;
method try_from (line 244) | fn try_from(
function v1_to_spec_builder (line 254) | pub(crate) fn v1_to_spec_builder(
FILE: bin/propolis-server/src/lib/spec/builder.rs
type SpecBuilderError (line 38) | pub(crate) enum SpecBuilderError {
type SpecBuilder (line 75) | pub(crate) struct SpecBuilder {
method with_instance_spec_board (line 83) | pub(super) fn with_instance_spec_board(
method add_boot_order (line 116) | pub fn add_boot_order(
method register_pci_device (line 149) | fn register_pci_device(
method add_storage_device (line 162) | pub(super) fn add_storage_device(
method add_network_device (line 192) | pub(super) fn add_network_device(
method add_pci_bridge (line 222) | pub fn add_pci_bridge(
method add_serial_port (line 239) | pub fn add_serial_port(
method add_pvpanic_device (line 259) | pub fn add_pvpanic_device(
method add_vsock_device (line 276) | pub fn add_vsock_device(
method add_migration_failure_device (line 296) | pub fn add_migration_failure_device(
method set_softnpu_pci_port (line 314) | pub fn set_softnpu_pci_port(
method set_softnpu_p9 (line 338) | pub fn set_softnpu_p9(
method set_p9fs (line 348) | pub fn set_p9fs(&mut self, p9fs: P9fs) -> Result<&Self, SpecBuilderErr...
method add_softnpu_port (line 355) | pub fn add_softnpu_port(
method finish (line 382) | pub fn finish(self) -> super::Spec {
function test_builder (line 400) | fn test_builder() -> SpecBuilder {
function duplicate_pci_slot (line 415) | fn duplicate_pci_slot() {
function duplicate_serial_port (line 451) | fn duplicate_serial_port() {
function device_with_same_name_as_backend (line 486) | fn device_with_same_name_as_backend() {
FILE: bin/propolis-server/src/lib/spec/mod.rs
method from (line 57) | fn from(val: Spec) -> Self {
type ComponentTypeMismatch (line 105) | pub struct ComponentTypeMismatch;
type Spec (line 117) | pub(crate) struct Spec {
type Error (line 76) | type Error = ApiSpecError;
method try_from (line 78) | fn try_from(value: InstanceSpec) -> Result<Self, Self::Error> {
type Board (line 158) | pub(crate) struct Board {
method default (line 166) | fn default() -> Self {
type BootSettings (line 177) | pub(crate) struct BootSettings {
type BootOrderEntry (line 183) | pub(crate) struct BootOrderEntry {
method from (line 191) | fn from(
function from (line 201) | fn from(value: BootOrderEntry) -> Self {
type StorageDevice (line 208) | pub enum StorageDevice {
method kind (line 214) | pub fn kind(&self) -> &'static str {
method pci_path (line 221) | pub fn pci_path(&self) -> PciPath {
method backend_id (line 228) | pub fn backend_id(&self) -> &SpecKey {
type Error (line 246) | type Error = ComponentTypeMismatch;
method try_from (line 248) | fn try_from(
function from (line 237) | fn from(value: StorageDevice) -> Self {
type StorageBackend (line 261) | pub enum StorageBackend {
method kind (line 268) | pub fn kind(&self) -> &'static str {
method read_only (line 276) | pub fn read_only(&self) -> bool {
type Error (line 296) | type Error = ComponentTypeMismatch;
method try_from (line 298) | fn try_from(
function from (line 286) | fn from(value: StorageBackend) -> Self {
type Disk (line 317) | pub struct Disk {
type Nic (line 323) | pub struct Nic {
type SerialPortDevice (line 330) | pub enum SerialPortDevice {
method fmt (line 338) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type SerialPort (line 353) | pub struct SerialPort {
type QemuPvpanic (line 359) | pub struct QemuPvpanic {
type VirtioSocket (line 366) | pub struct VirtioSocket {
type MigrationFailure (line 373) | pub struct MigrationFailure {
type SoftNpuPort (line 380) | pub struct SoftNpuPort {
type SoftNpu (line 388) | pub struct SoftNpu {
FILE: bin/propolis-server/src/lib/stats/mod.rs
constant OXIMETER_COLLECTION_INTERVAL (line 43) | const OXIMETER_COLLECTION_INTERVAL: tokio::time::Duration =
constant NETWORK_INTERFACE_SAMPLE_INTERVAL (line 51) | const NETWORK_INTERFACE_SAMPLE_INTERVAL: std::time::Duration =
constant VCPU_KSTAT_INTERVAL (line 56) | const VCPU_KSTAT_INTERVAL: std::time::Duration =
constant SAMPLE_BUFFER (line 62) | const SAMPLE_BUFFER: u32 = 64;
constant KSTAT_LIMIT_PER_VCPU (line 71) | const KSTAT_LIMIT_PER_VCPU: u32 =
type ServerStatsInner (line 76) | struct ServerStatsInner {
method new (line 86) | pub fn new(virtual_machine: VirtualMachine) -> Self {
type ServerStats (line 100) | pub struct ServerStats {
method new (line 106) | pub fn new(vm: VirtualMachine) -> Self {
method count_reset (line 111) | pub fn count_reset(&self) {
method produce (line 117) | fn produce(
function start_oximeter_server (line 147) | pub fn start_oximeter_server(
function create_kstat_sampler (line 188) | pub(crate) fn create_kstat_sampler(
function track_vcpu_kstats (line 214) | pub(crate) async fn track_vcpu_kstats(
function track_vcpu_kstats (line 224) | pub(crate) async fn track_vcpu_kstats(
function track_network_interface_kstats (line 244) | pub(crate) async fn track_network_interface_kstats(
function track_network_interface_kstats (line 258) | pub(crate) async fn track_network_interface_kstats(
type KstatList (line 303) | pub(crate) type KstatList<'a, 'k> =
type KstatTarget (line 306) | pub(crate) trait KstatTarget:
method interested (line 310) | fn interested(&self, kstat: &Kstat<'_>) -> bool;
method to_samples (line 313) | fn to_samples(
type Data (line 320) | pub(crate) enum Data<'a> {
type NamedData (line 326) | pub(crate) enum NamedData<'a> {
type Kstat (line 333) | pub(crate) struct Kstat<'a> {
type Named (line 341) | pub(crate) struct Named<'a> {
type ConvertNamedData (line 347) | pub(crate) trait ConvertNamedData {
method as_i32 (line 348) | fn as_i32(&self) -> Result<i32, Error>;
method as_u32 (line 349) | fn as_u32(&self) -> Result<u32, Error>;
method as_i64 (line 350) | fn as_i64(&self) -> Result<i64, Error>;
method as_u64 (line 351) | fn as_u64(&self) -> Result<u64, Error>;
method as_i32 (line 355) | fn as_i32(&self) -> Result<i32, Error> {
method as_u32 (line 359) | fn as_u32(&self) -> Result<u32, Error> {
method as_i64 (line 367) | fn as_i64(&self) -> Result<i64, Error> {
method as_u64 (line 371) | fn as_u64(&self) -> Result<u64, Error> {
type Error (line 381) | pub(crate) enum Error {
function hrtime_to_utc (line 392) | pub(crate) fn hrtime_to_utc(_: i64) -> Result<DateTime<Utc>, Error> {
FILE: bin/propolis-server/src/lib/stats/network_interface.rs
constant KSTAT_RX_BYTES (line 35) | const KSTAT_RX_BYTES: &str = "rx_bytes";
constant KSTAT_TX_BYTES (line 36) | const KSTAT_TX_BYTES: &str = "tx_bytes";
constant KSTAT_RX_PACKETS (line 37) | const KSTAT_RX_PACKETS: &str = "rx_packets";
constant KSTAT_TX_PACKETS (line 38) | const KSTAT_TX_PACKETS: &str = "tx_packets";
constant KSTAT_RX_DROPS (line 39) | const KSTAT_RX_DROPS: &str = "rx_drops";
constant KSTAT_RX_ERRORS (line 40) | const KSTAT_RX_ERRORS: &str = "rx_errors";
constant KSTAT_TX_ERRORS (line 41) | const KSTAT_TX_ERRORS: &str = "tx_errors";
constant KSTAT_FIELDS (line 45) | const KSTAT_FIELDS: &[&str] = &[
constant KSTAT_MODULE_NAME (line 57) | const KSTAT_MODULE_NAME: &str = "viona";
constant KSTAT_NAME (line 60) | const KSTAT_NAME: &str = "viona_stat";
function extract_nic_kstats (line 63) | fn extract_nic_kstats(
type InstanceNetworkInterfaces (line 133) | pub(crate) struct InstanceNetworkInterfaces {
method new (line 153) | pub(crate) fn new(
method interested (line 173) | fn interested(&self, kstat: &Kstat<'_>) -> bool {
method to_samples (line 181) | fn to_samples(
method name (line 233) | fn name(&self) -> &'static str {
method field_names (line 236) | fn field_names(&self) -> &'static [&'static str] {
method field_types (line 240) | fn field_types(&self) -> Vec<FieldType> {
method field_values (line 244) | fn field_values(&self) -> Vec<FieldValue> {
function test_network_interface (line 258) | fn test_network_interface() -> InstanceNetworkInterface {
function test_kstat_interested (line 277) | fn test_kstat_interested() {
function test_kstat_to_samples (line 319) | fn test_kstat_to_samples() {
FILE: bin/propolis-server/src/lib/stats/pvpanic.rs
type PvpanicProducer (line 18) | pub struct PvpanicProducer {
method new (line 31) | pub fn new(
method produce (line 48) | fn produce(
FILE: bin/propolis-server/src/lib/stats/virtual_disk.rs
type VirtualDiskStats (line 40) | struct VirtualDiskStats {
method on_completion (line 70) | fn on_completion(&mut self, sample: BlockSample) {
method on_read_completion (line 87) | fn on_read_completion(
method on_write_completion (line 110) | fn on_write_completion(
method on_flush_completion (line 133) | fn on_flush_completion(
constant N_IO_KINDS (line 155) | const N_IO_KINDS: usize = 3;
constant READ_INDEX (line 158) | const READ_INDEX: usize = 0;
constant WRITE_INDEX (line 159) | const WRITE_INDEX: usize = 1;
constant FLUSH_INDEX (line 160) | const FLUSH_INDEX: usize = 2;
constant READ_KIND (line 163) | const READ_KIND: &str = "read";
constant WRITE_KIND (line 164) | const WRITE_KIND: &str = "write";
constant FLUSH_KIND (line 165) | const FLUSH_KIND: &str = "flush";
constant N_FAILURE_KINDS (line 168) | const N_FAILURE_KINDS: usize = 3;
constant FAILURE_INDEX (line 171) | const FAILURE_INDEX: usize = 0;
constant READONLY_INDEX (line 172) | const READONLY_INDEX: usize = 1;
constant UNSUPPORTED_INDEX (line 173) | const UNSUPPORTED_INDEX: usize = 2;
constant FAILURE_KIND (line 176) | const FAILURE_KIND: &str = "failed";
constant READONLY_KIND (line 177) | const READONLY_KIND: &str = "read-only";
constant UNSUPPORTED_KIND (line 178) | const UNSUPPORTED_KIND: &str = "unsupported";
constant LATENCY_POWERS (line 182) | const LATENCY_POWERS: (u16, u16) = (3, 10);
constant SIZE_POWERS (line 187) | const SIZE_POWERS: (u16, u16) = (9, 30);
constant MAX_BUFFERED_SAMPLES (line 193) | const MAX_BUFFERED_SAMPLES: usize = 512;
type BlockMetrics (line 206) | pub(crate) struct BlockMetrics {
method new (line 211) | pub fn new(disk: VirtualDisk, max_queues: NonZeroUsize) -> Arc<Self> {
method producer (line 272) | pub(crate) fn producer(self: &Arc<Self>) -> VirtualDiskProducer {
method latency_histogram (line 280) | fn latency_histogram() -> Histogram<u64> {
method size_histogram (line 289) | fn size_histogram() -> Histogram<u64> {
method consolidate_one (line 297) | fn consolidate_one(&self, idx: usize) {
method consolidate_all (line 304) | fn consolidate_all(&self) {
method request_completed (line 326) | fn request_completed(
type BlockSample (line 316) | struct BlockSample {
type VirtualDiskProducer (line 323) | pub struct VirtualDiskProducer(Arc<BlockMetrics>);
method produce (line 351) | fn produce(
function test_latency_histogram (line 403) | fn test_latency_histogram() {
function test_size_histogram (line 416) | fn test_size_histogram() {
FILE: bin/propolis-server/src/lib/stats/virtual_machine.rs
type VirtualMachine (line 43) | pub struct VirtualMachine {
method new (line 65) | pub fn new(
method name (line 87) | fn name(&self) -> &'static str {
method field_names (line 91) | fn field_names(&self) -> &'static [&'static str] {
method field_types (line 95) | fn field_types(&self) -> Vec<FieldType> {
method field_values (line 99) | fn field_values(&self) -> Vec<FieldValue> {
function kstat_microstate_to_state_name (line 112) | fn kstat_microstate_to_state_name(ustate: &str) -> Option<&'static str> {
constant OXIMETER_EMULATION_STATE (line 123) | const OXIMETER_EMULATION_STATE: &str = "emulation";
constant OXIMETER_RUN_STATE (line 124) | const OXIMETER_RUN_STATE: &str = "run";
constant OXIMETER_IDLE_STATE (line 125) | const OXIMETER_IDLE_STATE: &str = "idle";
constant OXIMETER_WAITING_STATE (line 126) | const OXIMETER_WAITING_STATE: &str = "waiting";
constant OXIMETER_STATES (line 127) | const OXIMETER_STATES: [&str; 4] = [
constant N_VCPU_MICROSTATES (line 139) | pub const N_VCPU_MICROSTATES: u32 = OXIMETER_STATES.len() as _;
constant VMM_KSTAT_MODULE_NAME (line 142) | const VMM_KSTAT_MODULE_NAME: &str = "vmm";
constant VM_KSTAT_NAME (line 145) | const VM_KSTAT_NAME: &str = "vm";
constant VM_NAME_KSTAT (line 149) | const VM_NAME_KSTAT: &str = "vm_name";
constant VCPU_KSTAT_PREFIX (line 152) | const VCPU_KSTAT_PREFIX: &str = "vcpu";
method interested (line 172) | fn interested(&self, kstat: &Kstat<'_>) -> bool {
method to_samples (line 176) | fn to_samples(
function kstat_instance_from_instance_id (line 220) | fn kstat_instance_from_instance_id(
function produce_vcpu_usage (line 252) | fn produce_vcpu_usage<'a>(
function test_virtual_machine (line 335) | fn test_virtual_machine() -> VirtualMachine {
function test_usage (line 360) | fn test_usage() -> VcpuUsage {
function test_kstat_instance_from_instance_id (line 369) | fn test_kstat_instance_from_instance_id() {
function vcpu_state_kstats (line 398) | fn vcpu_state_kstats<'a>() -> (Kstat<'a>, Data<'a>) {
function test_produce_vcpu_usage (line 416) | fn test_produce_vcpu_usage() {
function test_consistent_kstat_to_oximeter_microstate_mapping (line 457) | fn test_consistent_kstat_to_oximeter_microstate_mapping() {
FILE: bin/propolis-server/src/lib/vcpu_tasks.rs
type VcpuTaskError (line 22) | pub enum VcpuTaskError {
type VcpuTasks (line 29) | pub struct VcpuTasks {
method new (line 43) | pub(crate) fn new(
method vcpu_loop (line 97) | fn vcpu_loop(
type VcpuTaskController (line 35) | pub(crate) trait VcpuTaskController: Send + Sync + 'static {
method new_generation (line 36) | fn new_generation(&self);
method pause_all (line 37) | fn pause_all(&mut self);
method resume_all (line 38) | fn resume_all(&mut self);
method exit_all (line 39) | fn exit_all(&mut self);
method pause_all (line 304) | fn pause_all(&mut self) {
method new_generation (line 310) | fn new_generation(&self) {
method resume_all (line 314) | fn resume_all(&mut self) {
method exit_all (line 320) | fn exit_all(&mut self) {
FILE: bin/propolis-server/src/lib/vm/active.rs
type ActiveVm (line 24) | pub(crate) struct ActiveVm {
method objects (line 53) | pub(crate) fn objects(&self) -> &Arc<VmObjects> {
method put_state (line 58) | pub(crate) fn put_state(
method request_migration_out (line 77) | pub(crate) async fn request_migration_out(
method reconfigure_crucible_volume (line 100) | pub(crate) fn reconfigure_crucible_volume(
method services (line 118) | pub(crate) fn services(&self) -> &VmServices {
FILE: bin/propolis-server/src/lib/vm/ensure.rs
type VmInitializationMethod (line 131) | pub(crate) enum VmInitializationMethod {
type VmEnsureRequest (line 136) | pub(crate) struct VmEnsureRequest {
method is_migration (line 142) | pub(crate) fn is_migration(&self) -> bool {
method migration_info (line 146) | pub(crate) fn migration_info(&self) -> Option<&MigrationTargetInfo> {
method spec (line 153) | pub(crate) fn spec(&self) -> Option<&Spec> {
type VmEnsureNotStarted (line 163) | pub(crate) struct VmEnsureNotStarted<'a> {
function new (line 179) | pub(super) fn new(
function state_publisher (line 197) | pub(crate) fn state_publisher(&mut self) -> &mut StatePublisher {
function migration_info (line 201) | pub(crate) fn migration_info(&self) -> Option<&MigrationTargetInfo> {
function create_objects_from_request (line 205) | pub(crate) async fn create_objects_from_request(
function create_objects_from_spec (line 219) | pub(crate) async fn create_objects_from_spec(
function create_objects (line 228) | async fn create_objects(
function fail (line 329) | pub(crate) async fn fail(self, reason: anyhow::Error) -> anyhow::Error {
type VmEnsureObjectsCreated (line 351) | pub(crate) struct VmEnsureObjectsCreated<'a> {
function prepare_for_migration (line 372) | pub(crate) async fn prepare_for_migration(&mut self) {
function ensure_active (line 383) | pub(crate) async fn ensure_active(self) -> VmEnsureActive<'a> {
type VmEnsureActive (line 430) | pub(crate) struct VmEnsureActive<'a> {
type VmEnsureActiveOutput (line 439) | pub(super) struct VmEnsureActiveOutput {
function vm_objects (line 446) | pub(crate) fn vm_objects(&self) -> &Arc<VmObjects> {
function state_publisher (line 450) | pub(crate) fn state_publisher(&mut self) -> &mut StatePublisher {
function fail (line 454) | pub(crate) async fn fail(mut self) {
function into_inner (line 474) | pub(super) fn into_inner(self) -> VmEnsureActiveOutput {
function initialize_vm_objects (line 483) | async fn initialize_vm_objects(
function initialize_kstat_sampler (line 701) | fn initialize_kstat_sampler(
FILE: bin/propolis-server/src/lib/vm/guest_event.rs
type GuestEvent (line 16) | pub(super) enum GuestEvent {
type GuestEventQueue (line 31) | pub(super) struct GuestEventQueue {
method enqueue (line 55) | pub(super) fn enqueue(&mut self, event: GuestEvent) -> bool {
method pop_front (line 64) | pub(super) fn pop_front(&mut self) -> Option<GuestEvent> {
type VcpuEventHandler (line 36) | pub(crate) trait VcpuEventHandler: Send + Sync {
method suspend_halt_event (line 37) | fn suspend_halt_event(&self, when: Duration);
method suspend_reset_event (line 38) | fn suspend_reset_event(&self, when: Duration);
method suspend_triple_fault_event (line 39) | fn suspend_triple_fault_event(&self, vcpu_id: i32, when: Duration);
method unhandled_vm_exit (line 40) | fn unhandled_vm_exit(
method io_error_event (line 45) | fn io_error_event(&self, vcpu_id: i32, error: std::io::Error);
type ChipsetEventHandler (line 49) | pub(crate) trait ChipsetEventHandler: Send + Sync {
method chipset_halt (line 50) | fn chipset_halt(&self);
method chipset_reset (line 51) | fn chipset_reset(&self);
FILE: bin/propolis-server/src/lib/vm/mod.rs
type DeviceMap (line 116) | pub(crate) type DeviceMap =
type NetworkInterfaceIds (line 121) | pub(crate) type NetworkInterfaceIds = Vec<(uuid::Uuid, KstatInstanceId)>;
type BlockBackendMap (line 124) | pub(crate) type BlockBackendMap =
type CrucibleBackendMap (line 128) | pub(crate) type CrucibleBackendMap =
type InstanceStateTx (line 133) | type InstanceStateTx = watch::Sender<InstanceStateMonitorResponse>;
type InstanceStateRx (line 137) | type InstanceStateRx = watch::Receiver<InstanceStateMonitorResponse>;
type CrucibleReplaceResult (line 141) | pub(crate) type CrucibleReplaceResult =
type CrucibleReplaceResultTx (line 146) | pub(crate) type CrucibleReplaceResultTx =
type KstatInstanceId (line 151) | type KstatInstanceId = u32;
type InstanceEnsureResponseTx (line 155) | type InstanceEnsureResponseTx =
constant VMM_MIN_RT_THREADS (line 160) | const VMM_MIN_RT_THREADS: usize = 8;
constant VMM_BASE_RT_THREADS (line 165) | const VMM_BASE_RT_THREADS: usize = 4;
type VmError (line 169) | pub(crate) enum VmError {
type Vm (line 190) | pub(crate) struct Vm {
method new (line 319) | pub fn new(log: &slog::Logger) -> Arc<Self> {
method active_vm (line 327) | pub(super) async fn active_vm(
method get (line 347) | pub(super) async fn get(&self) -> Option<InstanceSpecGetResponse> {
method state_watcher (line 389) | pub(super) async fn state_watcher(&self) -> Option<InstanceStateRx> {
method make_active (line 409) | async fn make_active(
method vm_init_failed (line 448) | async fn vm_init_failed(&self) {
method set_rundown (line 469) | async fn set_rundown(&self) {
method complete_rundown (line 505) | async fn complete_rundown(&self) {
method ensure (line 545) | pub(crate) async fn ensure(
type VmInner (line 204) | struct VmInner {
type MaybeSpec (line 214) | enum MaybeSpec {
method from (line 223) | fn from(value: MaybeSpec) -> Self {
type VmDescription (line 234) | struct VmDescription {
type VmState (line 248) | enum VmState {
method fmt (line 268) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type EnsureOptions (line 284) | pub(super) struct EnsureOptions {
FILE: bin/propolis-server/src/lib/vm/objects.rs
type VmObjects (line 30) | pub(crate) struct VmObjects {
method new (line 98) | pub(super) fn new(
method lock_shared (line 109) | pub(crate) async fn lock_shared(&self) -> VmObjectsShared<'_> {
method lock_exclusive (line 115) | pub(crate) async fn lock_exclusive(&self) -> VmObjectsExclusive<'_> {
type InputVmObjects (line 45) | pub(super) struct InputVmObjects {
type VmObjectsLocked (line 59) | pub(crate) struct VmObjectsLocked {
method new (line 122) | fn new(log: &slog::Logger, input: InputVmObjects) -> Self {
method instance_spec (line 139) | pub(crate) fn instance_spec(&self) -> &Spec {
method instance_spec_mut (line 144) | pub(crate) fn instance_spec_mut(&mut self) -> &mut Spec {
method machine (line 149) | pub(crate) fn machine(&self) -> &Machine {
method vmm_hdl (line 154) | pub(crate) fn vmm_hdl(&self) -> &Arc<VmmHdl> {
method access_mem (line 160) | pub(crate) fn access_mem(
method device_by_id (line 169) | pub(crate) fn device_by_id(
method crucible_backends (line 177) | pub(crate) fn crucible_backends(&self) -> &CrucibleBackendMap {
method com1 (line 183) | pub(crate) fn com1(&self) -> &Arc<Serial<LpcUart>> {
method framebuffer (line 188) | pub(crate) fn framebuffer(&self) -> &Option<Arc<RamFb>> {
method ps2ctrl (line 193) | pub(crate) fn ps2ctrl(&self) -> &Arc<PS2Ctrl> {
method device_map (line 197) | pub(crate) fn device_map(&self) -> &DeviceMap {
method block_backend_map (line 201) | pub(crate) fn block_backend_map(&self) -> &BlockBackendMap {
method for_each_device (line 207) | pub(crate) fn for_each_device(
method for_each_device_fallible (line 219) | pub(crate) fn for_each_device_fallible<E>(
method pause_kernel_vm (line 239) | pub(super) fn pause_kernel_vm(&self) {
method resume_kernel_vm (line 245) | pub(super) fn resume_kernel_vm(&self) {
method reset_devices_and_machine (line 251) | pub(super) fn reset_devices_and_machine(&self) {
method pause (line 261) | pub(crate) async fn pause(&mut self) {
method resume (line 271) | pub(crate) fn resume(&mut self) {
method resume_vcpus (line 286) | pub(crate) fn resume_vcpus(&mut self) {
method halt (line 291) | pub(super) async fn halt(&mut self) {
method reset_vcpus (line 297) | pub(super) fn reset_vcpus(&self) {
method reboot (line 304) | pub(super) async fn reboot(&mut self) {
method pause_devices (line 324) | async fn pause_devices(&self) {
method resume_devices (line 371) | fn resume_devices(&self) {
method halt_devices (line 380) | async fn halt_devices(&mut self) {
method reset_vcpu_state (line 402) | fn reset_vcpu_state(&self) {
method drop (line 421) | fn drop(&mut self) {
type VmObjectsShared (line 436) | pub(crate) struct VmObjectsShared<'o>(RwLockReadGuard<'o, VmObjectsLocke...
type VmObjectsExclusive (line 439) | pub(crate) struct VmObjectsExclusive<'o>(RwLockWriteGuard<'o, VmObjectsL...
type Target (line 442) | type Target = VmObjectsLocked;
method deref (line 444) | fn deref(&self) -> &Self::Target {
type Target (line 450) | type Target = VmObjectsLocked;
method deref (line 452) | fn deref(&self) -> &Self::Target {
method deref_mut (line 458) | fn deref_mut(&mut self) -> &mut Self::Target {
FILE: bin/propolis-server/src/lib/vm/request_queue.rs
type WebsocketConnection (line 31) | pub(crate) struct WebsocketConnection(Option<dropshot::WebsocketConnecti...
method from (line 34) | fn from(value: dropshot::WebsocketConnection) -> Self {
method into_inner (line 41) | pub(crate) fn into_inner(self) -> dropshot::WebsocketConnection {
type StateChangeRequest (line 50) | pub(crate) enum StateChangeRequest {
method fmt (line 69) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type ComponentChangeRequest (line 88) | pub enum ComponentChangeRequest {
method fmt (line 105) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type ExternalRequest (line 118) | pub(crate) enum ExternalRequest {
method start (line 128) | pub const fn start() -> Self {
method stop (line 133) | pub const fn stop() -> Self {
method reboot (line 138) | pub const fn reboot() -> Self {
method migrate_as_source (line 144) | pub fn migrate_as_source(
method reconfigure_crucible_volume (line 156) | pub fn reconfigure_crucible_volume(
method is_stop (line 168) | fn is_stop(&self) -> bool {
method assert_start (line 615) | fn assert_start(&self) {
method assert_stop (line 623) | fn assert_stop(&self) {
method assert_reboot (line 628) | fn assert_reboot(&self) {
method assert_migrate_as_source (line 636) | fn assert_migrate_as_source(&self) {
method assert_reconfigure_crucible (line 647) | fn assert_reconfigure_crucible(&self) {
method from (line 842) | fn from(value: RequestKind) -> Self {
type RequestDeniedReason (line 176) | pub(crate) enum RequestDeniedReason {
type CompletedRequest (line 204) | pub(super) enum CompletedRequest {
type QueueState (line 213) | enum QueueState {
method deny_reason (line 239) | fn deny_reason(&self) -> Option<RequestDeniedReason> {
type ExternalRequestQueue (line 250) | pub(super) struct ExternalRequestQueue {
method new (line 286) | pub fn new(log: Logger, auto_start: InstanceAutoStart) -> Self {
method pop_front (line 308) | pub fn pop_front(&mut self) -> Option<ExternalRequest> {
method is_empty (line 318) | pub fn is_empty(&self) -> bool {
method try_queue (line 326) | pub fn try_queue(
method should_queue (line 394) | fn should_queue(
method notify_request_completed (line 498) | pub(super) fn notify_request_completed(&mut self, req: CompletedReques...
method notify_stopped (line 538) | pub(super) fn notify_stopped(&mut self) {
type InstanceAutoStart (line 279) | pub(super) enum InstanceAutoStart {
method drop (line 549) | fn drop(&mut self) {
function test_logger (line 591) | fn test_logger() -> slog::Logger {
function make_migrate_as_source_request (line 595) | fn make_migrate_as_source_request() -> ExternalRequest {
function make_reconfigure_crucible_request (line 602) | fn make_reconfigure_crucible_request() -> ExternalRequest {
function start_requests_become_idempotent_after_first_request (line 661) | fn start_requests_become_idempotent_after_first_request() {
function migrate_as_source_is_not_idempotent (line 685) | fn migrate_as_source_is_not_idempotent() {
function stop_requests_are_idempotent (line 727) | fn stop_requests_are_idempotent() {
function stop_requests_ignored_after_vm_failure (line 742) | fn stop_requests_ignored_after_vm_failure() {
function reboot_requests_are_idempotent_except_when_stopping (line 755) | fn reboot_requests_are_idempotent_except_when_stopping() {
function mutation_disallowed_after_stopped (line 791) | fn mutation_disallowed_after_stopped() {
function vcr_requests_canceled_when_queue_drops (line 804) | async fn vcr_requests_canceled_when_queue_drops() {
type RequestKind (line 833) | enum RequestKind {
function request_strategy (line 859) | fn request_strategy() -> impl Strategy<Value = RequestKind> {
type QueueOp (line 1050) | enum QueueOp {
function queue_op_strategy (line 1055) | fn queue_op_strategy() -> impl Strategy<Value = QueueOp> {
type QueueDequeueTest (line 1065) | struct QueueDequeueTest {
method new (line 1098) | fn new() -> Self {
method run (line 1115) | fn run(&mut self, ops: Vec<QueueOp>) {
method queue_request (line 1134) | fn queue_request(&mut self, request: RequestKind) {
method dequeue_request (line 1204) | fn dequeue_request(&mut self) {
FILE: bin/propolis-server/src/lib/vm/services.rs
type OximeterState (line 26) | pub(crate) struct OximeterState {
type VmServices (line 37) | pub(crate) struct VmServices {
method new (line 54) | pub(super) async fn new(
method stop (line 92) | pub(super) async fn stop(&self, log: &Logger) {
function register_oximeter_producer (line 117) | async fn register_oximeter_producer(
function start_serial_task (line 170) | async fn start_serial_task(
FILE: bin/propolis-server/src/lib/vm/state_driver.rs
type HandleEventOutcome (line 133) | enum HandleEventOutcome {
type VmStartReason (line 140) | pub(super) enum VmStartReason {
type VmStartOutcome (line 146) | enum VmStartOutcome {
method final_vm_state (line 158) | fn final_vm_state(&self) -> Option<InstanceState> {
type InputQueueEvent (line 169) | enum InputQueueEvent {
type InputQueueInner (line 175) | struct InputQueueInner {
method new (line 185) | fn new(log: slog::Logger, auto_start: InstanceAutoStart) -> Self {
type InputQueue (line 196) | pub(super) struct InputQueue {
method new (line 211) | pub(super) fn new(
method wait_for_next_event (line 239) | async fn wait_for_next_event(&self) -> InputQueueEvent {
method notify_request_completed (line 263) | fn notify_request_completed(&self, state: CompletedRequest) {
method notify_stopped (line 271) | fn notify_stopped(&self) {
method queue_external_request (line 277) | pub(super) fn queue_external_request(
method suspend_halt_event (line 291) | fn suspend_halt_event(&self, when: Duration) {
method suspend_reset_event (line 301) | fn suspend_reset_event(&self, when: Duration) {
method suspend_triple_fault_event (line 311) | fn suspend_triple_fault_event(&self, vcpu_id: i32, when: Duration) {
method unhandled_vm_exit (line 320) | fn unhandled_vm_exit(
method io_error_event (line 328) | fn io_error_event(&self, vcpu_id: i32, error: std::io::Error) {
method chipset_halt (line 334) | fn chipset_halt(&self) {
method chipset_reset (line 341) | fn chipset_reset(&self) {
type StateDriver (line 350) | struct StateDriver {
method run (line 506) | pub(super) async fn run(mut self, migrated_in: bool) -> StateDriverOut...
method event_loop (line 529) | async fn event_loop(&mut self) -> InstanceState {
method start_vm (line 559) | async fn start_vm(
method handle_guest_event (line 776) | async fn handle_guest_event(
method handle_external_request (line 826) | async fn handle_external_request(
method do_reboot (line 898) | async fn do_reboot(&mut self) {
method do_halt (line 912) | async fn do_halt(&mut self) {
method migrate_as_source (line 932) | async fn migrate_as_source(
method reconfigure_crucible_volume (line 1014) | async fn reconfigure_crucible_volume(
type StateDriverOutput (line 375) | pub(super) struct StateDriverOutput {
function ensure_vm_and_launch_driver (line 391) | pub(super) async fn ensure_vm_and_launch_driver(
function ensure_active_vm (line 450) | async fn ensure_active_vm<'a>(
FILE: bin/propolis-server/src/lib/vm/state_publisher.rs
type MigrationStateUpdate (line 22) | pub(crate) struct MigrationStateUpdate {
method apply_to (line 36) | fn apply_to(
type ExternalStateUpdate (line 55) | pub(crate) enum ExternalStateUpdate {
type StatePublisher (line 67) | pub(crate) struct StatePublisher {
method new (line 73) | pub(super) fn new(
method update (line 83) | pub(crate) fn update(&mut self, update: ExternalStateUpdate) {
FILE: bin/propolis-server/src/lib/vnc.rs
constant MAX_RES (line 31) | const MAX_RES: Resolution = Resolution { width: 1920, height: 1200 };
constant UNINIT_RES (line 32) | const UNINIT_RES: Resolution = Resolution { width: 800, height: 600 };
constant UNINIT_FOURCC (line 33) | const UNINIT_FOURCC: FourCC = FourCC::XR24;
constant SERVER_NAME (line 34) | const SERVER_NAME: &str = "propolis-vnc";
constant FRAME_US_10FPS (line 36) | const FRAME_US_10FPS: usize = 1000000 / 10;
type Devices (line 38) | struct Devices {
type FrameKind (line 44) | enum FrameKind {
type State (line 50) | struct State {
type ClientState (line 55) | struct ClientState {
method default (line 62) | fn default() -> Self {
type Client (line 73) | pub struct Client {
type VncServer (line 78) | pub struct VncServer {
method new (line 108) | pub fn new(log: Logger) -> Arc<Self> {
method attach (line 117) | pub fn attach(&self, ps2: Arc<PS2Ctrl>, fb: Arc<RamFb>) {
method connect (line 121) | pub async fn connect(
method replace_client (line 173) | async fn replace_client(&self, new_id: String) -> oneshot::Receiver<()> {
method hup_client (line 182) | fn hup_client(&self) {
method wait_client_gone (line 188) | async fn wait_client_gone(&self) -> MutexGuard<'_, Client> {
method run (line 207) | async fn run(
method handle_msg (line 242) | async fn handle_msg(
method send_fbu (line 295) | async fn send_fbu(
method update_frame (line 321) | fn update_frame(&self, cstate: &mut ClientState) -> bool {
method wait_for_next_frame (line 349) | async fn wait_for_next_frame(&self, cstate: &mut ClientState) {
method stop (line 384) | pub async fn stop(&self) {
type ConnectError (line 88) | pub enum ConnectError {
type Connection (line 98) | pub trait Connection: AsyncRead + AsyncWrite + Unpin + Send + 'static {}
type TcpSock (line 396) | pub struct TcpSock {
method new (line 401) | pub async fn new(
method halt (line 413) | pub async fn halt(self) {
method run (line 420) | async fn run(
function blank_frame (line 456) | fn blank_frame(fourcc: FourCC) -> FrameSnap {
function spec_valid (line 473) | fn spec_valid(spec: &Spec) -> bool {
FILE: bin/propolis-server/src/main.rs
constant API_RT_THREADS (line 29) | const API_RT_THREADS: usize = 4;
type MetricRegistration (line 33) | enum MetricRegistration {
method fmt (line 40) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type Err (line 50) | type Err = anyhow::Error;
method from_str (line 52) | fn from_str(s: &str) -> Result<Self, Self::Err> {
function parse_log_level (line 70) | fn parse_log_level(s: &str) -> anyhow::Result<slog::Level> {
type Args (line 77) | enum Args {
function run_server (line 113) | fn run_server(
function build_logger (line 203) | fn build_logger(level: slog::Level) -> slog::Logger {
function is_valid_listen_addr_for_dns (line 238) | fn is_valid_listen_addr_for_dns(listen_addr: IpAddr) -> bool {
function build_metric_configuration (line 249) | fn build_metric_configuration(
function main (line 280) | fn main() -> anyhow::Result<()> {
FILE: bin/propolis-standalone/src/cidata.rs
constant SECTOR_SZ (line 14) | const SECTOR_SZ: usize = 512;
constant VOLUME_LABEL (line 15) | const VOLUME_LABEL: [u8; 11] = *b"cidata ";
function build_cidata_be (line 17) | pub(crate) fn build_cidata_be(
FILE: bin/propolis-standalone/src/config.rs
type Config (line 26) | pub struct Config {
method cpuid_profile (line 41) | pub fn cpuid_profile(&self) -> Option<&CpuidProfile> {
type Main (line 50) | pub struct Main {
type BindingStrategy (line 78) | pub enum BindingStrategy {
type Device (line 96) | pub struct Device {
type BlockOpts (line 104) | pub struct BlockOpts {
type BlockDevice (line 111) | pub struct BlockDevice {
type CloudInit (line 124) | pub struct CloudInit {
type FileConfig (line 136) | struct FileConfig {
type MemAsyncConfig (line 141) | struct MemAsyncConfig {
type VionaDeviceParams (line 147) | pub struct VionaDeviceParams {
method from_opts (line 152) | pub fn from_opts(
type VsockDevice (line 175) | pub struct VsockDevice {
method from_opts (line 181) | pub fn from_opts(
function opt_deser (line 189) | fn opt_deser<'de, T: Deserialize<'de>>(
constant DEFAULT_WORKER_COUNT (line 197) | const DEFAULT_WORKER_COUNT: usize = 8;
constant MAX_FILE_WORKERS (line 198) | const MAX_FILE_WORKERS: usize = 32;
function block_backend (line 200) | pub fn block_backend(
function parse (line 277) | pub fn parse(path: &str) -> anyhow::Result<Config> {
function parse_bdf (line 286) | pub fn parse_bdf(v: &str) -> Option<Bdf> {
function parse_cpuid (line 303) | pub fn parse_cpuid(config: &Config) -> anyhow::Result<Option<CpuidSet>> {
function create_crucible_backend (line 332) | fn create_crucible_backend(
function create_crucible_mem_backend (line 427) | fn create_crucible_mem_backend(
function create_crucible_backend (line 446) | fn create_crucible_backend(
function create_crucible_mem_backend (line 458) | fn create_crucible_mem_backend(
FILE: bin/propolis-standalone/src/main.rs
constant PAGE_OFFSET (line 43) | const PAGE_OFFSET: u64 = 0xfff;
constant MAX_ROM_SIZE (line 45) | const MAX_ROM_SIZE: usize = 0x20_0000;
constant MIN_RT_THREADS (line 47) | const MIN_RT_THREADS: usize = 8;
constant BASE_RT_THREADS (line 48) | const BASE_RT_THREADS: usize = 4;
type InstEvent (line 51) | enum InstEvent {
method priority (line 63) | fn priority(&self) -> u8 {
method supersedes (line 74) | fn supersedes(&self, comp: &Self) -> bool {
method from (line 79) | fn from(value: propolis::exits::Suspend) -> Self {
type EventCtx (line 92) | enum EventCtx {
type EQInner (line 100) | struct EQInner {
type EventQueue (line 104) | struct EventQueue {
method new (line 109) | fn new() -> Arc<Self> {
method push (line 112) | fn push(&self, ev: InstEvent, ctx: EventCtx) {
method pop_superseding (line 117) | fn pop_superseding(
method wait (line 138) | fn wait(&self) {
type State (line 145) | pub enum State {
method next (line 164) | fn next(&self, ev: InstEvent) -> (Self, Option<InstEvent>) {
type Inventory (line 207) | struct Inventory {
method register (line 212) | fn register<D: propolis::common::Lifecycle>(&mut self, dev: &Arc<D>) {
method register_instance (line 218) | fn register_instance<D: propolis::common::Lifecycle>(
method register_block (line 228) | fn register_block(
method destroy (line 235) | fn destroy(&mut self) {
type InstState (line 247) | struct InstState {
type InstInner (line 255) | struct InstInner {
type Instance (line 264) | struct Instance(Arc<InstInner>);
method new (line 266) | fn new(
method device_state_transition (line 356) | fn device_state_transition(
method state_loop (line 417) | fn state_loop(
method wait_destroyed (line 574) | fn wait_destroyed(&self) -> ExitCode {
method vcpu_loop (line 584) | fn vcpu_loop(
method generate_pins (line 727) | fn generate_pins(&self) -> (Arc<FuncPin>, Arc<FuncPin>) {
method lock (line 751) | fn lock(&self) -> Option<MutexGuard<'_, InstState>> {
method eq (line 757) | fn eq(&self) -> Arc<EventQueue> {
function build_machine (line 762) | fn build_machine(
function open_bootrom (line 798) | fn open_bootrom(path: &str) -> Result<(File, usize)> {
function build_log (line 816) | fn build_log(level: slog::Level) -> slog::Logger {
function populate_rom (line 849) | fn populate_rom(
type SmbiosParams (line 871) | struct SmbiosParams {
function generate_smbios (line 879) | fn generate_smbios(params: SmbiosParams) -> anyhow::Result<smbios::Table...
function generate_e820 (line 999) | fn generate_e820(
function generate_bootorder (line 1025) | fn generate_bootorder(
function setup_instance (line 1074) | fn setup_instance(
function api_version_checks (line 1462) | fn api_version_checks(log: &slog::Logger) -> std::io::Result<()> {
type Args (line 1489) | struct Args {
function main (line 1512) | fn main() -> anyhow::Result<ExitCode> {
type LogFilter (line 1592) | struct LogFilter(slog::Level);
method value_variants (line 1595) | fn value_variants<'a>() -> &'a [Self] {
method to_possible_value (line 1606) | fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
method fmt (line 1612) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
FILE: bin/propolis-standalone/src/snapshot.rs
type SnapshotDevice (line 43) | struct SnapshotDevice {
type SnapshotDevicePayload (line 48) | struct SnapshotDevicePayload {
constant DEVICE_DIR (line 54) | const DEVICE_DIR: &str = "devices";
constant MEMORY_DIR (line 55) | const MEMORY_DIR: &str = "memory";
constant CONFIG_NAME (line 56) | const CONFIG_NAME: &str = "config.toml";
constant GLOBAL_NAME (line 57) | const GLOBAL_NAME: &str = "global.json";
function save (line 60) | pub(crate) fn save(
function parse_mem_name (line 211) | fn parse_mem_name(name: &str) -> anyhow::Result<(usize, usize)> {
function restore_config (line 231) | pub(crate) fn restore_config(path: impl AsRef<Path>) -> anyhow::Result<C...
function restore (line 242) | pub(crate) fn restore(
type VmGlobalState (line 427) | pub struct VmGlobalState {
function export_global (line 435) | fn export_global(hdl: &VmmHdl) -> io::Result<VmGlobalState> {
function import_global (line 451) | fn import_global(hdl: &VmmHdl, state: &VmGlobalState) -> io::Result<()> {
type TarBuilder (line 469) | struct TarBuilder(tar::Builder<File>);
method new (line 471) | fn new(fp: File) -> Self {
method append_data (line 474) | fn append_data(
method append_space (line 483) | fn append_space(
method append_dir (line 502) | fn append_dir(
method rawfd (line 511) | fn rawfd(&mut self) -> RawFd {
method inner_builder (line 514) | fn inner_builder(&mut self) -> &mut tar::Builder<File> {
method into_file (line 517) | fn into_file(self) -> io::Result<File> {
type TarInner (line 523) | enum TarInner {
method as_tar (line 528) | fn as_tar(&mut self) -> Option<&mut tar::Archive<File>> {
method as_file (line 534) | fn as_file(&mut self) -> Option<&mut File> {
type TarArchive (line 545) | struct TarArchive(TarInner);
method new (line 547) | fn new(fp: File) -> Self {
method reset_tar (line 551) | fn reset_tar(&mut self) -> io::Result<&mut tar::Archive<File>> {
method as_file (line 565) | fn as_file(&mut self) -> &mut File {
method named_entry (line 577) | fn named_entry(&mut self, name: &str) -> io::Result<tar::Entry<'_, Fil...
method entries (line 599) | fn entries(&mut self) -> io::Result<tar::Entries<'_, File>> {
FILE: bin/propolis-utils/src/bin/cpuid-gen.rs
function print_text (line 10) | fn print_text(results: &CpuidSet) {
function print_toml (line 27) | fn print_toml(results: &CpuidSet) {
function print_json (line 41) | fn print_json(results: CpuidSet) {
type OutputFormat (line 47) | enum OutputFormat {
type Err (line 60) | type Err = &'static str;
method from_str (line 62) | fn from_str(s: &str) -> Result<Self, Self::Err> {
type Opts (line 77) | struct Opts {
function main (line 91) | fn main() -> anyhow::Result<()> {
FILE: bin/propolis-utils/src/bin/rsrvrctl.rs
type Opts (line 9) | struct Opts {
type Command (line 15) | enum Command {
constant MB (line 41) | const MB: usize = 1024 * 1024;
function do_resize (line 43) | fn do_resize(
function main (line 59) | fn main() -> anyhow::Result<()> {
FILE: crates/bhyve-api/header-check/build.rs
function ver_gt (line 19) | fn ver_gt(ver: u32) -> bool {
function ver_lt (line 23) | fn ver_lt(ver: u32) -> bool {
function ver_eq (line 27) | fn ver_eq(ver: u32) -> bool {
function main (line 31) | fn main() {
FILE: crates/bhyve-api/src/lib.rs
constant VMM_PATH_PREFIX (line 16) | pub const VMM_PATH_PREFIX: &str = "/dev/vmm";
constant VMM_CTL_PATH (line 17) | pub const VMM_CTL_PATH: &str = "/dev/vmmctl";
type VmmCtlFd (line 19) | pub struct VmmCtlFd(File);
method open (line 21) | pub fn open() -> Result<Self> {
method ioctl (line 36) | pub unsafe fn ioctl<T>(&self, cmd: i32, data: *mut T) -> Result<i32> {
method ioctl_usize (line 39) | pub fn ioctl_usize(&self, cmd: i32, data: usize) -> Result<i32> {
method api_version (line 54) | pub fn api_version(&self) -> Result<u32> {
method query_api_version (line 59) | fn query_api_version(&self) -> Result<u32> {
method reservoir_resize (line 74) | pub fn reservoir_resize(
method reservoir_query (line 99) | pub fn reservoir_query(&self) -> Result<vmm_resv_query> {
method vm_destroy (line 109) | pub fn vm_destroy(&self, name: &[u8]) -> Result<()> {
method ioctl_usize_safe (line 117) | const fn ioctl_usize_safe(cmd: i32) -> bool {
method as_raw_fd (line 123) | fn as_raw_fd(&self) -> RawFd {
type ReservoirError (line 128) | pub enum ReservoirError {
method from (line 139) | fn from(val: ReservoirError) -> Self {
type VmmFd (line 149) | pub struct VmmFd(File);
method open (line 151) | pub fn open(name: &str) -> Result<Self> {
method new_raw (line 166) | pub unsafe fn new_raw(fp: File) -> Self {
method ioctl (line 177) | pub unsafe fn ioctl<T>(&self, cmd: i32, data: *mut T) -> Result<i32> {
method ioctl_usize (line 180) | pub fn ioctl_usize(&self, cmd: i32, data: usize) -> Result<i32> {
method api_version (line 195) | pub fn api_version(&self) -> Result<u32> {
method rtc_settime (line 218) | pub fn rtc_settime(&self, time: Duration) -> Result<()> {
method data_op (line 236) | pub fn data_op(&self, class: u16, version: u16) -> VmmDataOp<'_> {
method ioctl_usize_safe (line 242) | const fn ioctl_usize_safe(cmd: i32) -> bool {
method as_raw_fd (line 256) | fn as_raw_fd(&self) -> RawFd {
type VmmDataResult (line 261) | pub type VmmDataResult<T> = std::result::Result<T, VmmDataError>;
type VmmDataOp (line 267) | pub struct VmmDataOp<'a> {
function new (line 275) | pub fn new(fd: &'a VmmFd, class: u16, version: u16) -> Self {
function for_vcpu (line 283) | pub fn for_vcpu(mut self, vcpuid: i32) -> Self {
function read (line 289) | pub fn read<T: Sized + Copy + Default>(self) -> VmmDataResult<T> {
function read_into (line 300) | pub fn read_into<T: Sized>(self, data: &mut T) -> VmmDataResult<()> {
function read_item (line 309) | pub fn read_item<T: Sized>(self, data: &mut T) -> VmmDataResult<()> {
function do_read_single (line 317) | fn do_read_single(
function read_many (line 335) | pub fn read_many<T: Sized>(self, data: &mut [T]) -> VmmDataResult<()> {
function read_all (line 353) | pub fn read_all<T: Sized>(self) -> VmmDataResult<Vec<T>> {
function write (line 385) | pub fn write<T: Sized>(self, data: &T) -> VmmDataResult<()> {
function write_many (line 398) | pub fn write_many<T: Sized>(self, data: &[T]) -> VmmDataResult<()> {
function xfer_base (line 410) | fn xfer_base(&self, len: u32, data: *mut libc::c_void) -> vm_data_xfer {
function do_read (line 421) | fn do_read(
function do_write (line 428) | fn do_write(
function do_ioctl (line 440) | fn do_ioctl(
type VmmDataError (line 458) | pub enum VmmDataError {
method from (line 464) | fn from(err: VmmDataError) -> Self {
function api_version (line 487) | pub fn api_version() -> Result<u32> {
function cache_api_version (line 495) | fn cache_api_version(do_query: impl FnOnce() -> Result<u32>) -> Result<u...
function ioctl (line 523) | unsafe fn ioctl(fd: RawFd, cmd: i32, data: *mut libc::c_void) -> Result<...
function ioctl (line 531) | unsafe fn ioctl(
type ApiVersion (line 543) | pub enum ApiVersion {
method current (line 592) | pub const fn current() -> Self {
function eq (line 598) | fn eq(&self, other: &ApiVersion) -> bool {
function partial_cmp (line 603) | fn partial_cmp(&self, other: &ApiVersion) -> Option<std::cmp::Ordering> {
function latest_api_version (line 613) | fn latest_api_version() {
function u32_comparisons (line 619) | fn u32_comparisons() {
FILE: crates/bhyve-api/sys/src/enums.rs
type vm_reg_name (line 10) | pub enum vm_reg_name {
type vm_exitcode (line 63) | pub enum vm_exitcode {
type vcpu_reset_kind (line 96) | pub enum vcpu_reset_kind {
type vm_entry_cmds (line 103) | pub enum vm_entry_cmds {
type vm_cap_type (line 113) | pub enum vm_cap_type {
type vm_suspend_how (line 124) | pub enum vm_suspend_how {
FILE: crates/bhyve-api/sys/src/ioctls.rs
constant VMMCTL_IOC_BASE (line 7) | const VMMCTL_IOC_BASE: i32 = ((b'V' as i32) << 16) | ((b'M' as i32) << 8);
constant VMM_IOC_BASE (line 8) | const VMM_IOC_BASE: i32 = ((b'v' as i32) << 16) | ((b'm' as i32) << 8);
constant VMM_LOCK_IOC_BASE (line 9) | const VMM_LOCK_IOC_BASE: i32 = ((b'v' as i32) << 16) | ((b'l' as i32) <<...
constant VMM_CPU_IOC_BASE (line 10) | const VMM_CPU_IOC_BASE: i32 = ((b'v' as i32) << 16) | ((b'p' as i32) << 8);
constant VMM_CREATE_VM (line 13) | pub const VMM_CREATE_VM: i32 = VMMCTL_IOC_BASE | 0x01;
constant VMM_DESTROY_VM (line 14) | pub const VMM_DESTROY_VM: i32 = VMMCTL_IOC_BASE | 0x02;
constant VMM_VM_SUPPORTED (line 15) | pub const VMM_VM_SUPPORTED: i32 = VMMCTL_IOC_BASE | 0x03;
constant VMM_INTERFACE_VERSION (line 16) | pub const VMM_INTERFACE_VERSION: i32 = VMMCTL_IOC_BASE | 0x04;
constant VMM_RESV_QUERY (line 19) | pub const VMM_RESV_QUERY: i32 = VMMCTL_IOC_BASE | 0x10;
constant VMM_RESV_SET_TARGET (line 20) | pub const VMM_RESV_SET_TARGET: i32 = VMMCTL_IOC_BASE | 0x11;
constant VM_RUN (line 23) | pub const VM_RUN: i32 = VMM_CPU_IOC_BASE | 0x01;
constant VM_SET_REGISTER (line 24) | pub const VM_SET_REGISTER: i32 = VMM_CPU_IOC_BASE | 0x02;
constant VM_GET_REGISTER (line 25) | pub const VM_GET_REGISTER: i32 = VMM_CPU_IOC_BASE | 0x03;
constant VM_SET_SEGMENT_DESCRIPTOR (line 26) | pub const VM_SET_SEGMENT_DESCRIPTOR: i32 = VMM_CPU_IOC_BASE | 0x04;
constant VM_GET_SEGMENT_DESCRIPTOR (line 27) | pub const VM_GET_SEGMENT_DESCRIPTOR: i32 = VMM_CPU_IOC_BASE | 0x05;
constant VM_SET_REGISTER_SET (line 28) | pub const VM_SET_REGISTER_SET: i32 = VMM_CPU_IOC_BASE | 0x06;
constant VM_GET_REGISTER_SET (line 29) | pub const VM_GET_REGISTER_SET: i32 = VMM_CPU_IOC_BASE | 0x07;
constant VM_INJECT_EXCEPTION (line 30) | pub const VM_INJECT_EXCEPTION: i32 = VMM_CPU_IOC_BASE | 0x08;
constant VM_SET_CAPABILITY (line 31) | pub const VM_SET_CAPABILITY: i32 = VMM_CPU_IOC_BASE | 0x09;
constant VM_GET_CAPABILITY (line 32) | pub const VM_GET_CAPABILITY: i32 = VMM_CPU_IOC_BASE | 0x0a;
constant VM_PPTDEV_MSI (line 33) | pub const VM_PPTDEV_MSI: i32 = VMM_CPU_IOC_BASE | 0x0b;
constant VM_PPTDEV_MSIX (line 34) | pub const VM_PPTDEV_MSIX: i32 = VMM_CPU_IOC_BASE | 0x0c;
constant VM_SET_X2APIC_STATE (line 35) | pub const VM_SET_X2APIC_STATE: i32 = VMM_CPU_IOC_BASE | 0x0d;
constant VM_GLA2GPA (line 36) | pub const VM_GLA2GPA: i32 = VMM_CPU_IOC_BASE | 0x0e;
constant VM_GLA2GPA_NOFAULT (line 37) | pub const VM_GLA2GPA_NOFAULT: i32 = VMM_CPU_IOC_BASE | 0x0f;
constant VM_ACTIVATE_CPU (line 38) | pub const VM_ACTIVATE_CPU: i32 = VMM_CPU_IOC_BASE | 0x10;
constant VM_SET_INTINFO (line 39) | pub const VM_SET_INTINFO: i32 = VMM_CPU_IOC_BASE | 0x11;
constant VM_GET_INTINFO (line 40) | pub const VM_GET_INTINFO: i32 = VMM_CPU_IOC_BASE | 0x12;
constant VM_RESTART_INSTRUCTION (line 41) | pub const VM_RESTART_INSTRUCTION: i32 = VMM_CPU_IOC_BASE | 0x13;
constant VM_SET_KERNEMU_DEV (line 42) | pub const VM_SET_KERNEMU_DEV: i32 = VMM_CPU_IOC_BASE | 0x14;
constant VM_GET_KERNEMU_DEV (line 43) | pub const VM_GET_KERNEMU_DEV: i32 = VMM_CPU_IOC_BASE | 0x15;
constant VM_RESET_CPU (line 44) | pub const VM_RESET_CPU: i32 = VMM_CPU_IOC_BASE | 0x16;
constant VM_GET_RUN_STATE (line 45) | pub const VM_GET_RUN_STATE: i32 = VMM_CPU_IOC_BASE | 0x17;
constant VM_SET_RUN_STATE (line 46) | pub const VM_SET_RUN_STATE: i32 = VMM_CPU_IOC_BASE | 0x18;
constant VM_GET_FPU (line 47) | pub const VM_GET_FPU: i32 = VMM_CPU_IOC_BASE | 0x19;
constant VM_SET_FPU (line 48) | pub const VM_SET_FPU: i32 = VMM_CPU_IOC_BASE | 0x1a;
constant VM_GET_CPUID (line 49) | pub const VM_GET_CPUID: i32 = VMM_CPU_IOC_BASE | 0x1b;
constant VM_SET_CPUID (line 50) | pub const VM_SET_CPUID: i32 = VMM_CPU_IOC_BASE | 0x1c;
constant VM_LEGACY_CPUID (line 51) | pub const VM_LEGACY_CPUID: i32 = VMM_CPU_IOC_BASE | 0x1d;
constant VM_REINIT (line 54) | pub const VM_REINIT: i32 = VMM_LOCK_IOC_BASE | 0x01;
constant VM_BIND_PPTDEV (line 55) | pub const VM_BIND_PPTDEV: i32 = VMM_LOCK_IOC_BASE | 0x02;
constant VM_UNBIND_PPTDEV (line 56) | pub const VM_UNBIND_PPTDEV: i32 = VMM_LOCK_IOC_BASE | 0x03;
constant VM_MAP_PPTDEV_MMIO (line 57) | pub const VM_MAP_PPTDEV_MMIO: i32 = VMM_LOCK_IOC_BASE | 0x04;
constant VM_ALLOC_MEMSEG (line 58) | pub const VM_ALLOC_MEMSEG: i32 = VMM_LOCK_IOC_BASE | 0x05;
constant VM_MMAP_MEMSEG (line 59) | pub const VM_MMAP_MEMSEG: i32 = VMM_LOCK_IOC_BASE | 0x06;
constant VM_PMTMR_LOCATE (line 60) | pub const VM_PMTMR_LOCATE: i32 = VMM_LOCK_IOC_BASE | 0x07;
constant VM_MUNMAP_MEMSEG (line 61) | pub const VM_MUNMAP_MEMSEG: i32 = VMM_LOCK_IOC_BASE | 0x08;
constant VM_UNMAP_PPTDEV_MMIO (line 62) | pub const VM_UNMAP_PPTDEV_MMIO: i32 = VMM_LOCK_IOC_BASE | 0x09;
constant VM_PAUSE (line 63) | pub const VM_PAUSE: i32 = VMM_LOCK_IOC_BASE | 0x0a;
constant VM_RESUME (line 64) | pub const VM_RESUME: i32 = VMM_LOCK_IOC_BASE | 0x0b;
constant VM_WRLOCK_CYCLE (line 66) | pub const VM_WRLOCK_CYCLE: i32 = VMM_LOCK_IOC_BASE | 0xff;
constant VM_GET_GPA_PMAP (line 69) | pub const VM_GET_GPA_PMAP: i32 = VMM_IOC_BASE | 0x01;
constant VM_GET_MEMSEG (line 70) | pub const VM_GET_MEMSEG: i32 = VMM_IOC_BASE | 0x02;
constant VM_MMAP_GETNEXT (line 71) | pub const VM_MMAP_GETNEXT: i32 = VMM_IOC_BASE | 0x03;
constant VM_LAPIC_IRQ (line 73) | pub const VM_LAPIC_IRQ: i32 = VMM_IOC_BASE | 0x04;
constant VM_LAPIC_LOCAL_IRQ (line 74) | pub const VM_LAPIC_LOCAL_IRQ: i32 = VMM_IOC_BASE | 0x05;
constant VM_LAPIC_MSI (line 75) | pub const VM_LAPIC_MSI: i32 = VMM_IOC_BASE | 0x06;
constant VM_IOAPIC_ASSERT_IRQ (line 77) | pub const VM_IOAPIC_ASSERT_IRQ: i32 = VMM_IOC_BASE | 0x07;
constant VM_IOAPIC_DEASSERT_IRQ (line 78) | pub const VM_IOAPIC_DEASSERT_IRQ: i32 = VMM_IOC_BASE | 0x08;
constant VM_IOAPIC_PULSE_IRQ (line 79) | pub const VM_IOAPIC_PULSE_IRQ: i32 = VMM_IOC_BASE | 0x09;
constant VM_ISA_ASSERT_IRQ (line 81) | pub const VM_ISA_ASSERT_IRQ: i32 = VMM_IOC_BASE | 0x0a;
constant VM_ISA_DEASSERT_IRQ (line 82) | pub const VM_ISA_DEASSERT_IRQ: i32 = VMM_IOC_BASE | 0x0b;
constant VM_ISA_PULSE_IRQ (line 83) | pub const VM_ISA_PULSE_IRQ: i32 = VMM_IOC_BASE | 0x0c;
constant VM_ISA_SET_IRQ_TRIGGER (line 84) | pub const VM_ISA_SET_IRQ_TRIGGER: i32 = VMM_IOC_BASE | 0x0d;
constant VM_RTC_WRITE (line 86) | pub const VM_RTC_WRITE: i32 = VMM_IOC_BASE | 0x0e;
constant VM_RTC_READ (line 87) | pub const VM_RTC_READ: i32 = VMM_IOC_BASE | 0x0f;
constant VM_RTC_SETTIME (line 88) | pub const VM_RTC_SETTIME: i32 = VMM_IOC_BASE | 0x10;
constant VM_RTC_GETTIME (line 89) | pub const VM_RTC_GETTIME: i32 = VMM_IOC_BASE | 0x11;
constant VM_SUSPEND (line 91) | pub const VM_SUSPEND: i32 = VMM_IOC_BASE | 0x12;
constant VM_IOAPIC_PINCOUNT (line 93) | pub const VM_IOAPIC_PINCOUNT: i32 = VMM_IOC_BASE | 0x13;
constant VM_GET_PPTDEV_LIMITS (line 94) | pub const VM_GET_PPTDEV_LIMITS: i32 = VMM_IOC_BASE | 0x14;
constant VM_GET_HPET_CAPABILITIES (line 95) | pub const VM_GET_HPET_CAPABILITIES: i32 = VMM_IOC_BASE | 0x15;
constant VM_STATS_IOC (line 97) | pub const VM_STATS_IOC: i32 = VMM_IOC_BASE | 0x16;
constant VM_STAT_DESC (line 98) | pub const VM_STAT_DESC: i32 = VMM_IOC_BASE | 0x17;
constant VM_INJECT_NMI (line 100) | pub const VM_INJECT_NMI: i32 = VMM_IOC_BASE | 0x18;
constant VM_GET_X2APIC_STATE (line 101) | pub const VM_GET_X2APIC_STATE: i32 = VMM_IOC_BASE | 0x19;
constant VM_SET_TOPOLOGY (line 102) | pub const VM_SET_TOPOLOGY: i32 = VMM_IOC_BASE | 0x1a;
constant VM_GET_TOPOLOGY (line 103) | pub const VM_GET_TOPOLOGY: i32 = VMM_IOC_BASE | 0x1b;
constant VM_GET_CPUS (line 104) | pub const VM_GET_CPUS: i32 = VMM_IOC_BASE | 0x1c;
constant VM_SUSPEND_CPU (line 105) | pub const VM_SUSPEND_CPU: i32 = VMM_IOC_BASE | 0x1d;
constant VM_RESUME_CPU (line 106) | pub const VM_RESUME_CPU: i32 = VMM_IOC_BASE | 0x1e;
constant VM_TRACK_DIRTY_PAGES (line 107) | pub const VM_TRACK_DIRTY_PAGES: i32 = VMM_IOC_BASE | 0x20;
constant VM_DESC_FPU_AREA (line 108) | pub const VM_DESC_FPU_AREA: i32 = VMM_IOC_BASE | 0x21;
constant VM_DATA_READ (line 109) | pub const VM_DATA_READ: i32 = VMM_IOC_BASE | 0x22;
constant VM_DATA_WRITE (line 110) | pub const VM_DATA_WRITE: i32 = VMM_IOC_BASE | 0x23;
constant VM_SET_AUTODESTRUCT (line 111) | pub const VM_SET_AUTODESTRUCT: i32 = VMM_IOC_BASE | 0x24;
constant VM_DESTROY_SELF (line 112) | pub const VM_DESTROY_SELF: i32 = VMM_IOC_BASE | 0x25;
constant VM_DESTROY_PENDING (line 113) | pub const VM_DESTROY_PENDING: i32 = VMM_IOC_BASE | 0x26;
constant VM_VCPU_BARRIER (line 114) | pub const VM_VCPU_BARRIER: i32 = VMM_IOC_BASE | 0x27;
constant VM_NPT_OPERATION (line 115) | pub const VM_NPT_OPERATION: i32 = VMM_IOC_BASE | 0x28;
constant VM_DEVMEM_GETOFFSET (line 117) | pub const VM_DEVMEM_GETOFFSET: i32 = VMM_IOC_BASE | 0xff;
FILE: crates/bhyve-api/sys/src/lib.rs
constant VM_MAXCPU (line 15) | pub const VM_MAXCPU: usize = 32;
constant VMM_CURRENT_INTERFACE_VERSION (line 20) | pub const VMM_CURRENT_INTERFACE_VERSION: u32 = 18;
FILE: crates/bhyve-api/sys/src/structs.rs
constant SEG_ACCESS_S (line 12) | pub const SEG_ACCESS_S: u32 = 1 << 4;
constant SEG_ACCESS_P (line 15) | pub const SEG_ACCESS_P: u32 = 1 << 7;
constant SEG_ACCESS_AVAIL (line 18) | pub const SEG_ACCESS_AVAIL: u32 = 1 << 12;
constant SEG_ACCESS_L (line 19) | pub const SEG_ACCESS_L: u32 = 1 << 13;
constant SEG_ACCESS_DB (line 20) | pub const SEG_ACCESS_DB: u32 = 1 << 14;
constant SEG_ACCESS_G (line 21) | pub const SEG_ACCESS_G: u32 = 1 << 15;
constant SEG_ACCESS_UNUSABLE (line 22) | pub const SEG_ACCESS_UNUSABLE: u32 = 1 << 16;
type seg_desc (line 26) | pub struct seg_desc {
constant INOUT_IN (line 32) | pub const INOUT_IN: u8 = 1 << 0;
type vm_inout (line 36) | pub struct vm_inout {
type vm_mmio (line 49) | pub struct vm_mmio {
type vm_rwmsr (line 59) | pub struct vm_rwmsr {
type vm_exit (line 66) | pub struct vm_exit {
type vm_entry (line 75) | pub struct vm_entry {
method default (line 98) | fn default() -> Self {
method default (line 113) | fn default() -> Self {
type vm_exit_vmx (line 120) | pub struct vm_exit_vmx {
type vm_exit_svm (line 130) | pub struct vm_exit_svm {
type vm_exit_msr (line 138) | pub struct vm_exit_msr {
type vm_exit_suspend (line 145) | pub struct vm_exit_suspend {
type vm_inst_emul (line 157) | pub struct vm_inst_emul {
type vm_paging (line 164) | pub struct vm_paging {
type vm_memmap (line 171) | pub struct vm_memmap {
constant VM_MEMMAP_F_WIRED (line 180) | pub const VM_MEMMAP_F_WIRED: c_int = 0x01;
constant VM_MEMMAP_F_IOMMU (line 182) | pub const VM_MEMMAP_F_IOMMU: c_int = 0x02;
type vm_memseg (line 186) | pub struct vm_memseg {
type vm_devmem_offset (line 194) | pub struct vm_devmem_offset {
type vm_register (line 201) | pub struct vm_register {
type vm_seg_desc (line 209) | pub struct vm_seg_desc {
type vm_intinfo (line 217) | pub struct vm_intinfo {
type vm_exception (line 225) | pub struct vm_exception {
type vm_lapic_msi (line 235) | pub struct vm_lapic_msi {
type vm_lapic_irq (line 242) | pub struct vm_lapic_irq {
type vm_ioapic_irq (line 249) | pub struct vm_ioapic_irq {
type vm_isa_irq (line 255) | pub struct vm_isa_irq {
type vm_isa_irq_trigger (line 262) | pub struct vm_isa_irq_trigger {
type vm_rtc_data (line 270) | pub struct vm_rtc_data {
type vm_capability (line 277) | pub struct vm_capability {
type vm_nmi (line 286) | pub struct vm_nmi {
type vm_suspend (line 292) | pub struct vm_suspend {
constant VM_REINIT_F_FORCE_SUSPEND (line 299) | pub const VM_REINIT_F_FORCE_SUSPEND: u64 = 1 << 0;
type vm_reinit (line 303) | pub struct vm_reinit {
type vm_vcpu_reset (line 309) | pub struct vm_vcpu_reset {
constant VRS_HALT (line 316) | pub const VRS_HALT: u32 = 0;
constant VRS_INIT (line 317) | pub const VRS_INIT: u32 = 1 << 0;
constant VRS_RUN (line 318) | pub const VRS_RUN: u32 = 1 << 1;
constant VRS_PEND_INIT (line 319) | pub const VRS_PEND_INIT: u32 = 1 << 14;
constant VRS_PEND_SIPI (line 320) | pub const VRS_PEND_SIPI: u32 = 1 << 15;
type vm_run_state (line 324) | pub struct vm_run_state {
type vm_fpu_state (line 333) | pub struct vm_fpu_state {
type vm_fpu_desc_entry (line 341) | pub struct vm_fpu_desc_entry {
type vm_fpu_desc (line 349) | pub struct vm_fpu_desc {
method default (line 355) | fn default() -> Self {
type vmm_dirty_tracker (line 366) | pub struct vmm_dirty_tracker {
constant VDX_FLAG_READ_COPYIN (line 373) | pub const VDX_FLAG_READ_COPYIN: u32 = 1 << 0;
constant VDX_FLAG_WRITE_COPYOUT (line 374) | pub const VDX_FLAG_WRITE_COPYOUT: u32 = 1 << 1;
constant VM_DATA_XFER_LIMIT (line 377) | pub const VM_DATA_XFER_LIMIT: u32 = 8192;
type vm_data_xfer (line 381) | pub struct vm_data_xfer {
method default (line 391) | fn default() -> Self {
constant VCE_FLAG_MATCH_INDEX (line 405) | pub const VCE_FLAG_MATCH_INDEX: u32 = 1 << 0;
type vcpu_cpuid_entry (line 409) | pub struct vcpu_cpuid_entry {
method match_idx (line 420) | fn match_idx(&self) -> bool {
method eval_sort (line 446) | pub fn eval_sort(a: &Self, b: &Self) -> std::cmp::Ordering {
constant VCC_FLAG_LEGACY_HANDLING (line 462) | pub const VCC_FLAG_LEGACY_HANDLING: u32 = 1 << 0;
constant VCC_FLAG_INTEL_FALLBACK (line 467) | pub const VCC_FLAG_INTEL_FALLBACK: u32 = 1 << 1;
type vm_vcpu_cpuid_config (line 471) | pub struct vm_vcpu_cpuid_config {
method default (line 479) | fn default() -> Self {
type vm_legacy_cpuid (line 492) | pub struct vm_legacy_cpuid {
constant VM_MAX_NAMELEN (line 500) | pub const VM_MAX_NAMELEN: usize = 128;
constant VM_MAX_SEG_NAMELEN (line 501) | pub const VM_MAX_SEG_NAMELEN: usize = 128;
function validate_name (line 505) | fn validate_name(value: &[u8]) -> Result<[u8; VM_MAX_NAMELEN]> {
type vm_create_req (line 521) | pub struct vm_create_req {
method new (line 531) | pub fn new(name: &[u8]) -> Result<Self> {
method default (line 526) | fn default() -> Self {
constant VCF_RESERVOIR_MEM (line 540) | pub const VCF_RESERVOIR_MEM: u64 = 1;
constant VCF_TRACK_DIRTY (line 543) | pub const VCF_TRACK_DIRTY: u64 = 1 << 1;
type vm_destroy_req (line 547) | pub struct vm_destroy_req {
method new (line 556) | pub fn new(name: &[u8]) -> Result<Self> {
method default (line 551) | fn default() -> Self {
type vmm_resv_query (line 563) | pub struct vmm_resv_query {
type vmm_resv_target (line 572) | pub struct vmm_resv_target {
type vm_npt_operation (line 587) | pub struct vm_npt_operation {
method default (line 594) | fn default() -> Self {
constant VNO_OP_RESET_DIRTY (line 606) | pub const VNO_OP_RESET_DIRTY: u32 = 0x1;
constant VNO_OP_SET_DIRTY (line 607) | pub const VNO_OP_SET_DIRTY: u32 = 0x2;
constant VNO_OP_GET_DIRTY (line 608) | pub const VNO_OP_GET_DIRTY: u32 = 0x3;
constant VNO_OP_GET_TRACK_DIRTY (line 609) | pub const VNO_OP_GET_TRACK_DIRTY: u32 = 0x20;
constant VNO_OP_EN_TRACK_DIRTY (line 610) | pub const VNO_OP_EN_TRACK_DIRTY: u32 = 0x21;
constant VNO_OP_DIS_TRACK_DIRTY (line 611) | pub const VNO_OP_DIS_TRACK_DIRTY: u32 = 0x22;
constant VNO_FLAG_BITMAP_IN (line 612) | pub const VNO_FLAG_BITMAP_IN: u32 = 1 << 30;
constant VNO_FLAG_BITMAP_OUT (line 613) | pub const VNO_FLAG_BITMAP_OUT: u32 = 1 << 31;
FILE: crates/bhyve-api/sys/src/vmm_data.rs
constant VDC_VERSION (line 8) | pub const VDC_VERSION: u16 = 1;
constant VDC_REGISTER (line 12) | pub const VDC_REGISTER: u16 = 2;
constant VDC_MSR (line 13) | pub const VDC_MSR: u16 = 3;
constant VDC_FPU (line 14) | pub const VDC_FPU: u16 = 4;
constant VDC_LAPIC (line 15) | pub const VDC_LAPIC: u16 = 5;
constant VDC_VMM_ARCH (line 16) | pub const VDC_VMM_ARCH: u16 = 6;
constant VDC_PMU_AMD (line 17) | pub const VDC_PMU_AMD: u16 = 14;
constant VDC_IOAPIC (line 21) | pub const VDC_IOAPIC: u16 = 7;
constant VDC_ATPIT (line 22) | pub const VDC_ATPIT: u16 = 8;
constant VDC_ATPIC (line 23) | pub const VDC_ATPIC: u16 = 9;
constant VDC_HPET (line 24) | pub const VDC_HPET: u16 = 10;
constant VDC_PM_TIMER (line 25) | pub const VDC_PM_TIMER: u16 = 11;
constant VDC_RTC (line 26) | pub const VDC_RTC: u16 = 12;
constant VDC_VMM_TIME (line 27) | pub const VDC_VMM_TIME: u16 = 13;
type vdi_version_entry_v1 (line 31) | pub struct vdi_version_entry_v1 {
type vdi_field_entry_v1 (line 40) | pub struct vdi_field_entry_v1 {
method new (line 50) | pub const fn new(ident: u32, value: u64) -> Self {
type vdi_lapic_page_v1 (line 57) | pub struct vdi_lapic_page_v1 {
type vdi_lapic_v1 (line 83) | pub struct vdi_lapic_v1 {
type vdi_ioapic_v1 (line 92) | pub struct vdi_ioapic_v1 {
type vdi_atpit_channel_v1 (line 101) | pub struct vdi_atpit_channel_v1 {
type vdi_atpit_v1 (line 121) | pub struct vdi_atpit_v1 {
type vdi_atpic_chip_v1 (line 127) | pub struct vdi_atpic_chip_v1 {
type vdi_atpic_v1 (line 151) | pub struct vdi_atpic_v1 {
type vdi_hpet_timer_v1 (line 157) | pub struct vdi_hpet_timer_v1 {
type vdi_hpet_v1 (line 167) | pub struct vdi_hpet_v1 {
type vdi_pm_timer_v1 (line 178) | pub struct vdi_pm_timer_v1 {
type vdi_rtc_v1 (line 187) | pub struct vdi_rtc_v1 {
method default (line 195) | fn default() -> Self {
type vdi_rtc_v2 (line 208) | pub struct vdi_rtc_v2 {
method default (line 215) | fn default() -> Self {
type vdi_time_info_v1 (line 229) | pub struct vdi_time_info_v1 {
constant VAI_VM_IS_PAUSED (line 243) | pub const VAI_VM_IS_PAUSED: u32 = 4;
constant VAI_TSC_BOOT_OFFSET (line 249) | pub const VAI_TSC_BOOT_OFFSET: u32 = 1;
constant VAI_BOOT_HRTIME (line 251) | pub const VAI_BOOT_HRTIME: u32 = 2;
constant VAI_TSC_FREQ (line 253) | pub const VAI_TSC_FREQ: u32 = 3;
constant VAI_PEND_NMI (line 258) | pub const VAI_PEND_NMI: u32 = 10;
constant VAI_PEND_EXTINT (line 260) | pub const VAI_PEND_EXTINT: u32 = 11;
constant VAI_PEND_EXCP (line 262) | pub const VAI_PEND_EXCP: u32 = 12;
constant VAI_PEND_INTINFO (line 264) | pub const VAI_PEND_INTINFO: u32 = 13;
type vdi_pmu_amd_v1 (line 270) | pub struct vdi_pmu_amd_v1 {
FILE: crates/cpuid-profile-config/src/lib.rs
type CpuVendor (line 11) | pub enum CpuVendor {
type CpuidProfile (line 17) | pub struct CpuidProfile {
type CpuidEntry (line 25) | pub struct CpuidEntry {
type CpuidParseError (line 36) | pub enum CpuidParseError {
type Error (line 44) | type Error = CpuidParseError;
function try_from (line 46) | fn try_from(value: &CpuidProfile) -> Result<Self, Self::Error> {
FILE: crates/cpuid-utils/src/bits.rs
constant STANDARD_BASE_LEAF (line 11) | pub const STANDARD_BASE_LEAF: u32 = 0;
constant HYPERVISOR_BASE_LEAF (line 12) | pub const HYPERVISOR_BASE_LEAF: u32 = 0x4000_0000;
constant EXTENDED_BASE_LEAF (line 13) | pub const EXTENDED_BASE_LEAF: u32 = 0x8000_0000;
constant MAX_REASONABLE_SUBLEAVES (line 21) | pub const MAX_REASONABLE_SUBLEAVES: u32 = 0x20;
type AmdExtLeaf1DCacheType (line 223) | pub enum AmdExtLeaf1DCacheType {
method is_null (line 232) | pub fn is_null(&self) -> bool {
type Error (line 238) | type Error = ();
method try_from (line 243) | fn try_from(value: u32) -> Result<Self, Self::Error> {
method cache_type (line 256) | pub fn cache_type(&self) -> AmdExtLeaf1DCacheType {
FILE: crates/cpuid-utils/src/host.rs
type GetHostCpuidError (line 18) | pub enum GetHostCpuidError {
type Vm (line 31) | struct Vm(bhyve_api::VmmFd);
method new (line 34) | fn new() -> Result<Self, GetHostCpuidError> {
method query (line 54) | fn query(
method drop (line 75) | fn drop(&mut self) {
function query (line 82) | pub fn query(leaf: CpuidIdent) -> CpuidValues {
function query (line 90) | pub fn query(leaf: CpuidIdent) -> CpuidValues {
function collect_cpuid (line 94) | fn collect_cpuid(
type CpuidSource (line 283) | pub enum CpuidSource {
function query_complete (line 295) | pub fn query_complete(
FILE: crates/cpuid-utils/src/instance_spec.rs
function into_instance_spec_cpuid (line 12) | pub fn into_instance_spec_cpuid(self) -> Cpuid {
function from (line 18) | fn from(value: CpuidMap) -> Self {
type Error (line 39) | type Error = CpuidMapConversionError;
method try_from (line 49) | fn try_from(
function subleaf_aliasing_forbidden (line 84) | fn subleaf_aliasing_forbidden() {
FILE: crates/cpuid-utils/src/lib.rs
type CpuidSubleafMap (line 67) | type CpuidSubleafMap = BTreeMap<u32, CpuidValues>;
type CpuidMapInsertResult (line 68) | type CpuidMapInsertResult = Result<Option<CpuidValues>, CpuidMapInsertEr...
type Subleaves (line 72) | enum Subleaves {
type CpuidMapInsertError (line 85) | pub enum CpuidMapInsertError {
type CpuidMap (line 117) | pub struct CpuidMap(BTreeMap<u32, Subleaves>);
method get (line 122) | pub fn get(&self, ident: CpuidIdent) -> Option<&CpuidValues> {
method get_mut (line 138) | pub fn get_mut(&mut self, ident: CpuidIdent) -> Option<&mut CpuidValue...
method contains_leaf (line 153) | pub fn contains_leaf(&self, leaf: u32) -> bool {
method insert_leaf_no_subleaf (line 157) | fn insert_leaf_no_subleaf(
method insert_leaf_subleaf (line 176) | fn insert_leaf_subleaf(
method insert (line 211) | pub fn insert(
method remove (line 227) | pub fn remove(&mut self, ident: CpuidIdent) -> Option<CpuidValues> {
method remove_leaf (line 270) | pub fn remove_leaf(&mut self, leaf: u32) {
method retain (line 275) | pub fn retain<F>(&mut self, mut f: F)
method clear (line 291) | pub fn clear(&mut self) {
method is_empty (line 296) | pub fn is_empty(&self) -> bool {
method len (line 301) | pub fn len(&self) -> usize {
method iter (line 315) | pub fn iter(&self) -> CpuidMapIterator<'_> {
type CpuidMapIterator (line 321) | pub struct CpuidMapIterator<'a> {
function new (line 327) | fn new(map: &'a CpuidMap) -> Self {
type Item (line 333) | type Item = (CpuidIdent, CpuidValues);
method next (line 335) | fn next(&mut self) -> Option<Self::Item> {
type CpuidSet (line 378) | pub struct CpuidSet {
method new (line 412) | pub fn new(vendor: CpuidVendor) -> Self {
method from_map (line 418) | pub fn from_map(map: CpuidMap, vendor: CpuidVendor) -> Self {
method vendor (line 423) | pub fn vendor(&self) -> CpuidVendor {
method new_host (line 434) | pub fn new_host() -> Self {
method insert (line 441) | pub fn insert(
method get (line 450) | pub fn get(&self, ident: CpuidIdent) -> Option<&CpuidValues> {
method get_mut (line 455) | pub fn get_mut(&mut self, ident: CpuidIdent) -> Option<&mut CpuidValue...
method contains_leaf (line 460) | pub fn contains_leaf(&self, leaf: u32) -> bool {
method remove_leaf (line 465) | pub fn remove_leaf(&mut self, leaf: u32) {
method retain (line 470) | pub fn retain<F>(&mut self, f: F)
method is_empty (line 478) | pub fn is_empty(&self) -> bool {
method iter (line 483) | pub fn iter(&self) -> CpuidMapIterator<'_> {
method is_equivalent_to (line 490) | pub fn is_equivalent_to(
method default (line 385) | fn default() -> Self {
type CpuidSetMismatch (line 392) | pub enum CpuidSetMismatch {
function from (line 533) | fn from(value: CpuidSet) -> Self {
type CpuidMapConversionError (line 558) | pub enum CpuidMapConversionError {
constant STANDARD_LEAVES (line 570) | pub const STANDARD_LEAVES: RangeInclusive<u32> = 0..=0xFFFF;
constant EXTENDED_LEAVES (line 574) | pub const EXTENDED_LEAVES: RangeInclusive<u32> = 0x8000_0000..=0x8000_FFFF;
function insert_leaf_then_subleaf_fails (line 583) | fn insert_leaf_then_subleaf_fails() {
function insert_subleaf_then_leaf_fails (line 595) | fn insert_subleaf_then_leaf_fails() {
function insert_leaf_then_remove_then_insert_subleaf (line 607) | fn insert_leaf_then_remove_then_insert_subleaf() {
function insert_multiple_subleaves (line 622) | fn insert_multiple_subleaves() {
function leaf_subleaf_removal (line 634) | fn leaf_subleaf_removal() {
type MapEntry (line 716) | enum MapEntry {
function map_entry_strategy (line 731) | fn map_entry_strategy() -> impl Strategy<Value = MapEntry> {
FILE: crates/dladm/src/lib.rs
type Handle (line 16) | pub struct Handle {
method new (line 20) | pub fn new() -> Result<Self> {
method query_link (line 28) | pub fn query_link(&self, name: &str) -> Result<LinkInfo> {
method get_mtu (line 72) | fn get_mtu(name: &str) -> Result<u16> {
method get_vnic_mac (line 92) | fn get_vnic_mac(name: &str, mac: &mut [u8]) -> Result<()> {
method get_misc_mac (line 123) | fn get_misc_mac(
method handle_dladm_err (line 187) | fn handle_dladm_err(v: i32) -> Result<()> {
method drop (line 197) | fn drop(&mut self) {
constant ETHERADDRL (line 203) | const ETHERADDRL: usize = 6;
type LinkInfo (line 206) | pub struct LinkInfo {
FILE: crates/dladm/src/sys.rs
function dladm_open (line 11) | pub fn dladm_open(handle: *mut dladm_handle_t) -> c_int;
function dladm_close (line 12) | pub fn dladm_close(handle: dladm_handle_t);
function dladm_name2info (line 13) | pub fn dladm_name2info(
function dladm_walk_macaddr (line 22) | pub fn dladm_walk_macaddr(
function dladm_open (line 38) | pub unsafe extern "C" fn dladm_open(handle: *mut dladm_handle_t) -> c_int {
function dladm_close (line 41) | pub unsafe extern "C" fn dladm_close(handle: dladm_handle_t) {
function dladm_name2info (line 44) | pub unsafe extern "C" fn dladm_name2info(
function dladm_walk_macaddr (line 55) | pub unsafe extern "C" fn dladm_walk_macaddr(
type dladm_handle (line 71) | pub enum dladm_handle {}
type dladm_handle_t (line 72) | pub type dladm_handle_t = *mut dladm_handle;
type datalink_id_t (line 73) | pub type datalink_id_t = u32;
type dladm_macaddr_attr_t (line 75) | pub struct dladm_macaddr_attr_t {
type boolean_t (line 84) | pub enum boolean_t {
constant MAXMACADDRLEN (line 89) | const MAXMACADDRLEN: usize = 20;
constant MAXNAMELEN (line 90) | const MAXNAMELEN: usize = 256;
type datalink_class (line 94) | pub enum datalink_class {
type dladm_status (line 109) | pub enum dladm_status {
FILE: crates/nvpair/header-check/build.rs
function main (line 7) | fn main() {
FILE: crates/nvpair/src/lib.rs
type NvList (line 12) | pub struct NvList(NonNull<nvlist_t>);
method new (line 15) | pub fn new() -> Self {
method pack (line 21) | pub fn pack(&mut self) -> Packed {
method unpack (line 29) | pub fn unpack(buf: &mut [u8]) -> std::io::Result<Self> {
method unpack_ptr (line 33) | pub fn unpack_ptr(buf: *mut u8, len: usize) -> std::io::Result<Self> {
method add (line 45) | pub fn add<'a>(
method add_name_value (line 53) | pub fn add_name_value(&mut self, name: NvName, value: NvData) {
method drop (line 105) | fn drop(&mut self) {
type Packed (line 112) | pub struct Packed {
method as_ptr (line 117) | pub fn as_ptr(&self) -> *const u8 {
method as_mut_ptr (line 120) | pub fn as_mut_ptr(&mut self) -> *mut u8 {
method as_ref (line 125) | fn as_ref(&self) -> &[u8] {
method drop (line 130) | fn drop(&mut self) {
type NvData (line 152) | pub enum NvData<'a> {
type NvName (line 179) | pub enum NvName<'a> {
function as_ptr (line 184) | pub fn as_ptr(&self) -> *const i8 {
function as_ref (line 192) | fn as_ref(&self) -> &[u8] {
method clone (line 200) | fn clone(&self) -> Self {
function from (line 208) | fn from(value: &'a str) -> Self {
function from (line 226) | fn from(value: &'a CStr) -> Self {
FILE: crates/nvpair/sys/src/lib.rs
type data_type_t (line 13) | pub enum data_type_t {
type nvpair_t (line 46) | pub struct nvpair_t {
method NVP_NAME (line 59) | pub const unsafe fn NVP_NAME(nvp: *mut nvpair_t) -> *mut c_char {
method NVP_VALUE (line 67) | pub unsafe fn NVP_VALUE(nvp: *mut nvpair_t) -> *mut c_char {
type nvlist_t (line 76) | pub struct nvlist_t {
constant NV_VERSION (line 84) | pub const NV_VERSION: i32 = 0;
constant NV_ENCODE_NATIVE (line 86) | pub const NV_ENCODE_NATIVE: u32 = 0;
constant NV_ENCODE_XDR (line 87) | pub const NV_ENCODE_XDR: u32 = 1;
constant NV_UNIQUE_NAME (line 89) | pub const NV_UNIQUE_NAME: u32 = 0x1;
constant NV_UNIQUE_NAME_TYPE (line 90) | pub const NV_UNIQUE_NAME_TYPE: u32 = 0x2;
constant NV_FLAG_NOENTOK (line 92) | pub const NV_FLAG_NOENTOK: u32 = 0x1;
function NV_ALIGN (line 94) | pub const fn NV_ALIGN(addr: usize) -> usize {
type boolean_t (line 99) | pub enum boolean_t {
method from (line 104) | fn from(value: bool) -> Self {
function from (line 112) | fn from(value: boolean_t) -> Self {
function nvlist_remove (line 122) | pub fn nvlist_remove(
function nvlist_remove_all (line 127) | pub fn nvlist_remove_all(nvl: *mut nvlist_t, name: *const c_char) -> c_int;
function nvlist_remove_nvpair (line 128) | pub fn nvlist_remove_nvpair(
function nvlist_lookup_nvpair (line 132) | pub fn nvlist_lookup_nvpair(
function nvlist_next_nvpair (line 138) | pub fn nvlist_next_nvpair(
function nvlist_prev_nvpair (line 142) | pub fn nvlist_prev_nvpair(
function nvlist_exists (line 147) | pub fn nvlist_exists(nvl: *mut nvlist_t, nvp: *const c_char) -> boolean_t;
function nvlist_empty (line 148) | pub fn nvlist_empty(nvl: *mut nvlist_t) -> boolean_t;
function nvlist_unpack (line 150) | pub fn nvlist_unpack(
function fnvlist_alloc (line 157) | pub fn fnvlist_alloc() -> *mut nvlist_t;
function fnvlist_free (line 158) | pub fn fnvlist_free(nvl: *mut nvlist_t);
function fnvlist_size (line 159) | pub fn fnvlist_size(nvl: *mut nvlist_t) -> size_t;
function fnvlist_pack (line 160) | pub fn fnvlist_pack(nvl: *mut nvlist_t, sizep: *mut size_t) -> *mut c_char;
function fnvlist_pack_free (line 161) | pub fn fnvlist_pack_free(packed: *mut c_char, size: size_t);
function fnvlist_unpack (line 162) | pub fn fnvlist_unpack(buf: *mut c_char, size: size_t) -> *mut nvlist_t;
function fnvlist_dup (line 163) | pub fn fnvlist_dup(nvl: *mut nvlist_t) -> *mut nvlist_t;
function fnvlist_merge (line 164) | pub fn fnvlist_merge(dst_nvl: *mut nvlist_t, src_nvl: *mut nvlist_t);
function fnvlist_num_pairs (line 165) | pub fn fnvlist_num_pairs(nvl: *mut nvlist_t) -> size_t;
function fnvlist_add_boolean (line 167) | pub fn fnvlist_add_boolean(nvl: *mut nvlist_t, name: *const c_char);
function fnvlist_add_boolean_value (line 168) | pub fn fnvlist_add_boolean_value(
function fnvlist_add_byte (line 173) | pub fn fnvlist_add_byte(nvl: *mut nvlist_t, name: *const c_char, val: u8);
function fnvlist_add_int8 (line 174) | pub fn fnvlist_add_int8(nvl: *mut nvlist_t, name: *const c_char, val: i8);
function fnvlist_add_uint8 (line 175) | pub fn fnvlist_add_uint8(nvl: *mut nvlist_t, name: *const c_char, val: u8);
function fnvlist_add_int16 (line 176) | pub fn fnvlist_add_int16(nvl: *mut nvlist_t, name: *const c_char, val: i...
function fnvlist_add_uint16 (line 177) | pub fn fnvlist_add_uint16(
function fnvlist_add_int32 (line 182) | pub fn fnvlist_add_int32(nvl: *mut nvlist_t, name: *const c_char, val: i...
function fnvlist_add_uint32 (line 183) | pub fn fnvlist_add_uint32(
function fnvlist_add_int64 (line 188) | pub fn fnvlist_add_int64(nvl: *mut nvlist_t, name: *const c_char, val: i...
function fnvlist_add_uint64 (line 189) | pub fn fnvlist_add_uint64(
function fnvlist_add_string (line 194) | pub fn fnvlist_add_string(
function fnvlist_add_nvlist (line 199) | pub fn fnvlist_add_nvlist(
function fnvlist_add_nvpair (line 204) | pub fn fnvlist_add_nvpair(nvl: *mut nvlist_t, val: *mut nvpair_t);
FILE: crates/pbind/src/lib.rs
type id_t (line 16) | pub type id_t = i32;
type processorid_t (line 19) | pub type processorid_t = i32;
type idtype_t (line 22) | pub type idtype_t = i32;
type IdType (line 29) | pub enum IdType {
function online_cpus (line 52) | pub fn online_cpus() -> Result<i32, Error> {
function bind_lwp (line 67) | pub fn bind_lwp(bind_cpu: processorid_t) -> Result<(), Error> {
function bind_lwp (line 100) | pub fn bind_lwp(_bind_cpu: processorid_t) -> Result<(), Error> {
FILE: crates/propolis-api-types-versions/src/add_vsock/api.rs
type InstanceInitializationMethod (line 20) | pub enum InstanceInitializationMethod {
method from (line 40) | fn from(old: v2::api::InstanceInitializationMethod) -> Self {
type InstanceEnsureRequest (line 32) | pub struct InstanceEnsureRequest {
method from (line 59) | fn from(old: v2::api::InstanceEnsureRequest) -> Self {
FILE: crates/propolis-api-types-versions/src/add_vsock/components/devices.rs
type VirtioSocket (line 15) | pub struct VirtioSocket {
FILE: crates/propolis-api-types-versions/src/add_vsock/instance_spec.rs
type Component (line 28) | pub enum Component {
method from (line 132) | fn from(old: V1Component) -> Self {
type InstanceSpec (line 50) | pub struct InstanceSpec {
method from (line 191) | fn from(old: v2::instance_spec::InstanceSpec) -> Self {
type InstanceSpecStatus (line 58) | pub enum InstanceSpecStatus {
type InstanceSpecGetResponse (line 64) | pub struct InstanceSpecGetResponse {
type InvalidV1Component (line 72) | pub struct InvalidV1Component(Component);
type Error (line 75) | type Error = InvalidV1Component;
method try_from (line 77) | fn try_from(value: Component) -> Result<Self, Self::Error> {
function from (line 116) | fn from(new: InstanceSpec) -> Self {
function from (line 168) | fn from(new: InstanceSpecStatus) -> Self {
function from (line 181) | fn from(new: InstanceSpecGetResponse) -> Self {
FILE: crates/propolis-api-types-versions/src/crucible_volume_info/disk.rs
type VolumeStatus (line 11) | pub struct VolumeStatus {
FILE: crates/propolis-api-types-versions/src/impls/instance.rs
method vm_name (line 11) | pub fn vm_name(&self) -> String {
method fmt (line 17) | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
type Err (line 23) | type Err = &'static str;
method from_str (line 24) | fn from_str(s: &str) -> Result<Self, Self::Err> {
FILE: crates/propolis-api-types-versions/src/impls/instance_spec.rs
method fmt (line 12) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Err (line 21) | type Err = core::convert::Infallible;
method from_str (line 22) | fn from_str(s: &str) -> Result<Self, Self::Err> {
method from (line 28) | fn from(s: &str) -> Self {
method from (line 37) | fn from(value: String) -> Self {
method from (line 46) | fn from(value: Uuid) -> Self {
type TestMap (line 61) | type TestMap = BTreeMap<SpecKey, Component>;
function spec_key_uuid_roundtrip (line 66) | fn spec_key_uuid_roundtrip() {
function spec_key_uuid_string_deserializes_as_uuid_variant (line 88) | fn spec_key_uuid_string_deserializes_as_uuid_variant() {
FILE: crates/propolis-api-types-versions/src/initial/components/backends.rs
type CrucibleStorageBackend (line 16) | pub struct CrucibleStorageBackend {
method fmt (line 32) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type FileStorageBackend (line 45) | pub struct FileStorageBackend {
type BlobStorageBackend (line 63) | pub struct BlobStorageBackend {
method fmt (line 72) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type VirtioNetworkBackend (line 83) | pub struct VirtioNetworkBackend {
type DlpiNetworkBackend (line 91) | pub struct DlpiNetworkBackend {
FILE: crates/propolis-api-types-versions/src/initial/components/board.rs
type I440Fx (line 20) | pub struct I440Fx {
type Chipset (line 36) | pub enum Chipset {
method default (line 42) | fn default() -> Self {
type Cpuid (line 50) | pub struct Cpuid {
type CpuidEntry (line 83) | pub struct CpuidEntry {
type HyperVFeatureFlag (line 124) | pub enum HyperVFeatureFlag {
type GuestHypervisorInterface (line 136) | pub enum GuestHypervisorInterface {
method is_default (line 148) | pub(crate) fn is_default(&self) -> bool {
type Board (line 156) | pub struct Board {
FILE: crates/propolis-api-types-versions/src/initial/components/devices.rs
type VirtioDisk (line 15) | pub struct VirtioDisk {
type NvmeDisk (line 26) | pub struct NvmeDisk {
type VirtioNic (line 41) | pub struct VirtioNic {
type SerialPortNumber (line 61) | pub enum SerialPortNumber {
type SerialPort (line 73) | pub struct SerialPort {
type PciPciBridge (line 83) | pub struct PciPciBridge {
type QemuPvpanic (line 105) | pub struct QemuPvpanic {
type BootSettings (line 116) | pub struct BootSettings {
type BootOrderEntry (line 123) | pub struct BootOrderEntry {
type SoftNpuPciPort (line 141) | pub struct SoftNpuPciPort {
type SoftNpuPort (line 152) | pub struct SoftNpuPort {
type SoftNpuP9 (line 167) | pub struct SoftNpuP9 {
type P9fs (line 178) | pub struct P9fs {
type MigrationFailureInjector (line 200) | pub struct MigrationFailureInjector {
FILE: crates/propolis-api-types-versions/src/initial/disk.rs
type InstanceVCRReplace (line 12) | pub struct InstanceVCRReplace {
type SnapshotRequestPathParams (line 18) | pub struct SnapshotRequestPathParams {
type VCRRequestPathParams (line 25) | pub struct VCRRequestPathParams {
type VolumeStatusPathParams (line 31) | pub struct VolumeStatusPathParams {
type VolumeStatus (line 36) | pub struct VolumeStatus {
FILE: crates/propolis-api-types-versions/src/initial/instance.rs
type InstanceMetadata (line 23) | pub struct InstanceMetadata {
type InstanceProperties (line 33) | pub struct InstanceProperties {
type InstanceState (line 48) | pub enum InstanceState {
type Instance (line 62) | pub struct Instance {
type InstanceGetResponse (line 68) | pub struct InstanceGetResponse {
type InstanceStateChange (line 74) | pub struct InstanceStateChange {
type InstanceStateRequested (line 79) | pub enum InstanceStateRequested {
type InstanceStateMonitorRequest (line 86) | pub struct InstanceStateMonitorRequest {
type InstanceStateMonitorResponse (line 91) | pub struct InstanceStateMonitorResponse {
type ReplacementComponent (line 124) | pub enum ReplacementComponent {
type InstanceInitializationMethod (line 132) | pub enum InstanceInitializationMethod {
type InstanceEnsureRequest (line 144) | pub struct InstanceEnsureRequest {
type InstanceEnsureResponse (line 150) | pub struct InstanceEnsureResponse {
type InstanceNameParams (line 156) | pub struct InstanceNameParams {
type InstancePathParams (line 162) | pub struct InstancePathParams {
type ErrorCode (line 170) | pub enum ErrorCode {
FILE: crates/propolis-api-types-versions/src/initial/instance_spec.rs
type SpecKey (line 55) | pub enum SpecKey {
method schema_name (line 63) | fn schema_name() -> String {
method json_schema (line 67) | fn json_schema(
type Component (line 124) | pub enum Component {
type InstanceSpec (line 146) | pub struct InstanceSpec {
type VersionedInstanceSpec (line 176) | pub enum VersionedInstanceSpec {
type InstanceSpecStatus (line 182) | pub enum InstanceSpecStatus {
type InstanceSpecGetResponse (line 188) | pub struct InstanceSpecGetResponse {
FILE: crates/propolis-api-types-versions/src/initial/migration.rs
type InstanceMigrateInitiateRequest (line 15) | pub struct InstanceMigrateInitiateRequest {
type InstanceMigrateInitiateResponse (line 22) | pub struct InstanceMigrateInitiateResponse {
type InstanceMigrateStartRequest (line 28) | pub struct InstanceMigrateStartRequest {
type InstanceMigrationStatus (line 34) | pub struct InstanceMigrationStatus {
type InstanceMigrateStatusResponse (line 55) | pub struct InstanceMigrateStatusResponse {
type MigrationState (line 77) | pub enum MigrationState {
FILE: crates/propolis-api-types-versions/src/initial/serial.rs
type InstanceSerialConsoleHistoryRequest (line 14) | pub struct InstanceSerialConsoleHistoryRequest {
type InstanceSerialConsoleHistoryResponse (line 32) | pub struct InstanceSerialConsoleHistoryResponse {
type InstanceSerialConsoleStreamRequest (line 45) | pub struct InstanceSerialConsoleStreamRequest {
type InstanceSerialConsoleControlMessage (line 67) | pub enum InstanceSerialConsoleControlMessage {
FILE: crates/propolis-api-types-versions/src/programmable_smbios/api.rs
type InstanceInitializationMethod (line 20) | pub enum InstanceInitializationMethod {
method from (line 40) | fn from(old: v1::instance::InstanceInitializationMethod) -> Self {
type InstanceEnsureRequest (line 32) | pub struct InstanceEnsureRequest {
method from (line 59) | fn from(old: v1::instance::InstanceEnsureRequest) -> Self {
FILE: crates/propolis-api-types-versions/src/programmable_smbios/instance_spec.rs
type SmbiosType1Input (line 19) | pub struct SmbiosType1Input {
type InstanceSpec (line 27) | pub struct InstanceSpec {
method from (line 79) | fn from(old: v1::instance_spec::InstanceSpec) -> Self {
type InstanceSpecStatus (line 35) | pub enum InstanceSpecStatus {
type InstanceSpecGetResponse (line 41) | pub struct InstanceSpecGetResponse {
function from (line 48) | fn from(new: InstanceSpec) -> Self {
function from (line 54) | fn from(new: InstanceSpecStatus) -> Self {
function from (line 69) | fn from(new: InstanceSpecGetResponse) -> Self {
FILE: crates/propolis-config-toml/src/lib.rs
type Config (line 23) | pub struct Config {
method default (line 40) | fn default() -> Self {
type Chipset (line 53) | pub struct Chipset {
method get_string (line 59) | pub fn get_string<S: AsRef<str>>(&self, key: S) -> Option<&str> {
method get (line 63) | pub fn get<T: FromStr, S: AsRef<str>>(&self, key: S) -> Option<T> {
type PciBridge (line 70) | pub struct PciBridge {
type Device (line 88) | pub struct Device {
method get_string (line 96) | pub fn get_string<S: AsRef<str>>(&self, key: S) -> Option<&str> {
method get (line 100) | pub fn get<T: FromStr, S: AsRef<str>>(&self, key: S) -> Option<T> {
type BlockOpts (line 106) | pub struct BlockOpts {
type BlockDevice (line 114) | pub struct BlockDevice {
type ParseError (line 127) | pub enum ParseError {
function parse (line 142) | pub fn parse<P: AsRef<Path>>(path: P) -> Result<Config, ParseError> {
function config_can_be_serialized_as_toml (line 153) | fn config_can_be_serialized_as_toml() {
function parse_basic_config (line 161) | fn parse_basic_config() {
FILE: crates/propolis-config-toml/src/spec.rs
constant MIGRATION_FAILURE_DEVICE_NAME (line 23) | pub const MIGRATION_FAILURE_DEVICE_NAME: &str = "test-migration-failure";
type TomlToSpecError (line 26) | pub enum TomlToSpecError {
type SpecConfig (line 74) | pub struct SpecConfig {
type Error (line 101) | type Error = TomlToSpecError;
method try_from (line 103) | fn try_from(config: &super::Config) -> Result<Self, Self::Error> {
function spec_component_add (line 87) | fn spec_component_add(
function parse_storage_device_from_config (line 294) | fn parse_storage_device_from_config(
function parse_storage_backend_from_config (line 348) | fn parse_storage_backend_from_config(
type ParsedNic (line 392) | struct ParsedNic {
function parse_network_device_from_config (line 398) | fn parse_network_device_from_config(
function parse_p9fs_from_config (line 422) | fn parse_p9fs_from_config(
function parse_vsock_from_config (line 445) | fn parse_vsock_from_config(
function translate_cpuid_entry (line 465) | fn translate_cpuid_entry(
function toml_cpuid_to_spec_cpuid (line 483) | pub fn toml_cpuid_to_spec_cpuid(
FILE: crates/propolis-server-api/src/lib.rs
type PropolisServerApi (line 45) | pub trait PropolisServerApi {
method instance_ensure (line 53) | async fn instance_ensure(
method instance_ensure_v2 (line 67) | async fn instance_ensure_v2(
method instance_ensure_v1 (line 87) | async fn instance_ensure_v1(
method instance_spec_get (line 106) | async fn instance_spec_get(
method instance_spec_get_v2 (line 119) | async fn instance_spec_get_v2(
method instance_spec_get_v1 (line 136) | async fn instance_spec_get_v1(
method instance_get (line 151) | async fn instance_get(
method instance_state_monitor (line 159) | async fn instance_state_monitor(
method instance_state_put (line 171) | async fn instance_state_put(
method instance_serial_history_get (line 180) | async fn instance_serial_history_get(
method instance_serial (line 192) | async fn instance_serial(
method instance_vnc (line 207) | async fn instance_vnc(
method instance_migrate_start (line 241) | async fn instance_migrate_start(
method instance_migrate_status (line 251) | async fn instance_migrate_status(
method instance_issue_crucible_snapshot_request (line 263) | async fn instance_issue_crucible_snapshot_request(
method disk_volume_status_v1 (line 275) | async fn disk_volume_status_v1(
method disk_volume_status (line 286) | async fn disk_volume_status(
method instance_issue_crucible_vcr_request (line 296) | async fn instance_issue_crucible_vcr_request(
method instance_issue_nmi (line 307) | async fn instance_issue_nmi(
FILE: crates/propolis-types/src/lib.rs
constant PCI_DEVICES_PER_BUS (line 19) | const PCI_DEVICES_PER_BUS: u8 = 32;
constant PCI_FUNCTIONS_PER_DEVICE (line 20) | const PCI_FUNCTIONS_PER_DEVICE: u8 = 8;
type PciPath (line 29) | pub struct PciPath {
method new (line 36) | pub fn new(
method bus (line 65) | pub fn bus(&self) -> u8 {
method device (line 70) | pub fn device(&self) -> u8 {
method function (line 75) | pub fn function(&self) -> u8 {
method deserialize (line 115) | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
type Err (line 81) | type Err = std::io::Error;
method from_str (line 82) | fn from_str(s: &str) -> Result<Self, Self::Err> {
method fmt (line 108) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
function pci_path_from_str (line 141) | fn pci_path_from_str() {
function check_pci_path_deserialization (line 167) | fn check_pci_path_deserialization<E>(
function pci_path_deserialization (line 185) | fn pci_path_deserialization() {
function pci_path_deserialization_exhaustive (line 207) | fn pci_path_deserialization_exhaustive() {
type CpuidIdent (line 240) | pub struct CpuidIdent {
method leaf (line 255) | pub fn leaf(leaf: u32) -> Self {
method subleaf (line 260) | pub fn subleaf(leaf: u32, subleaf: u32) -> Self {
type CpuidValues (line 277) | pub struct CpuidValues {
method iter_mut (line 286) | pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut u32> {
method all_zero (line 291) | pub fn all_zero(&self) -> bool {
method from (line 298) | fn from(value: core::arch::x86_64::CpuidResult) -> Self {
method from (line 304) | fn from(value: [u32; 4]) -> Self {
type CpuidVendor (line 315) | pub enum CpuidVendor {
method fmt (line 321) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method is_amd (line 334) | pub fn is_amd(self) -> bool {
method is_intel (line 338) | pub fn is_intel(self) -> bool {
type Error (line 344) | type Error = &'static str;
method try_from (line 346) | fn try_from(value: CpuidValues) -> Result<Self, Self::Error> {
FILE: crates/rfb/examples/shared.rs
constant IMG_OXIDE (line 21) | const IMG_OXIDE: &[u8] = include_bytes!("images/oxide.png");
constant IMG_COLORBARS (line 22) | const IMG_COLORBARS: &[u8] = include_bytes!("images/color-bars.png");
type Image (line 25) | pub enum Image {
type ExampleBackend (line 35) | pub struct ExampleBackend(Image);
method new (line 37) | pub fn new(img: Image) -> Self {
method generate (line 40) | pub async fn generate(
type Size (line 66) | struct Size {
method len (line 71) | const fn len(&self, bytes_per_pixel: usize) -> usize {
function generate_image (line 76) | fn generate_image(size: Size, img_bytes: &[u8]) -> Frame {
function generate_solid (line 107) | fn generate_solid(size: Size, rgb_pixel: [u8; 3]) -> Frame {
function generate_frame (line 124) | fn generate_frame(size: Size, img: Image) -> Frame {
function build_logger (line 136) | pub fn build_logger() -> slog::Logger {
FILE: crates/rfb/examples/socket.rs
constant WIDTH (line 25) | const WIDTH: usize = 1024;
constant HEIGHT (line 26) | const HEIGHT: usize = 768;
type Args (line 45) | struct Args {
function main (line 56) | async fn main() -> Result<()> {
FILE: crates/rfb/examples/websock.rs
constant WIDTH (line 31) | const WIDTH: usize = 1024;
constant HEIGHT (line 32) | const HEIGHT: usize = 768;
type Args (line 36) | struct Args {
type AppCtx (line 46) | struct AppCtx {
function run_server (line 51) | async fn run_server(
function main (line 133) | async fn main() -> Result<(), String> {
function ws_websockify (line 167) | async fn ws_websockify(
FILE: crates/rfb/src/encodings.rs
type EncodingType (line 13) | pub enum EncodingType {
type Encoding (line 31) | pub trait Encoding: Send {
method get_type (line 32) | fn get_type(&self) -> EncodingType;
method encode (line 36) | fn encode(&self) -> &[u8];
method get_type (line 51) | fn get_type(&self) -> EncodingType {
method encode (line 55) | fn encode(&self) -> &[u8] {
type RawEncoding (line 40) | pub struct RawEncoding {
method new (line 45) | pub fn new(pixels: Vec<u8>) -> Self {
type RREncoding (line 61) | struct RREncoding {
type Pixel (line 67) | struct Pixel {
type RRESubrectangle (line 72) | struct RRESubrectangle {
type HextileEncoding (line 79) | struct HextileEncoding {
type HextileTile (line 84) | enum HextileTile {
type HextileTileEncoded (line 90) | struct HextileTileEncoded {
FILE: crates/rfb/src/keysym.rs
constant ASCII_MAX (line 12) | const ASCII_MAX: u32 = 0x7f;
constant KEYSYM_BACKSPACE (line 14) | const KEYSYM_BACKSPACE: u32 = 0xff08;
constant KEYSYM_TAB (line 15) | const KEYSYM_TAB: u32 = 0xff09;
constant KEYSYM_RETURN_ENTER (line 16) | const KEYSYM_RETURN_ENTER: u32 = 0xff0d;
constant KEYSYM_ESCAPE (line 17) | const KEYSYM_ESCAPE: u32 = 0xff1b;
constant KEYSYM_INSERT (line 18) | const KEYSYM_INSERT: u32 = 0xff63;
constant KEYSYM_DELETE (line 19) | const KEYSYM_DELETE: u32 = 0xffff;
constant KEYSYM_HOME (line 20) | const KEYSYM_HOME: u32 = 0xff50;
constant KEYSYM_END (line 21) | const KEYSYM_END: u32 = 0xff57;
constant KEYSYM_PAGE_UP (line 22) | const KEYSYM_PAGE_UP: u32 = 0xff55;
constant KEYSYM_PAGE_DOWN (line 23) | const KEYSYM_PAGE_DOWN: u32 = 0xff56;
constant KEYSYM_PRINT (line 24) | const KEYSYM_PRINT: u32 = 0xff61;
constant KEYSYM_PAUSE (line 25) | const KEYSYM_PAUSE: u32 = 0xff13;
constant KEYSYM_CAPS_LOCK (line 26) | const KEYSYM_CAPS_LOCK: u32 = 0xffe5;
constant KEYSYM_SUPER_LEFT (line 27) | const KEYSYM_SUPER_LEFT: u32 = 0xffeb;
constant KEYSYM_SUPER_RIGHT (line 28) | const KEYSYM_SUPER_RIGHT: u32 = 0xffec;
constant KEYSYM_MENU (line 29) | const KEYSYM_MENU: u32 = 0xff67;
constant KEYSYM_LEFT (line 31) | const KEYSYM_LEFT: u32 = 0xff51;
constant KEYSYM_UP (line 32) | const KEYSYM_UP: u32 = 0xff52;
constant KEYSYM_RIGHT (line 33) | const KEYSYM_RIGHT: u32 = 0xff53;
constant KEYSYM_DOWN (line 34) | const KEYSYM_DOWN: u32 = 0xff54;
constant KEYSYM_F1 (line 36) | const KEYSYM_F1: u32 = 0xffbe;
constant KEYSYM_F12 (line 37) | const KEYSYM_F12: u32 = 0xffc9;
constant KEYSYM_SHIFT_LEFT (line 38) | const KEYSYM_SHIFT_LEFT: u32 = 0xffe1;
constant KEYSYM_SHIFT_RIGHT (line 39) | const KEYSYM_SHIFT_RIGHT: u32 = 0xffe2;
constant KEYSYM_CTRL_LEFT (line 40) | const KEYSYM_CTRL_LEFT: u32 = 0xffe3;
constant KEYSYM_CTRL_RIGHT (line 41) | const KEYSYM_CTRL_RIGHT: u32 = 0xffe4;
constant KEYSYM_ALT_LEFT (line 45) | const KEYSYM_ALT_LEFT: u32 = 0xffe9;
constant KEYSYM_ALT_RIGHT (line 46) | const KEYSYM_ALT_RIGHT: u32 = 0xffea;
constant KEYSYM_SCROLL_LOCK (line 47) | const KEYSYM_SCROLL_LOCK: u32 = 0xff14;
constant KEYSYM_NUM_LOCK (line 48) | const KEYSYM_NUM_LOCK: u32 = 0xff7f;
constant KEYSYM_KP_ENTER (line 50) | const KEYSYM_KP_ENTER: u32 = 0xff8d;
constant KEYSYM_KP_SLASH (line 51) | const KEYSYM_KP_SLASH: u32 = 0xffaf;
constant KEYSYM_KP_ASTERISK (line 52) | const KEYSYM_KP_ASTERISK: u32 = 0xffaa;
constant KEYSYM_KP_MINUS (line 53) | const KEYSYM_KP_MINUS: u32 = 0xffad;
constant KEYSYM_KP_PLUS (line 54) | const KEYSYM_KP_PLUS: u32 = 0xffab;
constant KEYSYM_KP_7 (line 55) | const KEYSYM_KP_7: u32 = 0xffb7;
constant KEYSYM_KP_HOME (line 56) | const KEYSYM_KP_HOME: u32 = 0xff95;
constant KEYSYM_KP_8 (line 57) | const KEYSYM_KP_8: u32 = 0xffb8;
constant KEYSYM_KP_UP (line 58) | const KEYSYM_KP_UP: u32 = 0xff97;
constant KEYSYM_KP_9 (line 59) | const KEYSYM_KP_9: u32 = 0xffb9;
constant KEYSYM_KP_PGUP (line 60) | const KEYSYM_KP_PGUP: u32 = 0xff9a;
constant KEYSYM_KP_4 (line 61) | const KEYSYM_KP_4: u32 = 0xffb4;
constant KEYSYM_KP_LEFT (line 62) | const KEYSYM_KP_LEFT: u32 = 0xff96;
constant KEYSYM_KP_5 (line 63) | const KEYSYM_KP_5: u32 = 0xffb5;
constant KEYSYM_KP_EMPTY (line 64) | const KEYSYM_KP_EMPTY: u32 = 0xff9d;
constant KEYSYM_KP_6 (line 65) | const KEYSYM_KP_6: u32 = 0xffb6;
constant KEYSYM_KP_RIGHT (line 66) | const KEYSYM_KP_RIGHT: u32 = 0xff98;
constant KEYSYM_KP_1 (line 67) | const KEYSYM_KP_1: u32 = 0xffb1;
constant KEYSYM_KP_END (line 68) | const KEYSYM_KP_END: u32 = 0xff9c;
constant KEYSYM_KP_2 (line 69) | const KEYSYM_KP_2: u32 = 0xffb2;
constant KEYSYM_KP_DOWN (line 70) | const KEYSYM_KP_DOWN: u32 = 0xff99;
constant KEYSYM_KP_3 (line 71) | const KEYSYM_KP_3: u32 = 0xffb3;
constant KEYSYM_KP_PGDOWN (line 72) | const KEYSYM_KP_PGDOWN: u32 = 0xff9b;
constant KEYSYM_KP_0 (line 73) | const KEYSYM_KP_0: u32 = 0xffb0;
constant KEYSYM_KP_INSERT (line 74) | const KEYSYM_KP_INSERT: u32 = 0xff9e;
constant KEYSYM_KP_PERIOD (line 75) | const KEYSYM_KP_PERIOD: u32 = 0xffae;
constant KEYSYM_KP_DELETE (line 76) | const KEYSYM_KP_DELETE: u32 = 0xff9f;
type KeySym (line 79) | pub enum KeySym {
type Error (line 149) | type Error = ();
method try_from (line 151) | fn try_from(value: u32) -> Result<Self, Self::Error> {
FILE: crates/rfb/src/proto.rs
type ProtocolError (line 22) | pub enum ProtocolError {
type Result (line 48) | pub type Result<T> = std::result::Result<T, ProtocolError>;
type ProtoVersion (line 51) | pub enum ProtoVersion {
method read_from (line 58) | pub async fn read_from(
method write_to (line 71) | pub async fn write_to(
type SecurityTypes (line 87) | pub struct SecurityTypes(pub Vec<SecurityType>);
method write_to (line 96) | pub async fn write_to(
type SecurityType (line 90) | pub enum SecurityType {
method read_from (line 111) | pub async fn read_from(
method write_to (line 121) | pub async fn write_to(
type SecurityResult (line 136) | pub enum SecurityResult {
method write_to (line 142) | pub async fn write_to(
type ClientInit (line 161) | pub struct ClientInit {
method read_from (line 166) | pub async fn read_from(
type ServerInit (line 176) | pub struct ServerInit {
method write_to (line 183) | pub async fn write_to(
type FramebufferUpdate (line 198) | pub struct FramebufferUpdate(pub Vec<Rectangle>);
method write_to (line 201) | pub async fn write_to(
type Position (line 218) | pub struct Position {
type Resolution (line 224) | pub struct Resolution {
method write_to (line 230) | pub async fn write_to(
type Rectangle (line 240) | pub struct Rectangle {
method write_to (line 247) | pub async fn write_to(
type PixelFormat (line 266) | pub struct PixelFormat {
method write_to (line 274) | pub async fn write_to(
type Error (line 284) | type Error = ProtocolError;
method try_from (line 286) | fn try_from(
method from (line 348) | fn from(value: FourCC) -> Self {
function from (line 312) | fn from(value: PixelFormat) -> Self {
type Error (line 366) | type Error = &'static str;
function try_into (line 368) | fn try_into(self) -> std::result::Result<FourCC, Self::Error> {
type ColorSpecification (line 392) | pub enum ColorSpecification {
type ColorFormat (line 398) | pub struct ColorFormat {
type ClientMessage (line 410) | pub enum ClientMessage {
type ClientMessageType (line 426) | enum ClientMessageType {
function read_data (line 435) | fn read_data<T: FromBytes>(buf: &mut BytesMut) -> Option<T> {
type ClientMessageDecoder (line 446) | pub struct ClientMessageDecoder {
method default (line 451) | fn default() -> Self {
type Item (line 458) | type Item = ClientMessage;
type Error (line 459) | type Error = ProtocolError;
method decode (line 461) | fn decode(
type FramebufferUpdateRequest (line 576) | pub struct FramebufferUpdateRequest {
method from (line 583) | fn from(value: raw::FramebufferUpdateRequest) -> Self {
type KeyEvent (line 593) | pub struct KeyEvent {
type Error (line 599) | type Error = ProtocolError;
method try_from (line 601) | fn try_from(
type PointerEvent (line 625) | pub struct PointerEvent {
method from (line 630) | fn from(value: raw::PointerEvent) -> Self {
type PixelFormat (line 645) | pub(crate) struct PixelFormat {
method write_to (line 274) | pub async fn write_to(
type Error (line 284) | type Error = ProtocolError;
method try_from (line 286) | fn try_from(
method from (line 348) | fn from(value: FourCC) -> Self {
type FramebufferUpdateRequest (line 661) | pub(crate) struct FramebufferUpdateRequest {
method from (line 583) | fn from(value: raw::FramebufferUpdateRequest) -> Self {
type KeyEvent (line 669) | pub(crate) struct KeyEvent {
type Error (line 599) | type Error = ProtocolError;
method try_from (line 601) | fn try_from(
type PointerEvent (line 677) | pub(crate) struct PointerEvent {
method from (line 630) | fn from(value: raw::PointerEvent) -> Self {
type Position (line 684) | pub(crate) struct Position {
function from (line 689) | fn from(value: Position) -> Self {
type Resolution (line 696) | pub(crate) struct Resolution {
method write_to (line 230) | pub async fn write_to(
function from (line 701) | fn from(value: Resolution) -> Self {
type FramebufferUpdateHeader (line 709) | pub(crate) struct FramebufferUpdateHeader {
method new (line 715) | pub fn new(num_rects: u16) -> Self {
FILE: crates/rfb/src/server.rs
type InitError (line 16) | pub enum InitError {
type Result (line 36) | pub type Result<T> = std::result::Result<T, InitError>;
type InitParams (line 38) | pub struct InitParams {
function rfb_handshake (line 53) | async fn rfb_handshake(
function rfb_initialization (line 87) | async fn rfb_initialization(
function initialize (line 103) | pub async fn initialize(
FILE: crates/rfb/src/tungstenite.rs
function tung_err_to_io (line 20) | fn tung_err_to_io(err: TungError) -> io::Error {
type BinaryWs (line 28) | pub struct BinaryWs<T> {
function new (line 34) | pub fn new(ws: WebSocketStream<T>) -> Self {
method poll_write (line 39) | fn poll_write(
method poll_flush (line 60) | fn poll_flush(
method poll_shutdown (line 68) | fn poll_shutdown(
method poll_read (line 77) | fn poll_read(
FILE: crates/rgb-frame/src/lib.rs
type Spec (line 11) | pub struct Spec {
method new (line 24) | pub fn new(width: usize, height: usize, fourcc: FourCC) -> Self {
type Frame (line 42) | pub struct Frame {
method new (line 52) | pub fn new(spec: Spec) -> Self {
method new_uninit (line 66) | pub fn new_uninit<F>(spec: Spec, populate: F) -> Self
method allocate_for_spec (line 79) | fn allocate_for_spec(spec: &Spec) -> (Vec<u8>, NonZeroUsize) {
method spec (line 95) | pub fn spec(&self) -> Spec {
method bytes (line 100) | pub fn bytes(&self) -> &[u8] {
method bytes_mut (line 105) | pub fn bytes_mut(&mut self) -> &mut [u8] {
method convert (line 110) | pub fn convert(&mut self, target: FourCC) {
type FourCC (line 156) | pub enum FourCC {
method has_alpha (line 177) | pub const fn has_alpha(self) -> bool {
method le_idx_rgba (line 182) | pub const fn le_idx_rgba(self) -> (usize, usize, usize, usize) {
method bytes_per_pixel (line 190) | pub const fn bytes_per_pixel(self) -> NonZeroUsize {
FILE: crates/viona-api/header-check/build.rs
function main (line 11) | fn main() {
FILE: crates/viona-api/src/ffi.rs
function vna_ioc (line 10) | const fn vna_ioc(ioc: i32) -> i32 {
constant VNA_IOC_CREATE (line 16) | pub const VNA_IOC_CREATE: i32 = vna_ioc(0x01);
constant VNA_IOC_DELETE (line 17) | pub const VNA_IOC_DELETE: i32 = vna_ioc(0x02);
constant VNA_IOC_VERSION (line 18) | pub const VNA_IOC_VERSION: i32 = vna_ioc(0x03);
constant VNA_IOC_DEFAULT_PARAMS (line 19) | pub const VNA_IOC_DEFAULT_PARAMS: i32 = vna_ioc(0x04);
constant VNA_IOC_RING_INIT (line 21) | pub const VNA_IOC_RING_INIT: i32 = vna_ioc(0x10);
constant VNA_IOC_RING_RESET (line 22) | pub const VNA_IOC_RING_RESET: i32 = vna_ioc(0x11);
constant VNA_IOC_RING_KICK (line 23) | pub const VNA_IOC_RING_KICK: i32 = vna_ioc(0x12);
constant VNA_IOC_RING_SET_MSI (line 24) | pub const VNA_IOC_RING_SET_MSI: i32 = vna_ioc(0x13);
constant VNA_IOC_RING_INTR_CLR (line 25) | pub const VNA_IOC_RING_INTR_CLR: i32 = vna_ioc(0x14);
constant VNA_IOC_RING_SET_STATE (line 26) | pub const VNA_IOC_RING_SET_STATE: i32 = vna_ioc(0x15);
constant VNA_IOC_RING_GET_STATE (line 27) | pub const VNA_IOC_RING_GET_STATE: i32 = vna_ioc(0x16);
constant VNA_IOC_RING_PAUSE (line 28) | pub const VNA_IOC_RING_PAUSE: i32 = vna_ioc(0x17);
constant VNA_IOC_RING_INIT_MODERN (line 29) | pub const VNA_IOC_RING_INIT_MODERN: i32 = vna_ioc(0x18);
constant VNA_IOC_INTR_POLL (line 31) | pub const VNA_IOC_INTR_POLL: i32 = vna_ioc(0x20);
constant VNA_IOC_SET_FEATURES (line 32) | pub const VNA_IOC_SET_FEATURES: i32 = vna_ioc(0x21);
constant VNA_IOC_GET_FEATURES (line 33) | pub const VNA_IOC_GET_FEATURES: i32 = vna_ioc(0x22);
constant VNA_IOC_SET_NOTIFY_IOP (line 34) | pub const VNA_IOC_SET_NOTIFY_IOP: i32 = vna_ioc(0x23);
constant VNA_IOC_SET_PROMISC (line 35) | pub const VNA_IOC_SET_PROMISC: i32 = vna_ioc(0x24);
constant VNA_IOC_GET_PARAMS (line 36) | pub const VNA_IOC_GET_PARAMS: i32 = vna_ioc(0x25);
constant VNA_IOC_SET_PARAMS (line 37) | pub const VNA_IOC_SET_PARAMS: i32 = vna_ioc(0x26);
constant VNA_IOC_GET_MTU (line 38) | pub const VNA_IOC_GET_MTU: i32 = vna_ioc(0x27);
constant VNA_IOC_SET_MTU (line 39) | pub const VNA_IOC_SET_MTU: i32 = vna_ioc(0x28);
constant VNA_IOC_SET_NOTIFY_MMIO (line 40) | pub const VNA_IOC_SET_NOTIFY_MMIO: i32 = vna_ioc(0x29);
constant VNA_IOC_INTR_POLL_MQ (line 41) | pub const VNA_IOC_INTR_POLL_MQ: i32 = vna_ioc(0x2a);
constant VNA_IOC_GET_PAIRS (line 44) | pub const VNA_IOC_GET_PAIRS: i32 = vna_ioc(0x30);
constant VNA_IOC_SET_PAIRS (line 45) | pub const VNA_IOC_SET_PAIRS: i32 = vna_ioc(0x31);
constant VNA_IOC_GET_USEPAIRS (line 46) | pub const VNA_IOC_GET_USEPAIRS: i32 = vna_ioc(0x32);
constant VNA_IOC_SET_USEPAIRS (line 47) | pub const VNA_IOC_SET_USEPAIRS: i32 = vna_ioc(0x33);
function test_vna_ioc (line 54) | fn test_vna_ioc() {
constant VIONA_MIN_QPAIR (line 60) | pub const VIONA_MIN_QPAIR: usize = 1;
constant VIONA_MAX_QPAIR (line 67) | pub const VIONA_MAX_QPAIR: usize = 0x100;
function howmany (line 69) | const fn howmany(x: usize, y: usize) -> usize {
constant VIONA_INTR_WORDS (line 77) | pub const VIONA_INTR_WORDS: usize = howmany(VIONA_MAX_QPAIR * 2, 32);
type vioc_create (line 80) | pub struct vioc_create {
type vioc_ring_init_modern (line 87) | pub struct vioc_ring_init_modern {
type vioc_ring_msi (line 98) | pub struct vioc_ring_msi {
type vioc_intr_poll_mq (line 107) | pub struct vioc_intr_poll_mq {
type vioc_notify_mmio (line 115) | pub struct vioc_notify_mmio {
type vioc_ring_state (line 122) | pub struct vioc_ring_state {
constant VIONA_PROMISC_NONE (line 132) | pub const VIONA_PROMISC_NONE: i32 = 0;
constant VIONA_PROMISC_MULTI (line 133) | pub const VIONA_PROMISC_MULTI: i32 = 1;
constant VIONA_PROMISC_ALL (line 134) | pub const VIONA_PROMISC_ALL: i32 = 2;
constant VIONA_PROMISC_ALL_VLAN (line 136) | pub const VIONA_PROMISC_ALL_VLAN: i32 = 3;
type vioc_get_params (line 140) | pub struct vioc_get_params {
type vioc_set_params (line 147) | pub struct vioc_set_params {
constant VIONA_CURRENT_INTERFACE_VERSION (line 157) | pub const VIONA_CURRENT_INTERFACE_VERSION: u32 = 6;
constant VIONA_MAX_PARAM_NVLIST_SZ (line 160) | pub const VIONA_MAX_PARAM_NVLIST_SZ: usize = 4096;
FILE: crates/viona-api/src/lib.rs
constant VIONA_DEV_PATH (line 18) | pub const VIONA_DEV_PATH: &str = "/dev/viona";
type VionaFd (line 20) | pub struct VionaFd(File);
method new (line 24) | pub fn new(link_id: u32, vm_fd: RawFd) -> Result<Self> {
method open (line 33) | pub fn open() -> Result<Self> {
method set_parameters (line 41) | pub fn set_parameters(
method get_parameters (line 72) | pub fn get_parameters(&self) -> Result<NvList> {
method ioctl (line 94) | pub unsafe fn ioctl<T>(&self, cmd: i32, data: *mut T) -> Result<i32> {
method ioctl_usize (line 98) | pub fn ioctl_usize(&self, cmd: i32, data: usize) -> Result<i32> {
method api_version (line 113) | pub fn api_version(&self) -> Result<u32> {
method instance_id (line 124) | pub fn instance_id(&self) -> Result<u32> {
method ioctl_usize_safe (line 131) | const fn ioctl_usize_safe(cmd: i32) -> bool {
method as_raw_fd (line 152) | fn as_raw_fd(&self) -> RawFd {
type ParamError (line 158) | pub enum ParamError {
function ioctl (line 164) | unsafe fn ioctl(fd: RawFd, cmd: i32, data: *mut libc::c_void) -> Result<...
function ioctl (line 172) | unsafe fn ioctl(
function minor (line 181) | fn minor(meta: &std::fs::Metadata) -> u32 {
function minor (line 190) | fn minor(meta: &std::fs::Metadata) -> u32 {
type ApiVersion (line 199) | pub enum ApiVersion {
method current (line 220) | pub const fn current() -> Self {
function eq (line 225) | fn eq(&self, other: &ApiVersion) -> bool {
function partial_cmp (line 230) | fn partial_cmp(&self, other: &ApiVersion) -> Option<std::cmp::Ordering> {
function api_version (line 248) | pub fn api_version() -> Result<u32> {
function cache_api_version (line 256) | fn cache_api_version(do_query: impl FnOnce() -> Result<u32>) -> Result<u...
function latest_api_version (line 288) | fn latest_api_version() {
function u32_comparisons (line 294) | fn u32_comparisons() {
FILE: lib/propolis-client/src/support.rs
method default (line 25) | fn default() -> Self {
function nvme_serial_from_str (line 46) | pub fn nvme_serial_from_str(s: &str, pad: u8) -> [u8; 20] {
type InstanceSerialConsoleControlMessage (line 62) | pub enum InstanceSerialConsoleControlMessage {
type SerialConsoleStream (line 67) | pub trait SerialConsoleStream: AsyncRead + AsyncWrite + Unpin + Send {}
type SerialConsoleStreamBuilder (line 72) | pub(crate) trait SerialConsoleStreamBuilder: Send {
method build (line 73) | async fn build(
method build (line 94) | async fn build(
method build (line 154) | async fn build(
type PropolisSerialBuilder (line 83) | struct PropolisSerialBuilder {}
method new (line 87) | pub fn new() -> Self {
type TestSerialBuilder (line 131) | pub struct TestSerialBuilder<St> {
function new (line 136) | fn new(
type WSClientOffset (line 175) | pub enum WSClientOffset {
type InstanceSerialConsoleHelper (line 191) | pub struct InstanceSerialConsoleHelper {
method new (line 202) | pub async fn new(
method new_test (line 216) | pub async fn new_test<St: SerialConsoleStream + 'static>(
method new_test_with_delays (line 237) | pub async fn new_test_with_delays<St: SerialConsoleStream + 'static>(
method new_with_builder (line 248) | pub(crate) async fn new_with_builder(
method recv (line 271) | pub async fn recv(
type Error (line 294) | type Error = WSError;
method poll_ready (line 296) | fn poll_ready(
method start_send (line 303) | fn start_send(
method poll_flush (line 310) | fn poll_flush(
method poll_close (line 317) | fn poll_close(
type InstanceSerialConsoleMessage (line 326) | pub struct InstanceSerialConsoleMessage<'a> {
function process (line 391) | pub async fn process(self) -> Result<WSMessage, WSError> {
function assert_send (line 432) | fn assert_send<T: Send>() {}
function _assert_impls (line 434) | fn _assert_impls() {
function test_connection_helper (line 458) | async fn test_connection_helper() {
function test_recv_cancel_safety (line 502) | async fn test_recv_cancel_safety() {
function make_ws_server (line 594) | async fn make_ws_server<S>(conn: S) -> WebSocketStream<S>
function test_nvme_serial_from_str (line 602) | fn test_nvme_serial_from_str() {
FILE: lib/propolis/src/accessors.rs
type AccessedResource (line 58) | pub trait AccessedResource {
method derive (line 63) | fn derive(root: &Self::Root) -> Self::Leaf;
method deref (line 64) | fn deref(leaf: &Self::Leaf) -> &Self::Target;
type Root (line 90) | type Root = ();
type Leaf (line 91) | type Leaf = ();
type Target (line 92) | type Target = ();
method derive (line 93) | fn derive(_root: &Self::Root) -> Self::Leaf {
method deref (line 96) | fn deref(_derived: &Self::Root) -> &Self::Target {
type Root (line 744) | type Root = Arc<VmmHdl>;
type Leaf (line 745) | type Leaf = Arc<VmmHdl>;
type Target (line 746) | type Target = VmmHdl;
method derive (line 748) | fn derive(root: &Self::Root) -> Self::Leaf {
method deref (line 751) | fn deref(leaf: &Self::Leaf) -> &Self::Target {
type Root (line 815) | type Root = Arc<AtomicUsize>;
type Leaf (line 816) | type Leaf = Arc<AtomicUsize>;
type Target (line 817) | type Target = AtomicUsize;
method derive (line 819) | fn derive(root: &Self::Root) -> Self::Leaf {
method deref (line 822) | fn deref(leaf: &Self::Leaf) -> &Self::Target {
type NodeKey (line 69) | pub struct NodeKey(NonNull<Node<NodeKeyNull>>);
method from (line 71) | fn from(value: &Arc<Node<T>>) -> Self {
method fmt (line 79) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type NodeKeyNull (line 88) | enum NodeKeyNull {}
type TreeNode (line 101) | struct TreeNode<T: AccessedResource> {
function new (line 115) | fn new(
function new_root (line 127) | fn new_root(node_ref: Weak<Node<T>>) -> Self {
type Tree (line 137) | struct Tree<T: AccessedResource> {
function add_child (line 156) | fn add_child(
function adopt (line 187) | fn adopt(&mut self, parent_key: NodeKey, adopt_tree: &mut Tree<T>) {
function remove_dead_node (line 241) | fn remove_dead_node(&mut self, key: NodeKey) {
function orphan_node (line 269) | fn orphan_node(&mut self, key: NodeKey) {
function rename_node (line 315) | fn rename_node(&mut self, key: NodeKey, name: Option<String>) {
function node_is_root (line 322) | fn node_is_root(&self, node: &Arc<Node<T>>) -> bool {
function set_root_resource (line 326) | fn set_root_resource(
function node_count (line 344) | fn node_count(&self) -> usize {
function print (line 350) | fn print(&self, print_fn: impl Fn(PrintNode)) {
function root_key (line 384) | fn root_key(&self) -> NodeKey {
function new_empty (line 389) | fn new_empty(primary: Option<T::Root>) -> Arc<Mutex<Tree<T>>> {
function new (line 401) | fn new(res_root: Option<T::Root>) -> Arc<Node<T>> {
type PrintNode (line 417) | pub struct PrintNode<'a> {
function print_basic (line 425) | fn print_basic(match_node: Option<NodeKey>) -> impl Fn(PrintNode) {
type TreeBackref (line 440) | type TreeBackref<T> = Arc<Mutex<Tree<T>>>;
type NodeEntry (line 442) | struct NodeEntry<T: AccessedResource> {
type Node (line 452) | struct Node<T: AccessedResource>(Mutex<NodeEntry<T>>);
function try_lock_tree (line 463) | fn try_lock_tree<'node, 'guard>(
function lock_tree (line 481) | fn lock_tree<'node, F, R>(&'node self, f: F) -> R
function new_root (line 499) | fn new_root(tree: Arc<Mutex<Tree<T>>>) -> Arc<Node<T>> {
function new_child (line 502) | fn new_child(self: &Arc<Node<T>>, name: Option<String>) -> Arc<Node<T>> {
function guard (line 511) | fn guard(&self) -> Option<Guard<'_, T>> {
function guard_borrow (line 526) | fn guard_borrow(&self) -> Option<MutexGuard<'_, NodeEntry<T>>> {
function drop_from_tree (line 546) | fn drop_from_tree(self: &mut Arc<Node<T>>) {
type Guard (line 565) | pub struct Guard<'a, T: AccessedResource> {
type Target (line 570) | type Target = T::Target;
function deref (line 571) | fn deref(&self) -> &Self::Target {
type LockedView (line 576) | pub struct LockedView<'node, T: AccessedResource> {
function view (line 581) | pub fn view(&self) -> &T::Target {
type Accessor (line 591) | pub struct Accessor<T: AccessedResource>(Arc<Node<T>>);
function new (line 594) | pub fn new(resource: T::Root) -> Self {
function new_orphan (line 601) | pub fn new_orphan() -> Self {
function child (line 606) | pub fn child(&self, name: Option<String>) -> Self {
function adopt (line 615) | pub fn adopt(&self, child: &Self, name: Option<String>) {
function remove_resource (line 648) | pub fn remove_resource(&self) -> Option<T::Root> {
function access (line 669) | pub fn access(&self) -> Option<Guard<'_, T>> {
function access_locked (line 696) | pub fn access_locked(&self) -> Option<LockedView<'_, T>> {
function node_count (line 706) | pub fn node_count(&self) -> usize {
function print (line 711) | pub fn print(&self, highlight_self: bool) {
method drop (line 735) | fn drop(&mut self) {
type MemAccessor (line 740) | pub type MemAccessor = Accessor<crate::vmm::mem::MemAccessed>;
type MsiAccessed (line 742) | enum MsiAccessed {}
type MsiAccessor (line 757) | pub struct MsiAccessor(Accessor<MsiAccessed>);
method new (line 760) | pub fn new(hdl: Arc<VmmHdl>) -> Self {
method new_orphan (line 764) | pub fn new_orphan() -> Self {
method child (line 768) | pub fn child(&self, name: Option<String>) -> Self {
method adopt (line 772) | pub fn adopt(&self, child: &Self, name: Option<String>) {
method remove_resource (line 776) | pub fn remove_resource(&self) -> Option<Arc<VmmHdl>> {
method send (line 783) | pub fn send(&self, addr: u64, msg: u64) -> Result<(), ()> {
method fmt (line 793) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type AtomicRes (line 813) | enum AtomicRes {}
function new_root (line 829) | fn new_root() -> Accessor<AtomicRes> {
function new_orphan (line 832) | fn new_orphan() -> Accessor<AtomicRes> {
function new_depth (line 835) | fn new_depth(
function tree_root (line 852) | fn tree_root() {
function simple_orphan (line 867) | fn simple_orphan() {
function only_root_can_remove_resource (line 880) | fn only_root_can_remove_resource() {
function adopt_self (line 892) | fn adopt_self() {
function adopt_nonroot (line 899) | fn adopt_nonroot() {
function simple_depth (line 908) | fn simple_depth() {
function orphan_split (line 926) | fn orphan_split() {
function orphan_sibling (line 964) | fn orphan_sibling() {
function print_names (line 985) | fn print_names() {
FILE: lib/propolis/src/api_version.rs
type Error (line 6) | pub enum Error {
type VersionCheckError (line 22) | pub struct VersionCheckError {
method vmm (line 30) | fn vmm(err: Error) -> Self {
method viona (line 34) | fn viona(err: Error) -> Self {
function check (line 39) | pub fn check() -> Result<(), VersionCheckError> {
FILE: lib/propolis/src/attestation/boot_digest/crucible.rs
function boot_disk_digest (line 18) | pub async fn boot_disk_digest(
FILE: lib/propolis/src/attestation/boot_digest/mod.rs
type Backend (line 14) | pub enum Backend {
function compute (line 19) | pub async fn compute(backend: Backend, log: &Logger) -> Result<Measureme...
FILE: lib/propolis/src/attestation/mod.rs
constant ATTESTATION_PORT (line 50) | pub const ATTESTATION_PORT: u16 = 605;
constant ATTESTATION_ADDR (line 51) | pub const ATTESTATION_ADDR: SocketAddr =
FILE: lib/propolis/src/attestation/server.rs
type AttestationServerConfig (line 24) | pub struct AttestationServerConfig {
method new (line 29) | pub fn new(sled_agent_addr: SocketAddrV6) -> Self {
type AttestationSock (line 34) | pub struct AttestationSock {
method new (line 108) | pub async fn new(log: Logger, sa_addr: SocketAddrV6) -> io::Result<Sel...
method halt (line 144) | pub async fn halt(self) {
method handle_conn (line 157) | async fn handle_conn(
method handle_conn_inner (line 175) | async fn handle_conn_inner(
method prepare_instance_conf (line 274) | pub fn prepare_instance_conf(
method run (line 302) | pub async fn run(
type AttestationInitState (line 42) | enum AttestationInitState {
type AttestationSockInit (line 57) | pub struct AttestationSockInit {
method run (line 67) | pub async fn run(self) {
FILE: lib/propolis/src/block/attachment.rs
constant MAX_WORKERS (line 43) | pub const MAX_WORKERS: NonZeroUsize = NonZeroUsize::new(64).unwrap();
type ReqCountHint (line 45) | pub type ReqCountHint = Option<NonZeroUsize>;
type QueueSlotState (line 48) | struct QueueSlotState {
type QueueSlot (line 52) | struct QueueSlot {
method new (line 59) | fn new(queue_id: QueueId) -> Self {
method request_notify (line 67) | fn request_notify(&self, hint: ReqCountHint) {
method flush_notifications (line 75) | fn flush_notifications(&self) {
type QueueColState (line 131) | struct QueueColState {
method queue_associate (line 137) | fn queue_associate(&mut self, qid: QueueId) -> Versioned<Bitmap> {
method queue_dissociate (line 141) | fn queue_dissociate(&mut self, qid: QueueId) -> Versioned<Bitmap> {
type QueueCollection (line 146) | struct QueueCollection {
method new (line 152) | fn new(max_queues: NonZeroUsize, devid: DeviceId) -> Arc<Self> {
method attach (line 160) | fn attach(&self, workers: &Arc<WorkerCollection>) {
method detach (line 166) | fn detach(&self) {
method slot (line 172) | fn slot(&self, queue_id: QueueId) -> &QueueSlot {
method notify (line 175) | fn notify(&self, queue_id: QueueId, hint: ReqCountHint) {
method set_metric_consumer (line 180) | fn set_metric_consumer(&self, consumer: Arc<dyn MetricConsumer>) {
method associated_qids (line 189) | fn associated_qids(&self) -> Versioned<Bitmap> {
method pause (line 192) | fn pause(&self) {
method resume (line 203) | fn resume(&self) {
method none_processing (line 219) | fn none_processing(&self) -> NoneProcessing {
method next_req (line 235) | fn next_req(
method next_req_any (line 257) | fn next_req_any(
type MinderRefs (line 292) | struct MinderRefs {
type Output (line 315) | type Output = ();
method poll (line 317) | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
type AttachPair (line 363) | struct AttachPair {
method attach (line 368) | fn attach(
method detach (line 406) | fn detach(self) {
method eq (line 443) | fn eq(&self, other: &Self) -> bool {
type OnAttachFn (line 449) | pub type OnAttachFn = Box<dyn Fn(DeviceInfo) + Send + Sync + 'static>;
type DeviceState (line 452) | struct DeviceState {
type DeviceAttachInner (line 456) | struct DeviceAttachInner {
type DeviceAttachment (line 465) | pub struct DeviceAttachment(Arc<DeviceAttachInner>);
method new (line 470) | pub fn new(max_queues: NonZeroUsize, acc_mem: MemAccessor) -> Self {
method queues_update_assoc (line 484) | fn queues_update_assoc(&self, queues_associated: Versioned<Bitmap>) {
method queue_associate (line 503) | pub fn queue_associate(
method queue_dissociate (line 546) | pub fn queue_dissociate(&self, queue_id: QueueId) {
method notify (line 565) | pub fn notify(&self, queue_id: QueueId, hint: ReqCountHint) {
method device_id (line 569) | pub fn device_id(&self) -> DeviceId {
method max_queues (line 574) | pub fn max_queues(&self) -> NonZeroUsize {
method info (line 579) | pub fn info(&self) -> Option<DeviceInfo> {
method detach (line 586) | pub fn detach(&self) {
method pause (line 597) | pub fn pause(&self) {
method resume (line 603) | pub fn resume(&self) {
method none_processing (line 609) | pub fn none_processing(&self) -> NoneProcessing {
method set_metric_consumer (line 615) | pub fn set_metric_consumer(&self, consumer: Arc<dyn MetricConsumer>) {
method on_attach (line 622) | pub fn on_attach(&self, cb: OnAttachFn) {
method drop (line 627) | fn drop(&mut self) {
type PollCursor (line 633) | struct PollCursor(QueueId);
method suggest (line 636) | fn suggest(&mut self, queue_id: QueueId) {
type WorkerState (line 642) | struct WorkerState {
type WorkerSlot (line 656) | pub(crate) struct WorkerSlot {
method new (line 664) | fn new(id: WorkerId) -> Self {
method block_for_req (line 673) | fn block_for_req(&self) -> Option<DeviceRequest> {
method next_req (line 696) | fn next_req(&self, state: &mut MutexGuard<WorkerState>) -> PollResult {
method async_start_sleep (line 725) | fn async_start_sleep(
method async_stop_sleep (line 734) | fn async_stop_sleep(&self) {
method wait_for_req (line 741) | fn wait_for_req(&self) -> WaitForReq<'_> {
method update_assignment (line 745) | fn update_assignment(&self, assign: &Assignment) {
method wake (line 765) | fn wake(
type PollAssignment (line 790) | enum PollAssignment {
type WorkerColState (line 803) | struct WorkerColState {
method set_worker_state (line 815) | fn set_worker_state(&mut self, wid: WorkerId, is_active: bool) {
method generate_assignments (line 825) | fn generate_assignments(&mut self) -> Assignment {
type Strategy (line 903) | pub enum Strategy {
method choose (line 920) | pub fn choose(worker_count: usize, queue_count: usize) -> Self {
type Assignment (line 936) | struct Assignment {
type WorkerCollection (line 942) | pub(crate) struct WorkerCollection {
method new (line 947) | fn new(max_workers: NonZeroUsize) -> Arc<Self> {
method set_active (line 955) | fn set_active(&self, id: WorkerId, new_type: Option<WorkerType>) -> bo...
method assignments_refresh (line 976) | fn assignments_refresh(&self, mut state: MutexGuard<WorkerColState>) {
method slot (line 990) | fn slot(&self, id: WorkerId) -> &WorkerSlot {
method attach (line 993) | fn attach(&self, parent_mem: &MemAccessor, queues: &Arc<QueueCollectio...
method detach (line 1005) | fn detach(&self) {
method wake (line 1017) | fn wake(
method update_queue_associations (line 1048) | fn update_queue_associations(&self, queues_associated: Versioned<Bitma...
method start (line 1053) | fn start(&self) {
method stop (line 1058) | fn stop(&self) {
type WorkerType (line 1066) | pub enum WorkerType {
type InactiveWorkerCtx (line 1071) | pub struct InactiveWorkerCtx {
method activate_sync (line 1080) | pub fn activate_sync(self) -> Option<SyncWorkerCtx> {
method activate_async (line 1092) | pub fn activate_async(self) -> Option<AsyncWorkerCtx> {
type SyncWorkerCtx (line 1105) | pub struct SyncWorkerCtx(WorkerCtxInner);
method block_for_req (line 1111) | pub fn block_for_req(&self) -> Option<DeviceRequest> {
method acc_mem (line 1115) | pub fn acc_mem(&self) -> &MemAccessor {
type AsyncWorkerCtx (line 1124) | pub struct AsyncWorkerCtx(WorkerCtxInner);
method wait_for_req (line 1128) | pub fn wait_for_req(&self) -> WaitForReq<'_> {
method acc_mem (line 1132) | pub fn acc_mem(&self) -> &MemAccessor {
type WorkerCtxInner (line 1137) | struct WorkerCtxInner {
method from (line 1142) | fn from(value: InactiveWorkerCtx) -> Self {
method acc_mem (line 1148) | fn acc_mem(&self) -> &MemAccessor {
method drop (line 1154) | fn drop(&mut self) {
type BackendAttachInner (line 1162) | struct BackendAttachInner {
type BackendAttachment (line 1170) | pub struct BackendAttachment(Arc<BackendAttachInner>);
method new (line 1172) | pub fn new(max_workers: NonZeroUsize, info: DeviceInfo) -> Self {
method worker (line 1181) | pub fn worker(&self, id: WorkerId) -> InactiveWorkerCtx {
method max_workers (line 1186) | pub fn max_workers(&self) -> NonZeroUsize {
method info (line 1191) | pub fn info(&self) -> DeviceInfo {
method backend_id (line 1195) | pub fn backend_id(&self) -> BackendId {
method start (line 1201) | pub fn start(&self) {
method stop (line 1208) | pub fn stop(&self) {
method detach (line 1213) | pub fn detach(&self) {
method drop (line 1224) | fn drop(&mut self) {
function attach (line 1230) | pub fn attach(
function new (line 1257) | fn new<'a>(slot: &'a WorkerSlot) -> WaitForReq<'a> {
type Output (line 1264) | type Output = Option<DeviceRequest>;
method poll (line 1265) | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
type PollResult (line 1305) | enum PollResult {
type AttachError (line 1317) | pub enum AttachError {
type Versioned (line 1326) | struct Versioned<T: Copy + Clone> {
function new (line 1331) | fn new(item: T) -> Self {
function newer_than (line 1335) | fn newer_than(&self, compare: &Self) -> bool {
function update (line 1339) | fn update(&mut self) -> &mut T {
function replace (line 1344) | fn replace(&mut self, item: T) {
function replace_if_newer (line 1347) | fn replace_if_newer(&mut self, compare: &Self) {
function get (line 1352) | fn get(&self) -> T {
function generation (line 1355) | fn generation(&self) -> usize {
method default (line 1360) | fn default() -> Self {
type Bitmap (line 1367) | pub(crate) struct Bitmap(u64);
constant TOP_BIT (line 1369) | const TOP_BIT: usize = u64::BITS as usize;
constant ALL (line 1371) | pub const ALL: Self = Self(u64::MAX);
method set (line 1373) | pub fn set(&mut self, idx: usize) {
method unset (line 1377) | pub fn unset(&mut self, idx: usize) {
method set_all (line 1381) | pub fn set_all(&mut self, other: Bitmap) {
method lowest_set (line 1384) | pub fn lowest_set(&self) -> Option<usize> {
method count (line 1391) | pub fn count(&self) -> usize {
method is_empty (line 1394) | pub fn is_empty(&self) -> bool {
method take (line 1397) | pub fn take(&mut self) -> Self {
method iter (line 1401) | pub fn iter(&self) -> BitIter {
method looping_iter (line 1407) | pub fn looping_iter(&self) -> LoopIter {
type BitIter (line 1412) | pub struct BitIter(Bitmap);
method remainder (line 1423) | fn remainder(self) -> Bitmap {
type Item (line 1414) | type Item = usize;
method next (line 1416) | fn next(&mut self) -> Option<Self::Item> {
type LoopIter (line 1427) | pub struct LoopIter {
type Item (line 1432) | type Item = usize;
method next (line 1434) | fn next(&mut self) -> Option<Self::Item> {
FILE: lib/propolis/src/block/crucible.rs
constant WORKER_COUNT (line 30) | const WORKER_COUNT: NonZeroUsize = NonZeroUsize::new(8).unwrap();
type CrucibleBackend (line 32) | pub struct CrucibleBackend {
method create (line 161) | pub async fn create(
method block_size (line 242) | pub async fn block_size(&self) -> Option<u32> {
method create_mem (line 253) | pub async fn create_mem(
method remove_read_only_parent (line 295) | async fn remove_read_only_parent(
method get_uuid (line 321) | pub async fn get_uuid(&self) -> io::Result<uuid::Uuid> {
method snapshot (line 326) | pub async fn snapshot(&self, snapshot_id: Uuid) -> io::Result<()> {
method vcr_replace (line 337) | pub async fn vcr_replace(
method spawn_workers (line 351) | fn spawn_workers(&self) {
method volume_is_active (line 366) | pub async fn volume_is_active(&self) -> Result<bool, CrucibleError> {
method clone_volume (line 370) | pub fn clone_volume(&self) -> Volume {
method is_read_only (line 374) | pub fn is_read_only(&self) -> bool {
method query_volume_info (line 378) | pub async fn query_volume_info(&self) -> Result<VolumeInfo, CrucibleEr...
method attachment (line 385) | fn attachment(&self) -> &block::BackendAttachment {
method start (line 388) | async fn start(&self) -> anyhow::Result<()> {
method stop (line 394) | async fn stop(&self) -> () {
method as_any (line 398) | fn as_any(&self) -> &dyn std::any::Any {
type WorkerState (line 37) | struct WorkerState {
method process_loop (line 43) | async fn process_loop(&self, wctx: block::AsyncWorkerCtx) {
method process_request (line 79) | async fn process_request(
type Error (line 404) | pub enum Error {
function from (line 425) | fn from(value: Error) -> Self {
function block_offset_count (line 435) | fn block_offset_count(
function err_on_bad_offset (line 457) | fn err_on_bad_offset() {
function err_on_bad_size (line 464) | fn err_on_bad_size() {
function ok_for_valid (line 471) | fn ok_for_valid() {
function block_calc_ok (line 478) | fn block_calc_ok() {
FILE: lib/propolis/src/block/file.rs
type FileBackend (line 19) | pub struct FileBackend {
method create (line 179) | pub fn create(
method spawn_workers (line 234) | fn spawn_workers(&self) -> std::io::Result<()> {
method attachment (line 258) | fn attachment(&self) -> &block::BackendAttachment {
method start (line 262) | async fn start(&self) -> anyhow::Result<()> {
method stop (line 273) | async fn stop(&self) -> () {
method as_any (line 278) | fn as_any(&self) -> &dyn std::any::Any {
type SharedState (line 26) | struct SharedState {
method new (line 42) | fn new(
method processing_loop (line 65) | fn processing_loop(&self, wctx: SyncWorkerCtx) {
method process_request (line 89) | fn process_request(
method set_wce (line 151) | fn set_wce(&self, enabled: bool) {
type WceState (line 37) | struct WceState {
method drop (line 166) | fn drop(&mut self) {
constant DKIOC (line 295) | const DKIOC: i32 = 0x04 << 8;
constant DKIOCGETWCE (line 296) | const DKIOCGETWCE: i32 = DKIOC | 36;
constant DKIOCSETWCE (line 297) | const DKIOCSETWCE: i32 = DKIOC | 37;
constant DKIOCGMEDIAINFOEXT (line 298) | const DKIOCGMEDIAINFOEXT: i32 = DKIOC | 48;
constant DKIOCFREE (line 299) | const DKIOCFREE: i32 = DKIOC | 50;
constant DKIOC_CANFREE (line 300) | const DKIOC_CANFREE: i32 = DKIOC | 60;
type DiscardMech (line 303) | pub(crate) enum DiscardMech {
type DiskInfo (line 312) | pub(crate) struct DiskInfo {
method default (line 326) | fn default() -> Self {
function disk_info (line 335) | pub(crate) fn disk_info(fp: &File) -> DiskInfo {
function set_wce (line 388) | pub(crate) fn set_wce(fp: &File, enabled: bool) -> Result<bool> {
function do_discard (line 396) | pub(crate) fn do_discard(
type dkioc_free_list_ext (line 466) | struct dkioc_free_list_ext {
type dkioc_free_list (line 473) | struct dkioc_free_list {
type dk_minfo_ext (line 482) | struct dk_minfo_ext {
FILE: lib/propolis/src/block/in_memory.rs
type InMemoryBackend (line 20) | pub struct InMemoryBackend {
method create (line 99) | pub fn create(
method spawn_workers (line 135) | fn spawn_workers(&self) -> Result<()> {
method attachment (line 156) | fn attachment(&self) -> &block::BackendAttachment {
method start (line 160) | async fn start(&self) -> anyhow::Result<()> {
method stop (line 171) | async fn stop(&self) -> () {
method as_any (line 176) | fn as_any(&self) -> &dyn std::any::Any {
type SharedState (line 26) | struct SharedState {
method processing_loop (line 31) | fn processing_loop(&self, wctx: block::SyncWorkerCtx) {
method process_request (line 57) | fn process_request(
function process_read_request (line 182) | fn process_read_request(
function process_write_request (line 215) | fn process_write_request(
method type_name (line 248) | fn type_name(&self) -> &'static str {
method migrate (line 252) | fn migrate(&self) -> Migrator<'_> {
method export (line 258) | fn export(
method import (line 266) | fn import(
type InMemoryBlockBackendV1 (line 293) | pub struct InMemoryBlockBackendV1 {
method fmt (line 298) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method id (line 306) | fn id() -> SchemaId {
FILE: lib/propolis/src/block/mem_async.rs
type MemAsyncBackend (line 19) | pub struct MemAsyncBackend {
method create (line 102) | pub fn create(
method spawn_workers (line 138) | fn spawn_workers(&self) {
method attachment (line 154) | fn attachment(&self) -> &block::BackendAttachment {
method start (line 157) | async fn start(&self) -> anyhow::Result<()> {
method stop (line 162) | async fn stop(&self) -> () {
method as_any (line 166) | fn as_any(&self) -> &dyn std::any::Any {
type SharedState (line 25) | struct SharedState {
method processing_loop (line 30) | async fn processing_loop(&self, wctx: block::AsyncWorkerCtx) {
method process_request (line 54) | fn process_request(
type MmapSeg (line 171) | struct MmapSeg(NonNull<u8>, usize);
method new (line 173) | fn new(size: usize) -> Result<Self> {
method write (line 190) | unsafe fn write(&self, off: usize, data: *const u8, sz: usize) -> bool {
method read (line 198) | unsafe fn read(&self, off: usize, data: *mut u8, sz: usize) -> bool {
method drop (line 208) | fn drop(&mut self) {
FILE: lib/propolis/src/block/minder.rs
type DeviceQueue (line 28) | pub trait DeviceQueue: Send + Sync + 'static {
method next_req (line 37) | fn next_req(&self) -> Option<(Request, Self::Token, Option<Instant>)>;
method complete (line 41) | fn complete(
method abandon (line 61) | fn abandon(&self, token: Self::Token);
type DeviceRequest (line 69) | pub struct DeviceRequest {
method new (line 76) | fn new(id: ReqId, req: Request, source: Weak<QueueMinder>) -> Self {
method req (line 81) | pub fn req(&self) -> &Request {
method complete (line 86) | pub fn complete(self, result: super::Result) {
type NoDropDevReq (line 98) | struct NoDropDevReq;
method drop (line 100) | fn drop(&mut self) {
type NextReqFn (line 107) | type NextReqFn = Box<
type CompleteReqFn (line 118) | type CompleteReqFn = Box<
type AbandonReqFn (line 124) | type AbandonReqFn = Box<dyn Fn(Box<dyn Any + Send + Sync>) + Send + Sync>;
type QmEntry (line 126) | struct QmEntry {
type QmInner (line 133) | struct QmInner {
method default (line 155) | fn default() -> Self {
type QueueMinder (line 168) | pub(super) struct QueueMinder {
method destroy (line 183) | pub fn destroy(self: Arc<Self>) {
method new (line 209) | pub fn new<DQ: DeviceQueue>(
method next_req (line 258) | pub fn next_req(&self, wid: WorkerId) -> Option<DeviceRequest> {
method complete (line 312) | pub fn complete(&self, id: ReqId, result: block::Result) {
method take_notifications (line 402) | pub(in crate::block) fn take_notifications(&self) -> Option<Bitmap> {
method add_notifications (line 418) | pub(in crate::block) fn add_notifications(&self, worker_ids: Bitmap) {
method set_metric_consumer (line 427) | pub(crate) fn set_metric_consumer(
method pause (line 434) | pub(crate) fn pause(&self) {
method resume (line 440) | pub(crate) fn resume(&self) {
method none_in_flight (line 446) | pub(crate) fn none_in_flight(&self) -> NoneInFlight<'_> {
type Output (line 461) | type Output = ();
method poll (line 463) | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
type ReqId (line 486) | pub struct ReqId(u64);
constant START (line 488) | const START: Self = ReqId(0);
method advance (line 490) | fn advance(&mut self) {
method borrow (line 495) | fn borrow(&self) -> &u64 {
function from (line 500) | fn from(value: ReqId) -> Self {
FILE: lib/propolis/src/block/mod.rs
type ByteOffset (line 38) | pub type ByteOffset = usize;
type ByteLen (line 39) | pub type ByteLen = usize;
constant DEFAULT_BLOCK_SIZE (line 43) | pub const DEFAULT_BLOCK_SIZE: u32 = 512;
function block_attach (line 47) | fn block_attach(dev_id: u32, backend_id: u32) {}
function block_detach (line 48) | fn block_detach(dev_id: u32, backend_id: u32) {}
function block_begin_read (line 50) | fn block_begin_read(devq_id: u64, req_id: u64, offset: u64, len: u64) {}
function block_begin_write (line 51) | fn block_begin_write(devq_id: u64, req_id: u64, offset: u64, len: u64) {}
function block_begin_flush (line 52) | fn block_begin_flush(devq_id: u64, req_id: u64) {}
function block_begin_discard (line 53) | fn block_begin_discard(devq_id: u64, req_id: u64, nr: u64) {}
function block_complete_read (line 55) | fn block_complete_read(
function block_complete_write (line 63) | fn block_complete_write(
function block_complete_flush (line 71) | fn block_complete_flush(
function block_complete_discard (line 79) | fn block_complete_discard(
function block_completion_sent (line 88) | fn block_completion_sent(devq_id: u64, req_id: u64, complete_ns: u64) {}
function block_poll (line 90) | fn block_poll(devq_id: u64, worker_id: u64, emit_req: u8) {}
function block_sleep (line 91) | fn block_sleep(dev_id: u32, worker_id: u64) {}
function block_wake (line 92) | fn block_wake(dev_id: u32, worker_id: u64) {}
function block_notify (line 93) | fn block_notify(devq_id: u64) {}
function block_strategy (line 94) | fn block_strategy(dev_id: u32, strat: String, generation: u64) {}
function block_worker_collection_wake (line 96) | fn block_worker_collection_wake(wake_wids: u64, limit: usize) {}
function block_worker_collection_woken (line 97) | fn block_worker_collection_woken(remaining_wids: u64, num_woken: usize) {}
type Operation (line 102) | pub enum Operation {
method is_read (line 113) | pub const fn is_read(&self) -> bool {
method is_write (line 116) | pub const fn is_write(&self) -> bool {
method is_flush (line 119) | pub const fn is_flush(&self) -> bool {
method is_discard (line 122) | pub const fn is_discard(&self) -> bool {
type Result (line 129) | pub enum Result {
method is_err (line 140) | pub const fn is_err(&self) -> bool {
type QueueId (line 146) | pub struct QueueId(u8);
constant MAX_QUEUES (line 150) | pub const MAX_QUEUES: usize = 64;
constant MAX (line 152) | pub const MAX: Self = Self(Self::MAX_QUEUES as u8);
method next (line 155) | fn next(self, max: usize) -> Self {
method from (line 168) | fn from(value: usize) -> Self {
method from (line 179) | fn from(value: u16) -> Self {
function from (line 174) | fn from(value: QueueId) -> Self {
function from (line 185) | fn from(value: QueueId) -> Self {
type WorkerId (line 190) | pub type WorkerId = usize;
function devq_id (line 193) | pub(crate) fn devq_id(dev: DeviceId, queue: QueueId) -> u64 {
type Request (line 199) | pub struct Request {
method new_read (line 212) | pub fn new_read(
method new_write (line 220) | pub fn new_write(
method new_flush (line 228) | pub fn new_flush() -> Self {
method new_discard (line 233) | pub fn new_discard(ranges: Vec<(ByteOffset, ByteLen)>) -> Self {
method mappings (line 238) | pub fn mappings<'a>(&self, mem: &'a MemCtx) -> Option<Vec<SubMapping<'...
type DeviceInfo (line 253) | pub struct DeviceInfo {
type BackendOpts (line 270) | pub struct BackendOpts {
method is_read_only (line 284) | pub fn is_read_only(&self) -> bool {
type Device (line 291) | pub trait Device: Send + Sync + 'static {
method attachment (line 293) | fn attachment(&self) -> &DeviceAttachment;
type Backend (line 299) | pub trait Backend: Send + Sync + 'static {
method attachment (line 301) | fn attachment(&self) -> &BackendAttachment;
method start (line 318) | async fn start(&self) -> anyhow::Result<()>;
method stop (line 333) | async fn stop(&self);
method as_any (line 336) | fn as_any(&self) -> &dyn std::any::Any;
type MetricConsumer (line 340) | pub trait MetricConsumer: Send + Sync + 'static {
method request_completed (line 343) | fn request_completed(
FILE: lib/propolis/src/chardev/file_out.rs
type Inner (line 16) | struct Inner {
type BlockingFileOutput (line 20) | pub struct BlockingFileOutput {
method new (line 28) | pub fn new(fp: FsFile) -> Arc<Self> {
method attach (line 39) | pub fn attach(&self, source: Arc<dyn BlockingSource>) {
method run (line 53) | async fn run(poller: Arc<pollers::BlockingSourceBuffer>, mut fp: File) {
constant BUF_SIZE (line 25) | const BUF_SIZE: usize = 256;
FILE: lib/propolis/src/chardev/mod.rs
type SinkNotifier (line 15) | pub type SinkNotifier = Box<dyn Fn(&dyn Sink) + Send + Sync + 'static>;
type SourceNotifier (line 16) | pub type SourceNotifier = Box<dyn Fn(&dyn Source) + Send + Sync + 'static>;
type BlockingSourceConsumer (line 17) | pub type BlockingSourceConsumer = Box<dyn Fn(&[u8]) + Send + Sync + 'sta...
type Sink (line 19) | pub trait Sink: Send + Sync + 'static {
method write (line 21) | fn write(&self, data: u8) -> bool;
method set_notifier (line 25) | fn set_notifier(&self, f: Option<SinkNotifier>);
type Source (line 28) | pub trait Source: Send + Sync + 'static {
method read (line 30) | fn read(&self) -> Option<u8>;
method discard (line 32) | fn discard(&self, count: usize) -> usize;
method set_autodiscard (line 33) | fn set_autodiscard(&self, active: bool);
method set_notifier (line 36) | fn set_notifier(&self, f: Option<SourceNotifier>);
type BlockingSource (line 41) | pub trait BlockingSource: Send + Sync + 'static {
method set_consumer (line 42) | fn set_consumer(&self, f: Option<BlockingSourceConsumer>);
type NotifierFn (line 45) | type NotifierFn<T> = dyn Fn(&T) + Send + Sync + 'static;
type NotifierCell (line 46) | pub struct NotifierCell<T: ?Sized> {
function new (line 51) | pub fn new() -> Self {
function set (line 56) | pub fn set(&self, f: Option<SinkNotifier>) {
function notify (line 61) | pub fn notify(&self, sink: &dyn Sink) {
function set (line 71) | pub fn set(&self, f: Option<SourceNotifier>) {
function notify (line 76) | pub fn notify(&self, source: &dyn Source) {
type ConsumerCell (line 86) | pub struct ConsumerCell {
method new (line 91) | pub fn new() -> Self {
method set (line 94) | pub fn set(&self, f: Option<BlockingSourceConsumer>) {
method consume (line 99) | pub fn consume(&self, data: &[u8]) {
FILE: lib/propolis/src/chardev/pollers.rs
type Params (line 16) | pub struct Params {
method test_defaults (line 548) | pub(crate) fn test_defaults() -> Self {
type SourceInner (line 21) | struct SourceInner {
method is_full (line 26) | fn is_full(&self) -> bool {
type SourceBuffer (line 30) | pub struct SourceBuffer {
method new (line 37) | pub fn new(params: Params) -> Arc<Self> {
method attach (line 50) | pub fn attach(self: &Arc<Self>, source: &dyn Source) {
method read (line 64) | pub async fn read(
method leading_delay (line 87) | async fn leading_delay(&self) -> Option<Duration> {
method wait (line 112) | async fn wait(&self) {
method read_data (line 140) | pub fn read_data(&self, buf: &mut [u8], source: &dyn Source) -> usize {
method notify (line 154) | fn notify(&self, source: &dyn Source) {
type SinkInner (line 173) | struct SinkInner {
method is_full (line 178) | fn is_full(&self) -> bool {
type SinkBuffer (line 183) | pub struct SinkBuffer {
method new (line 189) | pub fn new(size: NonZeroUsize) -> Arc<Self> {
method attach (line 200) | pub fn attach(self: &Arc<Self>, sink: &dyn Sink) {
method wait_empty (line 207) | pub async fn wait_empty(&self) {
method write (line 227) | pub async fn write(
method notify (line 275) | fn notify(&self, sink: &dyn Sink) {
type BlockingParams (line 289) | pub struct BlockingParams {
type BlockingSourceInner (line 294) | struct BlockingSourceInner {
method is_full (line 299) | fn is_full(&self) -> bool {
type BlockingSourceBuffer (line 303) | pub struct BlockingSourceBuffer {
method new (line 311) | pub fn new(params: BlockingParams) -> Arc<Self> {
method attach (line 325) | pub fn attach(self: &Arc<Self>, source: &dyn BlockingSource) {
method read (line 332) | pub async fn read(&self, buf: &mut [u8]) -> Option<usize> {
method leading_delay (line 357) | async fn leading_delay(&self) -> Option<Duration> {
method wait_for_data (line 382) | async fn wait_for_data(&self) {
method consume (line 410) | fn consume(&self, mut data: &[u8]) {
function copy_and_consume (line 444) | fn copy_and_consume(src: &mut Vec<u8>, dest: &mut [u8]) -> usize {
function test_copy_and_consume_1 (line 460) | fn test_copy_and_consume_1() {
function test_copy_and_consume_one_u8 (line 526) | fn test_copy_and_consume_one_u8() {
function read_empty_returns_zero_bytes (line 565) | async fn read_empty_returns_zero_bytes() {
function write_empty_fills_zero_bytes (line 576) | async fn write_empty_fills_zero_bytes() {
function read_byte (line 587) | async fn read_byte() {
function read_bytes (line 603) | async fn read_bytes() {
function read_bytes_blocking (line 621) | async fn read_bytes_blocking() {
function write_byte (line 646) | async fn write_byte() {
function write_bytes (line 660) | async fn write_bytes() {
function write_bytes_beyond_internal_buffer_size (line 675) | async fn write_bytes_beyond_internal_buffer_size() {
type TestUart (line 717) | struct TestUart {
method new (line 733) | fn new(sink_size: usize, source_size: usize) -> Self {
method push_source (line 746) | fn push_source(&self, byte: u8) {
method pop_sink (line 753) | fn pop_sink(&self) -> Option<u8> {
method notify_source (line 758) | async fn notify_source(&self) {
method notify_sink (line 762) | async fn notify_sink(&self) {
method write (line 768) | fn write(&self, data: u8) -> bool {
method set_notifier (line 777) | fn set_notifier(&self, f: Option<SinkNotifier>) {
method read (line 783) | fn read(&self) -> Option<u8> {
method discard (line 787) | fn discard(&self, _count: usize) -> usize {
method set_autodiscard (line 790) | fn set_autodiscard(&self, active: bool) {
method set_notifier (line 793) | fn set_notifier(&self, f: Option<SourceNotifier>) {
FILE: lib/propolis/src/chardev/sock.rs
constant BUF_SIZE (line 20) | const BUF_SIZE: usize = 512;
constant POLL_INTERVAL_MS (line 21) | const POLL_INTERVAL_MS: usize = 10;
constant POLL_MISS_THRESH (line 22) | const POLL_MISS_THRESH: usize = 5;
type Inner (line 24) | struct Inner {
type UDSock (line 29) | pub struct UDSock {
method bind (line 37) | pub fn bind(path: &Path) -> Result<Arc<Self>> {
method spawn (line 67) | pub fn spawn(
method notify_connected (line 82) | fn notify_connected(&self, addr: Option<SocketAddr>) {
method wait_for_connect (line 88) | pub fn wait_for_connect(&self) -> bool {
method wait_for_disconnect (line 104) | pub fn wait_for_disconnect(&self) {
method shutdown (line 112) | pub fn shutdown(&self) {
method run (line 117) | pub async fn run(
method run_sink (line 150) | async fn run_sink(
method run_source (line 165) | async fn run_source(
type TestChardev (line 189) | struct TestChardev {
method new (line 194) | fn new() -> Self {
method write (line 203) | fn write(&self, _data: u8) -> bool {
method set_notifier (line 208) | fn set_notifier(&self, f: Option<chardev::SinkNotifier>) {
method read (line 213) | fn read(&self) -> Option<u8> {
method discard (line 217) | fn discard(&self, count: usize) -> usize {
method set_autodiscard (line 221) | fn set_autodiscard(&self, _active: bool) {}
method set_notifier (line 223) | fn set_notifier(&self, f: Option<chardev::SourceNotifier>) {
function wait_connected (line 228) | async fn wait_connected(sock: &Arc<UDSock>) -> bool {
function wait_disconnected (line 236) | async fn wait_disconnected(sock: &Arc<UDSock>) {
function bail_on_shutdown_sock (line 245) | async fn bail_on_shutdown_sock() {
function abort_wait_for_connect (line 275) | async fn abort_wait_for_connect() {
FILE: lib/propolis/src/common.rs
type VcpuId (line 14) | pub struct VcpuId(u32);
method from (line 17) | fn from(value: u32) -> Self {
method from (line 30) | fn from(value: i32) -> Self {
method into (line 39) | fn into(self) -> u32 {
method into (line 52) | fn into(self) -> i32 {
type GuestData (line 88) | pub struct GuestData<T: ?Sized>(T);
function fmt (line 91) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
function fmt (line 101) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Target (line 111) | type Target = T;
function deref (line 113) | fn deref(&self) -> &Self::Target {
function deref_mut (line 119) | fn deref_mut(&mut self) -> &mut Self::Target {
function from (line 125) | fn from(value: T) -> Self {
function borrow (line 131) | fn borrow(&self) -> &T {
function numeric_bounds (line 136) | fn numeric_bounds(
type ROInner (line 173) | enum ROInner<'a> {
type ReadOp (line 181) | pub struct ReadOp<'a> {
function from_mapping (line 195) | pub fn from_mapping(op_offset: usize, mapping: SubMapping<'a>) -> Self {
function from_buf (line 210) | pub fn from_buf(op_offset: usize, buffer: &'a mut [u8]) -> Self {
function new_child (line 224) | pub fn new_child<'b, R>(
function len (line 252) | pub fn len(&self) -> usize {
function avail (line 258) | pub fn avail(&self) -> usize {
function offset (line 261) | pub fn offset(&self) -> usize {
function bytes_written (line 264) | pub fn bytes_written(&self) -> usize {
function write_u8 (line 268) | pub fn write_u8(&mut self, val: u8) {
function write_u16 (line 271) | pub fn write_u16(&mut self, val: u16) {
function write_u32 (line 274) | pub fn write_u32(&mut self, val: u32) {
function write_u64 (line 277) | pub fn write_u64(&mut self, val: u64) {
function write_bytes (line 280) | pub fn write_bytes(&mut self, data: &[u8]) {
function fill (line 298) | pub fn fill(&mut self, val: u8) {
type WOInner (line 313) | enum WOInner<'a> {
type WriteOp (line 321) | pub struct WriteOp<'a> {
function from_mapping (line 335) | pub fn from_mapping(op_offset: usize, mapping: SubMapping<'a>) -> Self {
function from_buf (line 347) | pub fn from_buf(op_offset: usize, buf: &'a [u8]) -> Self {
function new_child (line 361) | pub fn new_child<'b, R>(
function len (line 389) | pub fn len(&self) -> usize {
function avail (line 395) | pub fn avail(&self) -> usize {
function offset (line 398) | pub fn offset(&self) -> usize {
function bytes_read (line 401) | pub fn bytes_read(&self) -> usize {
function read_val (line 405) | fn read_val<const COUNT: usize>(&mut self) -> [u8; COUNT] {
function read_u8 (line 410) | pub fn read_u8(&mut self) -> u8 {
function read_u16 (line 413) | pub fn read_u16(&mut self) -> u16 {
function read_u32 (line 416) | pub fn read_u32(&mut self) -> u32 {
function read_u64 (line 419) | pub fn read_u64(&mut self) -> u64 {
function read_bytes (line 422) | pub fn read_bytes(&mut self, data: &mut [u8]) {
type RWOp (line 444) | pub enum RWOp<'a, 'b> {
function offset (line 449) | pub fn offset(&self) -> usize {
function len (line 455) | pub fn len(&self) -> usize {
function is_read (line 461) | pub fn is_read(&self) -> bool {
function is_write (line 464) | pub fn is_write(&self) -> bool {
type GuestAddr (line 471) | pub struct GuestAddr(pub u64);
method offset (line 474) | pub fn offset<T: Sized>(&self, count: usize) -> Self {
type Output (line 484) | type Output = Self;
method add (line 486) | fn add(self, rhs: usize) -> Self::Output {
type Output (line 491) | type Output = Self;
method bitand (line 493) | fn bitand(self, rhs: usize) -> Self::Output {
type GuestRegion (line 481) | pub struct GuestRegion(pub GuestAddr, pub usize);
constant PAGE_SIZE (line 500) | pub const PAGE_SIZE: usize = 0x1000;
constant PAGE_OFFSET (line 501) | pub const PAGE_OFFSET: usize = 0xfff;
constant PAGE_MASK (line 502) | pub const PAGE_MASK: usize = usize::MAX - PAGE_OFFSET;
constant PAGE_SHIFT (line 503) | pub const PAGE_SHIFT: usize = 12;
function round_up_p2 (line 505) | pub fn round_up_p2(val: usize, to: usize) -> usize {
constant KB (line 513) | pub const KB: usize = 1024;
constant MB (line 515) | pub const MB: usize = 1024 * 1024;
constant GB (line 517) | pub const GB: usize = 1024 * 1024 * 1024;
constant TB (line 519) | pub const TB: usize = 1024 * 1024 * 1024 * 1024;
function readop_base_size (line 526) | fn readop_base_size() {
function writeop_base_size (line 546) | fn writeop_base_size() {
function readop_oversize (line 563) | fn readop_oversize() {
function writeop_oversize (line 571) | fn writeop_oversize() {
function readop_short (line 578) | fn readop_short() {
function writeop_short (line 587) | fn writeop_short() {
FILE: lib/propolis/src/cpuid.rs
function from_raw (line 20) | pub fn from_raw(
type SpecializeError (line 41) | pub enum SpecializeError {
type Specializer (line 57) | pub struct Specializer {
method new (line 66) | pub fn new() -> Self {
method with_vcpu_count (line 71) | pub fn with_vcpu_count(self, count: NonZeroU8, has_smt: bool) -> Self {
method with_vcpuid (line 76) | pub fn with_vcpuid(self, vcpuid: i32) -> Self {
method with_cpu_topo (line 86) | pub fn with_cpu_topo(
method clear_cpu_topo (line 104) | pub fn clear_cpu_topo(self, clear: impl Iterator<Item = TopoKind>) -> ...
method with_cache_topo (line 115) | pub fn with_cache_topo(self) -> Self {
method execute (line 121) | pub fn execute(
method fix_amd_cache_topo (line 161) | fn fix_amd_cache_topo(
method fix_cpu_topo (line 201) | fn fix_cpu_topo(&self, set: &mut CpuidSet) -> Result<(), SpecializeErr...
type TopoKind (line 467) | pub enum TopoKind {
method supported (line 482) | pub fn supported() -> std::array::IntoIter<Self, 2> {
function parse_brand_string (line 491) | pub fn parse_brand_string(
FILE: lib/propolis/src/enlightenment/bhyve.rs
type BhyveGuestInterface (line 29) | pub struct BhyveGuestInterface;
method type_name (line 32) | fn type_name(&self) -> &'static str {
method add_cpuid (line 38) | fn add_cpuid(&self, cpuid: &mut CpuidSet) -> Result<(), AddCpuidError> {
method rdmsr (line 57) | fn rdmsr(&self, _vcpu: VcpuId, _msr: MsrId) -> RdmsrOutcome {
method wrmsr (line 61) | fn wrmsr(&self, _vcpu: VcpuId, _msr: MsrId, _value: u64) -> WrmsrOutcome {
method attach (line 65) | fn attach(&self, _parent: &MemAccessor, _vmm_hdl: Arc<VmmHdl>) {}
FILE: lib/propolis/src/enlightenment/hyperv/bits.rs
constant HYPERV_MIN_REQUIRED_CPUID_LEAF (line 16) | pub(super) const HYPERV_MIN_REQUIRED_CPUID_LEAF: u32 = 0x40000005;
constant HYPERV_LEAF_0_VALUES (line 27) | const HYPERV_LEAF_0_VALUES: CpuidValues = CpuidValues {
function hyperv_leaf_0_values (line 44) | pub(super) fn hyperv_leaf_0_values(max_leaf: u32) -> CpuidValues {
constant HYPERV_LEAF_1_VALUES (line 55) | pub(super) const HYPERV_LEAF_1_VALUES: CpuidValues =
constant HYPERV_LEAF_2_VALUES (line 62) | pub(super) const HYPERV_LEAF_2_VALUES: CpuidValues =
method default (line 83) | fn default() -> Self {
constant HYPERV_LEAF_4_VALUES (line 92) | pub(super) const HYPERV_LEAF_4_VALUES: CpuidValues =
constant HYPERV_LEAF_5_VALUES (line 98) | pub(super) const HYPERV_LEAF_5_VALUES: CpuidValues =
constant HV_X64_MSR_GUEST_OS_ID (line 108) | pub(super) const HV_X64_MSR_GUEST_OS_ID: u32 = 0x4000_0000;
constant HV_X64_MSR_HYPERCALL (line 117) | pub(super) const HV_X64_MSR_HYPERCALL: u32 = 0x4000_0001;
constant HV_X64_MSR_VP_INDEX (line 123) | pub(super) const HV_X64_MSR_VP_INDEX: u32 = 0x4000_0002;
constant HV_X64_MSR_TIME_REF_COUNT (line 130) | pub(super) const HV_X64_MSR_TIME_REF_COUNT: u32 = 0x4000_0020;
constant HV_X64_MSR_REFERENCE_TSC (line 140) | pub(super) const HV_X64_MSR_REFERENCE_TSC: u32 = 0x4000_0021;
FILE: lib/propolis/src/enlightenment/hyperv/hypercall.rs
constant LOCKED_BIT (line 12) | const LOCKED_BIT: u64 = 1;
constant LOCKED_MASK (line 13) | const LOCKED_MASK: u64 = 1 << LOCKED_BIT;
constant ENABLED_BIT (line 14) | const ENABLED_BIT: u64 = 0;
constant ENABLED_MASK (line 15) | const ENABLED_MASK: u64 = 1 << ENABLED_BIT;
type MsrHypercallValue (line 30) | pub(super) struct MsrHypercallValue(pub(super) u64);
method fmt (line 33) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method gpfn (line 46) | pub fn gpfn(&self) -> Pfn {
method gpa (line 52) | pub fn gpa(&self) -> GuestAddr {
method locked (line 59) | pub fn locked(&self) -> bool {
method enabled (line 64) | pub fn enabled(&self) -> bool {
method clear_enabled (line 69) | pub fn clear_enabled(&mut self) {
constant HYPERCALL_INSTRUCTION_SEQUENCE (line 81) | const HYPERCALL_INSTRUCTION_SEQUENCE: [u8; 8] =
function hypercall_page_contents (line 85) | pub(super) fn hypercall_page_contents() -> [u8; PAGE_SIZE] {
FILE: lib/propolis/src/enlightenment/hyperv/mod.rs
function hyperv_wrmsr_guest_os_id (line 49) | fn hyperv_wrmsr_guest_os_id(val: u64) {}
function hyperv_wrmsr_hypercall (line 50) | fn hyperv_wrmsr_hypercall(val: u64, gpa: u64, locked: bool, enabled: boo...
function hyperv_wrmsr_reference_tsc (line 52) | fn hyperv_wrmsr_reference_tsc(val: u64, gpa: u64, enabled: bool) {}
function hyperv_wrmsr_hypercall_bad_gpa (line 53) | fn hyperv_wrmsr_hypercall_bad_gpa(gpa: u64) {}
function hyperv_rdmsr_reference_time (line 54) | fn hyperv_rdmsr_reference_time(time_units: u64) {}
constant TYPE_NAME (line 57) | const TYPE_NAME: &str = "guest-hyperv-interface";
type Features (line 61) | pub struct Features {
type HypercallOverlay (line 67) | struct HypercallOverlay(OverlayPage);
type TscOverlay (line 70) | struct TscOverlay(OverlayPage);
type OverlayPages (line 75) | struct OverlayPages {
type Inner (line 80) | struct Inner {
method new (line 98) | fn new(features: &Features) -> Self {
method reset (line 114) | fn reset(&mut self) {
method handle_rdmsr_reference_tsc (line 132) | fn handle_rdmsr_reference_tsc(&self) -> RdmsrOutcome {
method handle_wrmsr_reference_tsc (line 150) | fn handle_wrmsr_reference_tsc(&mut self, value: u64) -> WrmsrOutcome {
type HyperV (line 181) | pub struct HyperV {
method new (line 198) | pub fn new(log: &slog::Logger, features: Features) -> Self {
method vmm_hdl (line 223) | fn vmm_hdl(&self) -> &Arc<VmmHdl> {
method handle_wrmsr_guest_os_id (line 231) | fn handle_wrmsr_guest_os_id(&self, value: u64) -> WrmsrOutcome {
method handle_wrmsr_hypercall (line 253) | fn handle_wrmsr_hypercall(&self, value: u64) -> WrmsrOutcome {
method handle_rdmsr_time_ref_count (line 319) | fn handle_rdmsr_time_ref_count(&self) -> RdmsrOutcome {
method handle_wrmsr_reference_tsc (line 387) | fn handle_wrmsr_reference_tsc(&self, value: u64) -> WrmsrOutcome {
method add_cpuid (line 393) | fn add_cpuid(&self, cpuid: &mut CpuidSet) -> Result<(), AddCpuidError> {
method rdmsr (line 439) | fn rdmsr(&self, vcpu: VcpuId, msr: MsrId) -> RdmsrOutcome {
method wrmsr (line 459) | fn wrmsr(&self, _vcpu: VcpuId, msr: MsrId, value: u64) -> WrmsrOutcome {
method attach (line 471) | fn attach(&self, mem_acc: &MemAccessor, vmm_hdl: Arc<VmmHdl>) {
method type_name (line 503) | fn type_name(&self) -> &'static str {
method pause (line 507) | fn pause(&self) {
method resume (line 531) | fn resume(&self) {
method reset (line 566) | fn reset(&self) {
method halt (line 577) | fn halt(&self) {
method migrate (line 582) | fn migrate(&'_ self) -> Migrator<'_> {
method export (line 588) | fn export(
method import (line 612) | fn import(
type ReferenceTscV1 (line 712) | pub struct ReferenceTscV1 {
type HyperVEnlightenmentV1 (line 730) | pub struct HyperVEnlightenmentV1 {
method id (line 737) | fn id() -> SchemaId {
FILE: lib/propolis/src/enlightenment/hyperv/overlay.rs
type OverlayError (line 92) | pub(super) enum OverlayError {
type OverlayContents (line 124) | pub(super) struct OverlayContents(pub(super) Box<[u8; PAGE_SIZE]>);
method fmt (line 133) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Error (line 139) | type Error = usize;
method try_from (line 141) | fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
method default (line 127) | fn default() -> Self {
type OverlayKind (line 150) | pub(super) enum OverlayKind {
method priority (line 179) | fn priority(&self) -> u32 {
method get_contents (line 198) | fn get_contents(&self) -> OverlayContents {
method eq (line 213) | fn eq(&self, other: &Self) -> bool {
method cmp (line 221) | fn cmp(&self, other: &Self) -> std::cmp::Ordering {
method partial_cmp (line 227) | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
type OverlayPage (line 234) | pub(super) struct OverlayPage {
method fmt (line 248) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method move_to (line 259) | pub(super) fn move_to(&mut self, new_pfn: Pfn) -> Result<(), OverlayEr...
method drop (line 267) | fn drop(&mut self) {
type OverlaySet (line 277) | struct OverlaySet {
method active (line 293) | fn active(&self) -> &OverlayKind {
type ManagerInner (line 300) | struct ManagerInner {
method add_overlay (line 307) | fn add_overlay(
method add_overlay_using_mapping (line 321) | fn add_overlay_using_mapping(
method remove_overlay (line 353) | fn remove_overlay(
method move_overlay (line 394) | fn move_overlay(
type OverlayManager (line 456) | pub(super) struct OverlayManager {
method attach (line 470) | pub(super) fn attach(&self, parent_mem: &MemAccessor) {
method is_empty (line 476) | pub(super) fn is_empty(&self) -> bool {
method add_overlay (line 482) | pub(super) fn add_overlay(
method remove_overlay (line 494) | fn remove_overlay(
method move_overlay (line 507) | fn move_overlay(
method default (line 462) | fn default() -> Self {
type MappedPfn (line 534) | pub(super) struct MappedPfn<'a> {
function new (line 541) | pub(super) fn new(
function pfn (line 557) | pub(super) fn pfn(&self) -> Pfn {
function write_page (line 568) | pub(super) fn write_page(&mut self, page: &OverlayContents) {
function read_page (line 583) | pub(super) fn read_page(&mut self, page: &mut OverlayContents) {
function filled (line 603) | fn filled(fill: u8) -> Self {
type TestCtx (line 609) | struct TestCtx {
method new (line 619) | fn new() -> Self {
method write_page (line 632) | fn write_page(&self, pfn: Pfn, contents: &OverlayContents) {
method assert_pfn_has_fill (line 640) | fn assert_pfn_has_fill(&self, pfn: Pfn, fill: u8) {
function basic_add (line 657) | fn basic_add() {
function basic_move (line 673) | fn basic_move() {
function underlay_restored_after_drop (line 695) | fn underlay_restored_after_drop() {
function out_of_bounds_pfn (line 713) | fn out_of_bounds_pfn() {
function duplicate_kind_at_pfn (line 727) | fn duplicate_kind_at_pfn() {
function multiple_overlays (line 742) | fn multiple_overlays() {
function remove_nonexistent_overlay (line 769) | fn remove_nonexistent_overlay() {
FILE: lib/propolis/src/enlightenment/hyperv/tsc.rs
constant ENABLED_BIT (line 148) | const ENABLED_BIT: u64 = 0;
constant ENABLED_MASK (line 149) | const ENABLED_MASK: u64 = 1 << ENABLED_BIT;
type MsrReferenceTscValue (line 155) | pub(super) struct MsrReferenceTscValue(pub(super) u64);
method fmt (line 158) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method gpfn (line 170) | pub fn gpfn(&self) -> Pfn {
method gpa (line 176) | pub fn gpa(&self) -> GuestAddr {
method enabled (line 181) | pub fn enabled(&self) -> bool {
type ReferenceTscPage (line 189) | pub(super) struct ReferenceTscPage {
method new (line 225) | pub(super) fn new(guest_freq: u64) -> Self {
function from (line 238) | fn from(value: &ReferenceTscPage) -> Self {
function guest_freq_to_scale (line 249) | fn guest_freq_to_scale(guest_freq: u64) -> Option<u64> {
type ReferenceTsc (line 261) | pub(super) enum ReferenceTsc {
method is_present (line 275) | pub(super) fn is_present(&self) -> bool {
method set_msr_value (line 287) | pub(super) fn set_msr_value(&mut self, value: MsrReferenceTscValue) {
method create_overlay (line 308) | pub(super) fn create_overlay(
FILE: lib/propolis/src/enlightenment/mod.rs
type Enlightenment (line 74) | pub trait Enlightenment: Lifecycle + Send + Sync {
method as_lifecycle (line 75) | fn as_lifecycle(self: Arc<Self>) -> Arc<dyn Lifecycle>
method attach (line 95) | fn attach(&self, mem_acc: &MemAccessor, vmm_hdl: Arc<VmmHdl>);
method add_cpuid (line 102) | fn add_cpuid(&self, cpuid: &mut CpuidSet) -> Result<(), AddCpuidError>;
method rdmsr (line 106) | fn rdmsr(&self, vcpu: VcpuId, msr: MsrId) -> RdmsrOutcome;
method wrmsr (line 110) | fn wrmsr(&self, vcpu: VcpuId, msr: MsrId, value: u64) -> WrmsrOutcome;
type AddCpuidError (line 119) | pub enum AddCpuidError {
function add_cpuid (line 142) | fn add_cpuid(
FILE: lib/propolis/src/exits.rs
type VmExit (line 18) | pub struct VmExit {
method parse (line 33) | pub fn parse(exit: &vm_exit, api_version: u32) -> Self {
method default (line 28) | fn default() -> Self {
type IoPort (line 43) | pub struct IoPort {
type InoutReq (line 49) | pub enum InoutReq {
type MmioReadReq (line 55) | pub struct MmioReadReq {
type MmioWriteReq (line 60) | pub struct MmioWriteReq {
type MmioReq (line 67) | pub enum MmioReq {
type SvmDetail (line 73) | pub struct SvmDetail {
type VmxDetail (line 79) | pub struct VmxDetail {
method from (line 87) | fn from(raw: &bhyve_api::vm_exit_vmx) -> Self {
type InstEmul (line 99) | pub struct InstEmul {
method bytes (line 105) | pub fn bytes(&self) -> &[u8]
Condensed preview — 383 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,584K chars).
[
{
"path": ".cargo/config.toml",
"chars": 513,
"preview": "[alias]\nxtask = \"run --package xtask --quiet --\"\n\n[env]\n# Currently required by Falcon due to\n# https://github.com/rust-"
},
{
"path": ".git-blame-ignore-revs",
"chars": 67,
"preview": "# Whitespace-only changes\nda65a727cceee1b386ded8609309015c366cca2a\n"
},
{
"path": ".github/buildomat/config.toml",
"chars": 361,
"preview": "#\n# This file, with this flag, must be present in the default branch in order for\n# the buildomat integration to create "
},
{
"path": ".github/buildomat/jobs/check-headers.sh",
"chars": 1046,
"preview": "#!/bin/bash\n#:\n#: name = \"header-check\"\n#: variety = \"basic\"\n#: target = \"helios-2.0\"\n#: rust_toolchain = true\n#:\n\n# Run"
},
{
"path": ".github/buildomat/jobs/falcon-build.sh",
"chars": 1295,
"preview": "#!/bin/bash\n#:\n#: name = \"falcon\"\n#: variety = \"basic\"\n#: target = \"helios-2.0\"\n#: rust_toolchain = \"stable\"\n#: output_r"
},
{
"path": ".github/buildomat/jobs/image.sh",
"chars": 1238,
"preview": "#!/bin/bash\n#:\n#: name = \"image\"\n#: variety = \"basic\"\n#: target = \"helios-2.0\"\n#: rust_toolchain = \"stable\"\n#: output_ru"
},
{
"path": ".github/buildomat/jobs/phd-build.sh",
"chars": 2936,
"preview": "#!/bin/bash\n#:\n#: name = \"phd-build\"\n#: variety = \"basic\"\n#: target = \"helios-2.0\"\n#: rust_toolchain = \"stable\"\n#: outpu"
},
{
"path": ".github/buildomat/jobs/phd-run-migrate-from-base.sh",
"chars": 1101,
"preview": "#!/bin/bash\n#:\n#: name = \"phd-run-migrate-from-base\"\n#: variety = \"basic\"\n#: target = \"lab-2.0-gimlet\"\n#: output_rules ="
},
{
"path": ".github/buildomat/jobs/phd-run.sh",
"chars": 666,
"preview": "#!/bin/bash\n#:\n#: name = \"phd-run\"\n#: variety = \"basic\"\n#: target = \"lab-2.0-gimlet\"\n#: output_rules = [\n#:\t\"/tmp/phd-ru"
},
{
"path": ".github/buildomat/jobs/test-gimlet.sh",
"chars": 1549,
"preview": "#!/bin/bash\n#:\n#: name = \"test-gimlet\"\n#: variety = \"basic\"\n#: target = \"lab-2.0-gimlet\"\n#: rust_toolchain = false\n#: sk"
},
{
"path": ".github/buildomat/phd-run-with-args.sh",
"chars": 2573,
"preview": "#!/bin/bash\n\n# Unpacks and executes the PHD runner in the Buildomat environment, passing\n# through to the runner any arg"
},
{
"path": ".github/workflows/rust.yml",
"chars": 2519,
"preview": "name: Rust\n\non:\n push:\n branches: [ master ]\n pull_request:\n branches: [ master ]\n\nenv:\n CARGO_TERM_COLOR: alwa"
},
{
"path": ".gitignore",
"chars": 92,
"preview": "/target\n/crates/*/header-check/target\n/crates/*/header-check/Cargo.lock\ndebug.out\ncore\nout/\n"
},
{
"path": ".licenserc.yaml",
"chars": 355,
"preview": "header:\n license:\n spdx-id: MPL-2.0\n\n content: |\n This Source Code Form is subject to the terms of the Mozil"
},
{
"path": "Cargo.toml",
"chars": 6752,
"preview": "[workspace]\nresolver = \"2\"\n\nmembers = [\n \"crates/*\",\n \"crates/*/sys\",\n \"bin/*\",\n \"lib/*\",\n \"packaging/propolis-pack"
},
{
"path": "LICENSE",
"chars": 16726,
"preview": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\""
},
{
"path": "README.md",
"chars": 2709,
"preview": "# Propolis\n\nPropolis VMM userspace for use with illumos bhyve.\n\n## Prerequisites\n\nGiven the current tight coupling of th"
},
{
"path": "bin/dropshot-apis/Cargo.toml",
"chars": 326,
"preview": "[package]\nname = \"propolis-dropshot-apis\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MPL-2.0\"\n\n[dependencies]\nanyhow."
},
{
"path": "bin/dropshot-apis/src/main.rs",
"chars": 2703,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/mock-server/Cargo.toml",
"chars": 1291,
"preview": "[package]\nname = \"propolis-mock-server\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\nname = \"propolis_m"
},
{
"path": "bin/mock-server/src/lib/api_types.rs",
"chars": 2912,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/mock-server/src/lib/lib.rs",
"chars": 29288,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/mock-server/src/main.rs",
"chars": 3901,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-cli/Cargo.toml",
"chars": 649,
"preview": "[package]\nname = \"propolis-cli\"\nversion = \"0.1.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[dependencies]\nanyhow.workspace "
},
{
"path": "bin/propolis-cli/README.md",
"chars": 953,
"preview": "# Propolis CLI\n\nThe `propolis-cli` utility provides a user-friendly frontend to the\n[`propolis-server`](../propolis-serv"
},
{
"path": "bin/propolis-cli/src/main.rs",
"chars": 35125,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/Cargo.toml",
"chars": 3052,
"preview": "[package]\nname = \"propolis-server\"\nversion = \"0.1.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\nname = \"propolis_server"
},
{
"path": "bin/propolis-server/README.md",
"chars": 967,
"preview": "# Propolis Server\n\nThis binary provides a REST API to create and manage a Propolis VM. It typically\nruns in the context "
},
{
"path": "bin/propolis-server/src/lib/config.rs",
"chars": 1512,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/initializer.rs",
"chars": 60884,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/lib.rs",
"chars": 336,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/migrate/codec.rs",
"chars": 18793,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/migrate/destination.rs",
"chars": 32703,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/migrate/memx.rs",
"chars": 3682,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/migrate/mod.rs",
"chars": 8755,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/migrate/preamble.rs",
"chars": 4255,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/migrate/protocol.rs",
"chars": 10306,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/migrate/source.rs",
"chars": 37691,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/serial/history_buffer.rs",
"chars": 10345,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/serial/mod.rs",
"chars": 15010,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/server.rs",
"chars": 24528,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/spec/api_spec_v0.rs",
"chars": 15533,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/spec/builder.rs",
"chars": 16221,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/spec/mod.rs",
"chars": 12003,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/stats/mod.rs",
"chars": 12901,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/stats/network_interface.rs",
"chars": 13753,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/stats/pvpanic.rs",
"chars": 2460,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/stats/virtual_disk.rs",
"chars": 15058,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/stats/virtual_machine.rs",
"chars": 20114,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/vcpu_tasks.rs",
"chars": 13242,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/vm/active.rs",
"chars": 4225,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/vm/ensure.rs",
"chars": 27335,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/vm/guest_event.rs",
"chars": 2298,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/vm/mod.rs",
"chars": 24156,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/vm/objects.rs",
"chars": 15302,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/vm/request_queue.rs",
"chars": 48789,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/vm/services.rs",
"chars": 6410,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/vm/state_driver.rs",
"chars": 40338,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/vm/state_publisher.rs",
"chars": 3682,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/lib/vnc.rs",
"chars": 15426,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/main.rs",
"chars": 10992,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-server/src/proptest-regressions/vm/request_queue.txt",
"chars": 1039,
"preview": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-r"
},
{
"path": "bin/propolis-standalone/Cargo.toml",
"chars": 1229,
"preview": "[package]\nname = \"propolis-standalone\"\nversion = \"0.1.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\nrust-version = \"1.73\"\n\n[[b"
},
{
"path": "bin/propolis-standalone/README.md",
"chars": 10936,
"preview": "# Propolis Standalone\n\nServer frontend aside, we also provide a standalone binary for quick\nprototyping, `propolis-stand"
},
{
"path": "bin/propolis-standalone/src/cidata.rs",
"chars": 3801,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-standalone/src/config.rs",
"chars": 14365,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-standalone/src/main.rs",
"chars": 55155,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-standalone/src/snapshot.rs",
"chars": 21067,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-utils/Cargo.toml",
"chars": 593,
"preview": "[package]\nname = \"propolis-utils\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[[bin]]\nname = \"cpuid-gen\"\ntes"
},
{
"path": "bin/propolis-utils/README.md",
"chars": 446,
"preview": "# Propolis Utilities\n\nThis is a collection of assorted utilities which may be useful for certain\ndevelopment activities,"
},
{
"path": "bin/propolis-utils/src/bin/cpuid-gen.rs",
"chars": 3016,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "bin/propolis-utils/src/bin/rsrvrctl.rs",
"chars": 2719,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/bhyve-api/Cargo.toml",
"chars": 228,
"preview": "[package]\nname = \"bhyve_api\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\ndoctest = false\n\n[dependencie"
},
{
"path": "crates/bhyve-api/README.md",
"chars": 338,
"preview": "# bhyve-api\n\nThis crate exposes the interfaces from the bhyve kernel VMM. Since those\ninterfaces are Private and subjec"
},
{
"path": "crates/bhyve-api/header-check/Cargo.toml",
"chars": 410,
"preview": "[package]\nname = \"bhyve_api-hdrchk\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nbuild = \"build.rs\"\npublish = false\n\n[dependenc"
},
{
"path": "crates/bhyve-api/header-check/README.md",
"chars": 926,
"preview": "# bhyve-api Header Check\n\nIn order to facilitate accurate reproduction of the bhyve kernel interfaces\nfrom their respect"
},
{
"path": "crates/bhyve-api/header-check/build.rs",
"chars": 5024,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/bhyve-api/header-check/test/main.rs",
"chars": 336,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/bhyve-api/src/lib.rs",
"chars": 20233,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/bhyve-api/sys/Cargo.toml",
"chars": 214,
"preview": "[package]\nname = \"bhyve_api_sys\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\ntest = false\ndoctest = fa"
},
{
"path": "crates/bhyve-api/sys/src/enums.rs",
"chars": 2948,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/bhyve-api/sys/src/ioctls.rs",
"chars": 5701,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/bhyve-api/sys/src/lib.rs",
"chars": 608,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/bhyve-api/sys/src/structs.rs",
"chars": 13859,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/bhyve-api/sys/src/vmm_data.rs",
"chars": 6600,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/cpuid-profile-config/Cargo.toml",
"chars": 249,
"preview": "[package]\nname = \"cpuid_profile_config\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\ntest = false\ndocte"
},
{
"path": "crates/cpuid-profile-config/src/lib.rs",
"chars": 2921,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/cpuid-utils/Cargo.toml",
"chars": 368,
"preview": "[package]\nname = \"cpuid_utils\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[dependencies]\nbitflags.workspace"
},
{
"path": "crates/cpuid-utils/src/bits.rs",
"chars": 10027,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/cpuid-utils/src/host.rs",
"chars": 12203,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/cpuid-utils/src/instance_spec.rs",
"chars": 3015,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/cpuid-utils/src/lib.rs",
"chars": 29496,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/dladm/Cargo.toml",
"chars": 206,
"preview": "[package]\nname = \"dladm\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\ntest = false\ndoctest = false\n\n[de"
},
{
"path": "crates/dladm/src/lib.rs",
"chars": 6902,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/dladm/src/sys.rs",
"chars": 5683,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/nvpair/Cargo.toml",
"chars": 170,
"preview": "[package]\nname = \"nvpair\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\ndoctest = false\n\n[dependencies]\n"
},
{
"path": "crates/nvpair/header-check/Cargo.toml",
"chars": 295,
"preview": "[package]\nname = \"nvpair-hdrchk\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\nbuild = \"build.rs\"\npublish = fal"
},
{
"path": "crates/nvpair/header-check/build.rs",
"chars": 833,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/nvpair/header-check/test/main.rs",
"chars": 272,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/nvpair/src/lib.rs",
"chars": 6296,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/nvpair/sys/Cargo.toml",
"chars": 159,
"preview": "[package]\nname = \"nvpair_sys\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\ntest = false\ndoctest = false"
},
{
"path": "crates/nvpair/sys/src/lib.rs",
"chars": 5854,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/pbind/Cargo.toml",
"chars": 118,
"preview": "[package]\nname = \"pbind\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[dependencies]\nlibc.workspace = true\n"
},
{
"path": "crates/pbind/src/lib.rs",
"chars": 2685,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types/Cargo.toml",
"chars": 216,
"preview": "[package]\nname = \"propolis_api_types\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\ndoctest = false\n\n[de"
},
{
"path": "crates/propolis-api-types/src/disk.rs",
"chars": 283,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types/src/instance.rs",
"chars": 294,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types/src/instance_spec/components/backends.rs",
"chars": 448,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types/src/instance_spec/components/board.rs",
"chars": 364,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types/src/instance_spec/components/devices.rs",
"chars": 389,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types/src/instance_spec/components/mod.rs",
"chars": 977,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types/src/instance_spec/mod.rs",
"chars": 8042,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types/src/lib.rs",
"chars": 665,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types/src/migration.rs",
"chars": 285,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types/src/serial.rs",
"chars": 287,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/Cargo.toml",
"chars": 358,
"preview": "[package]\nname = \"propolis-api-types-versions\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\ndoctest = f"
},
{
"path": "crates/propolis-api-types-versions/src/add_vsock/api.rs",
"chars": 1937,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/add_vsock/components/devices.rs",
"chars": 665,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/add_vsock/components/mod.rs",
"chars": 221,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/add_vsock/instance_spec.rs",
"chars": 7245,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/add_vsock/mod.rs",
"chars": 377,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/crucible_volume_info/disk.rs",
"chars": 446,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/crucible_volume_info/mod.rs",
"chars": 381,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/impls/instance.rs",
"chars": 1409,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/impls/instance_spec.rs",
"chars": 3059,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/impls/mod.rs",
"chars": 292,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/initial/components/backends.rs",
"chars": 3398,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/initial/components/board.rs",
"chars": 5916,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/initial/components/devices.rs",
"chars": 6516,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/initial/components/mod.rs",
"chars": 488,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/initial/disk.rs",
"chars": 996,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/initial/instance.rs",
"chars": 6106,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/initial/instance_spec.rs",
"chars": 7649,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/initial/migration.rs",
"chars": 2765,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/initial/mod.rs",
"chars": 411,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/initial/serial.rs",
"chars": 3376,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/latest.rs",
"chars": 4610,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/lib.rs",
"chars": 1313,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/programmable_smbios/api.rs",
"chars": 1977,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/programmable_smbios/instance_spec.rs",
"chars": 2467,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-api-types-versions/src/programmable_smbios/mod.rs",
"chars": 376,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-config-toml/Cargo.toml",
"chars": 342,
"preview": "[package]\nname = \"propolis-config-toml\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\ntest = false\ndocte"
},
{
"path": "crates/propolis-config-toml/src/lib.rs",
"chars": 6073,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-config-toml/src/spec.rs",
"chars": 16863,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-server-api/Cargo.toml",
"chars": 264,
"preview": "[package]\nname = \"propolis-server-api\"\nversion = \"0.1.0\"\nlicense = \"MPL-2.0\"\nedition = \"2024\"\n\n[dependencies]\ncrucible-c"
},
{
"path": "crates/propolis-server-api/src/lib.rs",
"chars": 10411,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/propolis-types/Cargo.toml",
"chars": 283,
"preview": "[package]\nname = \"propolis_types\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\ndoctest = false\n\n[depend"
},
{
"path": "crates/propolis-types/src/lib.rs",
"chars": 9821,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/rfb/Cargo.toml",
"chars": 1296,
"preview": "[package]\nname = \"rfb\"\nversion = \"0.0.0\"\ndescription = \"Implementation of the RFB protocol (RFC 6143)\"\nreadme = \"README."
},
{
"path": "crates/rfb/README.md",
"chars": 701,
"preview": "# RFB\n\nThis crate implements a server-side implementation of the Remote Framebuffer\nProtocol. Consumers of the crate can"
},
{
"path": "crates/rfb/examples/shared.rs",
"chars": 4243,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/rfb/examples/socket.rs",
"chars": 4754,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/rfb/examples/websock.rs",
"chars": 5096,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/rfb/src/encodings.rs",
"chars": 1846,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/rfb/src/keysym.rs",
"chars": 6472,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/rfb/src/lib.rs",
"chars": 368,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/rfb/src/proto.rs",
"chars": 20686,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/rfb/src/server.rs",
"chars": 3146,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/rfb/src/tungstenite.rs",
"chars": 4004,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/rgb-frame/Cargo.toml",
"chars": 155,
"preview": "[package]\nname = \"rgb_frame\"\nversion = \"0.0.0\"\nedition = \"2021\"\n\n[lib]\ndoctest = false\n\n[dependencies]\nstrum = { workspa"
},
{
"path": "crates/rgb-frame/src/lib.rs",
"chars": 6139,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/viona-api/Cargo.toml",
"chars": 358,
"preview": "[package]\nname = \"viona_api\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\n\n[lib]\ndoctest = false\n\n[dependencie"
},
{
"path": "crates/viona-api/header-check/Cargo.toml",
"chars": 293,
"preview": "[package]\nname = \"viona_api-hdrchk\"\nversion = \"0.0.0\"\nlicense = \"MPL-2.0\"\nbuild = \"build.rs\"\npublish = false\nedition = \""
},
{
"path": "crates/viona-api/header-check/build.rs",
"chars": 1502,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/viona-api/header-check/test/main.rs",
"chars": 271,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/viona-api/src/ffi.rs",
"chars": 4633,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "crates/viona-api/src/lib.rs",
"chars": 9309,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "docs/lifecycle.md",
"chars": 3368,
"preview": "This is, for now, an aspirational description of the various states a Propolis\ninstance could travel through over the co"
},
{
"path": "docs/migrate-with-crucible.md",
"chars": 6915,
"preview": "# Running a live migration \"by hand\" with a crucible boot disk\n\nIn the product, live migration is managed by nexus. Stil"
},
{
"path": "docs/server-send-vcr.md",
"chars": 4889,
"preview": "# How to use the VCR replacement endpoint in propolis-server\n\nThis document describes how to use the `/instance/disk/{id"
},
{
"path": "docs/standalone-with-crucible.md",
"chars": 7091,
"preview": "# Run propolis-standalone with crucible disks\n\nThis document serves as an overview for running propolis-standalone using"
},
{
"path": "lib/propolis/Cargo.toml",
"chars": 2195,
"preview": "[package]\nname = \"propolis\"\nversion = \"0.1.0\"\nlicense = \"MPL-2.0\"\nedition = \"2021\"\nrust-version = \"1.90\"\n\n[dependencies]"
},
{
"path": "lib/propolis/src/accessors.rs",
"chars": 35525,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/api_version.rs",
"chars": 1486,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/attestation/boot_digest/crucible.rs",
"chars": 3924,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/attestation/boot_digest/mod.rs",
"chars": 1451,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/attestation/mod.rs",
"chars": 2183,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/attestation/server.rs",
"chars": 12541,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/block/attachment.rs",
"chars": 47271,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/block/crucible.rs",
"chars": 15622,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/block/file.rs",
"chars": 16198,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/block/id.rs",
"chars": 2219,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/block/in_memory.rs",
"chars": 8821,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/block/mem_async.rs",
"chars": 6715,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/block/minder.rs",
"chars": 17965,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/block/mod.rs",
"chars": 10802,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/chardev/file_out.rs",
"chars": 1762,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/chardev/mod.rs",
"chars": 3580,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/chardev/pollers.rs",
"chars": 24763,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/chardev/sock.rs",
"chars": 8987,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/common.rs",
"chars": 17929,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/cpuid.rs",
"chars": 20874,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/enlightenment/bhyve.rs",
"chars": 2222,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/enlightenment/hyperv/bits.rs",
"chars": 6001,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/enlightenment/hyperv/hypercall.rs",
"chars": 3287,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/enlightenment/hyperv/mod.rs",
"chars": 28030,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/enlightenment/hyperv/overlay.rs",
"chars": 27702,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/enlightenment/hyperv/tsc.rs",
"chars": 13609,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/enlightenment/mod.rs",
"chars": 7736,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "lib/propolis/src/exits.rs",
"chars": 14984,
"preview": "// This Source Code Form is subject to the terms of the Mozilla Public\n// License, v. 2.0. If a copy of the MPL was not "
}
]
// ... and 183 more files (download for full content)
About this extraction
This page contains the full source code of the oxidecomputer/propolis GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 383 files (3.3 MB), approximately 879.6k tokens, and a symbol index with 6267 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.