Skip to content
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

tool: Restrict port IO interfaces to x86 #507

Closed
wants to merge 3 commits into from
Closed
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
88 changes: 47 additions & 41 deletions tool/Cargo.lock

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

4 changes: 3 additions & 1 deletion tool/Cargo.toml
Original file line number Diff line number Diff line change
@@ -20,9 +20,11 @@ clap = { version = "4.5", features = ["derive"], optional = true }
libc = { version = "0.2", optional = true }
# NOTE: Upgrading to 2.x blocked on Ubuntu shipping newer hidapi
hidapi = { version = "1.5", default-features = false, features = ["linux-shared-hidraw"], optional = true }
redox_hwio = { version = "0.1.6", default-features = false, optional = true }
downcast-rs = { version = "1.2.0", default-features = false }

[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
redox_hwio = { version = "0.1.6", default-features = false, optional = true }

[features]
default = ["std", "hidapi", "clap"]
std = ["libc", "downcast-rs/std"]
5 changes: 1 addition & 4 deletions tool/src/access/hid.rs
Original file line number Diff line number Diff line change
@@ -2,10 +2,7 @@

use hidapi::HidDevice;

use crate::{
Access,
Error,
};
use crate::{Access, Error};

/// Use USB HID access, only for USB ECs
pub struct AccessHid {
24 changes: 5 additions & 19 deletions tool/src/access/lpc/direct.rs
Original file line number Diff line number Diff line change
@@ -2,13 +2,7 @@

use hwio::{Io, Pio};

use crate::{
Access,
Error,
SuperIo,
Timeout,
timeout,
};
use crate::{timeout, Access, Error, SuperIo, Timeout};

use super::*;

@@ -24,9 +18,7 @@ impl<T: Timeout + Send> AccessLpcDirect<T> {
pub unsafe fn new(timeout: T) -> Result<Self, Error> {
// Make sure EC ID matches
let mut sio = SuperIo::new(0x2E);
let id =
(sio.read(0x20) as u16) << 8 |
(sio.read(0x21) as u16);
let id = (sio.read(0x20) as u16) << 8 | (sio.read(0x21) as u16);
match id {
0x5570 | 0x8587 => (),
_ => return Err(Error::SuperIoId(id)),
@@ -41,24 +33,18 @@ impl<T: Timeout + Send> AccessLpcDirect<T> {

/// Read from the command space
unsafe fn read_cmd(&mut self, addr: u8) -> u8 {
Pio::<u8>::new(
self.cmd + (addr as u16)
).read()
Pio::<u8>::new(self.cmd + (addr as u16)).read()
}

/// Write to the command space
unsafe fn write_cmd(&mut self, addr: u8, data: u8) {
Pio::<u8>::new(
self.cmd + (addr as u16)
).write(data)
Pio::<u8>::new(self.cmd + (addr as u16)).write(data)
}

/// Read from the debug space
//TODO: better public interface
pub unsafe fn read_debug(&mut self, addr: u8) -> u8 {
Pio::<u8>::new(
self.dbg + (addr as u16)
).read()
Pio::<u8>::new(self.dbg + (addr as u16)).read()
}

/// Returns Ok if a command can be sent
30 changes: 7 additions & 23 deletions tool/src/access/lpc/linux.rs
Original file line number Diff line number Diff line change
@@ -2,25 +2,13 @@

use std::{
fs,
io::{
self,
Read,
Write,
Seek,
SeekFrom,
},
io::{self, Read, Seek, SeekFrom, Write},
os::unix::io::AsRawFd,
path::Path,
time::Duration,
};

use crate::{
Access,
Error,
StdTimeout,
Timeout,
timeout,
};
use crate::{timeout, Access, Error, StdTimeout, Timeout};

use super::*;

@@ -35,7 +23,7 @@ impl PortLock {
if end < start {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"PortLock::new: end < start"
"PortLock::new: end < start",
));
}
let len = (end - start) + 1;
@@ -57,26 +45,22 @@ impl PortLock {
return Err(io::Error::last_os_error());
}

Ok(Self {
start,
len,
file,
})
Ok(Self { start, len, file })
}

fn seek(&mut self, offset: u16) -> io::Result<()> {
if offset >= self.len {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"PortLock::seek: offset >= len"
"PortLock::seek: offset >= len",
));
}
let port = self.start + offset;
let pos = self.file.seek(SeekFrom::Start(port as u64))?;
if pos != port as u64 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"PortLock::seek: failed to seek to port"
"PortLock::seek: failed to seek to port",
));
}
Ok(())
@@ -106,7 +90,7 @@ impl AccessLpcLinux {
/// Locks ports and then returns access object
pub unsafe fn new(timeout: Duration) -> Result<Self, Error> {
// TODO: is there a better way to probe before running a command?
if ! Path::new("/sys/bus/acpi/devices/17761776:00").is_dir() {
if !Path::new("/sys/bus/acpi/devices/17761776:00").is_dir() {
return Err(Error::Io(io::Error::new(
io::ErrorKind::NotFound,
"Failed to find System76 ACPI device",
20 changes: 5 additions & 15 deletions tool/src/access/lpc/sim.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
// SPDX-License-Identifier: MIT

use std::{
io,
net::UdpSocket,
time::Duration,
};

use crate::{
Access,
Error,
StdTimeout,
Timeout,
timeout,
};
use std::{io, net::UdpSocket, time::Duration};

use crate::{timeout, Access, Error, StdTimeout, Timeout};

use super::*;

@@ -39,15 +29,15 @@ impl AccessLpcSim {
if self.socket.send(&request)? != request.len() {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"Socket request incorrect size"
"Socket request incorrect size",
));
}

let mut response = [0];
if self.socket.recv(&mut response)? != response.len() {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"Socket response incorrect size"
"Socket response incorrect size",
));
}

12 changes: 6 additions & 6 deletions tool/src/access/mod.rs
Original file line number Diff line number Diff line change
@@ -11,14 +11,14 @@ pub use self::hid::AccessHid;
#[cfg(feature = "hidapi")]
mod hid;

#[cfg(any(
feature = "redox_hwio",
all(feature = "std", target_os = "linux")
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
any(feature = "redox_hwio", all(feature = "std", target_os = "linux"))
))]
pub use self::lpc::*;
#[cfg(any(
feature = "redox_hwio",
all(feature = "std", target_os = "linux")
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
any(feature = "redox_hwio", all(feature = "std", target_os = "linux"))
))]
mod lpc;

