Skip to content

Add FreeBSD Support #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ see the [Support Matrices](support-matrices.md).
- [x] OS Tool (`framework_tool`)
- [x] Tested on Linux
- [x] Tested on Windows
- [ ] Tested on FreeBSD
- [x] Tested on FreeBSD
- [x] UEFI Shell tool (`framework_uefi`)

###### Firmware Information

- [x] Show system information
- [x] ESRT table (UEFI and Linux only) (`--esrt`)
- [x] ESRT table (UEFI, Linux, FreeBSD only) (`--esrt`)
- [x] SMBIOS
- [x] Get firmware version from binary file
- [x] Legacy EC (Intel 13th Gen and earlier) (`--ec-bin`)
Expand Down Expand Up @@ -299,3 +299,15 @@ Keyboard backlight: 0%
[DEBUG] send_command(command=0x22, ver=0, data_len=0)
Keyboard backlight: 0%
```

## FreeBSD

```
sudo pkg install hidapi

# Build the library and tool
cargo build --no-default-features --features freebsd

# Running the tool
cargo run --no-default-features --features freebsd
```
15 changes: 9 additions & 6 deletions framework_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ build = "build.rs"
[features]
default = ["linux"]
# Linux/FreeBSD
unix = ["std", "cros_ec_driver", "raw_pio", "smbios", "dep:nix"]
linux = ["linux_pio", "unix"]
unix = ["std", "raw_pio", "smbios", "dep:nix", "dep:libc"]
linux = ["unix", "linux_pio", "cros_ec_driver"]
freebsd = ["unix", "freebsd_pio"]
# Windows does not have the cros_ec driver nor raw port I/O access to userspace
windows = ["std", "smbios", "dep:windows", "win_driver"]
windows = ["std", "smbios", "dep:windows", "win_driver", "raw_pio"]
smbios = ["dep:smbios-lib"]
std = ["dep:clap", "dep:clap-verbosity-flag", "dep:env_logger", "smbios-lib?/std", "dep:hidapi", "dep:rusb"]
uefi = [
Expand All @@ -22,8 +23,10 @@ uefi = [
"sha2/force-soft"
]

# EC communication via Port I/O on FreeBSD
freebsd_pio = ["redox_hwio/std"]
# EC communication via Port I/O on Linux
linux_pio = ["dep:libc"]
linux_pio = ["dep:libc", "redox_hwio/std"]
# EC communication via raw Port I/O (e.g. UEFI or other ring 0 code)
raw_pio = []
# EC communication via cros_ec driver on Linux
Expand All @@ -39,8 +42,8 @@ built = { version = "0.5", features = ["chrono", "git2"] }
lazy_static = "1.4.0"
sha2 = { version = "0.10.6", default_features = false, features = [ "force-soft" ] }
regex = { version = "1.10.0", default-features = false }
redox_hwio = { version = "0.1.5", default_features = false }
libc = { version = "0.2.137", optional = true }
redox_hwio = { git = "https://github.com/FrameworkComputer/rust-hwio", branch = "freebsd", default_features = false }
libc = { version = "0.2.155", optional = true }
clap = { version = "4.0", features = ["derive"], optional = true }
clap-verbosity-flag = { version = "2.0.1", optional = true }
nix = { version = "0.25.0", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion framework_lib/src/ccgx/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ fn read_version(
unsafe { std::ptr::read(data[0..version_len].as_ptr() as *const _) };

let base_version = BaseVersion::from(version_info.base_version);
let app_version = AppVersion::try_from(version_info.app_version).ok()?;
let app_version = AppVersion::from(version_info.app_version);

let fw_silicon_id = version_info.silicon_id;
let fw_silicon_family = version_info.silicon_family;
Expand Down
5 changes: 2 additions & 3 deletions framework_lib/src/ccgx/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use core::prelude::rust_2021::derive;

use crate::ccgx::{AppVersion, BaseVersion, ControllerVersion};
use crate::chromium_ec::command::EcCommands;
use crate::chromium_ec::{CrosEc, CrosEcDriver, EcError, EcResponseStatus, EcResult};
use crate::chromium_ec::{CrosEc, CrosEcDriver, EcError, EcResult};
use crate::util::{self, assert_win_len, Config, Platform};
use std::mem::size_of;

Expand Down Expand Up @@ -299,8 +299,7 @@ impl PdController {
let data = self.ccgx_read(register, 8)?;
Ok(ControllerVersion {
base: BaseVersion::from(&data[..4]),
app: AppVersion::try_from(&data[4..])
.or(Err(EcError::Response(EcResponseStatus::InvalidResponse)))?,
app: AppVersion::from(&data[4..]),
})
}

Expand Down
10 changes: 9 additions & 1 deletion framework_lib/src/ccgx/hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,17 @@ pub fn find_devices(api: &HidApi, filter_devs: &[u16], sn: Option<&str>) -> Vec<
let vid = dev_info.vendor_id();
let pid = dev_info.product_id();
let usage_page = dev_info.usage_page();

debug!("Found {:X}:{:X} Usage Page: {}", vid, pid, usage_page);
let usage_page_filter = usage_page == CCG_USAGE_PAGE;
// On FreeBSD it seems we don't get different usage pages
// There's just one entry overall
#[cfg(target_os = "freebsd")]
let usage_page_filter = true;

if vid == FRAMEWORK_VID
&& filter_devs.contains(&pid)
&& usage_page == CCG_USAGE_PAGE
&& usage_page_filter
&& (sn.is_none() || sn == dev_info.serial_number())
{
Some(dev_info.clone())
Expand Down
3 changes: 2 additions & 1 deletion framework_lib/src/chromium_ec/portio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use alloc::string::ToString;
use alloc::vec;
use alloc::vec::Vec;
use core::convert::TryInto;
#[cfg(any(feature = "linux_pio", feature = "freebsd_pio", feature = "raw_pio"))]
use hwio::{Io, Pio};
#[cfg(feature = "linux_pio")]
#[cfg(all(feature = "linux_pio", target_os = "linux"))]
use libc::ioperm;
use log::Level;
#[cfg(feature = "linux_pio")]
Expand Down
10 changes: 9 additions & 1 deletion framework_lib/src/commandline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ fn print_versions(ec: &CrosEc) {
println!("UEFI BIOS");
if let Some(smbios) = get_smbios() {
let bios_entries = smbios.collect::<SMBiosInformation>();
let bios = bios_entries.get(0).unwrap();
let bios = bios_entries.first().unwrap();
println!(" Version: {}", bios.version());
println!(" Release Date: {}", bios.release_date());
}
Expand Down Expand Up @@ -907,6 +907,14 @@ fn selftest(ec: &CrosEc) -> Option<()> {
}

fn smbios_info() {
println!("Summary");
println!(" Is Framework: {}", is_framework());
if let Some(platform) = smbios::get_platform() {
println!(" Platform: {:?}", platform);
} else {
println!(" Platform: Unknown",);
}

let smbios = get_smbios();
if smbios.is_none() {
error!("Failed to find SMBIOS");
Expand Down
91 changes: 68 additions & 23 deletions framework_lib/src/esrt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ use core::prelude::v1::derive;
#[cfg(not(feature = "uefi"))]
use guid_macros::guid;
#[cfg(feature = "uefi")]
use std::slice;
#[cfg(feature = "uefi")]
use uefi::{guid, Guid};

#[cfg(feature = "linux")]
Expand All @@ -31,6 +29,15 @@ use std::io;
#[cfg(feature = "linux")]
use std::path::Path;

#[cfg(target_os = "freebsd")]
use nix::ioctl_readwrite;
#[cfg(target_os = "freebsd")]
use std::fs::OpenOptions;
#[cfg(target_os = "freebsd")]
use std::os::fd::AsRawFd;
#[cfg(target_os = "freebsd")]
use std::os::unix::fs::OpenOptionsExt;

/// Decode from GUID string version
///
/// # Examples
Expand Down Expand Up @@ -316,7 +323,7 @@ fn esrt_from_sysfs(dir: &Path) -> io::Result<Esrt> {
Ok(esrt_table)
}

#[cfg(all(not(feature = "uefi"), feature = "linux"))]
#[cfg(all(not(feature = "uefi"), feature = "linux", target_os = "linux"))]
pub fn get_esrt() -> Option<Esrt> {
let res = esrt_from_sysfs(Path::new("/sys/firmware/efi/esrt/entries")).ok();
if res.is_none() {
Expand All @@ -332,11 +339,43 @@ pub fn get_esrt() -> Option<Esrt> {
None
}

#[cfg(target_os = "freebsd")]
#[repr(C)]
struct EfiGetTableIoc {
buf: *mut u8,
uuid: [u8; 16],
table_len: usize,
buf_len: usize,
}
#[cfg(target_os = "freebsd")]
ioctl_readwrite!(efi_get_table, b'E', 1, EfiGetTableIoc);

#[cfg(all(not(feature = "uefi"), target_os = "freebsd"))]
pub fn get_esrt() -> Option<Esrt> {
// TODO: Implement
println!("Reading ESRT is not implemented on FreeBSD yet.");
None
let file = OpenOptions::new()
.read(true)
.write(true)
.custom_flags(libc::O_NONBLOCK)
.open("/dev/efi")
.unwrap();

let mut buf: Vec<u8> = Vec::new();
let mut table = EfiGetTableIoc {
buf: std::ptr::null_mut(),
uuid: SYSTEM_RESOURCE_TABLE_GUID.to_bytes(),
buf_len: 0,
table_len: 0,
};
unsafe {
let fd = file.as_raw_fd();
let _res = efi_get_table(fd, &mut table).unwrap();
buf.resize(table.table_len, 0);
table.buf_len = table.table_len;
table.buf = buf.as_mut_ptr();

let _res = efi_get_table(fd, &mut table).unwrap();
esrt_from_buf(table.buf)
}
}

/// gEfiSystemResourceTableGuid from MdePkg/MdePkg.dec
Expand All @@ -353,26 +392,32 @@ pub fn get_esrt() -> Option<Esrt> {
let table_guid: Guid = unsafe { std::mem::transmute(table.guid) };
match table_guid {
SYSTEM_RESOURCE_TABLE_GUID => unsafe {
let raw_esrt = &*(table.address as *const _Esrt);
let mut esrt = Esrt {
resource_count: raw_esrt.resource_count,
resource_count_max: raw_esrt.resource_count_max,
resource_version: raw_esrt.resource_version,
entries: vec![],
};

// Make sure it's the version we expect
debug_assert!(esrt.resource_version == ESRT_FIRMWARE_RESOURCE_VERSION);

let src_ptr = std::ptr::addr_of!(raw_esrt.entries) as *const EsrtResourceEntry;
let slice_entries = slice::from_raw_parts(src_ptr, esrt.resource_count as usize);

esrt.entries = slice_entries.to_vec();

return Some(esrt);
return esrt_from_buf(table.address as *const u8);
},
_ => {}
}
}
None
}

/// Parse the ESRT table buffer
#[cfg(any(feature = "uefi", target_os = "freebsd"))]
unsafe fn esrt_from_buf(ptr: *const u8) -> Option<Esrt> {
let raw_esrt = &*(ptr as *const _Esrt);
let mut esrt = Esrt {
resource_count: raw_esrt.resource_count,
resource_count_max: raw_esrt.resource_count_max,
resource_version: raw_esrt.resource_version,
entries: vec![],
};

// Make sure it's the version we expect
debug_assert!(esrt.resource_version == ESRT_FIRMWARE_RESOURCE_VERSION);

let src_ptr = core::ptr::addr_of!(raw_esrt.entries) as *const EsrtResourceEntry;
let slice_entries = core::slice::from_raw_parts(src_ptr, esrt.resource_count as usize);

esrt.entries = slice_entries.to_vec();

Some(esrt)
}
Loading
Loading