From e8bd9890f37ecf7943140ece1e5c509c24b3c37d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 13 Nov 2022 20:54:47 +0100 Subject: [PATCH 1/6] Replace unmaintained `actions-rs/cargo` action with separate problem matcher Also: Run `cargo check` with `--all-targets --all`. --- .github/workflows/ci.yml | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a7ab020..220c99ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,10 +15,9 @@ jobs: steps: - uses: actions/checkout@v2 + - uses: r7kamura/rust-problem-matchers@v1.1.0 - name: "Run `cargo check`" - uses: actions-rs/cargo@v1 - with: - command: check + run: cargo check --all-targets --all test: name: Test @@ -53,34 +52,24 @@ jobs: - name: "Print QEMU Version" run: qemu-system-x86_64 --version + - uses: r7kamura/rust-problem-matchers@v1.1.0 - name: Run api tests - uses: actions-rs/cargo@v1 - with: - command: test - args: -p bootloader_api - + run: cargo test -p bootloader_api - name: Run integration tests - uses: actions-rs/cargo@v1 - with: - command: test + run: cargo test fmt: name: Check Formatting runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Run `cargo fmt --all -- --check` - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check + - uses: r7kamura/rust-problem-matchers@v1.1.0 + - run: cargo fmt --all -- --check clippy: name: Clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Run `cargo clippy` - uses: actions-rs/cargo@v1 - with: - command: clippy + - uses: r7kamura/rust-problem-matchers@v1.1.0 + - run: cargo clippy From 9c606eeec28ca3470336d2ee9fbdd63560d994e8 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 13 Nov 2022 20:55:42 +0100 Subject: [PATCH 2/6] Set up caching on CI --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 220c99ab..be8896cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,8 @@ jobs: steps: - uses: actions/checkout@v2 + - run: cargo --version --verbose + - uses: Swatinem/rust-cache@v2 - uses: r7kamura/rust-problem-matchers@v1.1.0 - name: "Run `cargo check`" run: cargo check --all-targets --all @@ -31,6 +33,8 @@ jobs: steps: - uses: actions/checkout@v2 + - run: cargo --version --verbose + - uses: Swatinem/rust-cache@v2 # install QEMU - name: Install QEMU (Linux) @@ -71,5 +75,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - run: cargo --version --verbose + - uses: Swatinem/rust-cache@v2 - uses: r7kamura/rust-problem-matchers@v1.1.0 - run: cargo clippy From daa554d6118b43d91c77118489d67f33dc7467db Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 13 Nov 2022 21:05:28 +0100 Subject: [PATCH 3/6] Run `cargo clippy` with `--all --all-targets` too --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be8896cc..644eb2a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,4 +78,4 @@ jobs: - run: cargo --version --verbose - uses: Swatinem/rust-cache@v2 - uses: r7kamura/rust-problem-matchers@v1.1.0 - - run: cargo clippy + - run: cargo clippy --all --all-targets From 2c8a630323960304ee73df2ddf8fc39e0aaf41d2 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 13 Nov 2022 21:06:33 +0100 Subject: [PATCH 4/6] Run `cargo clippy --fix` --- api/build.rs | 2 +- api/src/config.rs | 7 +++---- bios/boot_sector/src/main.rs | 2 +- bios/boot_sector/src/mbr.rs | 2 +- bios/stage-2/src/disk.rs | 2 +- bios/stage-2/src/fat.rs | 2 +- bios/stage-2/src/memory_map.rs | 2 +- src/fat.rs | 2 +- src/gpt.rs | 6 +++--- src/lib.rs | 2 +- src/mbr.rs | 2 +- uefi/src/main.rs | 2 +- 12 files changed, 16 insertions(+), 17 deletions(-) diff --git a/api/build.rs b/api/build.rs index 142c2e72..a5e4b72d 100644 --- a/api/build.rs +++ b/api/build.rs @@ -43,7 +43,7 @@ fn main() { ); } - fs::write(&dest_path, code).unwrap(); + fs::write(dest_path, code).unwrap(); println!("cargo:rerun-if-changed=build.rs"); let version_major: u16 = env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(); diff --git a/api/src/config.rs b/api/src/config.rs index eadd74d2..3eb4fb87 100644 --- a/api/src/config.rs +++ b/api/src/config.rs @@ -132,15 +132,14 @@ impl BootloaderConfig { Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()), }, ); - let buf = concat_106_9( + + concat_106_9( buf, match minimum_framebuffer_width { Option::None => [0; 9], Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()), }, - ); - - buf + ) } /// Tries to deserialize a config byte array that was created using [`Self::serialize`]. diff --git a/bios/boot_sector/src/main.rs b/bios/boot_sector/src/main.rs index aaacc224..f7290aea 100644 --- a/bios/boot_sector/src/main.rs +++ b/bios/boot_sector/src/main.rs @@ -54,7 +54,7 @@ pub extern "C" fn first_stage(disk_number: u16) { start_lba += u64::from(sectors); number_of_sectors -= u32::from(sectors); - target_addr = target_addr + u32::from(sectors) * 512; + target_addr += u32::from(sectors) * 512; if number_of_sectors == 0 { break; diff --git a/bios/boot_sector/src/mbr.rs b/bios/boot_sector/src/mbr.rs index 93fc08cb..39b1c756 100644 --- a/bios/boot_sector/src/mbr.rs +++ b/bios/boot_sector/src/mbr.rs @@ -11,7 +11,7 @@ pub(crate) fn get_partition(partitions_raw: &[u8], index: usize) -> PartitionTab let offset = index * ENTRY_SIZE; let buffer = partitions_raw.get(offset..).unwrap_or_fail(b'c'); - let bootable_raw = *buffer.get(0).unwrap_or_fail(b'd'); + let bootable_raw = *buffer.first().unwrap_or_fail(b'd'); let bootable = bootable_raw == 0x80; let partition_type = *buffer.get(4).unwrap_or_fail(b'e'); diff --git a/bios/stage-2/src/disk.rs b/bios/stage-2/src/disk.rs index 263d16a7..ec83eec3 100644 --- a/bios/stage-2/src/disk.rs +++ b/bios/stage-2/src/disk.rs @@ -47,7 +47,7 @@ impl Read for DiskAccess { start_lba += u64::from(sectors); number_of_sectors -= u64::from(sectors); - target_addr = target_addr + u32::from(sectors) * 512; + target_addr += u32::from(sectors) * 512; if number_of_sectors == 0 { break; diff --git a/bios/stage-2/src/fat.rs b/bios/stage-2/src/fat.rs index cfe9e24e..a04a550a 100644 --- a/bios/stage-2/src/fat.rs +++ b/bios/stage-2/src/fat.rs @@ -391,7 +391,7 @@ impl<'a> RawDirectoryEntry<'a> { } else { fn slice_to_string(slice: &[u8]) -> Result<&str, ()> { const SKIP_SPACE: u8 = 0x20; - let mut iter = slice.into_iter().copied(); + let mut iter = slice.iter().copied(); match iter.position(|c| c != SKIP_SPACE) { Some(start_idx) => { let end_idx = diff --git a/bios/stage-2/src/memory_map.rs b/bios/stage-2/src/memory_map.rs index a980d9b6..c3ba6c97 100644 --- a/bios/stage-2/src/memory_map.rs +++ b/bios/stage-2/src/memory_map.rs @@ -47,7 +47,7 @@ pub unsafe fn query_memory_map() -> Result<&'static mut [E820MemoryRegion], ()> if buf_written_len != 0 { let buf = &buf[..buf_written_len]; - let (&base_raw, rest) = split_array_ref(&buf); + let (&base_raw, rest) = split_array_ref(buf); let (&len_raw, rest) = split_array_ref(rest); let (&kind_raw, rest) = split_array_ref(rest); let acpi_extended_raw: [u8; 4] = rest.try_into().unwrap_or_default(); diff --git a/src/fat.rs b/src/fat.rs index c592b7ad..d94a57b6 100644 --- a/src/fat.rs +++ b/src/fat.rs @@ -24,7 +24,7 @@ pub fn create_fat_filesystem( .write(true) .create(true) .truncate(true) - .open(&out_fat_path) + .open(out_fat_path) .unwrap(); let fat_size_padded_and_rounded = ((needed_size + 1024 * 64 - 1) / MB + 1) * MB; fat_file.set_len(fat_size_padded_and_rounded).unwrap(); diff --git a/src/gpt.rs b/src/gpt.rs index 8adecbfd..73959578 100644 --- a/src/gpt.rs +++ b/src/gpt.rs @@ -12,11 +12,11 @@ pub fn create_gpt_disk(fat_image: &Path, out_gpt_path: &Path) -> anyhow::Result< .truncate(true) .read(true) .write(true) - .open(&out_gpt_path) + .open(out_gpt_path) .with_context(|| format!("failed to create GPT file at `{}`", out_gpt_path.display()))?; // set file size - let partition_size: u64 = fs::metadata(&fat_image) + let partition_size: u64 = fs::metadata(fat_image) .context("failed to read metadata of fat image")? .len(); let disk_size = partition_size + 1024 * 64; // for GPT headers @@ -61,7 +61,7 @@ pub fn create_gpt_disk(fat_image: &Path, out_gpt_path: &Path) -> anyhow::Result< disk.seek(io::SeekFrom::Start(start_offset)) .context("failed to seek to start offset")?; io::copy( - &mut File::open(&fat_image).context("failed to open FAT image")?, + &mut File::open(fat_image).context("failed to open FAT image")?, &mut disk, ) .context("failed to copy FAT image to GPT disk")?; diff --git a/src/lib.rs b/src/lib.rs index 47260d10..11d1ebf0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,7 +127,7 @@ impl UefiBoot { files.insert(KERNEL_FILE_NAME, self.kernel.as_path()); let out_file = NamedTempFile::new().context("failed to create temp file")?; - fat::create_fat_filesystem(files, &out_file.path()) + fat::create_fat_filesystem(files, out_file.path()) .context("failed to create UEFI FAT filesystem")?; Ok(out_file) diff --git a/src/mbr.rs b/src/mbr.rs index 8f7dfade..f5608cc1 100644 --- a/src/mbr.rs +++ b/src/mbr.rs @@ -68,7 +68,7 @@ pub fn create_mbr_disk( .truncate(true) .read(true) .write(true) - .open(&out_mbr_path) + .open(out_mbr_path) .with_context(|| { format!( "failed to create MBR disk image at `{}`", diff --git a/uefi/src/main.rs b/uefi/src/main.rs index f86d8334..087f6ff5 100644 --- a/uefi/src/main.rs +++ b/uefi/src/main.rs @@ -143,7 +143,7 @@ fn load_kernel_file(image: Handle, st: &SystemTable) -> Option<&'static mu fn load_kernel_file_from_disk(image: Handle, st: &SystemTable) -> Option<&'static mut [u8]> { let file_system_raw = { - let ref this = st.boot_services(); + let this = st.boot_services(); let loaded_image = this .open_protocol::( OpenProtocolParams { From 8665c59a94d1fab06ca1819c6218430ec7b16d23 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 13 Nov 2022 21:28:35 +0100 Subject: [PATCH 5/6] Resolve remaining clippy warnings --- bios/boot_sector/src/main.rs | 9 +++++++-- bios/common/src/lib.rs | 4 ++++ bios/common/src/racy_cell.rs | 5 +++++ bios/stage-2/src/fat.rs | 6 +++--- bios/stage-2/src/main.rs | 10 ++++++---- bios/stage-3/src/main.rs | 6 ++++-- bios/stage-4/src/main.rs | 2 +- common/src/legacy_memory_region.rs | 9 +++++++++ common/src/logger.rs | 3 ++- tests/lto.rs | 2 +- uefi/src/main.rs | 4 ++-- uefi/src/memory_descriptor.rs | 2 +- x86_64-stage-4.json | 3 ++- 13 files changed, 47 insertions(+), 18 deletions(-) diff --git a/bios/boot_sector/src/main.rs b/bios/boot_sector/src/main.rs index f7290aea..251495aa 100644 --- a/bios/boot_sector/src/main.rs +++ b/bios/boot_sector/src/main.rs @@ -2,7 +2,10 @@ #![no_main] #![warn(unsafe_op_in_unsafe_fn)] -use core::{arch::global_asm, slice}; +use core::{ + arch::{asm, global_asm}, + slice, +}; use fail::{print_char, UnwrapOrFail}; global_asm!(include_str!("boot.s")); @@ -73,5 +76,7 @@ pub extern "C" fn first_stage(disk_number: u16) { print_char(b'R'); } - loop {} + loop { + unsafe { asm!("hlt") } + } } diff --git a/bios/common/src/lib.rs b/bios/common/src/lib.rs index 160890f5..c55c6d31 100644 --- a/bios/common/src/lib.rs +++ b/bios/common/src/lib.rs @@ -62,3 +62,7 @@ pub struct E820MemoryRegion { pub region_type: u32, pub acpi_extended_attributes: u32, } + +pub fn hlt() { + unsafe { core::arch::asm!("hlt") }; +} diff --git a/bios/common/src/racy_cell.rs b/bios/common/src/racy_cell.rs index 087aae27..d2797f7b 100644 --- a/bios/common/src/racy_cell.rs +++ b/bios/common/src/racy_cell.rs @@ -7,6 +7,11 @@ impl RacyCell { Self(UnsafeCell::new(v)) } + /// Gets a mutable pointer to the wrapped value. + /// + /// ## Safety + /// Ensure that the access is unique (no active references, mutable or not). + #[allow(clippy::mut_from_ref)] pub unsafe fn get_mut(&self) -> &mut T { unsafe { &mut *self.0.get() } } diff --git a/bios/stage-2/src/fat.rs b/bios/stage-2/src/fat.rs index a04a550a..79eff1ac 100644 --- a/bios/stage-2/src/fat.rs +++ b/bios/stage-2/src/fat.rs @@ -28,7 +28,7 @@ struct Bpb { fat_size_16: u16, total_sectors_32: u32, fat_size_32: u32, - root_cluster: u32, + _root_cluster: u32, } impl Bpb { @@ -71,7 +71,7 @@ impl Bpb { fat_size_16, total_sectors_32, fat_size_32, - root_cluster, + _root_cluster: root_cluster, } } @@ -209,7 +209,7 @@ impl FileSystem { ) -> impl Iterator> + 'a { match self.bpb.fat_type() { FatType::Fat32 => { - self.bpb.root_cluster; + // self.bpb.root_cluster; unimplemented!(); } FatType::Fat12 | FatType::Fat16 => { diff --git a/bios/stage-2/src/main.rs b/bios/stage-2/src/main.rs index 0553f2ed..b8c85bd7 100644 --- a/bios/stage-2/src/main.rs +++ b/bios/stage-2/src/main.rs @@ -7,7 +7,7 @@ use crate::{ copy_to_protected_mode, enter_protected_mode_and_jump_to_stage_3, enter_unreal_mode, }, }; -use bootloader_x86_64_bios_common::{BiosFramebufferInfo, BiosInfo, Region}; +use bootloader_x86_64_bios_common::{hlt, BiosFramebufferInfo, BiosInfo, Region}; use byteorder::{ByteOrder, LittleEndian}; use core::{fmt::Write as _, slice}; use disk::AlignedArrayBuffer; @@ -50,12 +50,12 @@ fn start(disk_number: u16, partition_table_start: *const u8) -> ! { let mut entries = [PartitionTableEntry::empty(); MAX_ENTRIES]; let raw = unsafe { slice::from_raw_parts(partition_table_start, ENTRY_SIZE * MAX_ENTRIES) }; - for idx in 0..MAX_ENTRIES { + for (idx, entry) in entries.iter_mut().enumerate() { let offset = idx * ENTRY_SIZE; let partition_type = PartitionType::from_mbr_tag_byte(raw[offset + 4]); let lba = LittleEndian::read_u32(&raw[offset + 8..]); let len = LittleEndian::read_u32(&raw[offset + 12..]); - entries[idx] = PartitionTableEntry::new(partition_type, lba, len); + *entry = PartitionTableEntry::new(partition_type, lba, len); } entries }; @@ -146,7 +146,9 @@ fn start(disk_number: u16, partition_table_start: *const u8) -> ! { enter_protected_mode_and_jump_to_stage_3(STAGE_3_DST, &mut info); - loop {} + loop { + hlt(); + } } fn load_file( diff --git a/bios/stage-3/src/main.rs b/bios/stage-3/src/main.rs index c3d692d2..15d30f8b 100644 --- a/bios/stage-3/src/main.rs +++ b/bios/stage-3/src/main.rs @@ -3,7 +3,7 @@ #![deny(unsafe_op_in_unsafe_fn)] use crate::screen::Writer; -use bootloader_x86_64_bios_common::BiosInfo; +use bootloader_x86_64_bios_common::{hlt, BiosInfo}; use core::{arch::asm, fmt::Write as _}; mod gdt; @@ -24,7 +24,9 @@ pub extern "C" fn _start(info: &mut BiosInfo) { gdt::LONG_MODE_GDT.load(); enter_long_mode_and_jump_to_stage_4(info); - loop {} + loop { + hlt(); + } } #[no_mangle] diff --git a/bios/stage-4/src/main.rs b/bios/stage-4/src/main.rs index ce2237f2..bd44b396 100644 --- a/bios/stage-4/src/main.rs +++ b/bios/stage-4/src/main.rs @@ -227,7 +227,7 @@ fn detect_rsdp() -> Option { } } -#[cfg(all(not(test), target_os = "none"))] +#[cfg(target_os = "none")] #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { unsafe { diff --git a/common/src/legacy_memory_region.rs b/common/src/legacy_memory_region.rs index dfd3546a..8177a6a6 100644 --- a/common/src/legacy_memory_region.rs +++ b/common/src/legacy_memory_region.rs @@ -11,6 +11,10 @@ pub trait LegacyMemoryRegion: Copy + core::fmt::Debug { fn start(&self) -> PhysAddr; /// Returns the size of the region in bytes. fn len(&self) -> u64; + /// Returns whether this region is empty. + fn is_empty(&self) -> bool { + self.len() == 0 + } /// Returns the type of the region, e.g. whether it is usable or reserved. fn kind(&self) -> MemoryRegionKind; @@ -81,6 +85,11 @@ where self.original.len() } + /// Returns whether this memory map is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + /// Returns the largest detected physical memory address. /// /// Useful for creating a mapping for all physical memory. diff --git a/common/src/logger.rs b/common/src/logger.rs index a008886c..7aea0bf4 100644 --- a/common/src/logger.rs +++ b/common/src/logger.rs @@ -51,7 +51,7 @@ fn get_char_raster(c: char) -> RasterizedChar { font_constants::CHAR_RASTER_HEIGHT, ) } - get(c).unwrap_or(get(BACKUP_CHAR).expect("Should get raster of backup char.")) + get(c).unwrap_or_else(|| get(BACKUP_CHAR).expect("Should get raster of backup char.")) } impl LockedLogger { @@ -62,6 +62,7 @@ impl LockedLogger { /// Force-unlocks the logger to prevent a deadlock. /// + /// ## Safety /// This method is not memory safe and should be only used when absolutely necessary. pub unsafe fn force_unlock(&self) { unsafe { self.0.force_unlock() }; diff --git a/tests/lto.rs b/tests/lto.rs index f7f4a47e..00cfe60f 100644 --- a/tests/lto.rs +++ b/tests/lto.rs @@ -5,7 +5,7 @@ use bootloader_test_runner::run_test_kernel; #[test] fn basic_boot() { // build test kernel manually to force-enable link-time optimization - let mut cmd = Command::new(std::env::var("CARGO").unwrap_or("cargo".into())); + let mut cmd = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into())); cmd.arg("build"); cmd.arg("-p").arg("test_kernel_lto"); cmd.arg("--target").arg("x86_64-unknown-none"); diff --git a/uefi/src/main.rs b/uefi/src/main.rs index 087f6ff5..e94bc00c 100644 --- a/uefi/src/main.rs +++ b/uefi/src/main.rs @@ -74,7 +74,7 @@ fn main_inner(image: Handle, mut st: SystemTable) -> Status { ) .unwrap(); - let kernel = load_kernel(image, &mut st); + let kernel = load_kernel(image, &st); let framebuffer = init_logger(&st, kernel.config); @@ -428,7 +428,7 @@ fn init_logger(st: &SystemTable, config: BootloaderConfig) -> Option ! { use core::arch::asm; diff --git a/uefi/src/memory_descriptor.rs b/uefi/src/memory_descriptor.rs index ac3b2ba3..dcf05a9c 100644 --- a/uefi/src/memory_descriptor.rs +++ b/uefi/src/memory_descriptor.rs @@ -8,7 +8,7 @@ pub struct UefiMemoryDescriptor(pub MemoryDescriptor); const PAGE_SIZE: u64 = 4096; -impl<'a> LegacyMemoryRegion for UefiMemoryDescriptor { +impl LegacyMemoryRegion for UefiMemoryDescriptor { fn start(&self) -> PhysAddr { PhysAddr::new(self.0.phys_start) } diff --git a/x86_64-stage-4.json b/x86_64-stage-4.json index 5ea3e7e5..976fd56d 100644 --- a/x86_64-stage-4.json +++ b/x86_64-stage-4.json @@ -17,5 +17,6 @@ }, "static-position-independent-executables": true, "target-pointer-width": "64", - "relocation-model": "static" + "relocation-model": "static", + "os": "none" } From 6caa68497abc3bbe6fe91c9c645684e56f05c1ce Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 13 Nov 2022 21:35:50 +0100 Subject: [PATCH 6/6] Switch checkout action to v3 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 644eb2a2..e5a3fb4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: timeout-minutes: 10 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: cargo --version --verbose - uses: Swatinem/rust-cache@v2 - uses: r7kamura/rust-problem-matchers@v1.1.0 @@ -32,7 +32,7 @@ jobs: timeout-minutes: 30 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: cargo --version --verbose - uses: Swatinem/rust-cache@v2 @@ -66,7 +66,7 @@ jobs: name: Check Formatting runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: r7kamura/rust-problem-matchers@v1.1.0 - run: cargo fmt --all -- --check @@ -74,7 +74,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: cargo --version --verbose - uses: Swatinem/rust-cache@v2 - uses: r7kamura/rust-problem-matchers@v1.1.0