135 changes: 43 additions & 92 deletions tool/src/ec.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
// SPDX-License-Identifier: MIT

#[cfg(not(feature = "std"))]
use alloc::{
boxed::Box,
vec,
};
use alloc::{boxed::Box, vec};
use core::convert::TryFrom;

use crate::{
Access,
Error,
Spi,
SpiTarget,
};
use crate::{Access, Error, Spi, SpiTarget};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
@@ -83,10 +75,7 @@ impl<A: Access> Ec<A> {
/// Probes for a compatible EC
pub unsafe fn new(access: A) -> Result<Self, Error> {
// Create EC struct with provided access method and timeout
let mut ec = Ec {
access,
version: 0,
};
let mut ec = Ec { access, version: 0 };

// Read version of protocol
ec.version = ec.probe()?;
@@ -188,117 +177,76 @@ impl<A: Access> Ec<A> {

/// Read fan duty cycle by fan index
pub unsafe fn fan_get(&mut self, index: u8) -> Result<u8, Error> {
let mut data = [
index,
0
];
let mut data = [index, 0];
self.command(Cmd::FanGet, &mut data)?;
Ok(data[1])
}

/// Set fan duty cycle by fan index
pub unsafe fn fan_set(&mut self, index: u8, duty: u8) -> Result<(), Error> {
let mut data = [
index,
duty
];
let mut data = [index, duty];
self.command(Cmd::FanSet, &mut data)
}

/// Read keymap data by layout, output pin, and input pin
pub unsafe fn keymap_get(&mut self, layer: u8, output: u8, input: u8) -> Result<u16, Error> {
let mut data = [
layer,
output,
input,
0,
0
];
let mut data = [layer, output, input, 0, 0];
self.command(Cmd::KeymapGet, &mut data)?;
Ok(
(data[3] as u16) |
((data[4] as u16) << 8)
)
Ok((data[3] as u16) | ((data[4] as u16) << 8))
}

/// Set keymap data by layout, output pin, and input pin
pub unsafe fn keymap_set(&mut self, layer: u8, output: u8, input: u8, value: u16) -> Result<(), Error> {
let mut data = [
layer,
output,
input,
value as u8,
(value >> 8) as u8
];
pub unsafe fn keymap_set(
&mut self,
layer: u8,
output: u8,
input: u8,
value: u16,
) -> Result<(), Error> {
let mut data = [layer, output, input, value as u8, (value >> 8) as u8];
self.command(Cmd::KeymapSet, &mut data)
}

// Get LED value by index
pub unsafe fn led_get_value(&mut self, index: u8) -> Result<(u8, u8), Error> {
let mut data = [
index,
0,
0,
];
let mut data = [index, 0, 0];
self.command(Cmd::LedGetValue, &mut data)?;
Ok((data[1], data[2]))
}

// Set LED value by index
pub unsafe fn led_set_value(&mut self, index: u8, value: u8) -> Result<(), Error> {
let mut data = [
index,
value,
];
let mut data = [index, value];
self.command(Cmd::LedSetValue, &mut data)
}

// Get LED color by index
pub unsafe fn led_get_color(&mut self, index: u8) -> Result<(u8, u8, u8), Error> {
let mut data = [
index,
0,
0,
0,
];
let mut data = [index, 0, 0, 0];
self.command(Cmd::LedGetColor, &mut data)?;
Ok((
data[1],
data[2],
data[3],
))
Ok((data[1], data[2], data[3]))
}

// Set LED color by index
pub unsafe fn led_set_color(&mut self, index: u8, red: u8, green: u8, blue: u8) -> Result<(), Error> {
let mut data = [
index,
red,
green,
blue,
];
pub unsafe fn led_set_color(
&mut self,
index: u8,
red: u8,
green: u8,
blue: u8,
) -> Result<(), Error> {
let mut data = [index, red, green, blue];
self.command(Cmd::LedSetColor, &mut data)
}

pub unsafe fn led_get_mode(&mut self, layer: u8) -> Result<(u8, u8), Error> {
let mut data = [
layer,
0,
0,
];
let mut data = [layer, 0, 0];
self.command(Cmd::LedGetMode, &mut data)?;
Ok((
data[1],
data[2]
))
Ok((data[1], data[2]))
}

pub unsafe fn led_set_mode(&mut self, layer: u8, mode: u8, speed: u8) -> Result<(), Error> {
let mut data = [
layer,
mode,
speed,
];
let mut data = [layer, mode, speed];
self.command(Cmd::LedSetMode, &mut data)
}

@@ -316,23 +264,24 @@ impl<A: Access> Ec<A> {

/// Get security state
pub unsafe fn security_get(&mut self) -> Result<SecurityState, Error> {
let mut data = [0];
self.command(Cmd::SecurityGet, &mut data)?;
SecurityState::try_from(data[0])
let mut data = [0];
self.command(Cmd::SecurityGet, &mut data)?;
SecurityState::try_from(data[0])
}

/// Set security state
pub unsafe fn security_set(&mut self, state: SecurityState) -> Result<(), Error> {
let mut data = [state as u8];
self.command(Cmd::SecuritySet, &mut data)
let mut data = [state as u8];
self.command(Cmd::SecuritySet, &mut data)
}

pub fn into_dyn(self) -> Ec<Box<dyn Access>>
where A: 'static {
where
A: 'static,
{
Ec {
access: Box::new(self.access),
version: self.version,

}
}
}
@@ -364,7 +313,7 @@ impl<'a, A: Access> EcSpi<'a, A> {
SpiTarget::Main => (),
SpiTarget::Backup => {
flags |= CMD_SPI_FLAG_BACKUP;
},
}
}

flags
@@ -394,7 +343,8 @@ impl<'a, A: Access> Spi for EcSpi<'a, A> {
for chunk in data.chunks_mut(self.buffer.len() - 2) {
self.buffer[0] = flags;
self.buffer[1] = chunk.len() as u8;
self.ec.command(Cmd::Spi, &mut self.buffer[..(chunk.len() + 2)])?;
self.ec
.command(Cmd::Spi, &mut self.buffer[..(chunk.len() + 2)])?;
if self.buffer[1] != chunk.len() as u8 {
return Err(Error::Verify);
}
@@ -414,7 +364,8 @@ impl<'a, A: Access> Spi for EcSpi<'a, A> {
for i in 0..chunk.len() {
self.buffer[i + 2] = chunk[i];
}
self.ec.command(Cmd::Spi, &mut self.buffer[..(chunk.len() + 2)])?;
self.ec
.command(Cmd::Spi, &mut self.buffer[..(chunk.len() + 2)])?;
if self.buffer[1] != chunk.len() as u8 {
return Err(Error::Verify);
}
11 changes: 2 additions & 9 deletions tool/src/legacy.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
// SPDX-License-Identifier: MIT

use crate::{
Error,
Pmc,
SuperIo,
Timeout,
};
use crate::{Error, Pmc, SuperIo, Timeout};

/// Run some EC commands on previous proprietary firmware
pub struct EcLegacy<T: Timeout> {
@@ -17,9 +12,7 @@ impl<T: Timeout> EcLegacy<T> {
pub unsafe fn new(primary: bool, timeout: T) -> Result<Self, Error> {
let mut sio = SuperIo::new(if primary { 0x2E } else { 0x4E });

let id =
(sio.read(0x20) as u16) << 8 |
(sio.read(0x21) as u16);
let id = (sio.read(0x20) as u16) << 8 | (sio.read(0x21) as u16);

match id {
0x5570 | 0x8587 => (),
44 changes: 30 additions & 14 deletions tool/src/lib.rs
Original file line number Diff line number Diff line change
@@ -6,17 +6,15 @@
//!
//! There are some differences between targets and features that are listed below:
//! - `AccessHid` requires the `hidapi` feature. Only functional on USB ECs.
//! - `AccessLpcDirect` requires the `redox_hwio` feature and a nightly compiler. This method is
//! - `AccessLpcDirect` requires the `redox_hwio` feature and an x86 target. This method is
//! only recommended for use in firmware with LPC ECs, as mutual exclusion is not guaranteed.
//! - `AccessLpcLinux` requires the `std` feature and `linux` target_os. Recommended for LPC ECs,
//! as this method can utilize mutual exclusion.
//! - `EcLegacy`, `Pmc`, and `SuperIo` all require the `redox_hwio` feature and a nightly
//! compiler. It is only recommended to use these in firmware, as mutual exclusion is not
//! guaranteed.
//! - `AccessLpcLinux` requires the `std` feature, `linux` target_os, and x86 target. Recommended
//! for LPC ECs, as this method can utilize mutual exclusion.
//! - `EcLegacy`, `Pmc`, and `SuperIo` all require the `redox_hwio` feature and an x86 target.
//! It is only recommended to use these in firmware, as mutual exclusion is not guaranteed.
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::needless_range_loop)]

#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(not(feature = "std"))]
@@ -34,25 +32,43 @@ mod error;
pub use self::firmware::Firmware;
mod firmware;

#[cfg(feature = "redox_hwio")]
#[cfg(all(
feature = "redox_hwio",
any(target_arch = "x86", target_arch = "x86_64")
))]
pub use self::legacy::EcLegacy;
#[cfg(feature = "redox_hwio")]
#[cfg(all(
feature = "redox_hwio",
any(target_arch = "x86", target_arch = "x86_64")
))]
mod legacy;

#[cfg(feature = "redox_hwio")]
#[cfg(all(
feature = "redox_hwio",
any(target_arch = "x86", target_arch = "x86_64")
))]
pub use self::pmc::Pmc;
#[cfg(feature = "redox_hwio")]
#[cfg(all(
feature = "redox_hwio",
any(target_arch = "x86", target_arch = "x86_64")
))]
mod pmc;

