diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c5ba4b..2f66922 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Rust toolchain - run: rustup show + run: rustup show active-toolchain || rustup toolchain install - name: clippy env: @@ -27,7 +27,7 @@ jobs: - name: Install dependencies run: | sudo apt install --yes make mtools parted - rustup show + rustup show active-toolchain || rustup toolchain install - name: Build UEFI application run: make diff --git a/.gitignore b/.gitignore index d50d40e..024f946 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -build -firmware -prefix -target +/build +/firmware +/target diff --git a/Makefile b/Makefile index 6df8e29..bd82fe8 100644 --- a/Makefile +++ b/Makefile @@ -1,30 +1,31 @@ -TARGET?=x86_64-unknown-uefi -export BASEDIR?=system76-firmware-update - -export LD=ld -export RUST_TARGET_PATH=$(CURDIR)/targets -BUILD=build/$(TARGET) - -QEMU?=qemu-system-x86_64 -QEMU_FLAGS=\ - -accel kvm \ - -M q35 \ - -m 1024 \ - -net none \ - -vga std \ - -bios /usr/share/OVMF/OVMF_CODE.fd -all: $(BUILD)/boot.img +# SPDX-License-Identifier: GPL-3.0-only +TARGET = x86_64-unknown-uefi +BUILD = build/$(TARGET) +QEMU = qemu-system-x86_64 +OVMF = /usr/share/OVMF + +export BASEDIR ?= system76-firmware-update + +all: $(BUILD)/boot.efi + +.PHONY: clean clean: cargo clean rm -rf build -update: - git submodule update --init --recursive --remote - cargo update - -qemu: $(BUILD)/boot.img - $(QEMU) $(QEMU_FLAGS) $< +.PHONY: qemu +qemu: $(BUILD)/boot.img $(OVMF)/OVMF_VARS.fd $(OVMF)/OVMF_CODE.fd + cp $(OVMF)/OVMF_CODE.fd target/ + cp $(OVMF)/OVMF_VARS.fd target/ + $(QEMU) -enable-kvm -M q35 -m 1024 -vga std \ + -chardev stdio,mux=on,id=debug \ + -device isa-serial,index=2,chardev=debug \ + -device isa-debugcon,iobase=0x402,chardev=debug \ + -drive if=pflash,format=raw,readonly=on,file=target/OVMF_CODE.fd \ + -drive if=pflash,format=raw,readonly=on,file=target/OVMF_VARS.fd \ + -net none \ + $< $(BUILD)/boot.img: $(BUILD)/efi.img dd if=/dev/zero of=$@.tmp bs=512 count=100352 @@ -45,8 +46,9 @@ $(BUILD)/efi.img: $(BUILD)/boot.efi res/* if [ -d firmware ]; then mcopy -i $@.tmp -s firmware ::$(BASEDIR); fi mv $@.tmp $@ -$(BUILD)/boot.efi: Cargo.lock Cargo.toml src/* src/*/* - mkdir -p $(BUILD) +.PHONY: $(BUILD)/boot.efi +$(BUILD)/boot.efi: + mkdir -p $(@D) cargo rustc \ --target $(TARGET) \ --release \ diff --git a/README.md b/README.md index 9095f9d..7864b79 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,6 @@ firmware-update expects the firmware images to have specific names: The mechanism used to apply updates depends on the firmware image: -- coreboot-based SBIOS: firmware-update flashes using [intel-spi](https://github.com/system76/intel-spi) -- System76 EC: firmware-update flashes using [ecflash](https://github.com/system76/ecflash) -- Proprietary: The vendor-provided tools are used +- coreboot-based system firmware: [intel-spi](https://github.com/system76/intel-spi) +- System76 EC: [ectool](https://github.com/system76/ec) +- Proprietary: Vendor-provided tools diff --git a/src/app/bios.rs b/src/app/bios.rs index 7c39496..97756a4 100644 --- a/src/app/bios.rs +++ b/src/app/bios.rs @@ -387,7 +387,9 @@ impl Component for BiosComponent { new_offset, new_size / 1024 ); - let slice = data.get(offset..offset + size).ok_or(Status::DEVICE_ERROR)?; + let slice = data + .get(offset..offset + size) + .ok_or(Status::DEVICE_ERROR)?; if slice.len() == new_slice.len() { new_slice.copy_from_slice(slice); @@ -407,7 +409,7 @@ impl Component for BiosComponent { area_name ); } - } else if areas.get(area_name).is_some() { + } else if areas.contains_key(area_name) { println!( "{}: found in old firmware, but not found in new firmware", area_name @@ -487,7 +489,9 @@ impl Component for BiosComponent { // Have coreboot reset the option table to the defaults. let mut cmos_options = cmos::CmosOptionTable::new(); - unsafe { cmos_options.invalidate_checksum(); } + unsafe { + cmos_options.invalidate_checksum(); + } } else { find(FIRMWARENSH)?; diff --git a/src/app/ec.rs b/src/app/ec.rs index 906b8ff..f9b0e2b 100644 --- a/src/app/ec.rs +++ b/src/app/ec.rs @@ -1,22 +1,23 @@ // SPDX-License-Identifier: GPL-3.0-only -use ecflash::{Ec, EcFile, EcFlash}; -use ectool::{timeout, Access, AccessLpcDirect, Firmware, SecurityState, Spi, SpiRom, SpiTarget, Timeout}; -use plain::Plain; -use std::prelude::*; -use std::uefi::{ - self, - reset::ResetType, -}; use core::cell::Cell; use core::ptr; use core::str; +use ecflash::{Ec, EcFile, EcFlash}; +use ectool::{ + timeout, Access, AccessLpcDirect, Firmware, SecurityState, Spi, SpiRom, SpiTarget, Timeout, +}; +use plain::Plain; +use std::prelude::*; +use std::uefi::{self, reset::ResetType}; use std::{ ffi::wstr, fs::{find, load}, }; -use super::{pci_read, shell, Component, EC2ROM, ECROM, ECTAG, FIRMWAREDIR, FIRMWARENSH, sideband::Sideband}; +use super::{ + pci_read, shell, sideband::Sideband, Component, EC2ROM, ECROM, ECTAG, FIRMWAREDIR, FIRMWARENSH, +}; pub struct UefiTimeout { duration: u64, @@ -47,7 +48,10 @@ impl Timeout for UefiTimeout { pub enum EcKind { Pang(ectool::Pmc, String), - System76(ectool::Ec>, ectool::Pmc), + System76( + ectool::Ec>, + ectool::Pmc, + ), Legacy(EcFlash), Unknown, } @@ -74,21 +78,21 @@ impl EcKind { } } - if system_version == "pang12" || system_version == "pang13" || system_version == "pang14" || - system_version == "pang15" { + if system_version == "pang12" + || system_version == "pang13" + || system_version == "pang14" + || system_version == "pang15" + { return EcKind::Pang( ectool::Pmc::new(0x62, UefiTimeout::new(100_000)), - system_version + system_version, ); } } if let Ok(access) = AccessLpcDirect::new(UefiTimeout::new(100_000)) { if let Ok(ec) = ectool::Ec::new(access) { - return EcKind::System76( - ec, - ectool::Pmc::new(0x62, UefiTimeout::new(100_000)) - ); + return EcKind::System76(ec, ectool::Pmc::new(0x62, UefiTimeout::new(100_000))); } } @@ -104,15 +108,15 @@ impl EcKind { EcKind::Pang(ref mut pmc, _system_version) => { let ecwr = pmc.acpi_read(0x80).unwrap_or(0); (ecwr & 0x01) == 0x01 - }, + } EcKind::System76(_ec, ref mut pmc) => { let adp = pmc.acpi_read(0x10).unwrap_or(0); (adp & 0x01) == 0x01 - }, + } EcKind::Legacy(ref mut ec) => { let adp = ec.get_param(0x10).unwrap_or(0); (adp & 0x01) == 0x01 - }, + } EcKind::Unknown => true, } } @@ -121,7 +125,7 @@ impl EcKind { match self { EcKind::Pang(_pmc, system_version) => { return system_version.clone(); - }, + } EcKind::System76(ec, _pmc) => { let data_size = ec.access().data_size(); let mut data = vec![0; data_size]; @@ -149,7 +153,7 @@ impl EcKind { Err(err) => { println!("Failed to read build time: {:?}", err); return String::new(); - }, + } } } @@ -160,16 +164,15 @@ impl EcKind { Err(err) => { println!("Failed to read build date: {:?}", err); return String::new(); - }, + } } } return format!( "20{:02}/{:02}/{:02}_{:02}:{:02}:{:02}", - ymd[0], ymd[1], ymd[2], - hms[0], hms[1], hms[2] + ymd[0], ymd[1], ymd[2], hms[0], hms[1], hms[2] ); - }, + } EcKind::System76(ec, _pmc) => { let data_size = ec.access().data_size(); let mut data = vec![0; data_size]; @@ -247,7 +250,7 @@ impl EcComponent { } else { "system76/lemp13-b".to_string() } - }, + } "N130ZU" => "system76/galp3-c".to_string(), "N140CU" => "system76/galp4".to_string(), "N150ZU" => "system76/darp5".to_string(), @@ -288,7 +291,7 @@ impl EcComponent { "system76/darp10".to_string() } } - }, + } "NV40Mx" | "NV40Mx-DV" | "NV40MJ" => "system76/galp5".to_string(), "NV4xPZ" => "system76/galp6".to_string(), "NV40RZ" => "system76/galp7".to_string(), @@ -489,7 +492,7 @@ pub unsafe fn security_unlock() -> core::result::Result<(), ectool::Error> { ResetType::Shutdown, Status(0), 0, - ptr::null() + ptr::null(), ); } }, @@ -746,7 +749,7 @@ impl Component for EcComponent { println!("{} Flash Error: {}", self.name(), status); Err(Status::DEVICE_ERROR) } - }, + } EcKind::System76(_ec, _pmc) => { // System76 EC requires reset to load new firmware requires_reset = true; @@ -792,8 +795,7 @@ impl Component for EcComponent { | uefi::fs::FILE_MODE_READ | uefi::fs::FILE_MODE_WRITE, 0, - ) - { + ) { Status::SUCCESS => { unsafe { let _ = ((*file).Close)(&mut *file); diff --git a/src/app/mapper.rs b/src/app/mapper.rs index 5f12ec7..f1934cd 100644 --- a/src/app/mapper.rs +++ b/src/app/mapper.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-3.0-only + use intel_spi::{Mapper, PhysicalAddress, VirtualAddress}; pub struct UefiMapper; diff --git a/src/app/mod.rs b/src/app/mod.rs index 2e06ad2..b9a8924 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -9,8 +9,8 @@ use std::prelude::*; use std::proto::Protocol; use std::uefi::reset::ResetType; use std::vars::{ - get_boot_current, get_boot_item, get_boot_next, get_boot_order, - set_boot_item, set_boot_next, set_boot_order, + get_boot_current, get_boot_item, get_boot_next, get_boot_order, set_boot_item, set_boot_next, + set_boot_order, }; use crate::display::{Display, Output, ScaledDisplay}; @@ -159,7 +159,13 @@ fn reset_dmi() -> Result<()> { ))?; let empty = []; - Result::from((uefi.RuntimeServices.SetVariable)(wname.as_ptr(), &guid, attributes, 0, empty.as_ptr()))?; + Result::from((uefi.RuntimeServices.SetVariable)( + wname.as_ptr(), + &guid, + attributes, + 0, + empty.as_ptr(), + ))?; } Ok(()) @@ -278,7 +284,9 @@ fn inner() -> Result<()> { // XXX: Probably better to check for HECI device. if cmos_options.me_state() { println!("Disabling CSME for writing SPI flash"); - unsafe { cmos_options.set_me_state(false); } + unsafe { + cmos_options.set_me_state(false); + } println!("System will reboot in 5 seconds"); let _ = (std::system_table().BootServices.Stall)(5_000_000); @@ -396,7 +404,12 @@ pub fn main() -> Result<()> { for i in 0..output.0.Mode.MaxMode { let mut mode_ptr = ::core::ptr::null_mut(); let mut mode_size = 0; - Result::from((output.0.QueryMode)(output.0, i, &mut mode_size, &mut mode_ptr))?; + Result::from((output.0.QueryMode)( + output.0, + i, + &mut mode_size, + &mut mode_ptr, + ))?; let mode = unsafe { &mut *mode_ptr }; let w = mode.HorizontalResolution; diff --git a/src/app/pci.rs b/src/app/pci.rs index 2616f79..8eb0bb4 100644 --- a/src/app/pci.rs +++ b/src/app/pci.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-3.0-only + use core::{mem, slice}; use hwio::{Io, Pio}; use std::prelude::*; diff --git a/src/app/sideband.rs b/src/app/sideband.rs index 18b9c0c..6a1bc93 100644 --- a/src/app/sideband.rs +++ b/src/app/sideband.rs @@ -1,6 +1,5 @@ -// Copyright 2018-2021 System76 -// // SPDX-License-Identifier: GPL-3.0-only +// Copyright 2018-2021 System76 use core::ptr; @@ -14,10 +13,13 @@ pub struct Sideband { pub addr: u64, } +#[allow(dead_code)] impl Sideband { pub unsafe fn new(sbreg_phys: usize) -> Self { // On UEFI, physical memory is identity mapped - Self { addr: sbreg_phys as u64 } + Self { + addr: sbreg_phys as u64, + } } #[must_use] diff --git a/src/image/mod.rs b/src/image/mod.rs index 0e43587..b3ea301 100644 --- a/src/image/mod.rs +++ b/src/image/mod.rs @@ -60,7 +60,11 @@ impl Image { } /// Create a new image from a boxed slice of colors - pub fn from_data(width: u32, height: u32, data: Box<[Color]>) -> core::result::Result { + pub fn from_data( + width: u32, + height: u32, + data: Box<[Color]>, + ) -> core::result::Result { if (width * height) as usize != data.len() { return Err( "not enough or too much data given compared to width and height".to_string(), diff --git a/src/io.rs b/src/io.rs index 34fb0ed..bf6af49 100644 --- a/src/io.rs +++ b/src/io.rs @@ -8,7 +8,11 @@ pub fn wait_key() -> Result { let uefi = std::system_table(); let mut index = 0; - Result::from((uefi.BootServices.WaitForEvent)(1, &uefi.ConsoleIn.WaitForKey, &mut index))?; + Result::from((uefi.BootServices.WaitForEvent)( + 1, + &uefi.ConsoleIn.WaitForKey, + &mut index, + ))?; let mut input = TextInputKey { ScanCode: 0, diff --git a/src/key.rs b/src/key.rs index 021b966..b12c3ea 100644 --- a/src/key.rs +++ b/src/key.rs @@ -7,7 +7,11 @@ pub fn raw_key() -> Result { let uefi = std::system_table(); let mut index = 0; - Result::from((uefi.BootServices.WaitForEvent)(1, &uefi.ConsoleIn.WaitForKey, &mut index))?; + Result::from((uefi.BootServices.WaitForEvent)( + 1, + &uefi.ConsoleIn.WaitForKey, + &mut index, + ))?; let mut key = TextInputKey { ScanCode: 0, diff --git a/src/text.rs b/src/text.rs index 5344f05..8a64713 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only +#![allow(clippy::missing_transmute_annotations)] + use core::ops::Deref; use core::{char, mem}; use orbclient::{Color, Renderer}; @@ -14,15 +16,15 @@ use crate::display::{Display, Output, ScaledDisplay}; #[repr(C)] #[allow(non_snake_case)] pub struct TextDisplay<'a> { - pub Reset: extern "win64" fn(&mut TextDisplay, bool) -> Status, - pub OutputString: extern "win64" fn(&mut TextDisplay, *const u16) -> Status, - pub TestString: extern "win64" fn(&mut TextDisplay, *const u16) -> Status, - pub QueryMode: extern "win64" fn(&mut TextDisplay, usize, &mut usize, &mut usize) -> Status, - pub SetMode: extern "win64" fn(&mut TextDisplay, usize) -> Status, - pub SetAttribute: extern "win64" fn(&mut TextDisplay, usize) -> Status, - pub ClearScreen: extern "win64" fn(&mut TextDisplay) -> Status, - pub SetCursorPosition: extern "win64" fn(&mut TextDisplay, usize, usize) -> Status, - pub EnableCursor: extern "win64" fn(&mut TextDisplay, bool) -> Status, + pub Reset: extern "efiapi" fn(&mut TextDisplay, bool) -> Status, + pub OutputString: extern "efiapi" fn(&mut TextDisplay, *const u16) -> Status, + pub TestString: extern "efiapi" fn(&mut TextDisplay, *const u16) -> Status, + pub QueryMode: extern "efiapi" fn(&mut TextDisplay, usize, &mut usize, &mut usize) -> Status, + pub SetMode: extern "efiapi" fn(&mut TextDisplay, usize) -> Status, + pub SetAttribute: extern "efiapi" fn(&mut TextDisplay, usize) -> Status, + pub ClearScreen: extern "efiapi" fn(&mut TextDisplay) -> Status, + pub SetCursorPosition: extern "efiapi" fn(&mut TextDisplay, usize, usize) -> Status, + pub EnableCursor: extern "efiapi" fn(&mut TextDisplay, bool) -> Status, pub Mode: &'static TextOutputMode, pub mode: Box, @@ -33,22 +35,22 @@ pub struct TextDisplay<'a> { pub display: ScaledDisplay<'a>, } -extern "win64" fn reset(_output: &mut TextDisplay, _extra: bool) -> Status { +extern "efiapi" fn reset(_output: &mut TextDisplay, _extra: bool) -> Status { Status(0) } -extern "win64" fn output_string(output: &mut TextDisplay, string: *const u16) -> Status { +extern "efiapi" fn output_string(output: &mut TextDisplay, string: *const u16) -> Status { unsafe { output.write(string); } Status(0) } -extern "win64" fn test_string(_output: &mut TextDisplay, _string: *const u16) -> Status { +extern "efiapi" fn test_string(_output: &mut TextDisplay, _string: *const u16) -> Status { Status(0) } -extern "win64" fn query_mode( +extern "efiapi" fn query_mode( output: &mut TextDisplay, _mode: usize, columns: &mut usize, @@ -59,21 +61,21 @@ extern "win64" fn query_mode( Status(0) } -extern "win64" fn set_mode(_output: &mut TextDisplay, _mode: usize) -> Status { +extern "efiapi" fn set_mode(_output: &mut TextDisplay, _mode: usize) -> Status { Status(0) } -extern "win64" fn set_attribute(output: &mut TextDisplay, attribute: usize) -> Status { +extern "efiapi" fn set_attribute(output: &mut TextDisplay, attribute: usize) -> Status { output.mode.Attribute = attribute as i32; Status(0) } -extern "win64" fn clear_screen(output: &mut TextDisplay) -> Status { +extern "efiapi" fn clear_screen(output: &mut TextDisplay) -> Status { output.clear(); Status(0) } -extern "win64" fn set_cursor_position( +extern "efiapi" fn set_cursor_position( output: &mut TextDisplay, column: usize, row: usize, @@ -82,7 +84,7 @@ extern "win64" fn set_cursor_position( Status(0) } -extern "win64" fn enable_cursor(output: &mut TextDisplay, enable: bool) -> Status { +extern "efiapi" fn enable_cursor(output: &mut TextDisplay, enable: bool) -> Status { output.mode.CursorVisible = enable; Status(0) }