├── .buildkite └── pipeline.yml ├── .cargo └── config ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── pull_request_template.md ├── .gitignore ├── .mailmap ├── CHANGELOG.md ├── CHARTER.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CREDITS.md ├── Cargo.lock ├── Cargo.toml ├── FAQ.md ├── LICENSE ├── MAINTAINERS.md ├── NOTICE ├── PGP-KEY.asc ├── README.md ├── SECURITY.md ├── SPECIFICATION.md ├── THIRD-PARTY ├── build.rs ├── docs ├── RELEASE_POLICY.md ├── api_requests │ ├── actions.md │ ├── patch-block.md │ └── patch-network-interface.md ├── ballooning.md ├── benchmarks │ └── state-serialize.md ├── design.md ├── dev-machine-setup.md ├── devctr-image.md ├── device-api.md ├── getting-started.md ├── images │ ├── fc_decal_icon-dark_dark-bg.png │ ├── fc_decal_icon-dark_dark-bg.svg │ ├── fc_logo_full.svg │ ├── fc_logo_full_transparent-bg.png │ ├── fc_logo_full_transparent-bg_white-fg.png │ ├── fc_logo_full_white-bg.png │ ├── fc_logo_full_white-bg_tiny.png │ ├── fc_logo_icon-dark.svg │ ├── fc_logo_icon.svg │ ├── fc_logo_icon_transparent-bg.png │ ├── fc_logo_icon_transparent-bg_square.png │ ├── fc_logo_icon_white-bg.png │ ├── fc_logo_icon_white-bg_square.png │ ├── fc_sticker_full_white-bg.png │ ├── fc_sticker_full_white-bg.svg │ ├── fc_sticker_icon_white-bg.png │ ├── fc_sticker_icon_white-bg.svg │ ├── firecracker_host_integration.png │ ├── firecracker_threat_containment.png │ ├── version_graph.png │ ├── versionize.png │ ├── vsock-connections.drawio │ └── vsock-connections.png ├── initrd.md ├── jailer.md ├── logger.md ├── metrics.md ├── mmds │ ├── mmds-design.md │ └── mmds-user-guide.md ├── network-performance.md ├── network-setup.md ├── prod-host-setup.md ├── rootfs-and-kernel-setup.md ├── snapshotting │ ├── network-for-clones.md │ ├── random-for-clones.md │ ├── snapshot-support.md │ └── versioning.md └── vsock.md ├── resources ├── microvm-kernel-arm64.config ├── microvm-kernel-x86_64.config └── tests │ └── init.c ├── src ├── api_server │ ├── Cargo.toml │ ├── src │ │ ├── lib.rs │ │ ├── parsed_request.rs │ │ └── request │ │ │ ├── actions.rs │ │ │ ├── balloon.rs │ │ │ ├── boot_source.rs │ │ │ ├── drive.rs │ │ │ ├── instance_info.rs │ │ │ ├── logger.rs │ │ │ ├── machine_configuration.rs │ │ │ ├── metrics.rs │ │ │ ├── mmds.rs │ │ │ ├── mod.rs │ │ │ ├── net.rs │ │ │ ├── snapshot.rs │ │ │ └── vsock.rs │ └── swagger │ │ └── firecracker.yaml ├── arch │ ├── Cargo.toml │ └── src │ │ ├── aarch64 │ │ ├── cache_info.rs │ │ ├── fdt.rs │ │ ├── gic │ │ │ ├── gicv2.rs │ │ │ ├── gicv3.rs │ │ │ ├── mod.rs │ │ │ └── regs │ │ │ │ ├── dist_regs.rs │ │ │ │ ├── icc_regs.rs │ │ │ │ ├── mod.rs │ │ │ │ └── redist_regs.rs │ │ ├── layout.rs │ │ ├── mod.rs │ │ ├── output.dtb │ │ ├── output_with_initrd.dtb │ │ └── regs.rs │ │ ├── lib.rs │ │ └── x86_64 │ │ ├── gdt.rs │ │ ├── interrupts.rs │ │ ├── layout.rs │ │ ├── mod.rs │ │ ├── mptable.rs │ │ ├── msr.rs │ │ └── regs.rs ├── arch_gen │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── x86 │ │ ├── bootparam.rs │ │ ├── mod.rs │ │ ├── mpspec.rs │ │ └── msr_index.rs ├── cpuid │ ├── Cargo.toml │ └── src │ │ ├── bit_helper.rs │ │ ├── brand_string.rs │ │ ├── common.rs │ │ ├── cpu_leaf.rs │ │ ├── lib.rs │ │ ├── template │ │ ├── intel │ │ │ ├── c3.rs │ │ │ ├── mod.rs │ │ │ └── t2.rs │ │ └── mod.rs │ │ └── transformer │ │ ├── amd.rs │ │ ├── common.rs │ │ ├── intel.rs │ │ └── mod.rs ├── devices │ ├── Cargo.toml │ ├── src │ │ ├── bus.rs │ │ ├── legacy │ │ │ ├── i8042.rs │ │ │ ├── mod.rs │ │ │ ├── rtc_pl031.rs │ │ │ └── serial.rs │ │ ├── lib.rs │ │ ├── pseudo │ │ │ ├── boot_timer.rs │ │ │ └── mod.rs │ │ └── virtio │ │ │ ├── balloon │ │ │ ├── device.rs │ │ │ ├── event_handler.rs │ │ │ ├── mod.rs │ │ │ ├── persist.rs │ │ │ ├── test_utils.rs │ │ │ └── utils.rs │ │ │ ├── block │ │ │ ├── device.rs │ │ │ ├── event_handler.rs │ │ │ ├── mod.rs │ │ │ ├── persist.rs │ │ │ ├── request.rs │ │ │ └── test_utils.rs │ │ │ ├── device.rs │ │ │ ├── mmio.rs │ │ │ ├── mod.rs │ │ │ ├── net │ │ │ ├── device.rs │ │ │ ├── event_handler.rs │ │ │ ├── mod.rs │ │ │ ├── persist.rs │ │ │ ├── tap.rs │ │ │ └── test_utils.rs │ │ │ ├── persist.rs │ │ │ ├── queue.rs │ │ │ ├── test_utils.rs │ │ │ └── vsock │ │ │ ├── csm │ │ │ ├── connection.rs │ │ │ ├── mod.rs │ │ │ └── txbuf.rs │ │ │ ├── device.rs │ │ │ ├── event_handler.rs │ │ │ ├── mod.rs │ │ │ ├── packet.rs │ │ │ ├── persist.rs │ │ │ ├── test_utils.rs │ │ │ └── unix │ │ │ ├── mod.rs │ │ │ ├── muxer.rs │ │ │ ├── muxer_killq.rs │ │ │ └── muxer_rxq.rs │ └── tests │ │ ├── integration_tests.rs │ │ └── serial_utils │ │ └── mod.rs ├── dumbo │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── pdu │ │ ├── arp.rs │ │ ├── bytes.rs │ │ ├── ethernet.rs │ │ ├── ipv4.rs │ │ ├── mod.rs │ │ ├── tcp.rs │ │ └── udp.rs │ │ └── tcp │ │ ├── connection.rs │ │ ├── endpoint.rs │ │ ├── handler.rs │ │ └── mod.rs ├── firecracker │ ├── Cargo.toml │ └── src │ │ ├── api_server_adapter.rs │ │ ├── main.rs │ │ └── metrics.rs ├── jailer │ ├── Cargo.toml │ └── src │ │ ├── cgroup.rs │ │ ├── chroot.rs │ │ ├── env.rs │ │ └── main.rs ├── kernel │ ├── Cargo.toml │ └── src │ │ ├── cmdline │ │ └── mod.rs │ │ ├── lib.rs │ │ └── loader │ │ ├── elf.rs │ │ ├── mod.rs │ │ ├── test_elf.bin │ │ └── test_pe.bin ├── logger │ ├── Cargo.toml │ └── src │ │ ├── init.rs │ │ ├── lib.rs │ │ ├── logger.rs │ │ └── metrics.rs ├── micro_http │ ├── Cargo.toml │ └── src │ │ ├── common │ │ ├── headers.rs │ │ └── mod.rs │ │ ├── connection.rs │ │ ├── lib.rs │ │ ├── request.rs │ │ ├── response.rs │ │ └── server.rs ├── mmds │ ├── Cargo.toml │ └── src │ │ ├── data_store.rs │ │ ├── lib.rs │ │ ├── ns.rs │ │ └── persist.rs ├── net_gen │ ├── Cargo.toml │ └── src │ │ ├── if_tun.rs │ │ ├── iff.rs │ │ ├── inn.rs │ │ ├── lib.rs │ │ └── sockios.rs ├── polly │ ├── Cargo.toml │ └── src │ │ ├── event_manager.rs │ │ └── lib.rs ├── rate_limiter │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── persist.rs ├── seccomp │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── snapshot │ ├── Cargo.toml │ ├── benches │ │ └── version_map.rs │ ├── src │ │ ├── lib.rs │ │ └── persist.rs │ └── tests │ │ └── test.rs ├── utils │ ├── Cargo.toml │ └── src │ │ ├── arg_parser.rs │ │ ├── byte_order.rs │ │ ├── lib.rs │ │ ├── net │ │ ├── ipv4addr.rs │ │ ├── mac.rs │ │ └── mod.rs │ │ ├── signal.rs │ │ ├── sm.rs │ │ ├── structs.rs │ │ ├── time.rs │ │ └── validators.rs ├── virtio_gen │ ├── Cargo.toml │ ├── patches │ │ └── 0001-virtio_gen-remove-derive-Debug-from-packed-struct.patch │ └── src │ │ ├── lib.rs │ │ ├── virtio_blk.rs │ │ ├── virtio_net.rs │ │ └── virtio_ring.rs ├── vm-memory │ ├── Cargo.toml │ └── src │ │ ├── bitmap.rs │ │ ├── lib.rs │ │ └── mmap.rs └── vmm │ ├── Cargo.toml │ ├── benches │ └── main.rs │ ├── src │ ├── builder.rs │ ├── default_syscalls │ │ ├── filters.rs │ │ ├── macros.rs │ │ └── mod.rs │ ├── device_manager │ │ ├── legacy.rs │ │ ├── mmio.rs │ │ ├── mod.rs │ │ └── persist.rs │ ├── lib.rs │ ├── memory_snapshot.rs │ ├── persist.rs │ ├── resources.rs │ ├── rpc_interface.rs │ ├── signal_handler.rs │ ├── utilities │ │ ├── mock_devices │ │ │ └── mod.rs │ │ ├── mock_resources │ │ │ ├── dirtying_init.tgz │ │ │ ├── make_noisy_kernel.sh │ │ │ ├── mod.rs │ │ │ ├── test_elf.bin │ │ │ ├── test_noisy_elf.bin │ │ │ └── test_pe.bin │ │ ├── mock_seccomp │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── test_utils │ │ │ └── mod.rs │ ├── version_map.rs │ ├── vmm_config │ │ ├── balloon.rs │ │ ├── boot_source.rs │ │ ├── drive.rs │ │ ├── instance_info.rs │ │ ├── logger.rs │ │ ├── machine_config.rs │ │ ├── metrics.rs │ │ ├── mmds.rs │ │ ├── mod.rs │ │ ├── net.rs │ │ ├── snapshot.rs │ │ └── vsock.rs │ └── vstate │ │ ├── mod.rs │ │ ├── system.rs │ │ ├── vcpu │ │ ├── aarch64.rs │ │ ├── mod.rs │ │ └── x86_64.rs │ │ └── vm.rs │ └── tests │ └── integration_tests.rs ├── tests ├── README.md ├── conftest.py ├── framework │ ├── artifacts.py │ ├── builder.py │ ├── decorators.py │ ├── defs.py │ ├── http.py │ ├── jailer.py │ ├── matrix.py │ ├── microvm.py │ ├── microvms.py │ ├── mpsing.py │ ├── resources.py │ ├── s3fetcher.py │ ├── scheduler.py │ ├── state_machine.py │ ├── statistics │ │ ├── __init__.py │ │ ├── consumer.py │ │ ├── core.py │ │ ├── criteria.py │ │ ├── function.py │ │ ├── producer.py │ │ └── types.py │ ├── utils.py │ ├── utils_cpuid.py │ ├── utils_vsock.py │ └── vm_config.json ├── host_tools │ ├── cargo_build.py │ ├── change_net_config_space.c │ ├── cpu_load.py │ ├── drive.py │ ├── fillmem.c │ ├── logging.py │ ├── memory.py │ ├── network.py │ ├── newpid_cloner.c │ ├── proc.py │ ├── readmem.c │ ├── snapshot_helper.py │ └── vsock_helper.c ├── integration_tests │ ├── build │ │ ├── test_binary_size.py │ │ ├── test_clippy.py │ │ ├── test_coverage.py │ │ └── test_unittests.py │ ├── functional │ │ ├── test_api.py │ │ ├── test_balloon.py │ │ ├── test_cmd_line_start.py │ │ ├── test_concurrency.py │ │ ├── test_cpu_features.py │ │ ├── test_drives.py │ │ ├── test_error_code.py │ │ ├── test_initrd.py │ │ ├── test_logging.py │ │ ├── test_max_devices.py │ │ ├── test_max_vcpus.py │ │ ├── test_metrics.py │ │ ├── test_mmds.py │ │ ├── test_net.py │ │ ├── test_net_config_space.py │ │ ├── test_pause_resume.py │ │ ├── test_rate_limiter.py │ │ ├── test_rtc.py │ │ ├── test_serial_io.py │ │ ├── test_shut_down.py │ │ ├── test_signals.py │ │ ├── test_snapshot_advanced.py │ │ ├── test_snapshot_basic.py │ │ ├── test_snapshot_version.py │ │ ├── test_topology.py │ │ └── test_vsock.py │ ├── performance │ │ ├── network_tcp_throughput_test_config.py │ │ ├── test_boottime.py │ │ ├── test_network_latency.py │ │ ├── test_network_tcp_throughput.py │ │ ├── test_process_startup_time.py │ │ ├── test_snapshot_perf.py │ │ ├── test_versioned_serialization_benchmark.py │ │ ├── test_vsock_throughput.py │ │ └── vsock_throughput_test_config.py │ ├── security │ │ ├── demo_seccomp │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ └── bin │ │ │ │ ├── demo_advanced_jailer.rs │ │ │ │ ├── demo_basic_jailer.rs │ │ │ │ ├── demo_harmless.rs │ │ │ │ ├── demo_malicious.rs │ │ │ │ ├── demo_panic.rs │ │ │ │ └── seccomp_rules │ │ │ │ └── mod.rs │ │ ├── test_jail.py │ │ ├── test_sec_audit.py │ │ ├── test_seccomp.py │ │ └── test_ssbd_mitigation.py │ └── style │ │ ├── test_python.py │ │ ├── test_rust.py │ │ └── test_swagger.py └── pytest.ini └── tools ├── devctr ├── Dockerfile.aarch64 └── Dockerfile.x86_64 ├── devtool └── update-credits.sh /.buildkite/pipeline.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - label: "build" 3 | commands: 4 | - tools/devtool -y build --release 5 | - aws s3 cp build/cargo_target/x86_64-unknown-linux-musl/release/firecracker s3://flyio-builds/firecracker/${BUILDKITE_BRANCH}/firecracker --region us-east-2 & 6 | - aws s3 cp build/cargo_target/x86_64-unknown-linux-musl/release/jailer s3://flyio-builds/firecracker/${BUILDKITE_BRANCH}/jailer --region us-east-2 & 7 | - wait 8 | agents: 9 | queue: "high-concurrency" 10 | artifacts: 11 | - build/cargo_target/x86_64-unknown-linux-musl/release/firecracker 12 | - build/cargo_target/x86_64-unknown-linux-musl/release/jailer -------------------------------------------------------------------------------- /.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-linux-musl" 3 | target-dir = "build/cargo_target" 4 | 5 | [target.'cfg(any(target_arch="arm", target_arch="aarch64"))'] 6 | # On aarch64 musl depends on some libgcc functions (i.e `__addtf3` and other `*tf3` functions) for logic that uses 7 | # long double. Such functions are not builtin in the rust compiler, so we need to get them from libgcc. 8 | # No need for the `crt_static` flag as rustc appends it by default. 9 | rustflags = [ 10 | "-C", "link-arg=-lgcc", 11 | "-C", "link-arg=-lfdt", 12 | ] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[Bug] Title" 5 | labels: 'Quality: Bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the bug 11 | 12 | `[Author TODO: A clear and concise description of what the bug is.]` 13 | 14 | ## To Reproduce 15 | 16 | `[Author TODO: Steps to reproduce the behaviour:]` 17 | `[ 1. Start Firecracker via....]` 18 | `[ 2. Configure Firecracker via....]` 19 | `[ 3. ....]` 20 | 21 | ## Expected behaviour 22 | 23 | `[Author TODO: A clear and concise description of what you expected to happen.]` 24 | 25 | ## Environment 26 | 27 | `[Author TODO: Please supply the following information):]` 28 | `[ - Firecracker version.]` 29 | `[ - Host and guest kernel versions.]` 30 | `[ - Rootfs used.]` 31 | `[ - Architecture.]` 32 | `[ - Any other relevant software versions.]` 33 | 34 | ## Additional context 35 | 36 | `[Author TODO: How has this bug affected you?]` 37 | 38 | `[Author TODO: What are you trying to achieve?]` 39 | 40 | `[Author TODO: Do you have any idea of what the solution might be?]` 41 | 42 | ## Checks 43 | - [ ] Have you searched the Firecracker Issues database for similar problems? 44 | - [ ] Have you read the existing relevant Firecracker documentation? 45 | - [ ] Are you certain the bug being reported is a Firecracker issue? 46 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature Request] Title" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Why is this feature request important? What are the use cases? Please describe. 11 | 12 | `[Author TODO: A clear and concise description of what the problem is.]` 13 | 14 | ## Describe the desired solution 15 | 16 | `[Author TODO: A clear and concise description of how you would like the feature to work.]` 17 | 18 | ## Describe possible alternatives 19 | 20 | `[Author TODO: A clear and concise description of any alternative solutions or features you have considered.]` 21 | 22 | `[Author TODO: How do you work around not having this feature?]` 23 | 24 | ## Additional context 25 | 26 | `[Author TODO: Add additional context about this feature request here.]` 27 | 28 | ## Checks 29 | - [ ] Have you searched the Firecracker Issues database for similar requests? 30 | - [ ] Have you read all the existing relevant Firecracker documentation? 31 | - [ ] Have you read and understood Firecracker's core tenets? 32 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Reason for This PR 2 | 3 | `[Author TODO: add issue # or explain reasoning.]` 4 | 5 | ## Description of Changes 6 | 7 | `[Author TODO: add description of changes.]` 8 | 9 | - [ ] This functionality can be added in [`rust-vmm`](https://github.com/rust-vmm). 10 | 11 | ## License Acceptance 12 | 13 | By submitting this pull request, I confirm that my contribution is made under 14 | the terms of the Apache 2.0 license. 15 | 16 | ## PR Checklist 17 | 18 | `[Author TODO: Meet these criteria.]` 19 | `[Reviewer TODO: Verify that these criteria are met. Request changes if not]` 20 | 21 | - [ ] All commits in this PR are signed (`git commit -s`). 22 | - [ ] The reason for this PR is clearly provided (issue no. or explanation). 23 | - [ ] The description of changes is clear and encompassing. 24 | - [ ] Any required documentation changes (code and docs) are included in this PR. 25 | - [ ] Any newly added `unsafe` code is properly documented. 26 | - [ ] Any API changes are reflected in `firecracker/swagger.yaml`. 27 | - [ ] Any user-facing changes are mentioned in `CHANGELOG.md`. 28 | - [ ] All added/changed functionality is tested. 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | target 3 | *.rs.bk 4 | *.iml 5 | .idea 6 | __pycache__ 7 | *.pyc 8 | .vscode 9 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Alexandra Iordache Alexandra Ghecenco 2 | Alexandru Branciog <31914537+alexbranciog@users.noreply.github.com> 3 | Bogdan Ionita 4 | Liu Jiang 5 | Marc Brooker 6 | Radu Weiss <31901393+raduweiss@users.noreply.github.com> 7 | Rolf Neugebauer 8 | Serban Iorga 9 | Julian Stecklina 10 | Tamio-Vesa Nakajima 11 | Iulian Barbu 12 | Petre Eftime 13 | karthik nedunchezhiyan 14 | Alin Dima 15 | Andrei Sandu <54316454+sandreim@users.noreply.github.com> 16 | Diana Popa 17 | Alexandru Cihodaru 18 | Liviu Berciu 19 | -------------------------------------------------------------------------------- /CHARTER.md: -------------------------------------------------------------------------------- 1 | # Firecracker Charter 2 | 3 | ## Mission 4 | 5 | Our mission is to enable secure, multi-tenant, minimal-overhead 6 | execution of container and function workloads. 7 | 8 | ## Tenets (unless you know better ones) 9 | 10 | These tenets guide Firecracker's development: 11 | 12 | 1. **Built-In Security**: We provide compute security barriers that 13 | enable multi-tenant workloads, and cannot be mistakenly disabled by 14 | customers. Customer workloads are simultaneously considered sacred 15 | (shall not be touched) and malicious (shall be defended against). 16 | We continuously invest in defense in depth and maintain mechanisms 17 | that ensure security best practices. 18 | 1. **Light-Weight Virtualization**: We prioritize measuring 19 | Firecracker's hardware overhead in the dimensions that are important 20 | for our customers, and we strive to make this overhead negligible. 21 | 1. **Minimalist in Features**: If it's not clearly required for our 22 | mission, we won't build it. We maintain a single implementation per 23 | capability, and deprecate obsolete implementations; resolving 24 | exceptions is a high priority issue. 25 | 1. **Compute Oversubscription**: All of the hardware compute resources 26 | exposed by Firecracker to guests can be securely oversubscribed. 27 | 28 | ## Contributions & Project Roles 29 | 30 | All contributions must align with this charter and follow Firecracker's 31 | [contribution process](CONTRIBUTING.md). 32 | 33 | Firecracker [maintainers](MAINTAINERS.md) merge contributions into the 34 | master branch and create Firecracker releases. Maintainers are also 35 | subject to the mission and tenets outlined above. Anyone may submit 36 | and review contributions. 37 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project has adopted the 4 | [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 5 | For more information see the 6 | [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 7 | [opensource-codeofconduct@amazon.com](mailto:opensource-codeofconduct@amazon.com) 8 | with any additional questions or comments. 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["src/firecracker", "src/jailer"] 3 | default-members = ["src/firecracker"] 4 | 5 | [profile.dev] 6 | panic = "abort" 7 | 8 | [profile.release] 9 | panic = "abort" 10 | lto = true 11 | 12 | [patch.crates-io] 13 | kvm-bindings = { git = "https://github.com/firecracker-microvm/kvm-bindings", tag = "v0.3.0-3", features = ["fam-wrappers"] } 14 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Maintainers 2 | 3 | Firecracker is maintained by a dedicated team within Amazon: 4 | 5 | - Alex Agache 6 | - Adrian Catangiu 7 | - Alexandra Iordache 8 | - Andreea Florescu 9 | - Andrei Sandu 10 | - Dan Horobeanu 11 | - Diana Popa 12 | - Iulian Barbu 13 | - Serban Iorga 14 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Firecracker 2 | Copyright 2017-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | 5 | Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 6 | Use of this source code is governed by a BSD-style license that can be 7 | found in the THIRD-PARTY file. 8 | -------------------------------------------------------------------------------- /PGP-KEY.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mDMEXoNLVhYJKwYBBAHaRw8BAQdA/RBM+jgzq6EXzTc7zDmDgcSNENKJqZ7djI7G 4 | Iz1Os720TkZpcmVjcmFja2VyIFNlY3VyaXR5IERpc2Nsb3N1cmVzIDxmaXJlY3Jh 5 | Y2tlci1zZWN1cml0eS1kaXNjbG9zdXJlc0BhbWF6b24uY29tPoiZBBMWCABBAhsD 6 | BQkJZgGABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEEp07t53fN17pTgiajKCi5 7 | jaKQW+AFAl6DTtACGQEACgkQKCi5jaKQW+BnBwEA/+VBs/9t5aUBb20HZMSYL3tu 8 | Ffh+fI1FSOsMSjXfAe8A/2MlxfdDdYtAhxwwN1Y+hGrm/PdT1LULJqoz14r3xUUJ 9 | tCVCYXJidSwgSXVsaWFuIE1hcmlhbiA8aXVsQGFtYXpvbi5jb20+iHgEMBYIACAW 10 | IQSnTu3nd83XulOCJqMoKLmNopBb4AUCXoNO2QIdAAAKCRAoKLmNopBb4EHTAQDS 11 | ZzcN7MU7mtM79+ev9aoX0/OltBPq/QQy22qa3wbCwQD/cd1nXu3pKUKvyHtTpVFB 12 | Jd+YNpG2ox3e3SfVZyXeRgiIlgQTFggAPgIbAwUJCWYBgAULCQgHAgYVCgkICwIE 13 | FgIDAQIeAQIXgBYhBKdO7ed3zde6U4ImoygouY2ikFvgBQJeg07QAAoJECgouY2i 14 | kFvgKggBAIUpB5stOoo/NjSMwpTMwa/jiqephv/GarCrluYRbQ/aAQDTn7aCX4ab 15 | b/vqLjiePos5tuToBoRXkL8QzdGyeONpBrQsQ29tcHV0ZSBDYXBzdWxlIDxjb21w 16 | dXRlLWNhcHN1bGVAYW1hem9uLmNvbT6IlgQTFggAPhYhBKdO7ed3zde6U4Imoygo 17 | uY2ikFvgBQJeg09JAhsDBQkJZgGABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJ 18 | ECgouY2ikFvgyNcBAKSacUhi9Kb4C7Ybdzu4EKgQxfKnBucoubonF658K/GVAQDA 19 | d0sVeTVu3g/Z/7DHCBg8Jn+LSl3fCoEUeK4Z2fBeAbg4BF6DS1YSCisGAQQBl1UB 20 | BQEBB0B8rM1FwSBZzXAg2IxqJ8qUkwMVorbXlA+p/MNXQdeACAMBCAeIfgQYFggA 21 | JhYhBKdO7ed3zde6U4ImoygouY2ikFvgBQJeg0tWAhsMBQkJZgGAAAoJECgouY2i 22 | kFvgz8sA/jz0dUAwtTMtkdvjP4Dewrq1d8o7sgy1wV5Ax/a20DFyAQCJQIMf4MtF 23 | jrojJjAIHRUlukf4VVEtGS7IroK56JS3Bw== 24 | =1xBx 25 | -----END PGP PUBLIC KEY BLOCK----- 26 | 27 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Issue Policy 2 | 3 | If you uncover a security issue with Firecracker, please write to us on 4 | . Please encrypt sensitive 5 | information using the [PGP key](PGP-KEY.asc). 6 | 7 | Once the Firecracker [maintainers](MAINTAINERS.md) become aware (or are made 8 | aware) of a security issue, they will immediately assess it. Based on impact and 9 | complexity, they will determine an embargo period (if externally reported, the 10 | period will be agreed upon with the external party). 11 | 12 | During the embargo period, maintainers will prioritize developing a fix over 13 | other activities. Within this period, maintainers may also notify a limited 14 | number of trusted parties via a pre-disclosure list, providing them with 15 | technical information, a risk assessment, and early access to a fix. 16 | 17 | The external customers are included in this group based on the scale of their 18 | Firecracker usage in production. The pre-disclosure list may also contain 19 | significant external security contributors that can join the effort to fix the 20 | issue during the embargo period. 21 | 22 | At the end of the embargo period, maintainers will publicly release information 23 | about the security issue together with the Firecracker patches that mitigate it. 24 | -------------------------------------------------------------------------------- /THIRD-PARTY: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are 5 | // met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above 10 | // copyright notice, this list of conditions and the following disclaimer 11 | // in the documentation and/or other materials provided with the 12 | // distribution. 13 | // * Neither the name of Google Inc. nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::process::Command; 5 | 6 | // this build script is called on en every `devtool build`, 7 | // embedding the FIRECRACKER_VERSION directly in the resulting binary 8 | fn main() { 9 | let firecracker_version = Command::new("git") 10 | .args(&["describe", "--dirty"]) 11 | .output() 12 | .ok() 13 | .and_then(|output| { 14 | if output.status.success() { 15 | return Some(output.stdout); 16 | } 17 | None 18 | }) 19 | .and_then(|version_bytes| String::from_utf8(version_bytes).ok()) 20 | .map(|version_string| version_string.trim_start_matches('v').to_string()) 21 | .unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string()); 22 | 23 | let commit_hash = Command::new("git") 24 | .args(&["rev-parse", "HEAD"]) 25 | .output() 26 | .ok() 27 | .and_then(|output| { 28 | if output.status.success() { 29 | return Some(output.stdout); 30 | } 31 | None 32 | }) 33 | .and_then(|version_bytes| String::from_utf8(version_bytes).ok()) 34 | .unwrap_or_else(|| "".to_string()); 35 | 36 | println!( 37 | "cargo:rustc-env=FIRECRACKER_VERSION={}-fly-{}", 38 | firecracker_version, commit_hash 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /docs/api_requests/actions.md: -------------------------------------------------------------------------------- 1 | # Actions API Request 2 | 3 | Firecracker microVMs can execute actions that can be triggered via `PUT` 4 | requests on the `/actions` resource. 5 | 6 | Details about the required fields can be found in the 7 | [swagger definition](../../src/api_server/swagger/firecracker.yaml). 8 | 9 | ## InstanceStart 10 | 11 | The `InstanceStart` action powers on the microVM and starts the guest OS. It 12 | does not have a payload. It can only be successfully called once. 13 | 14 | ### InstanceStart Example 15 | 16 | ```bash 17 | curl --unix-socket ${socket} -i \ 18 | -X PUT "http://localhost/actions" \ 19 | -H "accept: application/json" \ 20 | -H "Content-Type: application/json" \ 21 | -d "{ 22 | \"action_type\": \"InstanceStart\" 23 | }" 24 | ``` 25 | 26 | ## FlushMetrics 27 | 28 | The `FlushMetrics` action flushes the metrics on user demand. 29 | 30 | ### FlushMetrics Example 31 | 32 | ```bash 33 | curl --unix-socket /tmp/firecracker.socket -i \ 34 | -X PUT "http://localhost/actions" \ 35 | -H "accept: application/json" \ 36 | -H "Content-Type: application/json" \ 37 | -d "{ 38 | \"action_type\": \"FlushMetrics\" 39 | }" 40 | ``` 41 | 42 | ## [Intel and AMD only] SendCtrlAltDel 43 | 44 | This action will send the CTRL+ALT+DEL key sequence to the microVM. By 45 | convention, this sequence has been used to trigger a soft reboot and, as such, 46 | most Linux distributions perform an orderly shutdown and reset upon receiving 47 | this keyboard input. Since Firecracker exits on CPU reset, `SendCtrlAltDel` 48 | can be used to trigger a clean shutdown of the microVM. 49 | 50 | For this action, Firecracker emulates a standard AT keyboard, connected via an 51 | i8042 controller. Driver support for both these devices needs to be present in 52 | the guest OS. For Linux, that means the guest kernel needs 53 | `CONFIG_SERIO_I8042` and `CONFIG_KEYBOARD_ATKBD`. 54 | 55 | **Note1**: at boot time, the Linux driver for i8042 spends 56 | a few tens of milliseconds probing the device. This can be disabled by using 57 | these kernel command line parameters: 58 | 59 | ```i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd``` 60 | 61 | **Note2** This action is only supported on `x86_64` architecture. 62 | 63 | ### SendCtrlAltDel Example 64 | 65 | ```bash 66 | curl --unix-socket /tmp/firecracker.socket -i \ 67 | -X PUT "http://localhost/actions" \ 68 | -H "accept: application/json" \ 69 | -H "Content-Type: application/json" \ 70 | -d "{ 71 | \"action_type\": \"SendCtrlAltDel\" 72 | }" 73 | ``` 74 | -------------------------------------------------------------------------------- /docs/api_requests/patch-network-interface.md: -------------------------------------------------------------------------------- 1 | # Updating A Network Interface 2 | 3 | After the microVM is started, the rate limiters assigned to a network 4 | interface can be updated via a `PATCH /network-interfaces/{id}` API 5 | call. 6 | 7 | E.g. for a network interface created with: 8 | 9 | ``` 10 | PUT /network-interfaces/iface_1 HTTP/1.1 11 | Host: localhost 12 | Content-Type: application/json 13 | Accept: application/json 14 | 15 | { 16 | "iface_id": "iface_1", 17 | "host_dev_name": "fctap1", 18 | "guest_mac": "06:00:c0:a8:34:02", 19 | "rx_rate_limiter": { 20 | "bandwidth": { 21 | "size": 1024, 22 | "one_time_burst": 1048576, 23 | "refill_time": 1000 24 | } 25 | }, 26 | "tx_rate_limiter": { 27 | "bandwidth": { 28 | "size": 1024, 29 | "one_time_burst": 1048576, 30 | "refill_time": 1000 31 | } 32 | } 33 | } 34 | ``` 35 | 36 | A `PATCH` request can be sent at any future time, to update the rate 37 | limiters: 38 | 39 | ``` 40 | PATCH /network-interfaces/iface_1 HTTP/1.1 41 | Host: localhost 42 | Content-Type: application/json 43 | Accept: application/json 44 | 45 | { 46 | "iface_id": "iface_1", 47 | "rx_rate_limiter": { 48 | "bandwidth": { 49 | "size": 1048576, 50 | "refill_time": 1000 51 | }, 52 | "ops": { 53 | "size": 2000, 54 | "refill_time": 1000 55 | } 56 | } 57 | } 58 | ``` 59 | 60 | The full specification of the data structures available for this call can be 61 | found in our [OpenAPI spec](../../src/api_server/swagger/firecracker.yaml). 62 | 63 | **Note**: The data provided for the update is merged with the existing data. 64 | In the above example, the RX rate limit is updated, but the TX rate limit 65 | remains unchanged. 66 | 67 | 68 | # Removing Rate Limiting 69 | 70 | A rate limit can be disabled by providing a 0-sized token bucket. E.g., 71 | following the above example, the TX rate limit can be disabled with: 72 | 73 | 74 | ``` 75 | PATCH /network-interfaces/iface_1 HTTP/1.1 76 | Host: localhost 77 | Content-Type: application/json 78 | Accept: application/json 79 | 80 | { 81 | "iface_id": "iface_1", 82 | "tx_rate_limiter": { 83 | "bandwidth": { 84 | "size": 0, 85 | "refill_time": 0 86 | }, 87 | "ops": { 88 | "size": 0, 89 | "refill_time": 0 90 | } 91 | } 92 | } 93 | ``` 94 | -------------------------------------------------------------------------------- /docs/benchmarks/state-serialize.md: -------------------------------------------------------------------------------- 1 | 2 | ### MicroVM state serialization benchmarks 3 | The benchmarks have been performed using a synthetic state snapshot that contains 100 structs and a 10k element array. 4 | Source code: [src/snapshot/benches/main.rs](../../src/snapshot/benches/main.rs). 5 | Snapshot size: 83886 bytes. 6 | 7 | ### Host configuration 8 | - Architecture: x86_64 9 | - CPU op-mode(s): 32-bit, 64-bit 10 | - Byte Order: Little Endian 11 | - CPU(s): 4 12 | - Thread(s) per core: 2 13 | - Core(s) per socket: 2 14 | - Socket(s): 1 15 | - NUMA node(s): 1 16 | - Vendor ID: GenuineIntel 17 | - CPU family: 6 18 | - Model: 142 19 | - Model name: Intel(R) Core(TM) i7-7600U CPU @ 2.80GHz 20 | - Stepping: 9 21 | - CPU MHz: 1100.008 22 | - CPU max MHz: 3900.0000 23 | - CPU min MHz: 400.0000 24 | - BogoMIPS: 5799.77 25 | - Virtualization: VT-x 26 | - L1d cache: 32K 27 | - L1i cache: 32K 28 | - L2 cache: 256K 29 | - L3 cache: 4096K 30 | - NUMA node0 CPU(s): 0-3 31 | - Flags: `fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d` 32 | 33 | ### Current baseline 34 | 35 | | Test | Mean | 36 | |---------------------|---------------| 37 | | Serialize | 371.38 us | 38 | | Serialize + crc64 | 493.26 us | 39 | | Deserialize | 90.755 us | 40 | | Deserialize + crc64 | 216.90 us | 41 | 42 | Detailed criterion benchmarks available [here](https://s3.amazonaws.com/spec.ccfc.min/perf/snapshot-0.23/report/index.html). -------------------------------------------------------------------------------- /docs/images/fc_decal_icon-dark_dark-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/fc_decal_icon-dark_dark-bg.png -------------------------------------------------------------------------------- /docs/images/fc_logo_full_transparent-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/fc_logo_full_transparent-bg.png -------------------------------------------------------------------------------- /docs/images/fc_logo_full_transparent-bg_white-fg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/fc_logo_full_transparent-bg_white-fg.png -------------------------------------------------------------------------------- /docs/images/fc_logo_full_white-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/fc_logo_full_white-bg.png -------------------------------------------------------------------------------- /docs/images/fc_logo_full_white-bg_tiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/fc_logo_full_white-bg_tiny.png -------------------------------------------------------------------------------- /docs/images/fc_logo_icon_transparent-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/fc_logo_icon_transparent-bg.png -------------------------------------------------------------------------------- /docs/images/fc_logo_icon_transparent-bg_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/fc_logo_icon_transparent-bg_square.png -------------------------------------------------------------------------------- /docs/images/fc_logo_icon_white-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/fc_logo_icon_white-bg.png -------------------------------------------------------------------------------- /docs/images/fc_logo_icon_white-bg_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/fc_logo_icon_white-bg_square.png -------------------------------------------------------------------------------- /docs/images/fc_sticker_full_white-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/fc_sticker_full_white-bg.png -------------------------------------------------------------------------------- /docs/images/fc_sticker_icon_white-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/fc_sticker_icon_white-bg.png -------------------------------------------------------------------------------- /docs/images/firecracker_host_integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/firecracker_host_integration.png -------------------------------------------------------------------------------- /docs/images/firecracker_threat_containment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/firecracker_threat_containment.png -------------------------------------------------------------------------------- /docs/images/version_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/version_graph.png -------------------------------------------------------------------------------- /docs/images/versionize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/versionize.png -------------------------------------------------------------------------------- /docs/images/vsock-connections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/docs/images/vsock-connections.png -------------------------------------------------------------------------------- /docs/initrd.md: -------------------------------------------------------------------------------- 1 | # Creating and using an initrd for Firecracker 2 | 3 | ## Creating 4 | 5 | ### Based on alpine or suse 6 | 7 | You can use this script to generate an initrd either based on alpine or suse linux: https://github.com/marcov/firecracker-initrd 8 | 9 | The script extracts the init system from each distribution and creates a initrd. 10 | 11 | ### Custom 12 | 13 | Use this option for creating an initrd if you're building your own init or if you need any specific files / logic in your initrd. 14 | 15 | ```bash 16 | mkdir initrd 17 | cp /path/to/your/init initrd/init 18 | # copy everything else you need in initrd/ 19 | cd initrd 20 | find . -print0 | cpio --null --create --verbose --format=newc > initrd.cpio 21 | ``` 22 | 23 | ## Usage 24 | 25 | When setting your boot source, add a `initrd_path` property like so: 26 | 27 | ```shell 28 | curl --unix-socket /tmp/firecracker.socket -i \ 29 | -X PUT 'http://localhost/boot-source' \ 30 | -H 'Accept: application/json' \ 31 | -H 'Content-Type: application/json' \ 32 | -d "{ 33 | \"kernel_image_path\": \"/path/to/kernel\", 34 | \"boot_args\": \"console=ttyS0 reboot=k panic=1 pci=off\", 35 | \"initrd_path\": \"/path/to/initrd.cpio\" 36 | }" 37 | ``` 38 | 39 | ### Notes 40 | 41 | - You should not use a drive with `is_root_device: true` when using an initrd 42 | - Make sure your kernel configuration has `CONFIG_BLK_DEV_INITRD=y` 43 | - If you don't want to place your init at the root of your initrd, you can add `rdinit=/path/to/init` to your `boot_args` property 44 | - If you intend to `pivot_root` in your init, it won't be possible because the initrd is mounted as a rootfs and cannot be unmounted. You will need to use `switch_root` instead. -------------------------------------------------------------------------------- /docs/logger.md: -------------------------------------------------------------------------------- 1 | # Firecracker logger Configuration 2 | 3 | For the logging capability, Firecracker uses a single Logger object. 4 | The Logger can be configured either by sending a `PUT` API Request to 5 | the `/logger` path or by command line. You can configure the Logger 6 | only once (by using one of these options) and once configured, you 7 | can not update it. 8 | 9 | ## Prerequisites 10 | 11 | In order to configure the Logger, first you have to create the resource 12 | that will be used for logging: 13 | 14 | ```bash 15 | # Create the required named pipe: 16 | mkfifo logs.fifo 17 | 18 | # The logger also works with usual files: 19 | touch logs.file 20 | ``` 21 | 22 | ## Using the API socket for configuration 23 | 24 | You can configure the Logger by sending the following API command: 25 | 26 | ```bash 27 | curl --unix-socket /tmp/firecracker.socket -i \ 28 | -X PUT "http://localhost/logger" \ 29 | -H "accept: application/json" \ 30 | -H "Content-Type: application/json" \ 31 | -d "{ 32 | "log_path": "logs.fifo", 33 | "level": "Warning", 34 | "show_level": false, 35 | "show_log_origin": false 36 | }" 37 | ``` 38 | 39 | Details about the required and optional fields can be found in the 40 | [swagger definition](../src/api_server/swagger/firecracker.yaml). 41 | 42 | ## Using command line parameters for configuration 43 | 44 | If you want to configure the Logger on startup and without using the 45 | API socket, you can do that by passing the parameter `--log-path` to 46 | the Firecracker process: 47 | 48 | ```bash 49 | ./firecracker --api-sock /tmp/firecracker.socket --log-path 50 | 51 | ``` 52 | 53 | The other Logger fields have, in this case, the default values: 54 | `Level -> Warning`, `show_level -> false`, `show_log_origin -> false`. 55 | For configuring these too, you can also pass the following optional 56 | parameters: `--level `, `--show-level`, `--show-log-origin`: 57 | 58 | ```bash 59 | ./firecracker --api-sock /tmp/firecracker.socket --log-path 60 | logs.fifo --level Error --show-level --show-log-origin 61 | ``` 62 | 63 | ## Reading from the logging destination 64 | 65 | The `logs.fifo` pipe will store the human readable logs, e.g. errors, 66 | warnings etc.(depending on the level). 67 | 68 | If the path provided is a named pipe, you can use the script below to 69 | read from it: 70 | 71 | ```shell script 72 | logs=logs.fifo 73 | 74 | while true 75 | do 76 | if read line <$logs; then 77 | echo $line 78 | fi 79 | done 80 | 81 | echo "Reader exiting" 82 | 83 | ``` 84 | 85 | Otherwise, if the path points to a normal file, you can simply do: 86 | 87 | ```shell script 88 | cat logs.file 89 | ``` 90 | -------------------------------------------------------------------------------- /docs/metrics.md: -------------------------------------------------------------------------------- 1 | # Firecracker Metrics Configuration 2 | 3 | For the metrics capability, Firecracker uses a single Metrics system. 4 | This system can be configured by sending a `PUT` API Request to the 5 | `/metrics` path. 6 | 7 | ## Prerequisites 8 | 9 | In order to configure the Metrics, first you have to create the resource 10 | that will be used for storing the metrics: 11 | 12 | ```bash 13 | # Create the required named pipe: 14 | mkfifo metrics.fifo 15 | 16 | # The Metrics system also works with usual files: 17 | touch metrics.file 18 | ``` 19 | 20 | ## Configuring the system 21 | 22 | You can configure the Metrics system by sending the following API command: 23 | 24 | ```bash 25 | curl --unix-socket /tmp/firecracker.socket -i \ 26 | -X PUT "http://localhost/metrics" \ 27 | -H "accept: application/json" \ 28 | -H "Content-Type: application/json" \ 29 | -d "{ 30 | \"metrics_path\": \"metrics.fifo\" 31 | }" 32 | ``` 33 | 34 | Details about this configuration can be found in the 35 | [swagger definition](../src/api_server/swagger/firecracker.yaml). 36 | 37 | The metrics are written to the `metrics_path` in JSON format. 38 | 39 | ## Flushing the metrics 40 | 41 | The metrics get flushed in two ways: 42 | 43 | * without user intervention every 60 seconds; 44 | * upon user demand, by issuing a `FlushMetrics` request. You can 45 | find how to use this request in the [actions API](api_requests/actions.md). 46 | 47 | If the path provided is a named pipe, you can use the script below to 48 | read from it: 49 | 50 | ```shell script 51 | metrics=metrics.fifo 52 | 53 | while true 54 | do 55 | if read line <$metrics; then 56 | echo $line 57 | fi 58 | done 59 | 60 | echo "Reader exiting" 61 | 62 | ``` 63 | 64 | Otherwise, if the path points to a normal file, you can simply do: 65 | 66 | ```shell script 67 | cat metrics.file 68 | ``` 69 | -------------------------------------------------------------------------------- /docs/network-performance.md: -------------------------------------------------------------------------------- 1 | # Firecracker network performance numbers 2 | 3 | This document provides details about Firecracker network performance. 4 | The numbers presented are dependent on the hardware (CPU, networking card, etc.), OS version and settings. 5 | Scope of the measurements is to illustrate the limits for the emulation thread. 6 | 7 | ## TCP Throughput 8 | 9 | Segment size/ Direction | 1460bytes | 256bytes | 128bytes | 96bytes 10 | --- | --- | --- | --- |--- 11 | Ingress| 25Gbps | 23Gbps | 20Gbps | 18Gbps 12 | Egress | 25Gbps | 23Gbps | 20Gbps | 18Gbps 13 | Bidirectional | 18Gbps | 18Gbps | 18Gbps | 18Gbps 14 | 15 | ### Setup and test description 16 | 17 | Throughput measurements were done using [iperf3](https://iperf.fr/). The target is to fully saturate the emulation thread and keep it at 100% utilization. 18 | No adjustments were done to socket buffer, or any other network related kernel parameters. 19 | 20 | To identify the limit of emulation thread, TCP throughput was measured between host and guest. An EC2 [M5d.metal](https://aws.amazon.com/ec2/instance-types/m5/) instance, running [Amazon Linux 2](https://aws.amazon.com/amazon-linux-ami/), was used as a host. 21 | 22 | For ingress or egress throughput measurements, a Firecracker microVM running Kernel 4.14 with 4GB of Ram, 8 vCPUs and one network interface was used. 23 | The measurements were taken using 6 iperf3 clients running on host and 6 iperf3 serves running on guest and vice versa. 24 | 25 | For bidirectional throughput measurements, a Firecracker microVM running Amazon Linux 2, Kernel 4.14 with 4GB of Ram, 12 vCPUs and one network interface was used. 26 | The measurements were taken using 4 iperf3 clients and 4 iperf3 servers running on both host and guest. 27 | 28 | ## Latency 29 | 30 | The virtualization layer, Firecracker emulation thread plus host kernel stack, is responsible for adding on average 0.06ms of network latency. 31 | 32 | ### Setup and test description 33 | 34 | Latency measurements were done using ping round trip times. 35 | 2 x EC2 M5d.metal instances running Amazon Linux 2 within the same [VPC](https://aws.amazon.com/vpc/) were used, with a security group configured so that it would allow traffic from instances using private IPs. A 10Mbps background traffic was running between instances. 36 | 37 | Round trip time between instances was measured. 38 | 39 | ```rtt min/avg/max/mdev = 0.101/0.198/0.237/0.044 ms``` 40 | 41 | On one of the instances, a Firecracker microVM running Kernel 4.14, with 1 GB of RAM, 2 vCPUs, one network interface running was used. 42 | Round trip between the microVM and the other instance was measured, while a 10Mbps background traffic was running. 43 | 44 | ```rtt min/avg/max/mdev = 0.191/0.321/0.519/0.058 ms``` 45 | 46 | From the difference between those we can conclude that ~0.06ms are the virtualization overhead. 47 | -------------------------------------------------------------------------------- /resources/tests/init.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Init wrapper for boot timing. Alpine-specific because it points at /sbin/openrc-init. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Base address values are defined in arch/src/lib.rs as arch::MMIO_MEM_START. 12 | // Values are computed in arch/src//mod.rs from the architecture layouts. 13 | // Position on the bus is defined by MMIO_LEN increments, where MMIO_LEN is 14 | // defined as 0x1000 in vmm/src/device_manager/mmio.rs. 15 | #ifdef __x86_64__ 16 | #define MAGIC_MMIO_SIGNAL_GUEST_BOOT_COMPLETE 0xd0000000 17 | #endif 18 | #ifdef __aarch64__ 19 | #define MAGIC_MMIO_SIGNAL_GUEST_BOOT_COMPLETE 0x40000000 20 | #endif 21 | 22 | #define MAGIC_VALUE_SIGNAL_GUEST_BOOT_COMPLETE 123 23 | 24 | int main () { 25 | int fd = open("/dev/mem", (O_RDWR | O_SYNC | O_CLOEXEC)); 26 | int mapped_size = getpagesize(); 27 | 28 | char *map_base = mmap(NULL, 29 | mapped_size, 30 | PROT_WRITE, 31 | MAP_SHARED, 32 | fd, 33 | MAGIC_MMIO_SIGNAL_GUEST_BOOT_COMPLETE); 34 | 35 | *map_base = MAGIC_VALUE_SIGNAL_GUEST_BOOT_COMPLETE; 36 | msync(map_base, mapped_size, MS_ASYNC); 37 | 38 | const char *init = "/sbin/openrc-init"; 39 | 40 | char *const argv[] = { "/sbin/init", NULL }; 41 | char *const envp[] = { }; 42 | 43 | execve(init, argv, envp); 44 | } 45 | -------------------------------------------------------------------------------- /src/api_server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "api_server" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | serde = ">=1.0.27" 9 | serde_derive = ">=1.0.27" 10 | serde_json = ">=1.0.9" 11 | libc = ">=0.2.39" 12 | 13 | logger = { path = "../logger" } 14 | micro_http = { path = "../micro_http" } 15 | mmds = { path = "../mmds" } 16 | seccomp = { path = "../seccomp" } 17 | utils = { path = "../utils" } 18 | vmm = { path = "../vmm" } 19 | 20 | [dev-dependencies] 21 | libc = ">=0.2.39" 22 | -------------------------------------------------------------------------------- /src/api_server/src/request/boot_source.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use super::super::VmmAction; 5 | use crate::parsed_request::{Error, ParsedRequest}; 6 | use crate::request::Body; 7 | use logger::{IncMetric, METRICS}; 8 | use vmm::vmm_config::boot_source::BootSourceConfig; 9 | 10 | pub(crate) fn parse_put_boot_source(body: &Body) -> Result { 11 | METRICS.put_api_requests.boot_source_count.inc(); 12 | Ok(ParsedRequest::new_sync(VmmAction::ConfigureBootSource( 13 | serde_json::from_slice::(body.raw()).map_err(|e| { 14 | METRICS.put_api_requests.boot_source_fails.inc(); 15 | Error::SerdeJson(e) 16 | })?, 17 | ))) 18 | } 19 | 20 | #[cfg(test)] 21 | mod tests { 22 | use super::*; 23 | 24 | #[test] 25 | fn test_parse_boot_request() { 26 | assert!(parse_put_boot_source(&Body::new("invalid_payload")).is_err()); 27 | 28 | let body = r#"{ 29 | "kernel_image_path": "/foo/bar", 30 | "initrd_path": "/bar/foo", 31 | "boot_args": "foobar" 32 | }"#; 33 | let same_body = BootSourceConfig { 34 | kernel_image_path: String::from("/foo/bar"), 35 | initrd_path: Some(String::from("/bar/foo")), 36 | boot_args: Some(String::from("foobar")), 37 | }; 38 | let result = parse_put_boot_source(&Body::new(body)); 39 | assert!(result.is_ok()); 40 | let parsed_req = result.unwrap_or_else(|_e| panic!("Failed test.")); 41 | 42 | assert!(parsed_req == ParsedRequest::new_sync(VmmAction::ConfigureBootSource(same_body))); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/api_server/src/request/instance_info.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use crate::parsed_request::{Error, ParsedRequest}; 5 | use logger::{IncMetric, METRICS}; 6 | 7 | pub(crate) fn parse_get_instance_info() -> Result { 8 | METRICS.get_api_requests.instance_info_count.inc(); 9 | Ok(ParsedRequest::GetInstanceInfo) 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use super::*; 15 | 16 | #[test] 17 | fn test_parse_get_instance_info_request() { 18 | match parse_get_instance_info() { 19 | Ok(ParsedRequest::GetInstanceInfo) => {} 20 | _ => panic!("Test failed."), 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/api_server/src/request/logger.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use super::super::VmmAction; 5 | use crate::parsed_request::{Error, ParsedRequest}; 6 | use crate::request::Body; 7 | use logger::{IncMetric, METRICS}; 8 | use vmm::vmm_config::logger::LoggerConfig; 9 | 10 | pub(crate) fn parse_put_logger(body: &Body) -> Result { 11 | METRICS.put_api_requests.logger_count.inc(); 12 | Ok(ParsedRequest::new_sync(VmmAction::ConfigureLogger( 13 | serde_json::from_slice::(body.raw()).map_err(|e| { 14 | METRICS.put_api_requests.logger_fails.inc(); 15 | Error::SerdeJson(e) 16 | })?, 17 | ))) 18 | } 19 | 20 | #[cfg(test)] 21 | mod tests { 22 | use std::path::PathBuf; 23 | 24 | use super::*; 25 | use crate::parsed_request::tests::vmm_action_from_request; 26 | use vmm::vmm_config::logger::LoggerLevel; 27 | 28 | #[test] 29 | fn test_parse_put_logger_request() { 30 | let mut body = r#"{ 31 | "log_path": "log", 32 | "level": "Warning", 33 | "show_level": false, 34 | "show_log_origin": false 35 | }"#; 36 | 37 | let mut expected_cfg = LoggerConfig { 38 | log_path: PathBuf::from("log"), 39 | level: LoggerLevel::Warning, 40 | show_level: false, 41 | show_log_origin: false, 42 | }; 43 | match vmm_action_from_request(parse_put_logger(&Body::new(body)).unwrap()) { 44 | VmmAction::ConfigureLogger(cfg) => assert_eq!(cfg, expected_cfg), 45 | _ => panic!("Test failed."), 46 | } 47 | 48 | body = r#"{ 49 | "log_path": "log", 50 | "level": "DEBUG", 51 | "show_level": false, 52 | "show_log_origin": false 53 | }"#; 54 | 55 | expected_cfg = LoggerConfig { 56 | log_path: PathBuf::from("log"), 57 | level: LoggerLevel::Debug, 58 | show_level: false, 59 | show_log_origin: false, 60 | }; 61 | match vmm_action_from_request(parse_put_logger(&Body::new(body)).unwrap()) { 62 | VmmAction::ConfigureLogger(cfg) => assert_eq!(cfg, expected_cfg), 63 | _ => panic!("Test failed."), 64 | } 65 | 66 | let invalid_body = r#"{ 67 | "invalid_field": "log", 68 | "level": "Warning", 69 | "show_level": false, 70 | "show_log_origin": false 71 | }"#; 72 | 73 | assert!(parse_put_logger(&Body::new(invalid_body)).is_err()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/api_server/src/request/metrics.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use super::super::VmmAction; 5 | use crate::parsed_request::{Error, ParsedRequest}; 6 | use crate::request::Body; 7 | use logger::{IncMetric, METRICS}; 8 | use vmm::vmm_config::metrics::MetricsConfig; 9 | 10 | pub(crate) fn parse_put_metrics(body: &Body) -> Result { 11 | METRICS.put_api_requests.metrics_count.inc(); 12 | Ok(ParsedRequest::new_sync(VmmAction::ConfigureMetrics( 13 | serde_json::from_slice::(body.raw()).map_err(|e| { 14 | METRICS.put_api_requests.metrics_fails.inc(); 15 | Error::SerdeJson(e) 16 | })?, 17 | ))) 18 | } 19 | 20 | #[cfg(test)] 21 | mod tests { 22 | use std::path::PathBuf; 23 | 24 | use super::*; 25 | use crate::parsed_request::tests::vmm_action_from_request; 26 | 27 | #[test] 28 | fn test_parse_put_metrics_request() { 29 | let body = r#"{ 30 | "metrics_path": "metrics" 31 | }"#; 32 | 33 | let expected_cfg = MetricsConfig { 34 | metrics_path: PathBuf::from("metrics"), 35 | }; 36 | match vmm_action_from_request(parse_put_metrics(&Body::new(body)).unwrap()) { 37 | VmmAction::ConfigureMetrics(cfg) => assert_eq!(cfg, expected_cfg), 38 | _ => panic!("Test failed."), 39 | } 40 | 41 | let invalid_body = r#"{ 42 | "invalid_field": "metrics" 43 | }"#; 44 | 45 | assert!(parse_put_metrics(&Body::new(invalid_body)).is_err()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/api_server/src/request/mmds.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use crate::parsed_request::{Error, ParsedRequest}; 5 | use crate::request::Body; 6 | use micro_http::StatusCode; 7 | use vmm::rpc_interface::VmmAction::SetMmdsConfiguration; 8 | use vmm::vmm_config::mmds::MmdsConfig; 9 | 10 | pub(crate) fn parse_get_mmds() -> Result { 11 | Ok(ParsedRequest::GetMMDS) 12 | } 13 | 14 | pub(crate) fn parse_put_mmds( 15 | body: &Body, 16 | path_second_token: Option<&&str>, 17 | ) -> Result { 18 | match path_second_token { 19 | Some(config_path) => match *config_path { 20 | "config" => Ok(ParsedRequest::new_sync(SetMmdsConfiguration( 21 | serde_json::from_slice::(body.raw()).map_err(Error::SerdeJson)?, 22 | ))), 23 | _ => Err(Error::Generic( 24 | StatusCode::BadRequest, 25 | format!("Unrecognized PUT request path `{}`.", *config_path), 26 | )), 27 | }, 28 | None => Ok(ParsedRequest::PutMMDS( 29 | serde_json::from_slice(body.raw()).map_err(Error::SerdeJson)?, 30 | )), 31 | } 32 | } 33 | 34 | pub(crate) fn parse_patch_mmds(body: &Body) -> Result { 35 | Ok(ParsedRequest::PatchMMDS( 36 | serde_json::from_slice(body.raw()).map_err(Error::SerdeJson)?, 37 | )) 38 | } 39 | 40 | #[cfg(test)] 41 | mod tests { 42 | use super::*; 43 | 44 | #[test] 45 | fn test_parse_get_mmds_request() { 46 | assert!(parse_get_mmds().is_ok()); 47 | } 48 | 49 | #[test] 50 | fn test_parse_put_mmds_request() { 51 | let body = r#"{ 52 | "foo": "bar" 53 | }"#; 54 | assert!(parse_put_mmds(&Body::new(body), None).is_ok()); 55 | let invalid_body = "invalid_body"; 56 | assert!(parse_put_mmds(&Body::new(invalid_body), None).is_err()); 57 | 58 | let body = r#"{ 59 | "ipv4_address": "169.254.170.2" 60 | }"#; 61 | let path = "config"; 62 | assert!(parse_put_mmds(&Body::new(body), Some(&path)).is_ok()); 63 | 64 | let body = r#"{ 65 | "ipv4_address": "" 66 | }"#; 67 | assert!(parse_put_mmds(&Body::new(body), Some(&path)).is_err()); 68 | 69 | // Equivalent to reset the mmds configuration. 70 | let empty_body = r#"{}"#; 71 | assert!(parse_put_mmds(&Body::new(empty_body), Some(&path)).is_ok()); 72 | 73 | let invalid_config_body = r#"{ 74 | "invalid_config": "invalid_value" 75 | }"#; 76 | assert!(parse_put_mmds(&Body::new(invalid_config_body), Some(&path)).is_err()); 77 | assert!(parse_put_mmds(&Body::new(body), Some(&"invalid_path")).is_err()); 78 | assert!(parse_put_mmds(&Body::new(invalid_body), Some(&path)).is_err()); 79 | } 80 | 81 | #[test] 82 | fn test_parse_patch_mmds_request() { 83 | let body = r#"{ 84 | "foo": "bar" 85 | }"#; 86 | assert!(parse_patch_mmds(&Body::new(body)).is_ok()); 87 | assert!(parse_patch_mmds(&Body::new("invalid_body")).is_err()); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/api_server/src/request/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | pub mod actions; 5 | pub mod balloon; 6 | pub mod boot_source; 7 | pub mod drive; 8 | pub mod instance_info; 9 | pub mod logger; 10 | pub mod machine_configuration; 11 | pub mod metrics; 12 | pub mod mmds; 13 | pub mod net; 14 | pub mod snapshot; 15 | pub mod vsock; 16 | pub use micro_http::{ 17 | Body, HttpServer, Method, Request, RequestError, Response, StatusCode, Version, 18 | }; 19 | -------------------------------------------------------------------------------- /src/api_server/src/request/vsock.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use super::super::VmmAction; 5 | use crate::parsed_request::{Error, ParsedRequest}; 6 | use crate::request::Body; 7 | use vmm::vmm_config::vsock::VsockDeviceConfig; 8 | 9 | pub(crate) fn parse_put_vsock(body: &Body) -> Result { 10 | Ok(ParsedRequest::new_sync(VmmAction::SetVsockDevice( 11 | serde_json::from_slice::(body.raw()).map_err(Error::SerdeJson)?, 12 | ))) 13 | } 14 | 15 | #[cfg(test)] 16 | mod tests { 17 | use super::*; 18 | 19 | #[test] 20 | fn test_parse_put_vsock_request() { 21 | let body = r#"{ 22 | "vsock_id": "foo", 23 | "guest_cid": 42, 24 | "uds_path": "vsock.sock" 25 | }"#; 26 | assert!(parse_put_vsock(&Body::new(body)).is_ok()); 27 | 28 | let body = r#"{ 29 | "vsock_id": "foo", 30 | "guest_cid": 42, 31 | "invalid_field": false 32 | }"#; 33 | assert!(parse_put_vsock(&Body::new(body)).is_err()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/arch/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "arch" 3 | version = "0.1.0" 4 | authors = ["The Chromium OS Authors"] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | kvm-bindings = { version = "0.3.0", features = ["fam-wrappers"] } 9 | kvm-ioctls = { version = "0.6.0" } 10 | libc = ">=0.2.39" 11 | vm-memory = { path = "../vm-memory" } 12 | versionize = ">=0.1.4" 13 | versionize_derive = ">=0.1.3" 14 | 15 | arch_gen = { path = "../arch_gen" } 16 | utils = { path = "../utils" } 17 | 18 | [dev-dependencies] 19 | device_tree = ">=1.1.0" 20 | -------------------------------------------------------------------------------- /src/arch/src/aarch64/gic/gicv2.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::{boxed::Box, result}; 5 | 6 | use kvm_ioctls::DeviceFd; 7 | 8 | use super::{Error, GICDevice}; 9 | 10 | type Result = result::Result; 11 | 12 | /// Represent a GIC v2 device 13 | pub struct GICv2 { 14 | /// The file descriptor for the KVM device 15 | fd: DeviceFd, 16 | 17 | /// GIC device properties, to be used for setting up the fdt entry 18 | properties: [u64; 4], 19 | 20 | /// Number of CPUs handled by the device 21 | vcpu_count: u64, 22 | } 23 | 24 | impl GICv2 { 25 | // Unfortunately bindgen omits defines that are based on other defines. 26 | // See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel. 27 | const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000; 28 | const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000; 29 | 30 | // Device trees specific constants 31 | const ARCH_GIC_V2_MAINT_IRQ: u32 = 8; 32 | 33 | /// Get the address of the GICv2 distributor. 34 | const fn get_dist_addr() -> u64 { 35 | super::layout::MAPPED_IO_START - GICv2::KVM_VGIC_V2_DIST_SIZE 36 | } 37 | 38 | /// Get the size of the GIC_v2 distributor. 39 | const fn get_dist_size() -> u64 { 40 | GICv2::KVM_VGIC_V2_DIST_SIZE 41 | } 42 | 43 | /// Get the address of the GIC_v2 CPU. 44 | const fn get_cpu_addr() -> u64 { 45 | GICv2::get_dist_addr() - GICv2::KVM_VGIC_V2_CPU_SIZE 46 | } 47 | 48 | /// Get the size of the GIC_v2 CPU. 49 | const fn get_cpu_size() -> u64 { 50 | GICv2::KVM_VGIC_V2_CPU_SIZE 51 | } 52 | } 53 | 54 | impl GICDevice for GICv2 { 55 | fn version() -> u32 { 56 | kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2 57 | } 58 | 59 | fn device_fd(&self) -> &DeviceFd { 60 | &self.fd 61 | } 62 | 63 | fn device_properties(&self) -> &[u64] { 64 | &self.properties 65 | } 66 | 67 | fn vcpu_count(&self) -> u64 { 68 | self.vcpu_count 69 | } 70 | 71 | fn fdt_compatibility(&self) -> &str { 72 | "arm,gic-400" 73 | } 74 | 75 | fn fdt_maint_irq(&self) -> u32 { 76 | GICv2::ARCH_GIC_V2_MAINT_IRQ 77 | } 78 | 79 | fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box { 80 | Box::new(GICv2 { 81 | fd, 82 | properties: [ 83 | GICv2::get_dist_addr(), 84 | GICv2::get_dist_size(), 85 | GICv2::get_cpu_addr(), 86 | GICv2::get_cpu_size(), 87 | ], 88 | vcpu_count, 89 | }) 90 | } 91 | 92 | fn init_device_attributes(gic_device: &dyn GICDevice) -> Result<()> { 93 | /* Setting up the distributor attribute. 94 | We are placing the GIC below 1GB so we need to substract the size of the distributor. */ 95 | Self::set_device_attribute( 96 | &gic_device.device_fd(), 97 | kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, 98 | u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST), 99 | &GICv2::get_dist_addr() as *const u64 as u64, 100 | 0, 101 | )?; 102 | 103 | /* Setting up the CPU attribute. */ 104 | Self::set_device_attribute( 105 | &gic_device.device_fd(), 106 | kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, 107 | u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU), 108 | &GICv2::get_cpu_addr() as *const u64 as u64, 109 | 0, 110 | )?; 111 | 112 | Ok(()) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/arch/src/aarch64/output.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/src/arch/src/aarch64/output.dtb -------------------------------------------------------------------------------- /src/arch/src/aarch64/output_with_initrd.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/src/arch/src/aarch64/output_with_initrd.dtb -------------------------------------------------------------------------------- /src/arch/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #![deny(missing_docs)] 5 | //! Implements platform specific functionality. 6 | //! Supported platforms: x86_64 and aarch64. 7 | use std::fmt; 8 | use std::result; 9 | 10 | use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult}; 11 | use versionize_derive::Versionize; 12 | 13 | /// Module for aarch64 related functionality. 14 | #[cfg(target_arch = "aarch64")] 15 | pub mod aarch64; 16 | 17 | #[cfg(target_arch = "aarch64")] 18 | pub use aarch64::{ 19 | arch_memory_regions, configure_system, get_kernel_start, initrd_load_addr, 20 | layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, regs, Error, MMIO_MEM_START, 21 | }; 22 | 23 | /// Module for x86_64 related functionality. 24 | #[cfg(target_arch = "x86_64")] 25 | pub mod x86_64; 26 | 27 | #[cfg(target_arch = "x86_64")] 28 | pub use crate::x86_64::{ 29 | arch_memory_regions, configure_system, get_kernel_start, initrd_load_addr, 30 | layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, Error, MMIO_MEM_START, 31 | }; 32 | 33 | /// Type for returning public functions outcome. 34 | pub type Result = result::Result; 35 | 36 | /// Types of devices that can get attached to this platform. 37 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Copy, Versionize)] 38 | pub enum DeviceType { 39 | /// Device Type: Virtio. 40 | Virtio(u32), 41 | /// Device Type: Serial. 42 | #[cfg(target_arch = "aarch64")] 43 | Serial, 44 | /// Device Type: RTC. 45 | #[cfg(target_arch = "aarch64")] 46 | RTC, 47 | /// Device Type: BootTimer. 48 | BootTimer, 49 | } 50 | 51 | /// Type for passing information about the initrd in the guest memory. 52 | pub struct InitrdConfig { 53 | /// Load address of initrd in guest memory 54 | pub address: vm_memory::GuestAddress, 55 | /// Size of initrd in guest memory 56 | pub size: usize, 57 | } 58 | 59 | /// Default (smallest) memory page size for the supported architectures. 60 | pub const PAGE_SIZE: usize = 4096; 61 | 62 | impl fmt::Display for DeviceType { 63 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 64 | write!(f, "{:?}", self) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/arch/src/x86_64/layout.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the THIRD-PARTY file. 7 | 8 | /// Magic addresses externally used to lay out x86_64 VMs. 9 | 10 | /// Initial stack for the boot CPU. 11 | pub const BOOT_STACK_POINTER: u64 = 0x8ff0; 12 | 13 | /// Kernel command line start address. 14 | pub const CMDLINE_START: u64 = 0x20000; 15 | /// Kernel command line start address maximum size. 16 | pub const CMDLINE_MAX_SIZE: usize = 0x10000; 17 | 18 | /// Start of the high memory. 19 | pub const HIMEM_START: u64 = 0x0010_0000; //1 MB. 20 | 21 | // Typically, on x86 systems 24 IRQs are used (0-23). 22 | /// First usable IRQ ID for virtio device interrupts on x86_64. 23 | pub const IRQ_BASE: u32 = 5; 24 | /// Last usable IRQ ID for virtio device interrupts on x86_64. 25 | pub const IRQ_MAX: u32 = 23; 26 | 27 | /// Address for the TSS setup. 28 | pub const KVM_TSS_ADDRESS: u64 = 0xfffb_d000; 29 | 30 | /// The 'zero page', a.k.a linux kernel bootparams. 31 | pub const ZERO_PAGE_START: u64 = 0x7000; 32 | -------------------------------------------------------------------------------- /src/arch_gen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "arch_gen" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /src/arch_gen/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[allow(clippy::all)] 5 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 6 | pub mod x86; 7 | -------------------------------------------------------------------------------- /src/arch_gen/src/x86/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the THIRD-PARTY file. 7 | 8 | #[allow(non_upper_case_globals)] 9 | #[allow(non_camel_case_types)] 10 | #[allow(non_snake_case)] 11 | pub mod bootparam; 12 | #[allow(non_camel_case_types)] 13 | #[allow(non_upper_case_globals)] 14 | pub mod mpspec; 15 | #[allow(non_upper_case_globals)] 16 | pub mod msr_index; 17 | -------------------------------------------------------------------------------- /src/cpuid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cpuid" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | kvm-bindings = { version = "0.3.0", features = ["fam-wrappers"] } 9 | kvm-ioctls = { version = "0.6.0" } 10 | 11 | utils = { path = "../utils"} 12 | -------------------------------------------------------------------------------- /src/cpuid/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the THIRD-PARTY file. 7 | 8 | #![deny(missing_docs)] 9 | //! Utility for configuring the CPUID (CPU identification) for the guest microVM. 10 | 11 | #![cfg(target_arch = "x86_64")] 12 | use kvm_bindings::CpuId; 13 | 14 | /// cpuid utility functions. 15 | pub mod common; 16 | use crate::common::*; 17 | 18 | /// Contains helper methods for bit operations. 19 | pub mod bit_helper; 20 | 21 | mod template; 22 | pub use crate::template::intel::c3; 23 | pub use crate::template::intel::t2; 24 | 25 | mod cpu_leaf; 26 | 27 | mod transformer; 28 | use crate::transformer::*; 29 | pub use crate::transformer::{Error, VmSpec}; 30 | 31 | mod brand_string; 32 | 33 | /// Sets up the CPUID entries for the given vcpu. 34 | /// 35 | /// # Arguments 36 | /// 37 | /// * `kvm_cpuid` - KVM related structure holding the relevant CPUID info. 38 | /// * `vm_spec` - The specifications of the VM. 39 | /// 40 | /// # Example 41 | /// ``` 42 | /// use cpuid::{filter_cpuid, VmSpec}; 43 | /// use kvm_bindings::{CpuId, KVM_MAX_CPUID_ENTRIES}; 44 | /// use kvm_ioctls::Kvm; 45 | /// 46 | /// let kvm = Kvm::new().unwrap(); 47 | /// let mut kvm_cpuid: CpuId = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); 48 | /// 49 | /// let vm_spec = VmSpec::new(0, 1, true).unwrap(); 50 | /// 51 | /// filter_cpuid(&mut kvm_cpuid, &vm_spec).unwrap(); 52 | /// 53 | /// // Get expected `kvm_cpuid` entries. 54 | /// let entries = kvm_cpuid.as_mut_slice(); 55 | /// ``` 56 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 57 | pub fn filter_cpuid(kvm_cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> { 58 | let maybe_cpuid_transformer: Option<&dyn CpuidTransformer> = match vm_spec.cpu_vendor_id() { 59 | VENDOR_ID_INTEL => Some(&intel::IntelCpuidTransformer {}), 60 | VENDOR_ID_AMD => Some(&amd::AmdCpuidTransformer {}), 61 | _ => None, 62 | }; 63 | 64 | if let Some(cpuid_transformer) = maybe_cpuid_transformer { 65 | cpuid_transformer.process_cpuid(kvm_cpuid, &vm_spec)?; 66 | } 67 | 68 | Ok(()) 69 | } 70 | -------------------------------------------------------------------------------- /src/cpuid/src/template/intel/mod.rs: -------------------------------------------------------------------------------- 1 | /// Follows a C3 template in setting up the CPUID. 2 | pub mod c3; 3 | /// Follows a T2 template in setting up the CPUID. 4 | pub mod t2; 5 | 6 | use crate::common::{get_vendor_id_from_host, VENDOR_ID_INTEL}; 7 | use crate::transformer::Error; 8 | 9 | pub fn validate_vendor_id() -> Result<(), Error> { 10 | let vendor_id = get_vendor_id_from_host().map_err(Error::InternalError)?; 11 | if &vendor_id != VENDOR_ID_INTEL { 12 | return Err(Error::InvalidVendor); 13 | } 14 | 15 | Ok(()) 16 | } 17 | -------------------------------------------------------------------------------- /src/cpuid/src/template/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Contains Intel specific templates. 5 | pub mod intel; 6 | -------------------------------------------------------------------------------- /src/devices/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "devices" 3 | version = "0.1.0" 4 | authors = ["The Chromium OS Authors"] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | libc = ">=0.2.39" 9 | timerfd = ">=1.0" 10 | versionize = ">=0.1.4" 11 | versionize_derive = ">=0.1.3" 12 | vm-memory = { path = "../vm-memory" } 13 | 14 | dumbo = { path = "../dumbo" } 15 | logger = { path = "../logger" } 16 | mmds = { path = "../mmds" } 17 | net_gen = { path = "../net_gen" } 18 | polly = { path = "../polly" } 19 | rate_limiter = { path = "../rate_limiter" } 20 | serde = { version = ">=1.0.27", features = ["derive"] } 21 | snapshot = { path = "../snapshot" } 22 | utils = { path = "../utils" } 23 | virtio_gen = { path = "../virtio_gen" } 24 | 25 | -------------------------------------------------------------------------------- /src/devices/src/legacy/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the THIRD-PARTY file. 7 | 8 | mod i8042; 9 | #[cfg(target_arch = "aarch64")] 10 | mod rtc_pl031; 11 | mod serial; 12 | 13 | pub use self::i8042::Error as I8042DeviceError; 14 | pub use self::i8042::I8042Device; 15 | #[cfg(target_arch = "aarch64")] 16 | pub use self::rtc_pl031::RTC; 17 | pub use self::serial::{ReadableFd, Serial}; 18 | -------------------------------------------------------------------------------- /src/devices/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the THIRD-PARTY file. 7 | 8 | //! Emulates virtual and hardware devices. 9 | use std::io; 10 | 11 | mod bus; 12 | pub mod legacy; 13 | pub mod pseudo; 14 | pub mod virtio; 15 | 16 | pub use self::bus::{Bus, BusDevice, Error as BusError}; 17 | use crate::virtio::QueueError; 18 | use logger::{error, IncMetric, METRICS}; 19 | 20 | // Function used for reporting error in terms of logging 21 | // but also in terms of METRICS net event fails. 22 | pub(crate) fn report_net_event_fail(err: Error) { 23 | error!("{:?}", err); 24 | METRICS.net.event_fails.inc(); 25 | } 26 | 27 | pub(crate) fn report_balloon_event_fail(err: virtio::balloon::Error) { 28 | error!("{:?}", err); 29 | METRICS.balloon.event_fails.inc(); 30 | } 31 | 32 | #[derive(Debug)] 33 | pub enum Error { 34 | /// Failed to read from the TAP device. 35 | FailedReadTap, 36 | /// Failed to signal the virtio used queue. 37 | FailedSignalingUsedQueue(io::Error), 38 | /// IO error. 39 | IoError(io::Error), 40 | /// Device received malformed payload. 41 | MalformedPayload, 42 | /// Device received malformed descriptor. 43 | MalformedDescriptor, 44 | /// Error during queue processing. 45 | QueueError(QueueError), 46 | } 47 | -------------------------------------------------------------------------------- /src/devices/src/pseudo/boot_timer.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use crate::bus::BusDevice; 5 | use logger::info; 6 | use utils::time::TimestampUs; 7 | 8 | const MAGIC_VALUE_SIGNAL_GUEST_BOOT_COMPLETE: u8 = 123; 9 | 10 | /// Pseudo device to record the kernel boot time. 11 | pub struct BootTimer { 12 | start_ts: TimestampUs, 13 | } 14 | 15 | impl BusDevice for BootTimer { 16 | fn write(&mut self, offset: u64, data: &[u8]) { 17 | // Only handle byte length instructions at a zero offset. 18 | if data.len() != 1 || offset != 0 { 19 | return; 20 | } 21 | 22 | if data[0] == MAGIC_VALUE_SIGNAL_GUEST_BOOT_COMPLETE { 23 | let now_tm_us = TimestampUs::default(); 24 | 25 | let boot_time_us = now_tm_us.time_us - self.start_ts.time_us; 26 | let boot_time_cpu_us = now_tm_us.cputime_us - self.start_ts.cputime_us; 27 | info!( 28 | "Guest-boot-time = {:>6} us {} ms, {:>6} CPU us {} CPU ms", 29 | boot_time_us, 30 | boot_time_us / 1000, 31 | boot_time_cpu_us, 32 | boot_time_cpu_us / 1000 33 | ); 34 | } 35 | } 36 | } 37 | 38 | impl BootTimer { 39 | pub fn new(start_ts: TimestampUs) -> BootTimer { 40 | BootTimer { start_ts } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/devices/src/pseudo/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | mod boot_timer; 5 | 6 | pub use self::boot_timer::BootTimer; 7 | -------------------------------------------------------------------------------- /src/devices/src/virtio/balloon/test_utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::os::unix::io::AsRawFd; 5 | use std::u32; 6 | 7 | use crate::virtio::balloon::NUM_QUEUES; 8 | use crate::virtio::test_utils::VirtQueue; 9 | use crate::virtio::Balloon; 10 | use ::utils::epoll::{EpollEvent, EventSet}; 11 | use polly::event_manager::{EventManager, Subscriber}; 12 | 13 | pub fn invoke_handler_for_queue_event(b: &mut Balloon, queue_index: usize) { 14 | assert!(queue_index < NUM_QUEUES); 15 | // Trigger the queue event. 16 | b.queue_evts[queue_index].write(1).unwrap(); 17 | // Handle event. 18 | b.process( 19 | &EpollEvent::new(EventSet::IN, b.queue_evts[queue_index].as_raw_fd() as u64), 20 | &mut EventManager::new().unwrap(), 21 | ); 22 | // Validate the queue operation finished successfully. 23 | assert_eq!(b.interrupt_evt.read().unwrap(), 1); 24 | } 25 | 26 | pub fn set_request(queue: &VirtQueue, idx: usize, addr: u64, len: u32, flags: u16) { 27 | // Set the index of the next request. 28 | queue.avail.idx.set((idx + 1) as u16); 29 | // Set the current descriptor table entry index. 30 | queue.avail.ring[idx].set(idx as u16); 31 | // Set the current descriptor table entry. 32 | queue.dtable[idx].set(addr, len, flags, 1); 33 | } 34 | 35 | pub fn check_request_completion(queue: &VirtQueue, idx: usize) { 36 | // Check that the next used will be idx + 1. 37 | assert_eq!(queue.used.idx.get(), (idx + 1) as u16); 38 | // Check that the current used is idx. 39 | assert_eq!(queue.used.ring[idx].get().id, idx as u32); 40 | // The length of the completed request is 0. 41 | assert_eq!(queue.used.ring[idx].get().len, 0); 42 | } 43 | -------------------------------------------------------------------------------- /src/devices/src/virtio/block/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | pub mod device; 5 | pub mod event_handler; 6 | pub mod persist; 7 | pub mod request; 8 | pub mod test_utils; 9 | 10 | pub use self::device::Block; 11 | pub use self::event_handler::*; 12 | pub use self::request::*; 13 | 14 | use vm_memory::GuestMemoryError; 15 | 16 | pub const CONFIG_SPACE_SIZE: usize = 8; 17 | pub const SECTOR_SHIFT: u8 = 9; 18 | pub const SECTOR_SIZE: u64 = (0x01 as u64) << SECTOR_SHIFT; 19 | pub const QUEUE_SIZE: u16 = 256; 20 | pub const NUM_QUEUES: usize = 1; 21 | pub const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE]; 22 | 23 | #[derive(Debug)] 24 | pub enum Error { 25 | /// Guest gave us too few descriptors in a descriptor chain. 26 | DescriptorChainTooShort, 27 | /// Guest gave us a descriptor that was too short to use. 28 | DescriptorLengthTooSmall, 29 | /// Getting a block's metadata fails for any reason. 30 | GetFileMetadata(std::io::Error), 31 | /// Guest gave us bad memory addresses. 32 | GuestMemory(GuestMemoryError), 33 | /// The requested operation would cause a seek beyond disk end. 34 | InvalidOffset, 35 | /// Guest gave us a read only descriptor that protocol says to write to. 36 | UnexpectedReadOnlyDescriptor, 37 | /// Guest gave us a write only descriptor that protocol says to read from. 38 | UnexpectedWriteOnlyDescriptor, 39 | } 40 | -------------------------------------------------------------------------------- /src/devices/src/virtio/block/test_utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::os::unix::io::AsRawFd; 5 | 6 | use crate::virtio::{Block, Queue}; 7 | use polly::event_manager::{EventManager, Subscriber}; 8 | use rate_limiter::RateLimiter; 9 | use utils::epoll::{EpollEvent, EventSet}; 10 | use utils::tempfile::TempFile; 11 | 12 | /// Create a default Block instance to be used in tests. 13 | pub fn default_block() -> Block { 14 | // Create backing file. 15 | let f = TempFile::new().unwrap(); 16 | f.as_file().set_len(0x1000).unwrap(); 17 | 18 | default_block_with_path(f.as_path().to_str().unwrap().to_string()) 19 | } 20 | 21 | /// Create a default Block instance using file at the specified path to be used in tests. 22 | pub fn default_block_with_path(path: String) -> Block { 23 | // Rate limiting is enabled but with a high operation rate (10 million ops/s). 24 | let rate_limiter = RateLimiter::new(0, 0, 0, 100_000, 0, 10).unwrap(); 25 | 26 | let id = "test".to_string(); 27 | // The default block device is read-write and non-root. 28 | Block::new(id, None, path, false, false, rate_limiter).unwrap() 29 | } 30 | 31 | pub fn invoke_handler_for_queue_event(b: &mut Block) { 32 | // Trigger the queue event. 33 | b.queue_evts[0].write(1).unwrap(); 34 | // Handle event. 35 | b.process( 36 | &EpollEvent::new(EventSet::IN, b.queue_evts[0].as_raw_fd() as u64), 37 | &mut EventManager::new().unwrap(), 38 | ); 39 | // Validate the queue operation finished successfully. 40 | assert_eq!(b.interrupt_evt.read().unwrap(), 1); 41 | } 42 | 43 | pub fn set_queue(blk: &mut Block, idx: usize, q: Queue) { 44 | blk.queues[idx] = q; 45 | } 46 | 47 | pub fn set_rate_limiter(blk: &mut Block, rl: RateLimiter) { 48 | blk.rate_limiter = rl; 49 | } 50 | 51 | pub fn rate_limiter(blk: &mut Block) -> &RateLimiter { 52 | &blk.rate_limiter 53 | } 54 | -------------------------------------------------------------------------------- /src/devices/src/virtio/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the THIRD-PARTY file. 7 | 8 | //! Implements virtio devices, queues, and transport mechanisms. 9 | use std::any::Any; 10 | use std::io::Error as IOError; 11 | 12 | pub mod balloon; 13 | pub mod block; 14 | pub mod device; 15 | mod mmio; 16 | pub mod net; 17 | pub mod persist; 18 | mod queue; 19 | pub mod test_utils; 20 | pub mod vsock; 21 | 22 | pub use self::balloon::*; 23 | pub use self::block::*; 24 | pub use self::device::*; 25 | pub use self::mmio::*; 26 | pub use self::net::*; 27 | pub use self::persist::*; 28 | pub use self::queue::*; 29 | pub use self::vsock::*; 30 | 31 | /// When the driver initializes the device, it lets the device know about the 32 | /// completed stages using the Device Status Field. 33 | /// 34 | /// These following consts are defined in the order in which the bits would 35 | /// typically be set by the driver. INIT -> ACKNOWLEDGE -> DRIVER and so on. 36 | /// 37 | /// This module is a 1:1 mapping for the Device Status Field in the virtio 1.0 38 | /// specification, section 2.1. 39 | mod device_status { 40 | pub const INIT: u32 = 0; 41 | pub const ACKNOWLEDGE: u32 = 1; 42 | pub const DRIVER: u32 = 2; 43 | pub const FAILED: u32 = 128; 44 | pub const FEATURES_OK: u32 = 8; 45 | pub const DRIVER_OK: u32 = 4; 46 | } 47 | 48 | /// Types taken from linux/virtio_ids.h. 49 | /// Type 0 is not used by virtio. Use it as wildcard for non-virtio devices 50 | pub const TYPE_NET: u32 = 1; 51 | pub const TYPE_BLOCK: u32 = 2; 52 | pub const TYPE_BALLOON: u32 = 5; 53 | 54 | /// Interrupt flags (re: interrupt status & acknowledge registers). 55 | /// See linux/virtio_mmio.h. 56 | pub const VIRTIO_MMIO_INT_VRING: u32 = 0x01; 57 | pub const VIRTIO_MMIO_INT_CONFIG: u32 = 0x02; 58 | 59 | /// Offset from the base MMIO address of a virtio device used by the guest to notify the device of 60 | /// queue events. 61 | pub const NOTIFY_REG_OFFSET: u32 = 0x50; 62 | 63 | #[derive(Debug)] 64 | pub enum ActivateError { 65 | EpollCtl(IOError), 66 | BadActivate, 67 | } 68 | 69 | pub type ActivateResult = std::result::Result<(), ActivateError>; 70 | 71 | /// Trait that helps in upcasting an object to Any 72 | pub trait AsAny { 73 | fn as_any(&self) -> &dyn Any; 74 | 75 | fn as_mut_any(&mut self) -> &mut dyn Any; 76 | } 77 | impl AsAny for T { 78 | fn as_any(&self) -> &dyn Any { 79 | self 80 | } 81 | 82 | fn as_mut_any(&mut self) -> &mut dyn Any { 83 | self 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/devices/src/virtio/net/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::{io, result}; 5 | 6 | pub const MAX_BUFFER_SIZE: usize = 65562; 7 | pub const QUEUE_SIZE: u16 = 256; 8 | pub const NUM_QUEUES: usize = 2; 9 | pub const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES]; 10 | // The index of the rx queue from Net device queues/queues_evts vector. 11 | pub const RX_INDEX: usize = 0; 12 | // The index of the tx queue from Net device queues/queues_evts vector. 13 | pub const TX_INDEX: usize = 1; 14 | 15 | pub mod device; 16 | pub mod event_handler; 17 | pub mod persist; 18 | mod tap; 19 | pub mod test_utils; 20 | 21 | pub use self::device::Net; 22 | pub use self::event_handler::*; 23 | pub use tap::Error as TapError; 24 | 25 | #[derive(Debug)] 26 | pub enum Error { 27 | /// Open tap device failed. 28 | TapOpen(TapError), 29 | /// Setting tap interface offload flags failed. 30 | TapSetOffload(TapError), 31 | /// Setting vnet header size failed. 32 | TapSetVnetHdrSize(TapError), 33 | /// Enabling tap interface failed. 34 | TapEnable(TapError), 35 | /// EventFd error. 36 | EventFd(io::Error), 37 | /// IO error. 38 | IO(io::Error), 39 | /// The VNET header is missing from the frame. 40 | VnetHeaderMissing, 41 | } 42 | 43 | pub type Result = result::Result; 44 | -------------------------------------------------------------------------------- /src/devices/src/virtio/vsock/unix/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | 5 | /// This module implements the Unix Domain Sockets backend for vsock - a mediator between 6 | /// guest-side AF_VSOCK sockets and host-side AF_UNIX sockets. The heavy lifting is performed by 7 | /// `muxer::VsockMuxer`, a connection multiplexer that uses `super::csm::VsockConnection` for 8 | /// handling vsock connection states. 9 | /// Check out `muxer.rs` for a more detailed explanation of the inner workings of this backend. 10 | mod muxer; 11 | mod muxer_killq; 12 | mod muxer_rxq; 13 | 14 | pub use muxer::VsockMuxer as VsockUnixBackend; 15 | 16 | mod defs { 17 | /// Maximum number of established connections that we can handle. 18 | pub const MAX_CONNECTIONS: usize = 1023; 19 | 20 | /// Size of the muxer RX packet queue. 21 | pub const MUXER_RXQ_SIZE: usize = 256; 22 | 23 | /// Size of the muxer connection kill queue. 24 | pub const MUXER_KILLQ_SIZE: usize = 128; 25 | } 26 | 27 | #[derive(Debug)] 28 | pub enum Error { 29 | /// Error registering a new epoll-listening FD. 30 | EpollAdd(std::io::Error), 31 | /// Error creating an epoll FD. 32 | EpollFdCreate(std::io::Error), 33 | /// The host made an invalid vsock port connection request. 34 | InvalidPortRequest, 35 | /// Error accepting a new connection from the host-side Unix socket. 36 | UnixAccept(std::io::Error), 37 | /// Error binding to the host-side Unix socket. 38 | UnixBind(std::io::Error), 39 | /// Error connecting to a host-side Unix socket. 40 | UnixConnect(std::io::Error), 41 | /// Error reading from host-side Unix socket. 42 | UnixRead(std::io::Error), 43 | /// Muxer connection limit reached. 44 | TooManyConnections, 45 | } 46 | 47 | type Result = std::result::Result; 48 | type MuxerConnection = super::csm::VsockConnection; 49 | -------------------------------------------------------------------------------- /src/devices/tests/serial_utils/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use devices::legacy::ReadableFd; 5 | use std::io; 6 | use std::os::raw::c_void; 7 | use std::os::unix::io::AsRawFd; 8 | use std::os::unix::io::RawFd; 9 | 10 | pub struct MockSerialInput(pub RawFd); 11 | 12 | impl io::Read for MockSerialInput { 13 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 14 | let count = unsafe { libc::read(self.0, buf.as_mut_ptr() as *mut c_void, buf.len()) }; 15 | if count < 0 { 16 | return Err(io::Error::last_os_error()); 17 | } 18 | 19 | Ok(count as usize) 20 | } 21 | } 22 | 23 | impl AsRawFd for MockSerialInput { 24 | fn as_raw_fd(&self) -> RawFd { 25 | self.0 26 | } 27 | } 28 | 29 | impl ReadableFd for MockSerialInput {} 30 | -------------------------------------------------------------------------------- /src/dumbo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dumbo" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | bitflags = ">=1.0.4" 9 | 10 | utils = { path = "../utils" } 11 | logger = { path = "../logger" } 12 | micro_http = { path = "../micro_http" } 13 | 14 | [dev-dependencies] 15 | serde_json = ">=1.0.9" 16 | -------------------------------------------------------------------------------- /src/dumbo/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #![deny(missing_docs)] 5 | //! Provides helper logic for parsing and writing protocol data units, and minimalist 6 | //! implementations of a TCP listener, a TCP connection, and an HTTP/1.1 server. 7 | pub mod pdu; 8 | pub mod tcp; 9 | 10 | pub use crate::pdu::arp::{EthIPv4ArpFrame, ETH_IPV4_FRAME_LEN}; 11 | pub use crate::pdu::ethernet::{ 12 | EthernetFrame, ETHERTYPE_ARP, ETHERTYPE_IPV4, PAYLOAD_OFFSET as ETHERNET_PAYLOAD_OFFSET, 13 | }; 14 | pub use crate::pdu::ipv4::{IPv4Packet, PROTOCOL_TCP, PROTOCOL_UDP}; 15 | pub use crate::pdu::udp::{UdpDatagram, UDP_HEADER_SIZE}; 16 | 17 | use utils::net::mac::MacAddr; 18 | 19 | use std::ops::Index; 20 | 21 | /// Represents a generalization of a borrowed `[u8]` slice. 22 | #[allow(clippy::len_without_is_empty)] 23 | pub trait ByteBuffer: Index { 24 | /// Returns the length of the buffer. 25 | fn len(&self) -> usize; 26 | 27 | /// Reads `buf.len()` bytes from `buf` into the inner buffer, starting at `offset`. 28 | /// 29 | /// # Panics 30 | /// 31 | /// Panics if `offset + buf.len()` < `self.len()`. 32 | fn read_to_slice(&self, offset: usize, buf: &mut [u8]); 33 | } 34 | 35 | impl ByteBuffer for [u8] { 36 | #[inline] 37 | fn len(&self) -> usize { 38 | self.len() 39 | } 40 | 41 | #[inline] 42 | fn read_to_slice(&self, offset: usize, buf: &mut [u8]) { 43 | let buf_len = buf.len(); 44 | buf.copy_from_slice(&self[offset..offset + buf_len]); 45 | } 46 | } 47 | 48 | #[cfg(test)] 49 | mod tests { 50 | use super::*; 51 | 52 | fn bb_len(buf: &T) -> usize { 53 | buf.len() 54 | } 55 | 56 | fn bb_is_empty(buf: &T) -> bool { 57 | buf.len() == 0 58 | } 59 | 60 | fn bb_read_from_1(src: &T, dst: &mut [u8]) { 61 | src.read_to_slice(1, dst); 62 | } 63 | 64 | #[test] 65 | fn test_u8_byte_buffer() { 66 | let a = [1u8, 2, 3]; 67 | let mut b = [0u8; 2]; 68 | assert_eq!(bb_len(a.as_ref()), a.len()); 69 | assert_eq!(bb_is_empty(a.as_ref()), false); 70 | bb_read_from_1(a.as_ref(), b.as_mut()); 71 | assert_eq!(b, [2, 3]); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/firecracker/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "firecracker" 3 | version = "0.24.6" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | build = "../../build.rs" 7 | 8 | [dependencies] 9 | libc = ">=0.2.39" 10 | timerfd = ">=1.0" 11 | 12 | api_server = { path = "../api_server" } 13 | logger = { path = "../logger" } 14 | mmds = { path = "../mmds" } 15 | polly = { path = "../polly" } 16 | seccomp = { path = "../seccomp" } 17 | utils = { path = "../utils" } 18 | vmm = { path = "../vmm" } 19 | -------------------------------------------------------------------------------- /src/jailer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jailer" 3 | version = "0.24.6" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | build = "../../build.rs" 7 | 8 | [dependencies] 9 | libc = ">=0.2.39" 10 | regex = ">=1.0.0" 11 | 12 | utils = { path = "../utils" } 13 | -------------------------------------------------------------------------------- /src/kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kernel" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | vm-memory = { path = "../vm-memory" } 8 | utils = { path = "../utils" } 9 | -------------------------------------------------------------------------------- /src/kernel/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the THIRD-PARTY file. 7 | 8 | pub mod cmdline; 9 | pub mod loader; 10 | -------------------------------------------------------------------------------- /src/kernel/src/loader/test_elf.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/src/kernel/src/loader/test_elf.bin -------------------------------------------------------------------------------- /src/kernel/src/loader/test_pe.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/src/kernel/src/loader/test_pe.bin -------------------------------------------------------------------------------- /src/logger/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "logger" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | lazy_static = ">=1.2" 9 | libc = ">=0.2.39" 10 | log = { version = ">=0.4", features = ["std"] } 11 | serde = { version = ">=1.0.27", features = ["derive"] } 12 | serde_json = ">=1.0.9" 13 | 14 | utils = { path = "../utils" } 15 | 16 | -------------------------------------------------------------------------------- /src/logger/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | mod init; 4 | mod logger; 5 | mod metrics; 6 | 7 | use std::sync::LockResult; 8 | 9 | pub use crate::logger::{LoggerError, LOGGER}; 10 | pub use crate::metrics::{ 11 | IncMetric, MetricsError, SharedIncMetric, SharedStoreMetric, StoreMetric, METRICS, 12 | }; 13 | pub use log::Level::*; 14 | pub use log::*; 15 | 16 | fn extract_guard(lock_result: LockResult) -> G { 17 | match lock_result { 18 | Ok(guard) => guard, 19 | // If a thread panics while holding this lock, the writer within should still be usable. 20 | // (we might get an incomplete log line or something like that). 21 | Err(poisoned) => poisoned.into_inner(), 22 | } 23 | } 24 | 25 | pub fn update_metric_with_elapsed_time(metric: &SharedStoreMetric, start_time_us: u64) -> u64 { 26 | let delta_us = utils::time::get_time_us(utils::time::ClockType::Monotonic) - start_time_us; 27 | metric.store(delta_us as usize); 28 | delta_us 29 | } 30 | -------------------------------------------------------------------------------- /src/micro_http/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "micro_http" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | libc = ">=0.2.39" 9 | 10 | utils = { path = "../utils" } 11 | logger = { path = "../logger" } 12 | -------------------------------------------------------------------------------- /src/mmds/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mmds" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | lazy_static = ">=1.1.0" 9 | serde_json = ">=1.0.9" 10 | versionize = ">=0.1.4" 11 | versionize_derive = ">=0.1.3" 12 | 13 | dumbo = { path = "../dumbo" } 14 | logger = { path = "../logger" } 15 | micro_http = { path = "../micro_http" } 16 | utils = { path = "../utils" } 17 | snapshot = { path = "../snapshot" } 18 | 19 | -------------------------------------------------------------------------------- /src/mmds/src/persist.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | //! Defines the structures needed for saving/restoring MmdsNetworkStack. 5 | 6 | use std::net::Ipv4Addr; 7 | 8 | use snapshot::Persist; 9 | use utils::net::mac::{MacAddr, MAC_ADDR_LEN}; 10 | use versionize::{VersionMap, Versionize, VersionizeResult}; 11 | use versionize_derive::Versionize; 12 | 13 | use super::ns::MmdsNetworkStack; 14 | 15 | /// State of a MmdsNetworkStack. 16 | #[derive(Clone, Versionize)] 17 | // NOTICE: Any changes to this structure require a snapshot version bump. 18 | pub struct MmdsNetworkStackState { 19 | mac_addr: [u8; MAC_ADDR_LEN], 20 | ipv4_addr: u32, 21 | tcp_port: u16, 22 | max_connections: usize, 23 | max_pending_resets: usize, 24 | } 25 | 26 | impl Persist<'_> for MmdsNetworkStack { 27 | type State = MmdsNetworkStackState; 28 | type ConstructorArgs = (); 29 | type Error = (); 30 | 31 | fn save(&self) -> Self::State { 32 | let mut mac_addr = [0; MAC_ADDR_LEN]; 33 | mac_addr.copy_from_slice(self.mac_addr.get_bytes()); 34 | 35 | MmdsNetworkStackState { 36 | mac_addr, 37 | ipv4_addr: self.ipv4_addr.into(), 38 | tcp_port: self.tcp_handler.local_port(), 39 | max_connections: self.tcp_handler.max_connections(), 40 | max_pending_resets: self.tcp_handler.max_pending_resets(), 41 | } 42 | } 43 | 44 | fn restore( 45 | _: Self::ConstructorArgs, 46 | state: &Self::State, 47 | ) -> std::result::Result { 48 | Ok(MmdsNetworkStack::new( 49 | MacAddr::from_bytes_unchecked(&state.mac_addr), 50 | Ipv4Addr::from(state.ipv4_addr), 51 | state.tcp_port, 52 | std::num::NonZeroUsize::new(state.max_connections).unwrap(), 53 | std::num::NonZeroUsize::new(state.max_pending_resets).unwrap(), 54 | )) 55 | } 56 | } 57 | 58 | #[cfg(test)] 59 | mod tests { 60 | use super::*; 61 | 62 | #[test] 63 | fn test_persistence() { 64 | let ns = MmdsNetworkStack::new_with_defaults(None); 65 | 66 | let mut mem = vec![0; 4096]; 67 | let version_map = VersionMap::new(); 68 | 69 | ns.save() 70 | .serialize(&mut mem.as_mut_slice(), &version_map, 1) 71 | .unwrap(); 72 | 73 | let restored_ns = MmdsNetworkStack::restore( 74 | (), 75 | &MmdsNetworkStackState::deserialize(&mut mem.as_slice(), &version_map, 1).unwrap(), 76 | ) 77 | .unwrap(); 78 | 79 | assert_eq!(restored_ns.mac_addr, ns.mac_addr); 80 | assert_eq!(restored_ns.ipv4_addr, ns.ipv4_addr); 81 | assert_eq!( 82 | restored_ns.tcp_handler.local_port(), 83 | ns.tcp_handler.local_port() 84 | ); 85 | assert_eq!( 86 | restored_ns.tcp_handler.max_connections(), 87 | ns.tcp_handler.max_connections() 88 | ); 89 | assert_eq!( 90 | restored_ns.tcp_handler.max_pending_resets(), 91 | ns.tcp_handler.max_pending_resets() 92 | ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/net_gen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "net_gen" 3 | version = "0.1.0" 4 | authors = ["The Chromium OS Authors"] 5 | edition = "2018" 6 | -------------------------------------------------------------------------------- /src/net_gen/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright TUNTAP, 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the THIRD-PARTY file. 4 | 5 | #![allow(clippy::all)] 6 | #![allow(non_upper_case_globals)] 7 | #![allow(non_camel_case_types)] 8 | #![allow(non_snake_case)] 9 | 10 | // generated with bindgen /usr/include/linux/if.h --no-unstable-rust 11 | // --constified-enum '*' --with-derive-default -- -D __UAPI_DEF_IF_IFNAMSIZ -D 12 | // __UAPI_DEF_IF_NET_DEVICE_FLAGS -D __UAPI_DEF_IF_IFREQ -D __UAPI_DEF_IF_IFMAP 13 | // Name is "iff" to avoid conflicting with "if" keyword. 14 | // Generated against Linux 4.11 to include fix "uapi: fix linux/if.h userspace 15 | // compilation errors". 16 | // Manual fixup of ifrn_name to be of type c_uchar instead of c_char. 17 | pub mod iff; 18 | // generated with bindgen /usr/include/linux/if_tun.h --no-unstable-rust 19 | // --constified-enum '*' --with-derive-default 20 | pub mod if_tun; 21 | // generated with bindgen /usr/include/linux/in.h --no-unstable-rust 22 | // --constified-enum '*' --with-derive-default 23 | // Name is "inn" to avoid conflicting with "in" keyword. 24 | pub mod inn; 25 | // generated with bindgen /usr/include/linux/sockios.h --no-unstable-rust 26 | // --constified-enum '*' --with-derive-default 27 | pub mod sockios; 28 | pub use crate::if_tun::*; 29 | pub use crate::iff::*; 30 | pub use crate::inn::*; 31 | pub use crate::sockios::*; 32 | -------------------------------------------------------------------------------- /src/polly/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "polly" 3 | version = "0.0.1" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | libc = ">=0.2.39" 9 | 10 | utils = { path="../utils" } 11 | -------------------------------------------------------------------------------- /src/polly/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | pub mod event_manager; 4 | -------------------------------------------------------------------------------- /src/rate_limiter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rate_limiter" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | libc = ">=0.2.39" 9 | timerfd = ">=1.0" 10 | versionize = ">=0.1.4" 11 | versionize_derive = ">=0.1.3" 12 | 13 | logger = { path = "../logger" } 14 | utils = { path = "../utils" } 15 | snapshot = { path = "../snapshot" } 16 | 17 | -------------------------------------------------------------------------------- /src/seccomp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "seccomp" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | libc = ">=0.2.39" 9 | -------------------------------------------------------------------------------- /src/snapshot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "snapshot" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | autobenches = false 7 | 8 | [dependencies] 9 | libc = "0.2" 10 | versionize = ">=0.1.4" 11 | versionize_derive = ">=0.1.3" 12 | 13 | [dev-dependencies] 14 | criterion = "0.3.0" 15 | 16 | [[bench]] 17 | name = "version_map" 18 | harness = false 19 | -------------------------------------------------------------------------------- /src/snapshot/src/persist.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | //! Defines an abstract interface for saving/restoring a component from state. 5 | 6 | /// An abstract interface for saving/restoring a component using a specific state. 7 | pub trait Persist<'a> 8 | where 9 | Self: Sized, 10 | { 11 | /// The type of the object representing the state of the component. 12 | type State; 13 | /// The type of the object holding the constructor arguments. 14 | type ConstructorArgs; 15 | /// The type of the error that can occur while constructing the object. 16 | type Error; 17 | 18 | /// Returns the current state of the component. 19 | fn save(&self) -> Self::State; 20 | /// Constructs a component from a specified state. 21 | fn restore( 22 | constructor_args: Self::ConstructorArgs, 23 | state: &Self::State, 24 | ) -> std::result::Result; 25 | } 26 | -------------------------------------------------------------------------------- /src/utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "utils" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | libc = ">=0.2.39" 9 | serde = ">=1.0.27" 10 | vmm-sys-util = ">=0.6.1" 11 | 12 | net_gen = { path = "../net_gen" } 13 | 14 | [dev-dependencies] 15 | serde_json = ">=1.0.9" 16 | 17 | -------------------------------------------------------------------------------- /src/utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // We use `utils` as a wrapper over `vmm_sys_util` to control the latter 5 | // dependency easier (i.e. update only in one place `vmm_sys_util` version). 6 | // More specifically, we are re-exporting modules from `vmm_sys_util` as part 7 | // of the `utils` crate. 8 | pub use vmm_sys_util::{ 9 | epoll, errno, eventfd, fam, ioctl, rand, syscall, tempdir, tempfile, terminal, 10 | }; 11 | pub use vmm_sys_util::{ioctl_expr, ioctl_ioc_nr, ioctl_iow_nr}; 12 | 13 | pub mod arg_parser; 14 | pub mod byte_order; 15 | pub mod net; 16 | pub mod signal; 17 | pub mod sm; 18 | pub mod structs; 19 | pub mod time; 20 | pub mod validators; 21 | -------------------------------------------------------------------------------- /src/utils/src/net/ipv4addr.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::net::Ipv4Addr; 5 | 6 | /// Checks if an IPv4 address is RFC 3927 compliant. 7 | /// # Examples 8 | /// 9 | /// ``` 10 | /// use std::net::Ipv4Addr; 11 | /// use utils::net::ipv4addr::is_link_local_valid; 12 | /// 13 | /// is_link_local_valid(Ipv4Addr::new(169, 254, 1, 1)); 14 | /// 15 | pub fn is_link_local_valid(ipv4_addr: Ipv4Addr) -> bool { 16 | match ipv4_addr.octets() { 17 | [169, 254, 0, _] => false, 18 | [169, 254, 255, _] => false, 19 | [169, 254, _, _] => true, 20 | _ => false, 21 | } 22 | } 23 | 24 | #[cfg(test)] 25 | mod tests { 26 | use crate::net::ipv4addr::is_link_local_valid; 27 | use std::net::Ipv4Addr; 28 | 29 | #[test] 30 | fn test_is_link_local_valid() { 31 | // Outside link-local IPv4 address range (169.254.0.0/16 - 169.254.255.255/16). 32 | let mut ipv4_addr = Ipv4Addr::new(1, 1, 1, 1); 33 | assert!(!is_link_local_valid(ipv4_addr)); 34 | 35 | // First 256 addresses can not be used, per RFC 3927. 36 | ipv4_addr = Ipv4Addr::new(169, 254, 0, 0); 37 | assert!(!is_link_local_valid(ipv4_addr)); 38 | ipv4_addr = Ipv4Addr::new(169, 254, 0, 10); 39 | assert!(!is_link_local_valid(ipv4_addr)); 40 | ipv4_addr = Ipv4Addr::new(169, 254, 0, 255); 41 | assert!(!is_link_local_valid(ipv4_addr)); 42 | 43 | // Last 256 addresses can not be used, per RFC 3927. 44 | ipv4_addr = Ipv4Addr::new(169, 254, 255, 0); 45 | assert!(!is_link_local_valid(ipv4_addr)); 46 | ipv4_addr = Ipv4Addr::new(169, 254, 255, 194); 47 | assert!(!is_link_local_valid(ipv4_addr)); 48 | ipv4_addr = Ipv4Addr::new(169, 254, 255, 255); 49 | assert!(!is_link_local_valid(ipv4_addr)); 50 | 51 | // First valid IPv4 link-local address. 52 | ipv4_addr = Ipv4Addr::new(169, 254, 1, 0); 53 | assert!(is_link_local_valid(ipv4_addr)); 54 | 55 | // Last valid IPv4 link-local address. 56 | ipv4_addr = Ipv4Addr::new(169, 254, 254, 255); 57 | assert!(is_link_local_valid(ipv4_addr)); 58 | 59 | // In between valid IPv4 link-local address. 60 | ipv4_addr = Ipv4Addr::new(169, 254, 170, 2); 61 | assert!(is_link_local_valid(ipv4_addr)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/utils/src/net/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the THIRD-PARTY file. 7 | 8 | #![deny(missing_docs)] 9 | //! # Network-related utilities 10 | //! 11 | //! Provides tools for representing and handling network related concepts like MAC addresses and 12 | //! network interfaces. 13 | 14 | /// Provides IPv4 address utility methods. 15 | pub mod ipv4addr; 16 | pub mod mac; 17 | -------------------------------------------------------------------------------- /src/utils/src/signal.rs: -------------------------------------------------------------------------------- 1 | use libc::c_int; 2 | pub use vmm_sys_util::signal::*; 3 | 4 | extern "C" { 5 | fn __libc_current_sigrtmin() -> c_int; 6 | fn __libc_current_sigrtmax() -> c_int; 7 | } 8 | 9 | pub fn sigrtmin() -> c_int { 10 | unsafe { __libc_current_sigrtmin() } 11 | } 12 | 13 | pub fn sigrtmax() -> c_int { 14 | unsafe { __libc_current_sigrtmax() } 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/src/validators.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Misc data format validations, shared by multiple Firecracker components. 5 | use std::fmt; 6 | 7 | const MAX_INSTANCE_ID_LEN: usize = 64; 8 | const MIN_INSTANCE_ID_LEN: usize = 1; 9 | 10 | #[derive(Debug, PartialEq)] 11 | pub enum Error { 12 | InvalidChar(char, usize), // (char, position) 13 | InvalidLen(usize, usize, usize), // (length, min, max) 14 | } 15 | 16 | impl fmt::Display for Error { 17 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 18 | match *self { 19 | Error::InvalidChar(ch, pos) => write!(f, "invalid char ({}) at position {}", ch, pos), 20 | Error::InvalidLen(len, min_len, max_len) => write!( 21 | f, 22 | "invalid len ({}); the length must be between {} and {}", 23 | len, min_len, max_len 24 | ), 25 | } 26 | } 27 | } 28 | 29 | /// Checks that the instance id only contains alphanumeric chars and hyphens 30 | /// and that the size is between 1 and 64 characters. 31 | pub fn validate_instance_id(input: &str) -> Result<(), Error> { 32 | if input.len() > MAX_INSTANCE_ID_LEN || input.len() < MIN_INSTANCE_ID_LEN { 33 | return Err(Error::InvalidLen( 34 | input.len(), 35 | MIN_INSTANCE_ID_LEN, 36 | MAX_INSTANCE_ID_LEN, 37 | )); 38 | } 39 | for (i, c) in input.chars().enumerate() { 40 | if !(c == '-' || c.is_alphanumeric()) { 41 | return Err(Error::InvalidChar(c, i)); 42 | } 43 | } 44 | Ok(()) 45 | } 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | use super::*; 50 | 51 | #[test] 52 | fn test_validate_instance_id() { 53 | assert_eq!( 54 | format!("{}", validate_instance_id("").unwrap_err()), 55 | "invalid len (0); the length must be between 1 and 64" 56 | ); 57 | assert!(validate_instance_id("12-3aa").is_ok()); 58 | assert_eq!( 59 | format!("{}", validate_instance_id("12_3aa").unwrap_err()), 60 | "invalid char (_) at position 2" 61 | ); 62 | assert_eq!( 63 | validate_instance_id("12:3aa").unwrap_err(), 64 | Error::InvalidChar(':', 2) 65 | ); 66 | assert_eq!( 67 | validate_instance_id(str::repeat("a", MAX_INSTANCE_ID_LEN + 1).as_str()).unwrap_err(), 68 | Error::InvalidLen( 69 | MAX_INSTANCE_ID_LEN + 1, 70 | MIN_INSTANCE_ID_LEN, 71 | MAX_INSTANCE_ID_LEN 72 | ) 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/virtio_gen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virtio_gen" 3 | version = "0.1.0" 4 | authors = ["The Chromium OS Authors"] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /src/virtio_gen/patches/0001-virtio_gen-remove-derive-Debug-from-packed-struct.patch: -------------------------------------------------------------------------------- 1 | From 67247b7abc56a11d8cae7eb354994d818fb72e93 Mon Sep 17 00:00:00 2001 2 | From: Andreea Florescu 3 | Date: Tue, 15 Jan 2019 18:30:07 +0200 4 | Subject: [PATCH] virtio_gen: remove derive Debug from packed struct 5 | 6 | Bindgen automatically adds derive debug on virtio_net_ctrl_mac, a packed 7 | structure. This generates a warning while building. 8 | 9 | Manually remove the Debug derive. 10 | 11 | Signed-off-by: Andreea Florescu 12 | --- 13 | virtio_gen/src/virtio_net.rs | 2 +- 14 | 1 file changed, 1 insertion(+), 1 deletion(-) 15 | 16 | diff --git a/virtio_gen/src/virtio_net.rs b/virtio_gen/src/virtio_net.rs 17 | index 0b68d09..a1c9dca 100644 18 | --- a/virtio_gen/src/virtio_net.rs 19 | +++ b/virtio_gen/src/virtio_net.rs 20 | @@ -681,7 +681,7 @@ fn bindgen_test_layout_virtio_net_ctrl_hdr() { 21 | } 22 | pub type virtio_net_ctrl_ack = __u8; 23 | #[repr(C, packed)] 24 | -#[derive(Debug, Default)] 25 | +#[derive(Default)] 26 | pub struct virtio_net_ctrl_mac { 27 | pub entries: __virtio32, 28 | pub macs: __IncompleteArrayField<[__u8; 6usize]>, 29 | -- 30 | 2.7.4 31 | -------------------------------------------------------------------------------- /src/virtio_gen/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the THIRD-PARTY file. 7 | 8 | #![allow(clippy::all)] 9 | #![allow(non_upper_case_globals)] 10 | #![allow(non_camel_case_types)] 11 | #![allow(non_snake_case)] 12 | 13 | pub mod virtio_blk; 14 | pub mod virtio_net; 15 | pub mod virtio_ring; 16 | -------------------------------------------------------------------------------- /src/vm-memory/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vm-memory" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | vm-memory-upstream = { package = "vm-memory", version = ">=0.2.2", features = ["backend-mmap"] } 9 | libc = ">=0.2.80" 10 | vmm-sys-util = ">= 0.4.0" 11 | -------------------------------------------------------------------------------- /src/vm-memory/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the THIRD-PARTY file. 7 | 8 | //! This is a "proxy" crate for Firecracker. It links to upstream vm-memory implementation 9 | //! and re-exports symbols for consumption. 10 | //! This crate implements a custom vm-memory backend implementation that overrides the 11 | //! upstream implementation and adds dirty page tracking functionality. 12 | pub mod bitmap; 13 | pub mod mmap; 14 | 15 | // Export local backend implementation. 16 | pub use mmap::{GuestMemoryMmap, GuestRegionMmap}; 17 | 18 | // Re-export only what is needed in Firecracker. 19 | pub use vm_memory_upstream::{ 20 | address, Address, ByteValued, Bytes, Error, FileOffset, GuestAddress, GuestMemory, 21 | GuestMemoryError, GuestMemoryRegion, MemoryRegionAddress, MmapRegion, 22 | }; 23 | -------------------------------------------------------------------------------- /src/vmm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vmm" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | lazy_static = ">=1.4.0" 9 | libc = ">=0.2.39" 10 | serde = { version = ">=1.0.27", features = ["derive"] } 11 | serde_json = ">=1.0.9" 12 | sysconf = ">=0.3.4" 13 | versionize = ">=0.1.4" 14 | versionize_derive = ">=0.1.3" 15 | vm-memory = { path = "../vm-memory" } 16 | arch = { path = "../arch" } 17 | devices = { path = "../devices" } 18 | kernel = { path = "../kernel" } 19 | kvm-bindings = { version = "0.3.0", features = ["fam-wrappers"] } 20 | kvm-ioctls = { version = "0.6.0" } 21 | logger = { path = "../logger" } 22 | mmds = { path = "../mmds" } 23 | polly = { path = "../polly" } 24 | rate_limiter = { path = "../rate_limiter" } 25 | seccomp = { path = "../seccomp" } 26 | snapshot = { path = "../snapshot"} 27 | utils = { path = "../utils" } 28 | 29 | [target.'cfg(target_arch = "x86_64")'.dependencies] 30 | cpuid = { path = "../cpuid" } 31 | 32 | [dev-dependencies] 33 | criterion = "0.3.0" 34 | 35 | [[bench]] 36 | name = "main" 37 | harness = false 38 | -------------------------------------------------------------------------------- /src/vmm/src/default_syscalls/macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// Shorthand for chaining `SeccompCondition`s with the `and` operator in a `SeccompRule`. 5 | /// The rule will take the `Allow` action if _all_ the conditions are true. 6 | /// 7 | /// [`Allow`]: enum.SeccompAction.html 8 | /// [`SeccompCondition`]: struct.SeccompCondition.html 9 | /// [`SeccompRule`]: struct.SeccompRule.html 10 | macro_rules! and { 11 | ($($x:expr,)*) => (SeccompRule::new(vec![$($x),*], SeccompAction::Allow)); 12 | ($($x:expr),*) => (SeccompRule::new(vec![$($x),*], SeccompAction::Allow)) 13 | } 14 | 15 | /// Shorthand for chaining `SeccompRule`s with the `or` operator in a `SeccompFilter`. 16 | /// 17 | /// [`SeccompFilter`]: struct.SeccompFilter.html 18 | /// [`SeccompRule`]: struct.SeccompRule.html 19 | macro_rules! or { 20 | ($($x:expr,)*) => (vec![$($x),*]); 21 | ($($x:expr),*) => (vec![$($x),*]) 22 | } 23 | -------------------------------------------------------------------------------- /src/vmm/src/device_manager/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the THIRD-PARTY file. 7 | 8 | /// Legacy Device Manager. 9 | pub mod legacy; 10 | /// Memory Mapped I/O Manager. 11 | pub mod mmio; 12 | /// Device managers (de)serialization support. 13 | pub mod persist; 14 | -------------------------------------------------------------------------------- /src/vmm/src/utilities/mock_devices/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #![allow(missing_docs)] 4 | 5 | use std::fs::File; 6 | use std::io; 7 | use std::os::unix::io::{AsRawFd, RawFd}; 8 | 9 | use devices::legacy::ReadableFd; 10 | 11 | pub struct MockSerialInput(pub File); 12 | 13 | impl io::Read for MockSerialInput { 14 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 15 | self.0.read(buf) 16 | } 17 | } 18 | 19 | impl AsRawFd for MockSerialInput { 20 | fn as_raw_fd(&self) -> RawFd { 21 | self.0.as_raw_fd() 22 | } 23 | } 24 | 25 | impl ReadableFd for MockSerialInput {} 26 | -------------------------------------------------------------------------------- /src/vmm/src/utilities/mock_resources/dirtying_init.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/src/vmm/src/utilities/mock_resources/dirtying_init.tgz -------------------------------------------------------------------------------- /src/vmm/src/utilities/mock_resources/make_noisy_kernel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | # This script illustrates the build steps for `test_noisy_elf.bin`. 7 | 8 | set -e 9 | 10 | WORKDIR="/tmp/noisy_kernel" 11 | SOURCE=$(readlink -f "$0") 12 | TEST_RESOURCE_DIR="$(dirname "$SOURCE")" 13 | FC_DIR="$TEST_RESOURCE_DIR/../../../.." 14 | 15 | KERNEL="linux-4.14.176" 16 | KERNEL_ARCHIVE="$KERNEL.tar.xz" 17 | KERNEL_URL="https://cdn.kernel.org/pub/linux/kernel/v4.x/$KERNEL_ARCHIVE" 18 | 19 | INIT_PROJ="dirtying_init" 20 | INIT_ARCHIVE="$INIT_PROJ.tgz" 21 | 22 | rm -rf "$WORKDIR" && mkdir -p "$WORKDIR" 23 | 24 | # Prepare dirtying init. 25 | echo "Preparing init..." 26 | cp "$INIT_ARCHIVE" "$WORKDIR" 27 | cd "$WORKDIR" 28 | tar xzf "$INIT_ARCHIVE" 29 | pushd "$INIT_PROJ" &>/dev/null 30 | cargo build --release 31 | popd &>/dev/null 32 | 33 | # Download kernel sources. 34 | echo "Downloading kernel..." 35 | curl "$KERNEL_URL" > "$KERNEL_ARCHIVE" 36 | echo "Extracting kernel sources..." 37 | tar xf "$KERNEL_ARCHIVE" 38 | cd "$KERNEL" 39 | 40 | # Copy base kernel config from Firecracker resources. 41 | cp "$FC_DIR/resources/microvm-kernel-x86_64.config" .config 42 | 43 | # Prepare initramfs. 44 | echo "Preparing initramfs..." 45 | mkdir -p initramfs 46 | cp "../$INIT_PROJ/target/x86_64-unknown-linux-musl/release/dirtying_init" initramfs/init 47 | pushd initramfs &>/dev/null 48 | fakeroot mkdir -p dev 49 | fakeroot mknod dev/console c 5 1 50 | fakeroot chown root init 51 | find . | cpio -H newc -o > ../initramfs.cpio 52 | fakeroot chown root ../initramfs.cpio 53 | popd &>/dev/null 54 | 55 | # Update kernel config with initramfs settings. 56 | echo "Writing initramfs settings in kernel config..." 57 | sed -i 's/CONFIG_INITRAMFS_SOURCE=""/CONFIG_INITRAMFS_SOURCE="initramfs.cpio"/' .config 58 | echo "CONFIG_INITRAMFS_ROOT_GID=0" >> .config 59 | echo "CONFIG_INITRAMFS_ROOT_UID=0" >> .config 60 | 61 | # Build kernel. 62 | echo "Building kernel..." 63 | make vmlinux 64 | cp vmlinux "$TEST_RESOURCE_DIR/test_noisy_elf.bin" 65 | 66 | echo "Done!" 67 | 68 | exit 0 69 | -------------------------------------------------------------------------------- /src/vmm/src/utilities/mock_resources/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #![allow(missing_docs)] 4 | 5 | use std::path::PathBuf; 6 | 7 | use crate::resources::VmResources; 8 | use crate::vmm_config::boot_source::BootSourceConfig; 9 | use crate::vmm_config::machine_config::VmConfig; 10 | 11 | pub const DEFAULT_BOOT_ARGS: &str = "reboot=k panic=1 pci=off"; 12 | #[cfg(target_arch = "x86_64")] 13 | pub const DEFAULT_KERNEL_IMAGE: &str = "test_elf.bin"; 14 | #[cfg(target_arch = "aarch64")] 15 | pub const DEFAULT_KERNEL_IMAGE: &str = "test_pe.bin"; 16 | #[cfg(target_arch = "x86_64")] 17 | pub const NOISY_KERNEL_IMAGE: &str = "test_noisy_elf.bin"; 18 | #[cfg(target_arch = "aarch64")] 19 | pub const NOISY_KERNEL_IMAGE: &str = "test_pe.bin"; 20 | 21 | fn kernel_image_path(kernel_image: Option<&str>) -> String { 22 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 23 | path.push("src/utilities/mock_resources"); 24 | path.push(kernel_image.unwrap_or(DEFAULT_KERNEL_IMAGE)); 25 | path.as_os_str().to_str().unwrap().to_string() 26 | } 27 | 28 | macro_rules! generate_into { 29 | ($src_type: ty, $dst_type: ty) => { 30 | impl Into<$dst_type> for $src_type { 31 | fn into(self) -> $dst_type { 32 | self.0 33 | } 34 | } 35 | }; 36 | } 37 | 38 | pub struct MockBootSourceConfig(BootSourceConfig); 39 | 40 | impl MockBootSourceConfig { 41 | pub fn new() -> MockBootSourceConfig { 42 | MockBootSourceConfig(BootSourceConfig { 43 | kernel_image_path: kernel_image_path(None), 44 | initrd_path: None, 45 | boot_args: None, 46 | }) 47 | } 48 | 49 | pub fn with_default_boot_args(mut self) -> Self { 50 | self.0.boot_args = Some(DEFAULT_BOOT_ARGS.to_string()); 51 | self 52 | } 53 | 54 | #[cfg(target_arch = "x86_64")] 55 | pub fn with_kernel(mut self, kernel_image: &str) -> Self { 56 | self.0.kernel_image_path = kernel_image_path(Some(kernel_image)); 57 | self 58 | } 59 | } 60 | 61 | impl Default for MockBootSourceConfig { 62 | fn default() -> Self { 63 | Self::new() 64 | } 65 | } 66 | 67 | #[derive(Default)] 68 | pub struct MockVmResources(VmResources); 69 | 70 | impl MockVmResources { 71 | pub fn new() -> MockVmResources { 72 | MockVmResources::default() 73 | } 74 | 75 | pub fn with_boot_source(mut self, boot_source_cfg: BootSourceConfig) -> Self { 76 | self.0.set_boot_source(boot_source_cfg).unwrap(); 77 | self 78 | } 79 | 80 | pub fn with_vm_config(mut self, vm_config: VmConfig) -> Self { 81 | self.0.set_vm_config(&vm_config).unwrap(); 82 | self 83 | } 84 | } 85 | 86 | #[derive(Default)] 87 | pub struct MockVmConfig(VmConfig); 88 | 89 | impl MockVmConfig { 90 | pub fn new() -> MockVmConfig { 91 | MockVmConfig::default() 92 | } 93 | 94 | pub fn with_dirty_page_tracking(mut self) -> Self { 95 | self.0.track_dirty_pages = true; 96 | self 97 | } 98 | } 99 | 100 | generate_into!(MockBootSourceConfig, BootSourceConfig); 101 | generate_into!(MockVmResources, VmResources); 102 | generate_into!(MockVmConfig, VmConfig); 103 | -------------------------------------------------------------------------------- /src/vmm/src/utilities/mock_resources/test_elf.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/src/vmm/src/utilities/mock_resources/test_elf.bin -------------------------------------------------------------------------------- /src/vmm/src/utilities/mock_resources/test_noisy_elf.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/src/vmm/src/utilities/mock_resources/test_noisy_elf.bin -------------------------------------------------------------------------------- /src/vmm/src/utilities/mock_resources/test_pe.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superfly/firecracker/cfc85ce2f4a79a4e2c65dfbc535395395c72fc17/src/vmm/src/utilities/mock_resources/test_pe.bin -------------------------------------------------------------------------------- /src/vmm/src/utilities/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | 3 | pub mod mock_devices; 4 | pub mod mock_resources; 5 | pub mod mock_seccomp; 6 | pub mod test_utils; 7 | -------------------------------------------------------------------------------- /src/vmm/src/utilities/test_utils/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #![allow(missing_docs)] 4 | 5 | use crate::Vmm; 6 | use std::io; 7 | use std::panic; 8 | use std::sync::{Arc, Mutex}; 9 | 10 | use crate::builder::build_microvm_for_boot; 11 | use crate::default_syscalls::get_seccomp_filter; 12 | use crate::resources::VmResources; 13 | use crate::utilities::mock_resources::{MockBootSourceConfig, MockVmConfig, MockVmResources}; 14 | use crate::vmm_config::boot_source::BootSourceConfig; 15 | use polly::event_manager::EventManager; 16 | use seccomp::SeccompLevel; 17 | use utils::terminal::Terminal; 18 | 19 | const VMM_ERR_EXIT: i32 = 42; 20 | 21 | pub fn create_vmm(_kernel_image: Option<&str>, is_diff: bool) -> (Arc>, EventManager) { 22 | let mut event_manager = EventManager::new().unwrap(); 23 | let empty_seccomp_filter = get_seccomp_filter(SeccompLevel::None).unwrap(); 24 | 25 | let boot_source_cfg = MockBootSourceConfig::new().with_default_boot_args(); 26 | #[cfg(target_arch = "aarch64")] 27 | let boot_source_cfg: BootSourceConfig = boot_source_cfg.into(); 28 | #[cfg(target_arch = "x86_64")] 29 | let boot_source_cfg: BootSourceConfig = match _kernel_image { 30 | Some(kernel) => boot_source_cfg.with_kernel(kernel).into(), 31 | None => boot_source_cfg.into(), 32 | }; 33 | let mock_vm_res = MockVmResources::new().with_boot_source(boot_source_cfg); 34 | let resources: VmResources = if is_diff { 35 | mock_vm_res 36 | .with_vm_config(MockVmConfig::new().with_dirty_page_tracking().into()) 37 | .into() 38 | } else { 39 | mock_vm_res.into() 40 | }; 41 | 42 | ( 43 | build_microvm_for_boot(&resources, &mut event_manager, &empty_seccomp_filter).unwrap(), 44 | event_manager, 45 | ) 46 | } 47 | 48 | pub fn default_vmm(kernel_image: Option<&str>) -> (Arc>, EventManager) { 49 | create_vmm(kernel_image, false) 50 | } 51 | 52 | #[cfg(target_arch = "x86_64")] 53 | pub fn dirty_tracking_vmm(kernel_image: Option<&str>) -> (Arc>, EventManager) { 54 | create_vmm(kernel_image, true) 55 | } 56 | 57 | pub fn wait_vmm_child_process(vmm_pid: i32) { 58 | // Parent process: wait for the vmm to exit. 59 | let mut vmm_status: i32 = -1; 60 | let pid_done = unsafe { libc::waitpid(vmm_pid, &mut vmm_status, 0) }; 61 | assert_eq!(pid_done, vmm_pid); 62 | restore_stdin(); 63 | // If any panics occurred, its exit status will be != 0. 64 | assert!(libc::WIFEXITED(vmm_status)); 65 | assert_eq!(libc::WEXITSTATUS(vmm_status), 0); 66 | } 67 | 68 | pub fn restore_stdin() { 69 | let stdin = io::stdin(); 70 | stdin.lock().set_canon_mode().unwrap(); 71 | } 72 | 73 | pub fn set_panic_hook() { 74 | panic::set_hook(Box::new(move |_| { 75 | restore_stdin(); 76 | unsafe { 77 | libc::exit(VMM_ERR_EXIT); 78 | } 79 | })); 80 | } 81 | -------------------------------------------------------------------------------- /src/vmm/src/version_map.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | //! Provides the VersionMap that deals with the microvm state versions. 5 | 6 | use std::collections::HashMap; 7 | 8 | use crate::device_manager::persist::DeviceStates; 9 | 10 | use lazy_static::lazy_static; 11 | use versionize::VersionMap; 12 | use versionize::Versionize; 13 | 14 | lazy_static! { 15 | // Note: until we have a better design, this needs to be updated when the version changes. 16 | /// Static instance used for handling microVM state versions. 17 | pub static ref VERSION_MAP: VersionMap = { 18 | let mut version_map = VersionMap::new(); 19 | version_map.new_version().set_type_version(DeviceStates::type_id(), 2); 20 | version_map 21 | }; 22 | 23 | /// Static instance used for creating a 1:1 mapping between Firecracker release version 24 | /// and snapshot data format version. 25 | pub static ref FC_VERSION_TO_SNAP_VERSION: HashMap = { 26 | let mut mapping = HashMap::new(); 27 | mapping.insert(String::from("0.23.0"), 1); 28 | mapping.insert(String::from("0.24.0"), 2); 29 | 30 | mapping 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /src/vmm/src/vmm_config/boot_source.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::fmt::{Display, Formatter, Result}; 5 | use std::io; 6 | 7 | use serde::{Deserialize, Serialize}; 8 | 9 | /// Default guest kernel command line: 10 | /// - `reboot=k` shut down the guest on reboot, instead of well... rebooting; 11 | /// - `panic=1` on panic, reboot after 1 second; 12 | /// - `pci=off` do not scan for PCI devices (save boot time); 13 | /// - `nomodules` disable loadable kernel module support; 14 | /// - `8250.nr_uarts=0` disable 8250 serial interface; 15 | /// - `i8042.noaux` do not probe the i8042 controller for an attached mouse (save boot time); 16 | /// - `i8042.nomux` do not probe i8042 for a multiplexing controller (save boot time); 17 | /// - `i8042.nopnp` do not use ACPIPnP to discover KBD/AUX controllers (save boot time); 18 | /// - `i8042.dumbkbd` do not attempt to control kbd state via the i8042 (save boot time). 19 | pub const DEFAULT_KERNEL_CMDLINE: &str = "reboot=k panic=1 pci=off nomodules 8250.nr_uarts=0 \ 20 | i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd"; 21 | 22 | /// Strongly typed data structure used to configure the boot source of the 23 | /// microvm. 24 | #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] 25 | #[serde(deny_unknown_fields)] 26 | pub struct BootSourceConfig { 27 | /// Path of the kernel image. 28 | pub kernel_image_path: String, 29 | /// Path of the initrd, if there is one. 30 | pub initrd_path: Option, 31 | /// The boot arguments to pass to the kernel. If this field is uninitialized, the default 32 | /// kernel command line is used: `reboot=k panic=1 pci=off nomodules 8250.nr_uarts=0`. 33 | #[serde(skip_serializing_if = "Option::is_none")] 34 | pub boot_args: Option, 35 | } 36 | 37 | /// Errors associated with actions on `BootSourceConfig`. 38 | #[derive(Debug)] 39 | pub enum BootSourceConfigError { 40 | /// The kernel file cannot be opened. 41 | InvalidKernelPath(io::Error), 42 | /// The initrd file cannot be opened. 43 | InvalidInitrdPath(io::Error), 44 | /// The kernel command line is invalid. 45 | InvalidKernelCommandLine(String), 46 | } 47 | 48 | impl Display for BootSourceConfigError { 49 | fn fmt(&self, f: &mut Formatter) -> Result { 50 | use self::BootSourceConfigError::*; 51 | match *self { 52 | InvalidKernelPath(ref e) => write!(f, "The kernel file cannot be opened: {}", e), 53 | InvalidInitrdPath(ref e) => write!( 54 | f, 55 | "The initrd file cannot be opened due to invalid path or \ 56 | invalid permissions. {}", 57 | e, 58 | ), 59 | InvalidKernelCommandLine(ref e) => { 60 | write!(f, "The kernel command line is invalid: {}", e.as_str()) 61 | } 62 | } 63 | } 64 | } 65 | 66 | /// Holds the kernel configuration. 67 | #[derive(Debug)] 68 | pub struct BootConfig { 69 | /// The commandline validated against correctness. 70 | pub cmdline: kernel::cmdline::Cmdline, 71 | /// The descriptor to the kernel file. 72 | pub kernel_file: std::fs::File, 73 | /// The descriptor to the initrd file, if there is one 74 | pub initrd_file: Option, 75 | } 76 | -------------------------------------------------------------------------------- /src/vmm/src/vmm_config/instance_info.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use serde::Serialize; 4 | 5 | /// The strongly typed that contains general information about the microVM. 6 | #[derive(Clone, Debug, Serialize)] 7 | pub struct InstanceInfo { 8 | /// The ID of the microVM. 9 | pub id: String, 10 | /// Whether the microVM is not started/running/paused. 11 | pub state: String, 12 | /// The version of the VMM that runs the microVM. 13 | pub vmm_version: String, 14 | /// The name of the application that runs the microVM. 15 | pub app_name: String, 16 | } 17 | -------------------------------------------------------------------------------- /src/vmm/src/vmm_config/metrics.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | //! Auxiliary module for configuring the metrics system. 5 | use std::fmt::{Display, Formatter}; 6 | use std::path::PathBuf; 7 | 8 | use super::{open_file_nonblock, FcLineWriter}; 9 | use logger::METRICS; 10 | 11 | use serde::{Deserialize, Serialize}; 12 | 13 | /// Strongly typed structure used to describe the metrics system. 14 | #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] 15 | pub struct MetricsConfig { 16 | /// Named pipe or file used as output for metrics. 17 | pub metrics_path: PathBuf, 18 | } 19 | 20 | /// Errors associated with actions on the `MetricsConfig`. 21 | #[derive(Debug)] 22 | pub enum MetricsConfigError { 23 | /// Cannot initialize the metrics system due to bad user input. 24 | InitializationFailure(String), 25 | } 26 | 27 | impl Display for MetricsConfigError { 28 | fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 29 | use self::MetricsConfigError::*; 30 | match *self { 31 | InitializationFailure(ref err_msg) => write!(f, "{}", err_msg.replace("\"", "")), 32 | } 33 | } 34 | } 35 | 36 | /// Configures the metrics as described in `metrics_cfg`. 37 | pub fn init_metrics(metrics_cfg: MetricsConfig) -> std::result::Result<(), MetricsConfigError> { 38 | let writer = FcLineWriter::new( 39 | open_file_nonblock(&metrics_cfg.metrics_path) 40 | .map_err(|e| MetricsConfigError::InitializationFailure(e.to_string()))?, 41 | ); 42 | METRICS 43 | .init(Box::new(writer)) 44 | .map_err(|e| MetricsConfigError::InitializationFailure(e.to_string())) 45 | } 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | use super::*; 50 | use utils::tempfile::TempFile; 51 | 52 | #[test] 53 | fn test_init_metrics() { 54 | // Error case: initializing metrics with invalid pipe returns error. 55 | let desc = MetricsConfig { 56 | metrics_path: PathBuf::from("not_found_file_metrics"), 57 | }; 58 | assert!(init_metrics(desc).is_err()); 59 | 60 | // Initializing metrics with valid pipe is ok. 61 | let metrics_file = TempFile::new().unwrap(); 62 | let desc = MetricsConfig { 63 | metrics_path: metrics_file.as_path().to_path_buf(), 64 | }; 65 | 66 | assert!(init_metrics(desc.clone()).is_ok()); 67 | assert!(init_metrics(desc).is_err()); 68 | } 69 | 70 | #[test] 71 | fn test_error_display() { 72 | assert_eq!( 73 | format!( 74 | "{}", 75 | MetricsConfigError::InitializationFailure(String::from( 76 | "Failed to initialize metrics" 77 | )) 78 | ), 79 | "Failed to initialize metrics" 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/vmm/src/vmm_config/mmds.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use serde::{export::Formatter, Deserialize}; 5 | use std::fmt::{Display, Result}; 6 | use std::net::Ipv4Addr; 7 | 8 | /// Keeps the MMDS configuration. 9 | #[derive(Debug, Deserialize, PartialEq)] 10 | #[serde(deny_unknown_fields)] 11 | pub struct MmdsConfig { 12 | /// MMDS IPv4 configured address. 13 | pub ipv4_address: Option, 14 | } 15 | 16 | impl MmdsConfig { 17 | /// Returns the MMDS IPv4 address if one was configured. 18 | /// Otherwise returns None. 19 | pub fn ipv4_addr(&self) -> Option { 20 | self.ipv4_address 21 | } 22 | } 23 | 24 | /// MMDS configuration related errors. 25 | #[derive(Debug)] 26 | pub enum MmdsConfigError { 27 | /// The provided IPv4 address is not link-local valid. 28 | InvalidIpv4Addr, 29 | } 30 | 31 | impl Display for MmdsConfigError { 32 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 33 | match self { 34 | MmdsConfigError::InvalidIpv4Addr => { 35 | write!(f, "The MMDS IPv4 address is not link local.") 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/vmm/src/vmm_config/snapshot.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | //! Configurations used in the snapshotting context. 5 | 6 | use std::path::PathBuf; 7 | 8 | use serde::{Deserialize, Serialize}; 9 | 10 | /// The snapshot type options that are available when 11 | /// creating a new snapshot. 12 | #[derive(Debug, Deserialize, PartialEq, Serialize)] 13 | pub enum SnapshotType { 14 | /// Diff snapshot. 15 | Diff, 16 | /// Full snapshot. 17 | Full, 18 | } 19 | 20 | impl Default for SnapshotType { 21 | fn default() -> SnapshotType { 22 | SnapshotType::Full 23 | } 24 | } 25 | 26 | /// Stores the configuration that will be used for creating a snapshot. 27 | #[derive(Debug, Deserialize, PartialEq, Serialize)] 28 | #[serde(deny_unknown_fields)] 29 | pub struct CreateSnapshotParams { 30 | /// This marks the type of snapshot we want to create. 31 | /// The default value is `Full`, which means a full snapshot. 32 | #[serde(default = "SnapshotType::default")] 33 | pub snapshot_type: SnapshotType, 34 | /// Path to the file that will contain the microVM state. 35 | pub snapshot_path: PathBuf, 36 | /// Path to the file that will contain the guest memory. 37 | pub mem_file_path: PathBuf, 38 | /// Optional field for the microVM version. The default 39 | /// value is the current version. 40 | pub version: Option, 41 | } 42 | 43 | /// Stores the configuration that will be used for loading a snapshot. 44 | #[derive(Debug, Deserialize, PartialEq, Serialize)] 45 | #[serde(deny_unknown_fields)] 46 | pub struct LoadSnapshotParams { 47 | /// Path to the file that contains the microVM state to be loaded. 48 | pub snapshot_path: PathBuf, 49 | /// Path to the file that contains the guest memory to be loaded. 50 | pub mem_file_path: PathBuf, 51 | /// Setting this flag will enable KVM dirty page tracking and will 52 | /// allow taking subsequent incremental snapshots. 53 | #[serde(default)] 54 | pub enable_diff_snapshots: bool, 55 | /// When set to true, the vm is also resumed if the snapshot load 56 | /// is successful. 57 | #[serde(default)] 58 | pub resume_vm: bool, 59 | } 60 | 61 | /// The microVM state options. 62 | #[derive(Debug, Deserialize, Serialize)] 63 | pub enum VmState { 64 | /// The microVM is paused, which means that we can create a snapshot of it. 65 | Paused, 66 | /// The microVM is resumed; this state should be set after we load a snapshot. 67 | Resumed, 68 | } 69 | 70 | /// Keeps the microVM state necessary in the snapshotting context. 71 | #[derive(Debug, Deserialize, Serialize)] 72 | #[serde(deny_unknown_fields)] 73 | pub struct Vm { 74 | /// The microVM state, which can be `paused` or `resumed`. 75 | pub state: VmState, 76 | } 77 | -------------------------------------------------------------------------------- /src/vmm/src/vstate/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | pub(crate) mod system; 5 | pub(crate) mod vcpu; 6 | pub(crate) mod vm; 7 | -------------------------------------------------------------------------------- /tests/framework/decorators.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Module for declaring decorators used throughout integration tests.""" 4 | 5 | import time 6 | 7 | from framework.defs import MAX_API_CALL_DURATION_MS 8 | 9 | 10 | def timed_request(method): 11 | """Decorate functions to monitor their duration.""" 12 | class ApiTimeoutException(Exception): 13 | """A custom exception containing the details of the failed API call.""" 14 | 15 | def __init__(self, duration, method, resource, payload): 16 | """Compose the error message from the API call components.""" 17 | super().__init__( 18 | 'API call exceeded maximum duration: {:.2f} ms.\n' 19 | 'Call: {} {} {}'.format(duration, method, resource, payload) 20 | ) 21 | 22 | def timed(*args, **kwargs): 23 | """Raise an exception if method's duration exceeds the max value.""" 24 | start = time.time() 25 | result = method(*args, **kwargs) 26 | duration_ms = (time.time() - start) * 1000 27 | 28 | if duration_ms > MAX_API_CALL_DURATION_MS: 29 | try: 30 | # The positional arguments are: 31 | # 1. The Session object 32 | # 2. The URL from which we extract the resource for readability 33 | resource = args[1][(args[1].rfind("/")):] 34 | except IndexError: 35 | # Ignore formatting errors. 36 | resource = '' 37 | 38 | # The payload is JSON-encoded and passed as an argument. 39 | payload = kwargs['json'] if 'json' in kwargs else '' 40 | 41 | raise ApiTimeoutException( 42 | duration_ms, 43 | method.__name__.upper(), 44 | resource, 45 | payload 46 | ) 47 | 48 | return result 49 | 50 | return timed 51 | 52 | 53 | def test_context(cap, count=1): 54 | """Set the image capability and vm count attribute for individual tests.""" 55 | def wrap(func): 56 | setattr(func, '_capability', cap) 57 | setattr(func, '_pool_size', count) 58 | return func 59 | return wrap 60 | -------------------------------------------------------------------------------- /tests/framework/defs.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Some common defines used in different modules of the testing framework.""" 4 | 5 | from pathlib import Path 6 | 7 | API_USOCKET_URL_PREFIX = 'http+unix://' 8 | """URL prefix used for the API calls through a UNIX domain socket.""" 9 | FC_BINARY_NAME = 'firecracker' 10 | """Firecracker's binary name.""" 11 | JAILER_BINARY_NAME = 'jailer' 12 | """Jailer's binary name.""" 13 | FC_WORKSPACE_DIR = Path(__file__).parent.parent.parent.resolve() 14 | """The Firecracker sources workspace dir.""" 15 | FC_WORKSPACE_TARGET_DIR = Path(FC_WORKSPACE_DIR).joinpath("build/cargo_target") 16 | """Cargo target dir for the Firecracker workspace. Set via .cargo/config.""" 17 | MAX_API_CALL_DURATION_MS = 300 18 | """Maximum accepted duration of an API call, in milliseconds.""" 19 | MICROVM_KERNEL_RELPATH = 'kernel/' 20 | """Relative path to the location of the kernel file.""" 21 | MICROVM_FSFILES_RELPATH = 'fsfiles/' 22 | """Relative path to the location of the filesystems.""" 23 | -------------------------------------------------------------------------------- /tests/framework/http.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Wrapper over an http session with timed requests.""" 4 | # pylint: disable=unused-import 5 | import requests 6 | from requests_unixsocket import DEFAULT_SCHEME, UnixAdapter 7 | 8 | from framework import decorators 9 | 10 | 11 | class Session(requests.Session): 12 | """Wrapper over requests_unixsocket.Session limiting the call duration. 13 | 14 | Only the API calls relevant to Firecracker (GET, PUT, PATCH) are 15 | implemented. 16 | """ 17 | 18 | def __init__(self): 19 | """Create a Session object and set the is_good_response callback.""" 20 | super().__init__() 21 | 22 | # The `pool_connections` argument indicates the maximum number of 23 | # open connections allowed at a time. This value is set to 10 for 24 | # consistency with the micro-http's `MAX_CONNECTIONS`. 25 | self.mount(DEFAULT_SCHEME, UnixAdapter(pool_connections=10)) 26 | 27 | def is_good_response(response: int): 28 | """Return `True` for all HTTP 2xx response codes.""" 29 | return 200 <= response < 300 30 | 31 | def is_status_ok(response: int): 32 | """Return `True` when HTTP response code is 200 OK.""" 33 | return response == 200 34 | 35 | def is_status_no_content(response: int): 36 | """Return `True` when HTTP response code is 204 NoContent.""" 37 | return response == 204 38 | 39 | def is_status_bad_request(response: int): 40 | """Return `True` when HTTP response code is 400 BadRequest.""" 41 | return response == 400 42 | 43 | def is_status_not_found(response: int): 44 | """Return `True` when HTTP response code is 404 NotFound.""" 45 | return response == 404 46 | 47 | self.is_good_response = is_good_response 48 | self.is_status_ok = is_status_ok 49 | self.is_status_no_content = is_status_no_content 50 | self.is_status_bad_request = is_status_bad_request 51 | self.is_status_not_found = is_status_not_found 52 | 53 | @decorators.timed_request 54 | def get(self, url, **kwargs): 55 | """Wrap the GET call with duration limit.""" 56 | # pylint: disable=method-hidden 57 | # The `untime` method overrides this, and pylint disapproves. 58 | return super().get(url, **kwargs) 59 | 60 | @decorators.timed_request 61 | def patch(self, url, data=None, **kwargs): 62 | """Wrap the PATCH call with duration limit.""" 63 | # pylint: disable=method-hidden 64 | # The `untime` method overrides this, and pylint disapproves. 65 | return super().patch(url, data=data, **kwargs) 66 | 67 | @decorators.timed_request 68 | def put(self, url, data=None, **kwargs): 69 | """Wrap the PUT call with duration limit.""" 70 | # pylint: disable=method-hidden 71 | # The `untime` method overrides this, and pylint disapproves. 72 | return super().put(url, data=data, **kwargs) 73 | 74 | def untime(self): 75 | """Restore the HTTP methods to their un-timed selves.""" 76 | self.get = super().get 77 | self.patch = super().patch 78 | self.put = super().put 79 | -------------------------------------------------------------------------------- /tests/framework/state_machine.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Defines a stream based string matcher and a generic state object.""" 4 | 5 | 6 | # Too few public methods (1/2) (too-few-public-methods) 7 | # pylint: disable=R0903 8 | class MatchStaticString: 9 | """Match a static string versus input.""" 10 | 11 | # Prevent state objects from being collected by pytest. 12 | __test__ = False 13 | 14 | def __init__(self, match_string): 15 | """Initialize using specified match string.""" 16 | self._string = match_string 17 | self._input = "" 18 | 19 | def match(self, input_char) -> bool: 20 | """ 21 | Check if `_input` matches the match `_string`. 22 | 23 | Process one char at a time and build `_input` string. 24 | Preserve built `_input` if partially matches `_string`. 25 | Return True when `_input` is the same as `_string`. 26 | """ 27 | self._input += str(input_char) 28 | if self._input == self._string[:len(self._input)]: 29 | if len(self._input) == len(self._string): 30 | self._input = "" 31 | return True 32 | return False 33 | 34 | self._input = self._input[1:] 35 | return False 36 | 37 | 38 | class TestState(MatchStaticString): 39 | """Generic test state object.""" 40 | 41 | # Prevent state objects from being collected by pytest. 42 | __test__ = False 43 | 44 | def __init__(self, match_string=''): 45 | """Initialize state fields.""" 46 | MatchStaticString.__init__(self, match_string) 47 | print('\n*** Current test state: ', str(self), end='') 48 | 49 | def handle_input(self, serial, input_char): 50 | """Handle input event and return next state.""" 51 | 52 | def __repr__(self): 53 | """Leverages the __str__ method to describe the TestState.""" 54 | return self.__str__() 55 | 56 | def __str__(self): 57 | """Return state name.""" 58 | return self.__class__.__name__ 59 | -------------------------------------------------------------------------------- /tests/framework/statistics/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """Single threaded producer/consumer for statistics gathering.""" 5 | 6 | from . import core 7 | from . import consumer 8 | from . import producer 9 | from . import types 10 | from . import criteria 11 | from . import function 12 | -------------------------------------------------------------------------------- /tests/framework/statistics/criteria.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """Module for comparision criteria.""" 5 | 6 | from numbers import Number 7 | from abc import ABC, abstractmethod 8 | 9 | 10 | class Failed(Exception): 11 | """Exception to be raised when criteria fails.""" 12 | 13 | def __init__(self, msg=""): 14 | """Initialize the exception.""" 15 | super().__init__() 16 | self._msg = msg 17 | 18 | @property 19 | def msg(self): 20 | """Return the exception message.""" 21 | return self._msg 22 | 23 | @msg.setter 24 | def msg(self, msg): 25 | """Set the exception message.""" 26 | self._msg = msg 27 | 28 | 29 | # pylint: disable=R0903 30 | class ComparisonCriteria(ABC): 31 | """Comparison criteria between results and targets.""" 32 | 33 | def __init__(self, name: str, target: Number): 34 | """Initialize the comparison criteria.""" 35 | self.target = target 36 | self.name = name 37 | 38 | @abstractmethod 39 | def check(self, actual): 40 | """Compare the target and the actual.""" 41 | 42 | 43 | # pylint: disable=R0903 44 | class GraterThan(ComparisonCriteria): 45 | """Greater than comparison criteria.""" 46 | 47 | def __init__(self, target: Number): 48 | """Initialize the criteria.""" 49 | super().__init__("GreaterThan", target) 50 | 51 | def check(self, actual): 52 | """Compare the target and the actual.""" 53 | fail_msg = self.name + f" failed. Target: '{self.target} " \ 54 | f"vs Actual: '{actual}'." 55 | if self.target > actual: 56 | raise Failed(msg=fail_msg) 57 | 58 | 59 | # pylint: disable=R0903 60 | class LowerThan(ComparisonCriteria): 61 | """Lower than comparison criteria.""" 62 | 63 | def __init__(self, target: Number): 64 | """Initialize the criteria.""" 65 | super().__init__("LowerThan", target) 66 | 67 | def check(self, actual): 68 | """Compare the target and the actual.""" 69 | fail_msg = self.name + f" failed. Target: '{self.target} " \ 70 | f"vs Actual: '{actual}'." 71 | if self.target < actual: 72 | raise Failed(msg=fail_msg) 73 | 74 | 75 | # pylint: disable=R0903 76 | class EqualWith(ComparisonCriteria): 77 | """Equal with comparison criteria.""" 78 | 79 | def __init__(self, target: Number, tolerance: Number): 80 | """Initialize the criteria.""" 81 | super().__init__("EqualWith", target) 82 | self.tolerance = tolerance 83 | 84 | def check(self, actual): 85 | """Compare the target and the actual.""" 86 | fail_msg = self.name + f" failed. Target: '{self.target} +- " \ 87 | f"{self.tolerance}' " \ 88 | f"vs Actual: '{actual}'." 89 | if abs(self.target - actual) > self.tolerance: 90 | raise Failed(msg=fail_msg) 91 | -------------------------------------------------------------------------------- /tests/framework/utils_cpuid.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Helper functions for testing CPU identification functionality.""" 4 | 5 | import subprocess 6 | from enum import Enum, auto 7 | 8 | from framework.utils import run_cmd 9 | import host_tools.network as net_tools 10 | 11 | 12 | class CpuVendor(Enum): 13 | """CPU vendors enum.""" 14 | 15 | AMD = auto() 16 | INTEL = auto() 17 | 18 | 19 | def get_cpu_vendor(): 20 | """Return the CPU vendor.""" 21 | brand_str = subprocess.check_output("lscpu", shell=True).strip().decode() 22 | if 'AuthenticAMD' in brand_str: 23 | return CpuVendor.AMD 24 | return CpuVendor.INTEL 25 | 26 | 27 | def get_cpu_model_name(): 28 | """Return the CPU model name.""" 29 | _, stdout, _ = run_cmd("cat /proc/cpuinfo | grep 'model name' | uniq") 30 | info = stdout.strip().split(sep=":") 31 | assert len(info) == 2 32 | return info[1].strip() 33 | 34 | 35 | def check_guest_cpuid_output(vm, guest_cmd, expected_header, 36 | expected_separator, 37 | expected_key_value_store): 38 | """Parse cpuid output inside guest and match with expected one.""" 39 | ssh_connection = net_tools.SSHConnection(vm.ssh_config) 40 | _, stdout, stderr = ssh_connection.execute_command(guest_cmd) 41 | 42 | assert stderr.read() == '' 43 | while True: 44 | line = stdout.readline() 45 | if line != '': 46 | # All the keys have been matched. Stop. 47 | if not expected_key_value_store: 48 | break 49 | 50 | # Try to match the header if needed. 51 | if expected_header not in (None, ''): 52 | if line.strip() == expected_header: 53 | expected_header = None 54 | continue 55 | 56 | # See if any key matches. 57 | # We Use a try-catch block here since line.split() may fail. 58 | try: 59 | [key, value] = list( 60 | map(lambda x: x.strip(), line.split(expected_separator))) 61 | except ValueError: 62 | continue 63 | 64 | if key in expected_key_value_store.keys(): 65 | assert value == expected_key_value_store[key], \ 66 | "%s does not have the expected value" % key 67 | del expected_key_value_store[key] 68 | 69 | else: 70 | break 71 | 72 | assert not expected_key_value_store, \ 73 | "some keys in dictionary have not been found in the output: %s" \ 74 | % expected_key_value_store 75 | 76 | 77 | def read_guest_file(vm, file): 78 | """Parse cpuid output inside guest and match with expected one.""" 79 | ssh_connection = net_tools.SSHConnection(vm.ssh_config) 80 | _, stdout, stderr = ssh_connection.execute_command("cat {}".format(file)) 81 | assert stderr.read() == "" 82 | return stdout.read().strip() 83 | -------------------------------------------------------------------------------- /tests/framework/vm_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "boot-source": { 3 | "kernel_image_path": "vmlinux.bin", 4 | "boot_args": "console=ttyS0 reboot=k panic=1 pci=off" 5 | }, 6 | "drives": [ 7 | { 8 | "drive_id": "rootfs", 9 | "path_on_host": "xenial.rootfs.ext4", 10 | "is_root_device": true, 11 | "is_read_only": false 12 | } 13 | ], 14 | "machine-config": { 15 | "vcpu_count": 2, 16 | "mem_size_mib": 1024, 17 | "ht_enabled": false 18 | } 19 | } -------------------------------------------------------------------------------- /tests/host_tools/cargo_build.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Functionality for a shared binary build and release path for all tests.""" 4 | 5 | import os 6 | import platform 7 | 8 | import framework.utils as utils 9 | 10 | from framework.defs import ( 11 | FC_BINARY_NAME, FC_WORKSPACE_DIR, FC_WORKSPACE_TARGET_DIR, 12 | JAILER_BINARY_NAME 13 | ) 14 | 15 | CARGO_BUILD_REL_PATH = 'firecracker_binaries' 16 | """Keep a single build path across all build tests.""" 17 | 18 | CARGO_RELEASE_REL_PATH = os.path.join( 19 | CARGO_BUILD_REL_PATH, 'release' 20 | ) 21 | """Keep a single Firecracker release binary path across all test types.""" 22 | 23 | 24 | DEFAULT_BUILD_TARGET = '{}-unknown-linux-musl'.format(platform.machine()) 25 | RELEASE_BINARIES_REL_PATH = '{}/release/'.format(DEFAULT_BUILD_TARGET) 26 | 27 | CARGO_UNITTEST_REL_PATH = os.path.join(CARGO_BUILD_REL_PATH, "test") 28 | 29 | 30 | def cargo_build(path, extra_args='', src_dir='', extra_env=''): 31 | """Trigger build depending on flags provided.""" 32 | cmd = 'CARGO_TARGET_DIR={} {} cargo build {}'.format( 33 | path, 34 | extra_env, 35 | extra_args 36 | ) 37 | if src_dir: 38 | cmd = 'cd {} && {}'.format(src_dir, cmd) 39 | 40 | utils.run_cmd(cmd) 41 | 42 | 43 | def cargo_test(path, extra_args=''): 44 | """Trigger unit tests depending on flags provided.""" 45 | path = os.path.join(path, CARGO_UNITTEST_REL_PATH) 46 | cmd = 'CARGO_TARGET_DIR={} RUST_TEST_THREADS=1 RUST_BACKTRACE=1 ' \ 47 | 'RUSTFLAGS="{}" cargo test {} --all --no-fail-fast'.format( 48 | path, get_rustflags(), extra_args) 49 | utils.run_cmd(cmd) 50 | 51 | 52 | def get_firecracker_binaries(): 53 | """Build the Firecracker and Jailer binaries if they don't exist. 54 | 55 | Returns the location of the firecracker related binaries eventually after 56 | building them in case they do not exist at the specified root_path. 57 | """ 58 | target = DEFAULT_BUILD_TARGET 59 | out_dir = "{target_dir}/{target}/release".format( 60 | target_dir=FC_WORKSPACE_TARGET_DIR, target=target 61 | ) 62 | fc_bin_path = "{}/{}".format(out_dir, FC_BINARY_NAME) 63 | jailer_bin_path = "{}/{}".format(out_dir, JAILER_BINARY_NAME) 64 | 65 | if getattr(get_firecracker_binaries, 'binaries_built', False): 66 | return fc_bin_path, jailer_bin_path 67 | 68 | cd_cmd = "cd {}".format(FC_WORKSPACE_DIR) 69 | flags = 'RUSTFLAGS="{}"'.format(get_rustflags()) 70 | cargo_default_cmd = "cargo build --release --target {}".format( 71 | target 72 | ) 73 | cargo_jailer_cmd = "cargo build -p jailer --release --target {}".format( 74 | target 75 | ) 76 | cmd = "{0} && {1} {2} && {1} {3}".format( 77 | cd_cmd, 78 | flags, 79 | cargo_default_cmd, 80 | cargo_jailer_cmd 81 | ) 82 | 83 | utils.run_cmd(cmd) 84 | 85 | utils.run_cmd( 86 | "strip --strip-debug {} {}" 87 | .format(fc_bin_path, jailer_bin_path) 88 | ) 89 | 90 | setattr(get_firecracker_binaries, 'binaries_built', True) 91 | 92 | return fc_bin_path, jailer_bin_path 93 | 94 | 95 | def get_rustflags(): 96 | """Get the relevant rustflags for building/unit testing.""" 97 | rustflags = "-D warnings" 98 | if platform.machine() == "aarch64": 99 | rustflags += " -C link-arg=-lgcc -C link-arg=-lfdt " 100 | return rustflags 101 | -------------------------------------------------------------------------------- /tests/host_tools/change_net_config_space.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // This is used by the `test_net_config_space.py` integration test, which writes 5 | // into the microVM configured network device config space a new MAC address. 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | int show_usage() { 17 | printf("Usage: ./change_net_config_space.bin [dev_addr_start] [mac_addr]\n"); 18 | printf("Example:\n"); 19 | printf("> ./change_net_config_space.bin 0xd00001000 0x060504030201\n"); 20 | return 0; 21 | } 22 | 23 | int main(int argc, char *argv[]) { 24 | int fd, i, offset; 25 | uint8_t *map_base; 26 | volatile uint8_t *virt_addr; 27 | 28 | uint64_t mapped_size, page_size, offset_in_page, target; 29 | uint64_t width = 6; 30 | 31 | uint64_t config_offset = 0x100; 32 | uint64_t device_start_addr = 0x00000000; 33 | uint64_t mac = 0; 34 | 35 | if (argc != 3) { 36 | return show_usage(); 37 | } 38 | 39 | device_start_addr = strtoull(argv[1], NULL, 0); 40 | mac = strtoull(argv[2], NULL, 0); 41 | 42 | fd = open("/dev/mem", O_RDWR | O_SYNC); 43 | if (fd < 0) { 44 | perror("Failed to open '/dev/mem'."); 45 | return 1; 46 | } 47 | 48 | target = device_start_addr + config_offset; 49 | // Get the page size. 50 | mapped_size = page_size = getpagesize(); 51 | // Get the target address physical frame page offset. 52 | offset_in_page = (unsigned) target & (page_size - 1); 53 | /* If the data length goes out of the current page, 54 | * double the needed map size. */ 55 | if (offset_in_page + width > page_size) { 56 | /* This access spans pages. 57 | * Must map two pages to make it possible. */ 58 | mapped_size *= 2; 59 | } 60 | 61 | // Map the `/dev/mem` to virtual memory. 62 | map_base = mmap(NULL, 63 | mapped_size, 64 | PROT_READ | PROT_WRITE, 65 | MAP_SHARED, 66 | fd, 67 | target & ~(off_t)(page_size - 1)); 68 | if (map_base == MAP_FAILED) { 69 | perror("Failed to mmap '/dev/mem'."); 70 | return 2; 71 | } 72 | 73 | // Write in the network device config space a new MAC. 74 | virt_addr = (volatile uint8_t*) (map_base + offset_in_page); 75 | *virt_addr = (uint8_t) (mac >> 40); 76 | printf("%02x", *virt_addr); 77 | 78 | for (i = 1; i <= 5; i++) { 79 | *(virt_addr + i) = (uint8_t) (mac >> (5 - i) * 8); 80 | printf(":%02x", *(virt_addr + i)); 81 | } 82 | 83 | // Deallocate resources. 84 | munmap(map_base, mapped_size); 85 | close(fd); 86 | 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /tests/host_tools/cpu_load.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Utilities for measuring cpu utilisation for a process.""" 4 | import time 5 | from threading import Thread 6 | import framework.utils as utils 7 | 8 | # /proc//stat output taken from 9 | # https://www.man7.org/linux/man-pages/man5/proc.5.html 10 | STAT_UTIME_IDX = 13 11 | STAT_STIME_IDX = 14 12 | STAT_STARTTIME_IDX = 21 13 | 14 | 15 | class CpuLoadExceededException(Exception): 16 | """A custom exception containing details on excessive cpu load.""" 17 | 18 | def __init__(self, cpu_load_samples, threshold): 19 | """Compose the error message containing the cpu load details.""" 20 | super().__init__( 21 | 'Cpu load samples {} exceeded maximum threshold {}.\n' 22 | .format(cpu_load_samples, threshold) 23 | ) 24 | 25 | 26 | class CpuLoadMonitor(Thread): 27 | """Class to represent a cpu load monitor for a thread.""" 28 | 29 | CPU_LOAD_SAMPLES_TIMEOUT_S = 1 30 | 31 | def __init__( 32 | self, 33 | process_pid, 34 | thread_pid, 35 | threshold 36 | ): 37 | """Set up monitor attributes.""" 38 | Thread.__init__(self) 39 | self._process_pid = process_pid 40 | self._thread_pid = thread_pid 41 | self._cpu_load_samples = [] 42 | self._threshold = threshold 43 | self._should_stop = False 44 | 45 | @property 46 | def process_pid(self): 47 | """Get the process pid.""" 48 | return self._process_pid 49 | 50 | @property 51 | def thread_pid(self): 52 | """Get the thread pid.""" 53 | return self._thread_pid 54 | 55 | @property 56 | def threshold(self): 57 | """Get the cpu load threshold.""" 58 | return self._threshold 59 | 60 | @property 61 | def cpu_load_samples(self): 62 | """Get the cpu load samples.""" 63 | return self._cpu_load_samples 64 | 65 | def signal_stop(self): 66 | """Signal that the thread should stop.""" 67 | self._should_stop = True 68 | 69 | def run(self): 70 | """Thread for monitoring cpu load of some pid. 71 | 72 | It is up to the caller to check the queue. 73 | """ 74 | while not self._should_stop: 75 | cpu_load = utils.ProcessManager.get_cpu_percent( 76 | self._process_pid)["real"] 77 | if cpu_load > self.threshold: 78 | self.cpu_load_samples.append(cpu_load) 79 | time.sleep(1) # 1 second granularity. 80 | 81 | def check_samples(self): 82 | """Check that there are no samples above the threshold.""" 83 | if len(self.cpu_load_samples) > 0: 84 | raise CpuLoadExceededException( 85 | self._cpu_load_samples, self._threshold) 86 | -------------------------------------------------------------------------------- /tests/host_tools/drive.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Utilities for creating filesystems on the host.""" 4 | 5 | import os 6 | 7 | import framework.utils as utils 8 | 9 | 10 | class FilesystemFile: 11 | """Facility for creating and working with filesystems.""" 12 | 13 | KNOWN_FILEFS_FORMATS = {'ext4'} 14 | path = None 15 | 16 | def __init__(self, path: str, size: int = 256, fs_format: str = 'ext4'): 17 | """Create a new file system in a file. 18 | 19 | Raises if the file system format is not supported, if the file already 20 | exists, or if it ends in '/'. 21 | """ 22 | if fs_format not in self.KNOWN_FILEFS_FORMATS: 23 | raise ValueError( 24 | 'Format not in: + ' + str(self.KNOWN_FILEFS_FORMATS) 25 | ) 26 | # Here we append the format as a 27 | path = os.path.join(path + '.' + fs_format) 28 | 29 | if os.path.isfile(path): 30 | raise FileExistsError("File already exists: " + path) 31 | 32 | utils.run_cmd( 33 | 'dd status=none if=/dev/zero' 34 | ' of=' + path + 35 | ' bs=1M count=' + str(size)) 36 | utils.run_cmd('mkfs.ext4 -qF ' + path) 37 | self.path = path 38 | 39 | def resize(self, new_size): 40 | """Resize the filesystem.""" 41 | utils.run_cmd('truncate --size ' + str(new_size) + 'M ' + self.path) 42 | utils.run_cmd('resize2fs ' + self.path) 43 | 44 | def size(self): 45 | """Return the size of the filesystem.""" 46 | return os.stat(self.path).st_size 47 | 48 | def __del__(self): 49 | """Destructor cleaning up filesystem from where it was created.""" 50 | if self.path: 51 | try: 52 | os.remove(self.path) 53 | except OSError: 54 | pass 55 | -------------------------------------------------------------------------------- /tests/host_tools/fillmem.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #define _GNU_SOURCE 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | #define MB (1024 * 1024) 19 | 20 | 21 | int fill_mem(int mb_count) { 22 | int i, j; 23 | char *ptr = NULL; 24 | for(j = 0; j < mb_count; j++) { 25 | do { 26 | // We can't map the whole chunk of memory at once because 27 | // in case the system is already in a memory pressured 28 | // state and we are trying to achieve a process death by 29 | // OOM killer, a large allocation is far less likely to 30 | // succeed than more granular ones. 31 | ptr = mmap( 32 | NULL, 33 | MB * sizeof(char), 34 | PROT_READ | PROT_WRITE, 35 | MAP_ANONYMOUS | MAP_PRIVATE, 36 | -1, 37 | 0 38 | ); 39 | } while (ptr == MAP_FAILED); 40 | memset(ptr, 1, MB * sizeof(char)); 41 | } 42 | 43 | return 0; 44 | } 45 | 46 | 47 | int main(int argc, char *const argv[]) { 48 | 49 | if (argc != 2) { 50 | printf("Usage: ./fillmem mb_count\n"); 51 | return -1; 52 | } 53 | 54 | int mb_count = atoi(argv[1]); 55 | 56 | int pid = fork(); 57 | if (pid == 0) { 58 | return fill_mem(mb_count); 59 | } else { 60 | int status; 61 | wait(&status); 62 | int fd = open("/tmp/fillmem_output.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR); 63 | if (fd < 0) { 64 | return -1; 65 | } 66 | 67 | if (WIFSIGNALED(status)) { 68 | char buf[200]; 69 | sprintf(buf, "OOM Killer stopped the program with signal %d, exit code %d\n", WTERMSIG(status), WEXITSTATUS(status)); 70 | write(fd, buf, strlen(buf) + 1); 71 | } else { 72 | write(fd, "Memory filling was successful\n", 31); 73 | } 74 | 75 | close(fd); 76 | return 0; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tests/host_tools/logging.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Utilities for testing the logging system (metrics, common logs).""" 4 | 5 | import fcntl 6 | import os 7 | import sys 8 | 9 | from queue import Queue 10 | from threading import Thread 11 | 12 | 13 | class Fifo: 14 | """Facility for creating and working with named pipes (FIFOs).""" 15 | 16 | path = None 17 | fifo = None 18 | 19 | def __init__(self, path, blocking=False): 20 | """Create a new named pipe.""" 21 | if os.path.exists(path): 22 | raise FileExistsError("Named pipe {} already exists.".format(path)) 23 | 24 | os.mkfifo(path) 25 | if not blocking: 26 | fd = os.open(path, os.O_NONBLOCK) 27 | self.fifo = os.fdopen(fd, "r") 28 | else: 29 | self.fifo = open(path, "r") 30 | 31 | self.path = path 32 | 33 | def sequential_reader(self, max_lines): 34 | """Return up to `max_lines` lines from a non blocking fifo. 35 | 36 | :return: A list containing the read lines. 37 | """ 38 | return self.fifo.readlines()[:max_lines] 39 | 40 | @property 41 | def flags(self): 42 | """Return flags of the opened fifo. 43 | 44 | :return An integer with flags of the opened file. 45 | """ 46 | fd = self.fifo.fileno() 47 | return fcntl.fcntl(fd, fcntl.F_GETFL) 48 | 49 | @flags.setter 50 | def flags(self, flags): 51 | """Set new flags for the opened fifo.""" 52 | fd = self.fifo.fileno() 53 | fcntl.fcntl(fd, fcntl.F_SETFL, flags) 54 | 55 | def threaded_reader(self, check_func, *args): 56 | """Start a thread to read fifo. 57 | 58 | The thread that runs the `check_func` on each line 59 | in the FIFO and enqueues any exceptions in the `exceptions_queue`. 60 | """ 61 | exceptions_queue = Queue() 62 | metric_reader_thread = Thread( 63 | target=self._do_thread_reader, args=( 64 | exceptions_queue, 65 | check_func, 66 | *args 67 | ) 68 | ) 69 | metric_reader_thread.start() 70 | return exceptions_queue 71 | 72 | def _do_thread_reader(self, exceptions_queue, check_func, *args): 73 | """Read from a FIFO opened as read-only. 74 | 75 | This applies a function for checking output on each 76 | line of the logs received. 77 | Failures and exceptions are propagated to the main thread 78 | through the `exceptions_queue`. 79 | """ 80 | max_iter = 20 81 | while max_iter > 0: 82 | data = self.fifo.readline() 83 | if not data: 84 | break 85 | try: 86 | check_func( 87 | "{0}".format(data), *args 88 | ) 89 | # pylint: disable=broad-except 90 | # We need to propagate all type of exceptions to the main thread. 91 | except Exception: 92 | exceptions_queue.put(sys.exc_info()) 93 | max_iter = max_iter-1 94 | exceptions_queue.put("Done") 95 | 96 | def __del__(self): 97 | """Destructor cleaning up the FIFO from where it was created.""" 98 | if self.path: 99 | try: 100 | os.remove(self.path) 101 | except OSError: 102 | pass 103 | -------------------------------------------------------------------------------- /tests/host_tools/newpid_cloner.c: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /* 5 | * This is a very simple tool, used by the testing system to clone/exec into 6 | * the jailer. 7 | * All it does is 8 | * - clone() into a new PID namespace, then 9 | * - have the child process exec() into the binary received via command line, 10 | * and 11 | * - have the parent process print the child PID to stdout. 12 | * 13 | * Usage: ./newpid_cloner ... 14 | * Example: ./newpid_cloner /bin/firecracker --api-sock /var/run/fire.sock 15 | * 16 | */ 17 | 18 | #define _GNU_SOURCE 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | 27 | #define CHILD_STACK_SIZE 4096 28 | 29 | 30 | int child_main(void *arg) { 31 | char **argv = (char**)arg; 32 | execv(argv[0], argv); 33 | } 34 | 35 | int main(int argc, char *const argv[]) { 36 | 37 | char child_stack[CHILD_STACK_SIZE]; 38 | int child_pid = child_pid = clone( 39 | child_main, 40 | (char*)child_stack + CHILD_STACK_SIZE, 41 | CLONE_NEWPID, 42 | ((char **)argv) + 1 43 | ); 44 | 45 | printf("%d", child_pid); 46 | return (child_pid != -1) ? 0 : errno; 47 | } 48 | -------------------------------------------------------------------------------- /tests/host_tools/proc.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Utility functions for interacting with the processor.""" 4 | import re 5 | from framework import utils 6 | 7 | 8 | def proc_type(): 9 | """Obtain the model processor on a Linux system.""" 10 | cmd = "cat /proc/cpuinfo" 11 | result = utils.run_cmd(cmd) 12 | lines = result.stdout.strip().splitlines() 13 | for line in lines: 14 | if "model name" in line: 15 | return re.sub(".*model name.*:", "", line, 1) 16 | 17 | cmd = "uname -m" 18 | result = utils.run_cmd(cmd).stdout.strip() 19 | if "aarch64" in result: 20 | return "ARM" 21 | return "" 22 | -------------------------------------------------------------------------------- /tests/host_tools/readmem.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // This is a balloon device helper tool, which allocates an amount of 5 | // memory, given as the first starting parameter, and then tries to find 6 | // 4 consecutive occurences of an integer, given as the second starting 7 | // parameter, in that memory chunk. The program returns 1 if it succeeds 8 | // in finding these occurences, 0 otherwise. After performing a deflate 9 | // operation on the balloon device, we run this program with the second 10 | // starting parameter equal to `1`, which is the value we are using to 11 | // write in memory when dirtying it with `fillmem`. If the memory is 12 | // indeed scrubbed, we won't be able to find any 4 consecutive occurences 13 | // of the integer `1` in newly allocated memory. 14 | 15 | #define _GNU_SOURCE 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #define MB (1024 * 1024) 23 | 24 | 25 | int read_mem(int mb_count, int value) { 26 | int i; 27 | char *ptr = NULL; 28 | int *cur = NULL; 29 | int buf[4] = { value }; 30 | 31 | do { 32 | ptr = mmap( 33 | NULL, 34 | mb_count * MB * sizeof(char), 35 | PROT_READ | PROT_WRITE, 36 | MAP_ANONYMOUS | MAP_PRIVATE, 37 | -1, 38 | 0 39 | ); 40 | } while (ptr == MAP_FAILED); 41 | 42 | cur = (int *) ptr; 43 | // We will go through all the memory allocated with an `int` pointer, 44 | // so we have to divide the amount of bytes available by the size of 45 | // `int`. Furthermore, we compare 4 `int`s at a time, so we will 46 | // divide the upper limit of the loop by 4 and also increment the index 47 | // by 4. 48 | for (i = 0; i < (mb_count * MB * sizeof(char)) / (4 * sizeof(int)); i += 4) { 49 | if (memcmp(cur, buf, 4 * sizeof(int)) == 0) { 50 | return 1; 51 | } 52 | } 53 | 54 | return 0; 55 | } 56 | 57 | 58 | int main(int argc, char *const argv[]) { 59 | 60 | if (argc != 3) { 61 | printf("Usage: ./readmem mb_count value\n"); 62 | return -1; 63 | } 64 | 65 | int mb_count = atoi(argv[1]); 66 | int value = atoi(argv[2]); 67 | 68 | return read_mem(mb_count, value); 69 | } 70 | -------------------------------------------------------------------------------- /tests/host_tools/snapshot_helper.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Utility functions for snapshot testing.""" 4 | from framework.utils import run_cmd 5 | 6 | 7 | # Merges layer on top of base. 8 | def merge_memory_bitmaps(base, layer, block_size=4096): 9 | """Merge a sparse layer on top of base.""" 10 | dd_command = 'dd bs={} if={} of={} conv=sparse,notrunc' 11 | dd_command = dd_command.format(block_size, layer, base) 12 | _ = run_cmd(dd_command) 13 | -------------------------------------------------------------------------------- /tests/integration_tests/build/test_binary_size.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests that check if the release binary sizes fall within expected size.""" 4 | 5 | import os 6 | import platform 7 | import pytest 8 | 9 | import host_tools.cargo_build as host 10 | 11 | MACHINE = platform.machine() 12 | """ Platform definition used to select the correct size target""" 13 | 14 | SIZES_DICT = { 15 | "x86_64": { 16 | "FC_BINARY_SIZE_TARGET": 2067944, 17 | "JAILER_BINARY_SIZE_TARGET": 1439512, 18 | "FC_BINARY_SIZE_LIMIT": 2171516, 19 | "JAILER_BINARY_SIZE_LIMIT": 1511488, 20 | }, 21 | "aarch64": { 22 | "FC_BINARY_SIZE_TARGET": 1958512, 23 | "JAILER_BINARY_SIZE_TARGET": 1338312, 24 | "FC_BINARY_SIZE_LIMIT": 2000000, 25 | "JAILER_BINARY_SIZE_LIMIT": 1511488, 26 | } 27 | } 28 | 29 | FC_BINARY_SIZE_TARGET = SIZES_DICT[MACHINE]["FC_BINARY_SIZE_TARGET"] 30 | """Firecracker target binary size in bytes""" 31 | 32 | FC_BINARY_SIZE_LIMIT = SIZES_DICT[MACHINE]["FC_BINARY_SIZE_LIMIT"] 33 | """Firecracker maximum binary size in bytes""" 34 | 35 | JAILER_BINARY_SIZE_TARGET = SIZES_DICT[MACHINE]["JAILER_BINARY_SIZE_TARGET"] 36 | """Jailer target binary size in bytes""" 37 | 38 | JAILER_BINARY_SIZE_LIMIT = SIZES_DICT[MACHINE]["JAILER_BINARY_SIZE_LIMIT"] 39 | """Jailer maximum binary size in bytes""" 40 | 41 | BINARY_SIZE_TOLERANCE = 0.05 42 | """Tolerance of 5% allowed for binary size""" 43 | 44 | 45 | @pytest.mark.timeout(500) 46 | def test_binary_sizes(): 47 | """Test if the sizes of the release binaries are within expected ranges.""" 48 | fc_binary, jailer_binary = host.get_firecracker_binaries() 49 | 50 | check_binary_size("firecracker", fc_binary, FC_BINARY_SIZE_TARGET, 51 | BINARY_SIZE_TOLERANCE, FC_BINARY_SIZE_LIMIT) 52 | 53 | check_binary_size("jailer", jailer_binary, JAILER_BINARY_SIZE_TARGET, 54 | BINARY_SIZE_TOLERANCE, JAILER_BINARY_SIZE_LIMIT) 55 | 56 | 57 | def check_binary_size(name, binary_path, size_target, tolerance, limit): 58 | """Check if the specified binary falls within the expected range.""" 59 | # Get the size of the release binary. 60 | binary_size = os.path.getsize(binary_path) 61 | 62 | # Get the name of the variable that needs updating. 63 | namespace = globals() 64 | size_target_name = [name for name in namespace if namespace[name] 65 | is size_target][0] 66 | 67 | # Compute concrete binary size difference. 68 | delta_size = size_target - binary_size 69 | 70 | binary_low_msg = ( 71 | 'Current {} binary size of {} bytes is below the target' 72 | ' of {} bytes with {} bytes.\n' 73 | 'Update the {} threshold' 74 | .format(name, binary_size, size_target, delta_size, size_target_name) 75 | ) 76 | 77 | assert binary_size > size_target * (1 - tolerance), binary_low_msg 78 | 79 | binary_high_msg = ( 80 | 'Current {} binary size of {} bytes is above the target' 81 | ' of {} bytes with {} bytes.\n' 82 | .format(name, binary_size, size_target, -delta_size) 83 | ) 84 | 85 | assert binary_size < size_target * (1 + tolerance), binary_high_msg 86 | 87 | binary_limit_msg = ( 88 | 'Current {} binary size of {} bytes is above the limit' 89 | ' of {} bytes with {} bytes.\n' 90 | .format(name, binary_size, limit, binary_size - limit) 91 | ) 92 | 93 | assert binary_size < limit, binary_limit_msg 94 | -------------------------------------------------------------------------------- /tests/integration_tests/build/test_clippy.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests ensuring codebase style compliance for Rust and Python.""" 4 | 5 | 6 | import platform 7 | import pytest 8 | import framework.utils as utils 9 | 10 | SUCCESS_CODE = 0 11 | MACHINE = platform.machine() 12 | TARGETS = ["{}-unknown-linux-gnu".format(MACHINE), 13 | "{}-unknown-linux-musl".format(MACHINE)] 14 | 15 | 16 | @pytest.mark.parametrize( 17 | "target", 18 | TARGETS 19 | ) 20 | def test_rust_clippy(target): 21 | """Fails if clippy generates any error, warnings are ignored.""" 22 | utils.run_cmd( 23 | 'cargo clippy --target {} --all --profile test' 24 | ' -- -D warnings'.format(target)) 25 | -------------------------------------------------------------------------------- /tests/integration_tests/build/test_unittests.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """A test that ensures that all unit tests pass at integration time.""" 4 | 5 | import platform 6 | 7 | import pytest 8 | 9 | import host_tools.cargo_build as host # pylint:disable=import-error 10 | 11 | MACHINE = platform.machine() 12 | TARGETS = ["{}-unknown-linux-gnu".format(MACHINE), 13 | "{}-unknown-linux-musl".format(MACHINE)] 14 | 15 | 16 | @pytest.mark.parametrize( 17 | "target", 18 | TARGETS 19 | ) 20 | def test_unittests(test_session_root_path, target): 21 | """Run unit and doc tests for all supported targets.""" 22 | extra_args = "--release --target {} ".format(target) 23 | 24 | if "musl" in target and MACHINE == "x86_64": 25 | pytest.skip("On x86_64 with musl target unit tests" 26 | " are already run as part of testing" 27 | " code-coverage.") 28 | 29 | host.cargo_test( 30 | test_session_root_path, 31 | extra_args=extra_args 32 | ) 33 | -------------------------------------------------------------------------------- /tests/integration_tests/functional/test_cmd_line_start.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests microvm start with configuration file as command line parameter.""" 4 | 5 | import os 6 | import re 7 | 8 | from retry.api import retry_call 9 | 10 | import pytest 11 | 12 | import framework.utils as utils 13 | 14 | 15 | def _configure_vm_from_json(test_microvm, vm_config_file): 16 | """Configure a microvm using a file sent as command line parameter. 17 | 18 | Create resources needed for the configuration of the microvm and 19 | set as configuration file a copy of the file that was passed as 20 | parameter to this helper function. 21 | """ 22 | test_microvm.create_jailed_resource(test_microvm.kernel_file, 23 | create_jail=True) 24 | test_microvm.create_jailed_resource(test_microvm.rootfs_file, 25 | create_jail=True) 26 | 27 | # vm_config_file is the source file that keeps the desired vmm 28 | # configuration. vm_config_path is the configuration file we 29 | # create inside the jail, such that it can be accessed by 30 | # firecracker after it starts. 31 | vm_config_path = os.path.join(test_microvm.path, 32 | os.path.basename(vm_config_file)) 33 | with open(vm_config_file) as f1: 34 | with open(vm_config_path, "w") as f2: 35 | for line in f1: 36 | f2.write(line) 37 | test_microvm.create_jailed_resource(vm_config_path, create_jail=True) 38 | test_microvm.jailer.extra_args = {'config-file': os.path.basename( 39 | vm_config_file)} 40 | 41 | 42 | @pytest.mark.parametrize( 43 | "vm_config_file", 44 | ["framework/vm_config.json"] 45 | ) 46 | def test_config_start_with_api(test_microvm_with_ssh, vm_config_file): 47 | """Test if a microvm configured from file boots successfully.""" 48 | test_microvm = test_microvm_with_ssh 49 | 50 | _configure_vm_from_json(test_microvm, vm_config_file) 51 | test_microvm.spawn() 52 | 53 | response = test_microvm.machine_cfg.get() 54 | assert test_microvm.api_session.is_status_ok(response.status_code) 55 | 56 | 57 | @pytest.mark.parametrize( 58 | "vm_config_file", 59 | ["framework/vm_config.json"] 60 | ) 61 | def test_config_start_no_api(test_microvm_with_ssh, vm_config_file): 62 | """Test microvm start when API server thread is disabled.""" 63 | test_microvm = test_microvm_with_ssh 64 | 65 | _configure_vm_from_json(test_microvm, vm_config_file) 66 | test_microvm.jailer.extra_args.update({'no-api': None}) 67 | 68 | test_microvm.spawn() 69 | 70 | # Get Firecracker PID so we can check the names of threads. 71 | firecracker_pid = test_microvm.jailer_clone_pid 72 | 73 | # Get names of threads in Firecracker. 74 | cmd = 'ps -T --no-headers -p {} | awk \'{{print $5}}\''.format( 75 | firecracker_pid 76 | ) 77 | 78 | # Retry running 'ps' in case it failed to list the firecracker process 79 | # The regex matches any expression that contains 'firecracker' and does 80 | # not contain 'fc_api' 81 | retry_call( 82 | utils.search_output_from_cmd, 83 | fkwargs={ 84 | "cmd": cmd, 85 | "find_regex": re.compile("^(?!.*fc_api)(?:.*)?firecracker", 86 | re.DOTALL) 87 | }, 88 | exceptions=RuntimeError, 89 | tries=10, 90 | delay=1) 91 | -------------------------------------------------------------------------------- /tests/integration_tests/functional/test_concurrency.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Ensure multiple microVMs work correctly when spawned simultaneously.""" 4 | 5 | from framework import decorators 6 | 7 | import host_tools.network as net_tools 8 | 9 | NO_OF_MICROVMS = 20 10 | 11 | 12 | @decorators.test_context('ssh', NO_OF_MICROVMS) 13 | def test_run_concurrency(test_multiple_microvms, network_config): 14 | """Check we can spawn multiple microvms.""" 15 | microvms = test_multiple_microvms 16 | 17 | for i in range(NO_OF_MICROVMS): 18 | microvm = microvms[i] 19 | _ = _configure_and_run(microvm, { 20 | "config": network_config, "iface_id": str(i) 21 | }) 22 | # We check that the vm is running by testing that the ssh does 23 | # not time out. 24 | _ = net_tools.SSHConnection(microvm.ssh_config) 25 | 26 | 27 | def _configure_and_run(microvm, network_info): 28 | """Auxiliary function for configuring and running microVM.""" 29 | microvm.spawn() 30 | 31 | # Machine configuration specified in the SLA. 32 | config = { 33 | 'vcpu_count': 1, 34 | 'mem_size_mib': 128 35 | } 36 | 37 | microvm.basic_config(**config) 38 | 39 | _tap, _, _ = microvm.ssh_network_config( 40 | network_info["config"], 41 | network_info["iface_id"] 42 | ) 43 | 44 | microvm.start() 45 | return _tap 46 | -------------------------------------------------------------------------------- /tests/integration_tests/functional/test_error_code.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests scenarios for Firecracker kvm exit handling.""" 4 | 5 | import os 6 | import platform 7 | import pytest 8 | from framework.utils import wait_process_termination 9 | 10 | 11 | @pytest.mark.skipif( 12 | platform.machine() != "aarch64", 13 | reason="The error code returned on aarch64 will not be returned on x86 " 14 | "under the same conditions." 15 | ) 16 | def test_enosys_error_code(test_microvm_with_initrd): 17 | """Test that ENOSYS error is caught and firecracker exits gracefully.""" 18 | # On aarch64 we trigger this error by adding to initrd a C program that 19 | # maps a file into memory and then tries to load the content from an 20 | # offset in the file bigger than its length into a register asm volatile 21 | # ("ldr %0, [%1], 4" : "=r" (ret), "+r" (buf)); 22 | vm = test_microvm_with_initrd 23 | vm.jailer.daemonize = False 24 | vm.spawn() 25 | vm.memory_monitor = None 26 | 27 | vm.initrd_file = os.path.join(vm.path, "fsfiles/", "initrd_enosys.img") 28 | vm.basic_config( 29 | add_root_device=False, 30 | vcpu_count=1, 31 | boot_args='console=ttyS0 reboot=k panic=1 pci=off', 32 | use_initrd=True 33 | ) 34 | 35 | vm.start() 36 | 37 | # Check if FC process is closed 38 | wait_process_termination(vm.jailer_clone_pid) 39 | 40 | log_data = vm.log_data 41 | assert "Received ENOSYS error because KVM failed to emulate " \ 42 | "an instruction." in log_data 43 | assert "Vmm is stopping." in log_data 44 | -------------------------------------------------------------------------------- /tests/integration_tests/functional/test_initrd.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests for initrd.""" 4 | 5 | from framework.microvm import Serial 6 | 7 | INITRD_FILESYSTEM = "rootfs" 8 | 9 | 10 | def test_microvm_initrd_with_serial( 11 | test_microvm_with_initrd): 12 | """Check microvm started with an inird has / mounted as rootfs.""" 13 | vm = test_microvm_with_initrd 14 | vm.jailer.daemonize = False 15 | vm.spawn() 16 | vm.memory_monitor = None 17 | 18 | vm.basic_config( 19 | add_root_device=False, 20 | vcpu_count=1, 21 | boot_args='console=ttyS0 reboot=k panic=1 pci=off', 22 | use_initrd=True 23 | ) 24 | 25 | vm.start() 26 | serial = Serial(vm) 27 | serial.open() 28 | serial.rx(token='login: ') 29 | serial.tx("root") 30 | 31 | serial.rx(token='Password: ') 32 | serial.tx("root") 33 | 34 | serial.rx(token='# ') 35 | 36 | serial.tx("findmnt /") 37 | serial.rx( 38 | token=f"/ {INITRD_FILESYSTEM} {INITRD_FILESYSTEM}") 39 | -------------------------------------------------------------------------------- /tests/integration_tests/functional/test_max_devices.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests scenario for adding the maximum number of devices to a microVM.""" 4 | 5 | import platform 6 | import pytest 7 | import host_tools.network as net_tools 8 | 9 | # IRQs are available from 5 to 23, so the maximum number of devices 10 | # supported at the same time is 19. 11 | MAX_DEVICES_ATTACHED = 19 12 | 13 | 14 | @pytest.mark.skipif( 15 | platform.machine() != "x86_64", 16 | reason="Firecracker supports 24 IRQs on x86_64." 17 | ) 18 | def test_attach_maximum_devices(test_microvm_with_ssh, network_config): 19 | """Test attaching maximum number of devices to the microVM.""" 20 | test_microvm = test_microvm_with_ssh 21 | test_microvm.spawn() 22 | 23 | # Set up a basic microVM. 24 | test_microvm.basic_config() 25 | 26 | # Add (`MAX_DEVICES_ATTACHED` - 1) devices because the rootfs 27 | # has already been configured in the `basic_config()`function. 28 | guest_ips = [] 29 | for i in range(MAX_DEVICES_ATTACHED - 1): 30 | # Create tap before configuring interface. 31 | _tap, _host_ip, guest_ip = test_microvm.ssh_network_config( 32 | network_config, 33 | str(i) 34 | ) 35 | guest_ips.append(guest_ip) 36 | 37 | test_microvm.start() 38 | 39 | # Test that network devices attached are operational. 40 | for i in range(MAX_DEVICES_ATTACHED - 1): 41 | test_microvm.ssh_config['hostname'] = guest_ips[i] 42 | ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config) 43 | # Verify if guest can run commands. 44 | exit_code, _, _ = ssh_connection.execute_command("sync") 45 | assert exit_code == 0 46 | 47 | 48 | @pytest.mark.skipif( 49 | platform.machine() != "x86_64", 50 | reason="Firecracker supports 24 IRQs on x86_64." 51 | ) 52 | def test_attach_too_many_devices(test_microvm_with_ssh, network_config): 53 | """Test attaching to a microVM more devices than available IRQs.""" 54 | test_microvm = test_microvm_with_ssh 55 | test_microvm.spawn() 56 | 57 | # Set up a basic microVM. 58 | test_microvm.basic_config() 59 | 60 | # Add `MAX_DEVICES_ATTACHED` network devices on top of the 61 | # already configured rootfs. 62 | for i in range(MAX_DEVICES_ATTACHED): 63 | # Create tap before configuring interface. 64 | _tap, _host_ip, _guest_ip = test_microvm.ssh_network_config( 65 | network_config, 66 | str(i) 67 | ) 68 | 69 | # Attempting to start a microVM with more than 70 | # `MAX_DEVICES_ATTACHED` devices should fail. 71 | response = test_microvm.actions.put(action_type='InstanceStart') 72 | assert test_microvm.api_session.is_status_bad_request(response.status_code) 73 | assert "no more IRQs are available" in response.text 74 | -------------------------------------------------------------------------------- /tests/integration_tests/functional/test_max_vcpus.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests scenario for microvms with max vcpus(32).""" 4 | import host_tools.network as net_tools # pylint: disable=import-error 5 | 6 | MAX_VCPUS = 32 7 | 8 | 9 | def test_max_vcpus(test_microvm_with_ssh, network_config): 10 | """Test if all configured guest vcpus are online.""" 11 | microvm = test_microvm_with_ssh 12 | microvm.spawn() 13 | 14 | # Configure a microVM with 32 vCPUs. 15 | microvm.basic_config(vcpu_count=MAX_VCPUS) 16 | _tap, _, _ = microvm.ssh_network_config(network_config, '1') 17 | 18 | microvm.start() 19 | 20 | ssh_connection = net_tools.SSHConnection(microvm.ssh_config) 21 | cmd = "nproc" 22 | _, stdout, stderr = ssh_connection.execute_command(cmd) 23 | assert stderr.read() == "" 24 | assert int(stdout.read()) == MAX_VCPUS 25 | -------------------------------------------------------------------------------- /tests/integration_tests/functional/test_metrics.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests the metrics system.""" 4 | 5 | import os 6 | import json 7 | import host_tools.logging as log_tools 8 | 9 | 10 | def test_flush_metrics(test_microvm_with_api): 11 | """Check the `FlushMetrics` vmm action.""" 12 | microvm = test_microvm_with_api 13 | microvm.spawn() 14 | microvm.basic_config() 15 | 16 | # Configure metrics system. 17 | metrics_fifo_path = os.path.join(microvm.path, 'metrics_fifo') 18 | metrics_fifo = log_tools.Fifo(metrics_fifo_path) 19 | 20 | response = microvm.metrics.put( 21 | metrics_path=microvm.create_jailed_resource(metrics_fifo.path) 22 | ) 23 | assert microvm.api_session.is_status_no_content(response.status_code) 24 | 25 | microvm.start() 26 | 27 | res = metrics_fifo.sequential_reader(1) 28 | metrics = json.loads(res[0]) 29 | 30 | exp_keys = [ 31 | 'utc_timestamp_ms', 32 | 'api_server', 33 | 'balloon', 34 | 'block', 35 | 'get_api_requests', 36 | 'i8042', 37 | 'latencies_us', 38 | 'logger', 39 | 'mmds', 40 | 'net', 41 | 'patch_api_requests', 42 | 'put_api_requests', 43 | 'rtc', 44 | 'seccomp', 45 | 'vcpu', 46 | 'vmm', 47 | 'uart', 48 | 'signals', 49 | 'vsock' 50 | ] 51 | 52 | assert set(metrics.keys()) == set(exp_keys) 53 | 54 | microvm.flush_metrics(metrics_fifo) 55 | -------------------------------------------------------------------------------- /tests/integration_tests/functional/test_net.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests for the net device.""" 4 | import time 5 | 6 | import framework.utils as utils 7 | import host_tools.network as net_tools 8 | 9 | # The iperf version to run this tests with 10 | IPERF_BINARY = 'iperf3' 11 | 12 | 13 | def test_high_ingress_traffic(test_microvm_with_ssh, network_config): 14 | """Run iperf rx with high UDP traffic.""" 15 | test_microvm = test_microvm_with_ssh 16 | test_microvm.spawn() 17 | 18 | test_microvm.basic_config() 19 | 20 | # Create tap before configuring interface. 21 | tap, _host_ip, guest_ip = test_microvm.ssh_network_config( 22 | network_config, 23 | '1' 24 | ) 25 | # Set the tap's tx queue len to 5. This increases the probability 26 | # of filling the tap under high ingress traffic. 27 | tap.set_tx_queue_len(5) 28 | 29 | # Start the microvm. 30 | test_microvm.start() 31 | 32 | # Start iperf3 server on the guest. 33 | ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config) 34 | ssh_connection.execute_command('{} -sD\n'.format(IPERF_BINARY)) 35 | time.sleep(1) 36 | 37 | # Start iperf3 client on the host. Send 1Gbps UDP traffic. 38 | # If the net device breaks, iperf will freeze. We have to use a timeout. 39 | utils.run_cmd( 40 | 'timeout 30 {} {} -c {} -u -V -b 1000000000 -t 30'.format( 41 | test_microvm.jailer.netns_cmd_prefix(), 42 | IPERF_BINARY, 43 | guest_ip, 44 | ), 45 | ignore_return_code=True 46 | ) 47 | 48 | # Check if the high ingress traffic broke the net interface. 49 | # If the net interface still works we should be able to execute 50 | # ssh commands. 51 | exit_code, _, _ = ssh_connection.execute_command('echo success\n') 52 | assert exit_code == 0 53 | -------------------------------------------------------------------------------- /tests/integration_tests/functional/test_rtc.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Check the well functioning af the RTC device on aarch64 platforms.""" 4 | import re 5 | import platform 6 | import pytest 7 | 8 | import framework.utils as utils 9 | from host_tools.network import SSHConnection 10 | 11 | DMESG_LOG_REGEX = r'rtc-pl031\s+(\d+).rtc: setting system clock to' 12 | 13 | 14 | @pytest.mark.skipif( 15 | platform.machine() != "aarch64", 16 | reason="RTC exists only on aarch64." 17 | ) 18 | def test_rtc(test_microvm_with_ssh, network_config): 19 | """Test RTC functionality on aarch64.""" 20 | vm = test_microvm_with_ssh 21 | vm.spawn() 22 | vm.memory_monitor = None 23 | vm.basic_config() 24 | _tap, _, _ = vm.ssh_network_config(network_config, '1') 25 | 26 | vm.start() 27 | conn = SSHConnection(vm.ssh_config) 28 | 29 | # check that the kernel creates an rtcpl031 base device. 30 | _, stdout, _ = conn.execute_command("dmesg") 31 | rtc_log = re.findall(DMESG_LOG_REGEX, stdout.read()) 32 | assert rtc_log is not None 33 | 34 | _, stdout, _ = conn.execute_command("stat /dev/rtc0") 35 | assert "character special file" in stdout.read() 36 | 37 | _, host_stdout, _ = utils.run_cmd("date +%s") 38 | _, guest_stdout, _ = conn.execute_command("date +%s") 39 | assert abs(int(guest_stdout.read()) - int(host_stdout)) < 5 40 | -------------------------------------------------------------------------------- /tests/integration_tests/functional/test_shut_down.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests scenarios for shutting down Firecracker/VM.""" 4 | import os 5 | import time 6 | import json 7 | import platform 8 | 9 | import framework.utils as utils 10 | 11 | import host_tools.logging as log_tools 12 | import host_tools.network as net_tools # pylint: disable=import-error 13 | 14 | 15 | def test_reboot(test_microvm_with_ssh, network_config): 16 | """Test reboot from guest kernel.""" 17 | test_microvm = test_microvm_with_ssh 18 | test_microvm.jailer.daemonize = False 19 | test_microvm.spawn() 20 | 21 | # We don't need to monitor the memory for this test because we are 22 | # just rebooting and the process dies before pmap gets the RSS. 23 | test_microvm.memory_monitor = None 24 | 25 | # Set up the microVM with 4 vCPUs, 256 MiB of RAM, 0 network ifaces, and 26 | # a root file system with the rw permission. The network interfaces is 27 | # added after we get a unique MAC and IP. 28 | test_microvm.basic_config(vcpu_count=4) 29 | _tap, _, _ = test_microvm.ssh_network_config(network_config, '1') 30 | 31 | # Configure metrics system. 32 | metrics_fifo_path = os.path.join(test_microvm.path, 'metrics_fifo') 33 | metrics_fifo = log_tools.Fifo(metrics_fifo_path) 34 | response = test_microvm.metrics.put( 35 | metrics_path=test_microvm.create_jailed_resource(metrics_fifo.path) 36 | ) 37 | assert test_microvm.api_session.is_status_no_content(response.status_code) 38 | 39 | test_microvm.start() 40 | 41 | # Get Firecracker PID so we can count the number of threads. 42 | firecracker_pid = test_microvm.jailer_clone_pid 43 | 44 | # Get number of threads in Firecracker 45 | cmd = 'ps -o nlwp {} | tail -1 | awk \'{{print $1}}\''.format( 46 | firecracker_pid 47 | ) 48 | _, stdout, _ = utils.run_cmd(cmd) 49 | nr_of_threads = stdout.rstrip() 50 | assert int(nr_of_threads) == 6 51 | 52 | # Consume existing metrics 53 | lines = metrics_fifo.sequential_reader(100) 54 | assert len(lines) == 1 55 | # Rebooting Firecracker sends an exit event and should gracefully kill. 56 | # the instance. 57 | ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config) 58 | 59 | ssh_connection.execute_command("reboot") 60 | 61 | while True: 62 | # Pytest's timeout will kill the test even if the loop doesn't exit. 63 | try: 64 | os.kill(firecracker_pid, 0) 65 | time.sleep(0.01) 66 | except OSError: 67 | break 68 | 69 | # Consume existing metrics 70 | lines = metrics_fifo.sequential_reader(100) 71 | assert len(lines) == 1 72 | 73 | if platform.machine() != "x86_64": 74 | log_data = test_microvm.log_data 75 | assert "Received KVM_SYSTEM_EVENT: type: 2, event: 0" in log_data 76 | assert "Vmm is stopping." in log_data 77 | 78 | # Make sure that the FC process was not killed by a seccomp fault 79 | assert json.loads(lines[0])["seccomp"]["num_faults"] == 0 80 | -------------------------------------------------------------------------------- /tests/integration_tests/performance/test_process_startup_time.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Test that the process startup time up to socket bind is within spec.""" 4 | 5 | import json 6 | import os 7 | import platform 8 | import time 9 | 10 | import host_tools.logging as log_tools 11 | 12 | MAX_STARTUP_TIME_CPU_US = {'x86_64': 5500, 'aarch64': 2600} 13 | """ The maximum acceptable startup time in CPU us. """ 14 | # TODO: Keep a `current` startup time in S3 and validate we don't regress 15 | 16 | 17 | def test_startup_time(test_microvm_with_api): 18 | """Check the startup time for jailer and Firecracker up to socket bind.""" 19 | microvm = test_microvm_with_api 20 | microvm.spawn() 21 | 22 | microvm.basic_config(vcpu_count=2, mem_size_mib=1024) 23 | 24 | # Configure metrics. 25 | metrics_fifo_path = os.path.join(microvm.path, 'metrics_fifo') 26 | metrics_fifo = log_tools.Fifo(metrics_fifo_path) 27 | 28 | response = microvm.metrics.put( 29 | metrics_path=microvm.create_jailed_resource(metrics_fifo.path) 30 | ) 31 | assert microvm.api_session.is_status_no_content(response.status_code) 32 | 33 | microvm.start() 34 | time.sleep(0.4) 35 | 36 | # The metrics fifo should be at index 1. 37 | # Since metrics are flushed at InstanceStart, the first line will suffice. 38 | lines = metrics_fifo.sequential_reader(1) 39 | metrics = json.loads(lines[0]) 40 | startup_time_us = metrics['api_server']['process_startup_time_us'] 41 | cpu_startup_time_us = metrics['api_server']['process_startup_time_cpu_us'] 42 | 43 | print('Process startup time is: {} us ({} CPU us)' 44 | .format(startup_time_us, cpu_startup_time_us)) 45 | 46 | assert cpu_startup_time_us > 0 47 | assert cpu_startup_time_us <= MAX_STARTUP_TIME_CPU_US[platform.machine()] 48 | -------------------------------------------------------------------------------- /tests/integration_tests/security/demo_seccomp/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "demo_seccomp" 5 | version = "0.1.0" 6 | dependencies = [ 7 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 8 | "seccomp 0.1.0", 9 | ] 10 | 11 | [[package]] 12 | name = "libc" 13 | version = "0.2.66" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | 16 | [[package]] 17 | name = "seccomp" 18 | version = "0.1.0" 19 | dependencies = [ 20 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 21 | ] 22 | 23 | [metadata] 24 | "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" 25 | -------------------------------------------------------------------------------- /tests/integration_tests/security/demo_seccomp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "demo_seccomp" 3 | version = "0.1.0" 4 | authors = ["Amazon Firecracker team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | libc = ">=0.2.39" 9 | 10 | seccomp = { path = "../../../../src/seccomp" } 11 | 12 | [workspace] 13 | -------------------------------------------------------------------------------- /tests/integration_tests/security/demo_seccomp/src/bin/demo_advanced_jailer.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | mod seccomp_rules; 4 | 5 | use std::convert::TryInto; 6 | use std::env::args; 7 | use std::os::unix::process::CommandExt; 8 | use std::process::{Command, Stdio}; 9 | 10 | use seccomp::{ 11 | SeccompAction, SeccompCmpArgLen, SeccompCmpOp, SeccompCondition, SeccompFilter, SeccompRule, 12 | }; 13 | use seccomp_rules::*; 14 | 15 | fn main() { 16 | let args: Vec = args().collect(); 17 | let exec_file = &args[1]; 18 | let mut filter = SeccompFilter::new(vec![].into_iter().collect(), SeccompAction::Trap).unwrap(); 19 | 20 | // Adds required rules. 21 | let mut all_rules = rust_required_rules(); 22 | all_rules.extend(jailer_required_rules()); 23 | 24 | // Adds rule to allow the harmless demo Firecracker. 25 | all_rules.push(( 26 | libc::SYS_write, 27 | vec![SeccompRule::new( 28 | vec![ 29 | SeccompCondition::new( 30 | 0, 31 | SeccompCmpArgLen::DWORD, 32 | SeccompCmpOp::Eq, 33 | libc::STDOUT_FILENO as u64, 34 | ) 35 | .unwrap(), 36 | SeccompCondition::new(2, SeccompCmpArgLen::QWORD, SeccompCmpOp::Eq, 14).unwrap(), 37 | ], 38 | SeccompAction::Allow, 39 | )], 40 | )); 41 | 42 | all_rules 43 | .into_iter() 44 | .try_for_each(|(syscall_number, rules)| filter.add_rules(syscall_number, rules)) 45 | .unwrap(); 46 | 47 | // Loads filters. 48 | SeccompFilter::apply(filter.try_into().unwrap()).unwrap(); 49 | 50 | Command::new(exec_file) 51 | .stdin(Stdio::inherit()) 52 | .stdout(Stdio::inherit()) 53 | .stderr(Stdio::inherit()) 54 | .exec(); 55 | } 56 | -------------------------------------------------------------------------------- /tests/integration_tests/security/demo_seccomp/src/bin/demo_basic_jailer.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | mod seccomp_rules; 4 | 5 | use std::convert::TryInto; 6 | use std::env::args; 7 | use std::os::unix::process::CommandExt; 8 | use std::process::{Command, Stdio}; 9 | 10 | use seccomp::{SeccompAction, SeccompFilter}; 11 | use seccomp_rules::*; 12 | 13 | fn main() { 14 | let args: Vec = args().collect(); 15 | let exec_file = &args[1]; 16 | 17 | let mut filter = SeccompFilter::new(vec![].into_iter().collect(), SeccompAction::Trap).unwrap(); 18 | 19 | // Adds required rules. 20 | let mut all_rules = rust_required_rules(); 21 | all_rules.extend(jailer_required_rules()); 22 | all_rules 23 | .into_iter() 24 | .try_for_each(|(syscall_number, rules)| filter.add_rules(syscall_number, rules)) 25 | .unwrap(); 26 | 27 | // Loads filters generated by the context. 28 | SeccompFilter::apply(filter.try_into().unwrap()).unwrap(); 29 | 30 | Command::new(exec_file) 31 | .stdin(Stdio::inherit()) 32 | .stdout(Stdio::inherit()) 33 | .stderr(Stdio::inherit()) 34 | .exec(); 35 | } 36 | -------------------------------------------------------------------------------- /tests/integration_tests/security/demo_seccomp/src/bin/demo_harmless.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | fn main() { 4 | unsafe { 5 | // Harmless print to standard output. 6 | libc::syscall(libc::SYS_write, libc::STDOUT_FILENO, "Hello, world!\n", 14); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/integration_tests/security/demo_seccomp/src/bin/demo_malicious.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | fn main() { 4 | unsafe { 5 | // In this example, the malicious component is outputting to standard input. 6 | libc::syscall(libc::SYS_write, libc::STDIN_FILENO, "Hello, world!\n", 14); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/integration_tests/security/demo_seccomp/src/bin/demo_panic.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | fn main() { 4 | unsafe { 5 | // Simulate a Firecracker panic by aborting. 6 | // The Firecracker build is configured with panic = "abort". 7 | unsafe { libc::abort() }; 8 | } 9 | } -------------------------------------------------------------------------------- /tests/integration_tests/security/demo_seccomp/src/bin/seccomp_rules/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use seccomp::{allow_syscall, allow_syscall_if, SyscallRuleSet}; 4 | 5 | /// Returns a list of rules that allow syscalls required for running a rust program. 6 | pub fn rust_required_rules() -> Vec { 7 | vec![ 8 | allow_syscall(libc::SYS_exit_group), 9 | allow_syscall(libc::SYS_futex), 10 | allow_syscall(libc::SYS_munmap), 11 | allow_syscall(libc::SYS_rt_sigaction), 12 | allow_syscall(libc::SYS_rt_sigprocmask), 13 | allow_syscall(libc::SYS_sigaltstack), 14 | allow_syscall(libc::SYS_tkill), 15 | ] 16 | } 17 | 18 | /// Returns a list of rules that allow syscalls required for executing another program. 19 | pub fn jailer_required_rules() -> Vec { 20 | vec![ 21 | allow_syscall(libc::SYS_execve), 22 | allow_syscall(libc::SYS_mmap), 23 | allow_syscall(libc::SYS_mprotect), 24 | #[cfg(target_arch = "x86_64")] 25 | allow_syscall(libc::SYS_arch_prctl), 26 | allow_syscall(libc::SYS_set_tid_address), 27 | #[cfg(target_arch = "x86_64")] 28 | allow_syscall(libc::SYS_readlink), 29 | #[cfg(target_arch = "x86_64")] 30 | allow_syscall(libc::SYS_open), 31 | allow_syscall(libc::SYS_read), 32 | allow_syscall(libc::SYS_close), 33 | allow_syscall(libc::SYS_brk), 34 | allow_syscall(libc::SYS_sched_getaffinity), 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /tests/integration_tests/security/test_sec_audit.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests ensuring security vulnerabilities are not present in dependencies.""" 4 | 5 | 6 | import os 7 | import platform 8 | import pytest 9 | 10 | import framework.utils as utils 11 | 12 | 13 | @pytest.mark.skipif( 14 | platform.machine() != "x86_64", 15 | reason="The audit is based on cargo.lock which " 16 | "is identical on all platforms" 17 | ) 18 | def test_cargo_audit(): 19 | """Fail if there are crates with security vulnerabilities.""" 20 | cargo_lock_path = os.path.normpath( 21 | os.path.join( 22 | os.path.dirname(os.path.realpath(__file__)), 23 | '../../../Cargo.lock') 24 | ) 25 | 26 | # Run command and raise exception if non-zero return code 27 | utils.run_cmd('cargo audit -q -f {}'.format(cargo_lock_path)) 28 | -------------------------------------------------------------------------------- /tests/integration_tests/security/test_ssbd_mitigation.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests Speculative Store Bypass mitigations in jailer/Firecracker.""" 4 | 5 | from framework.utils import run_cmd 6 | 7 | 8 | def test_ssbd_mitigation(test_microvm_with_initrd): 9 | """Test that SSBD mitigation is enabled.""" 10 | vm = test_microvm_with_initrd 11 | vm.jailer.daemonize = False 12 | vm.spawn() 13 | vm.memory_monitor = None 14 | 15 | vm.basic_config( 16 | add_root_device=False, 17 | vcpu_count=1, 18 | boot_args='console=ttyS0 reboot=k panic=1 pci=off', 19 | use_initrd=True 20 | ) 21 | 22 | vm.start() 23 | 24 | cmd = 'ps -T --no-headers -p {} | awk \'{{print $2}}\''.format( 25 | vm.jailer_clone_pid 26 | ) 27 | process = run_cmd(cmd) 28 | threads_out_lines = process.stdout.splitlines() 29 | for tid in threads_out_lines: 30 | # Verify each thread's status 31 | cmd = 'cat /proc/{}/status | grep Speculation_Store_Bypass'.format(tid) 32 | _, output, _ = run_cmd(cmd) 33 | assert "thread force mitigated" in output or \ 34 | "globally mitigated" in output 35 | -------------------------------------------------------------------------------- /tests/integration_tests/style/test_python.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests ensuring codebase style compliance for Python.""" 4 | 5 | import framework.utils as utils 6 | 7 | 8 | def test_python_style(): 9 | """Fail if there's misbehaving Python style in the test system.""" 10 | # List of linter commands that should be executed for each file 11 | linter_cmds = [ 12 | # Pylint 13 | 'python3 -m pylint --jobs=0 --persistent=no --score=no ' \ 14 | '--output-format=colorized --attr-rgx="[a-z_][a-z0-9_]{1,30}$" ' \ 15 | '--argument-rgx="[a-z_][a-z0-9_]{1,35}$" ' \ 16 | '--variable-rgx="[a-z_][a-z0-9_]{1,30}$" --disable=' \ 17 | 'bad-continuation,fixme,too-many-instance-attributes,import-error,' \ 18 | 'too-many-locals,too-many-arguments', 19 | 20 | # pycodestyle 21 | 'python3 -m pycodestyle --show-pep8 --show-source --exclude=../build', 22 | 23 | # pydocstyle 24 | "python3 -m pydocstyle --explain --source"] 25 | 26 | # Get all *.py files from the project 27 | python_files = utils.get_files_from( 28 | find_path="..", 29 | pattern="*.py", 30 | exclude_names=["build"]) 31 | 32 | # Assert if somehow no python files were found 33 | assert len(python_files) != 0 34 | 35 | # Run commands 36 | utils.run_cmd_list_async([ 37 | f"{cmd} {fname}" for cmd in linter_cmds for fname in python_files 38 | ]) 39 | -------------------------------------------------------------------------------- /tests/integration_tests/style/test_swagger.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Tests ensuring codebase style compliance for the OpenAPI specification.""" 4 | 5 | import os 6 | import yaml 7 | import framework.utils as utils 8 | 9 | 10 | def check_yaml_style(yaml_spec): 11 | """Check if the swagger definition is correctly formatted.""" 12 | with open(yaml_spec, 'r') as file_stream: 13 | try: 14 | yaml.safe_load(file_stream) 15 | # pylint: disable=broad-except 16 | except Exception as exception: 17 | print(str(exception)) 18 | 19 | 20 | def validate_swagger(swagger_spec): 21 | """Fail if OpenApi spec is not followed.""" 22 | validate_cmd = 'swagger-cli validate {}'.format(swagger_spec) 23 | retcode, stdout, _ = utils.run_cmd(validate_cmd) 24 | 25 | # Verify validity. 26 | assert "is valid" in stdout 27 | assert retcode == 0 28 | 29 | 30 | def test_firecracker_swagger(): 31 | """Fail if Firecracker swagger specification is malformed.""" 32 | swagger_spec = os.path.normpath( 33 | os.path.join(os.getcwd(), '../src/api_server/swagger/firecracker.yaml') 34 | ) 35 | check_yaml_style(swagger_spec) 36 | validate_swagger(swagger_spec) 37 | -------------------------------------------------------------------------------- /tests/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | ; Omit verbose tracebacks, since they tend to pollute the output. 3 | addopts = --tb=short 4 | 5 | ; Overwrite the default norecurseirs, which includes 'build'. 6 | norecursedirs = .* 7 | 8 | ; Default timeout for tests. can be overwritten at finer grained levels. 9 | timeout = 300 10 | 11 | ; Set the cache dir location to our build dir, so we don't litter the source 12 | ; tree. 13 | cache_dir = ../build/pytest_cache 14 | 15 | ; Set logger format and level 16 | log_level = INFO 17 | log_format = %(asctime)s %(name)s: %(levelname)s %(message)s 18 | ; Uncomment the line below to get the live logger output 19 | ;log_cli = true 20 | -------------------------------------------------------------------------------- /tools/devctr/Dockerfile.aarch64: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | # TODO: use a multi-stage build to reduce the download size when updating this container. 4 | # The Rust toolchain layer will get updated most frequently, but we could keep the system 5 | # dependencies layer intact for much longer. 6 | 7 | ARG RUST_TOOLCHAIN="1.46.0" 8 | ARG TINI_VERSION_TAG="v0.18.0" 9 | ARG TMP_BUILD_DIR=/tmp/build 10 | ARG FIRECRACKER_SRC_DIR="/firecracker" 11 | ARG FIRECRACKER_BUILD_DIR="$FIRECRACKER_SRC_DIR/build" 12 | ARG CARGO_REGISTRY_DIR="$FIRECRACKER_BUILD_DIR/cargo_registry" 13 | ARG CARGO_GIT_REGISTRY_DIR="$FIRECRACKER_BUILD_DIR/cargo_git_registry" 14 | ARG DEBIAN_FRONTEND=noninteractive 15 | 16 | ENV CARGO_HOME=/usr/local/rust 17 | ENV RUSTUP_HOME=/usr/local/rust 18 | ENV PATH="$PATH:$CARGO_HOME/bin" 19 | 20 | # Install system dependencies 21 | # 22 | RUN apt-get update \ 23 | && apt-get -y install --no-install-recommends \ 24 | binutils-dev \ 25 | cmake \ 26 | curl \ 27 | file \ 28 | g++ \ 29 | gcc \ 30 | git \ 31 | iperf3 \ 32 | iproute2 \ 33 | jq \ 34 | libbfd-dev \ 35 | libcurl4-openssl-dev \ 36 | libdw-dev \ 37 | libfdt-dev \ 38 | libiberty-dev \ 39 | libssl-dev \ 40 | lsof \ 41 | make \ 42 | net-tools \ 43 | openssh-client \ 44 | pkgconf \ 45 | python \ 46 | python3 \ 47 | python3-dev \ 48 | python3-pip \ 49 | python3-venv \ 50 | zlib1g-dev \ 51 | screen \ 52 | tzdata \ 53 | && python3 -m pip install \ 54 | setuptools \ 55 | wheel \ 56 | && python3 -m pip install \ 57 | boto3 \ 58 | dataclasses \ 59 | nsenter \ 60 | psutil \ 61 | pycodestyle \ 62 | pydocstyle \ 63 | pylint \ 64 | pytest \ 65 | pytest-timeout \ 66 | pyyaml \ 67 | requests \ 68 | requests-unixsocket \ 69 | retry \ 70 | typing-extensions \ 71 | && rm -rf /var/lib/apt/lists/* 72 | 73 | # Install the Rust toolchain 74 | # 75 | RUN mkdir "$TMP_BUILD_DIR" \ 76 | && curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" \ 77 | && rustup target add aarch64-unknown-linux-musl \ 78 | && cd "$TMP_BUILD_DIR" \ 79 | && cargo install cargo-kcov \ 80 | && cargo kcov --print-install-kcov-sh | sh \ 81 | && rm -rf "$CARGO_HOME/registry" \ 82 | && ln -s "$CARGO_REGISTRY_DIR" "$CARGO_HOME/registry" \ 83 | && rm -rf "$CARGO_HOME/git" \ 84 | && ln -s "$CARGO_GIT_REGISTRY_DIR" "$CARGO_HOME/git" \ 85 | && cd / \ 86 | && rm -rf "$TMP_BUILD_DIR" 87 | 88 | # Add the tini init binary. 89 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION_TAG}/tini-static-arm64 /sbin/tini 90 | RUN chmod +x /sbin/tini 91 | 92 | WORKDIR "$FIRECRACKER_SRC_DIR" 93 | ENTRYPOINT ["/sbin/tini", "--"] 94 | -------------------------------------------------------------------------------- /tools/update-credits.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | cd "$(dirname "$BASH_SOURCE")/.." 5 | 6 | # see also ".mailmap" for how email addresses and names are deduplicated 7 | 8 | { 9 | cat <<-'EOH' 10 | 11 | # Firecracker Credits and Thanks 12 | 13 | (This file is autogenerated using [update-credits.sh](tools/update-credits.sh).) 14 | 15 | Firecracker started with the code from the Chrome OS Virtual Machine Monitor 16 | ([crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/)), a VMM 17 | written in Rust with a focus on safety and security. Thanks go to: 18 | 19 | * [Zach Reizner](https://github.com/zachreizner) 20 | * [Dylan Reid](https://github.com/dgreid) 21 | * [Daniel Verkamp](https://github.com/danielverkamp) 22 | * [Stephen Barber](https://github.com/smibarber) 23 | * [Chirantan Ekbote](https://github.com/jynnantonix) 24 | * [Jason D. Clinton](https://github.com/jclinton) 25 | * Sonny Rao 26 | 27 | 28 | Contributors to the Firecracker repository: 29 | EOH 30 | echo 31 | git log --format='* %aN <%aE>' | LC_ALL=C.UTF-8 sort -uf 32 | } > CREDITS.md 33 | --------------------------------------------------------------------------------