pub use self::spi::{Spi, SpiRom, SpiTarget};
mod spi;

#[cfg(feature = "redox_hwio")]
#[cfg(all(
feature = "redox_hwio",
any(target_arch = "x86", target_arch = "x86_64")
))]
pub use self::super_io::SuperIo;
#[cfg(feature = "redox_hwio")]
#[cfg(all(
feature = "redox_hwio",
any(target_arch = "x86", target_arch = "x86_64")
))]
mod super_io;

pub use self::timeout::Timeout;
#[cfg(feature = "std")]
pub use self::timeout::StdTimeout;
pub use self::timeout::Timeout;
mod timeout;
299 changes: 159 additions & 140 deletions tool/src/main.rs

Large diffs are not rendered by default.

6 changes: 1 addition & 5 deletions tool/src/pmc.rs
Original file line number Diff line number Diff line change
@@ -2,11 +2,7 @@

use hwio::{Io, Pio};

use crate::{
Error,
Timeout,
timeout
};
use crate::{timeout, Error, Timeout};

/// Standard ACPI EC interface
pub struct Pmc<T: Timeout> {
100 changes: 47 additions & 53 deletions tool/src/spi.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// SPDX-License-Identifier: MIT

use crate::{
Error,
Timeout,
};
use crate::{Error, Timeout};

/// SPI bus transactions
pub trait Spi {
@@ -38,10 +35,7 @@ pub struct SpiRom<'a, S: Spi, T: Timeout> {
impl<'a, S: Spi, T: Timeout> SpiRom<'a, S, T> {
/// Create a SPI ROM using the specified SPI bus and timeout
pub fn new(spi: &'a mut S, timeout: T) -> Self {
Self {
spi,
timeout,
}
Self { spi, timeout }
}

/// Get sector size in bytes
@@ -153,55 +147,55 @@ impl<'a, S: Spi, T: Timeout> SpiRom<'a, S, T> {

//TODO: automatically detect write command
match self.spi.target() {
SpiTarget::Main => for (i, word) in data.chunks(2).enumerate() {
#[allow(clippy::get_first)]
let low = *word.get(0).unwrap_or(&0xFF);
let high = *word.get(1).unwrap_or(&0xFF);

self.spi.reset()?;
if i == 0 {
self.spi.write(&[
0xAD,
(address >> 16) as u8,
(address >> 8) as u8,
address as u8,
low,
high
])?;
} else {
SpiTarget::Main => {
for (i, word) in data.chunks(2).enumerate() {
#[allow(clippy::get_first)]
let low = *word.get(0).unwrap_or(&0xFF);
let high = *word.get(1).unwrap_or(&0xFF);

self.spi.reset()?;
if i == 0 {
self.spi.write(&[
0xAD,
(address >> 16) as u8,
(address >> 8) as u8,
address as u8,
low,
high,
])?;
} else {
self.spi.write(&[0xAD, low, high])?;
}

// Poll status for busy unset
self.status_wait(1, 0)?;
}
}
SpiTarget::Backup => {
for (i, page) in data.chunks(256).enumerate() {
let page_address = address + i as u32 * 256;
if page_address % 256 != 0 {
return Err(Error::Parameter);
}

if i > 0 {
// Write enable clears after each page is written
self.write_enable()?;
}

self.spi.reset()?;
self.spi.write(&[
0xAD,
low,
high
0xF2,
(page_address >> 16) as u8,
(page_address >> 8) as u8,
page_address as u8,
])?;
}

// Poll status for busy unset
self.status_wait(1, 0)?;
},
SpiTarget::Backup => for (i, page) in data.chunks(256).enumerate() {
let page_address = address + i as u32 * 256;
if page_address % 256 != 0 {
return Err(Error::Parameter);
}
self.spi.write(page)?;

if i > 0 {
// Write enable clears after each page is written
self.write_enable()?;
// Poll status for busy unset
self.status_wait(1, 0)?;
}

self.spi.reset()?;
self.spi.write(&[
0xF2,
(page_address >> 16) as u8,
(page_address >> 8) as u8,
page_address as u8,
])?;
self.spi.write(page)?;

// Poll status for busy unset
self.status_wait(1, 0)?;
},
}
}

self.write_disable()?;
4 changes: 2 additions & 2 deletions tool/src/timeout.rs
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ macro_rules! timeout {
Ok(ok) => {
result = Ok(ok);
break;
},
}
Err(err) => match err {
$crate::Error::WouldBlock => (),
_ => {
@@ -48,7 +48,7 @@ impl StdTimeout {
pub fn new(duration: Duration) -> Self {
StdTimeout {
instant: Instant::now(),
duration
duration,
}
}
}