Skip to content

Commit 04ea416

Browse files
committed
Implement getting, setting and listing vars
Cannot list on windows Signed-off-by: Daniel Schaefer <dhs@frame.work>
1 parent 3b03ba0 commit 04ea416

File tree

7 files changed

+400
-66
lines changed

7 files changed

+400
-66
lines changed

completions/bash/framework_tool

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ _framework_tool() {
2323

2424
case "${cmd}" in
2525
framework_tool)
26-
opts="-v -q -t -f -h --flash-gpu-descriptor --verbose --quiet --versions --version --features --esrt --device --compare-version --power --thermal --sensors --fansetduty --fansetrpm --autofanctrl --pdports --info --meinfo --pd-info --pd-reset --pd-disable --pd-enable --dp-hdmi-info --dp-hdmi-update --audio-card-info --privacy --pd-bin --ec-bin --capsule --dump --h2o-capsule --dump-ec-flash --flash-ec --flash-ro-ec --flash-rw-ec --intrusion --inputdeck --inputdeck-mode --expansion-bay --charge-limit --charge-current-limit --charge-rate-limit --get-gpio --fp-led-level --fp-brightness --kblight --remap-key --rgbkbd --ps2-enable --tablet-mode --touchscreen-enable --stylus-battery --console --reboot-ec --ec-hib-delay --uptimeinfo --s0ix-counter --hash --driver --pd-addrs --pd-ports --test --test-retimer --boardid --force --dry-run --flash-gpu-descriptor-file --dump-gpu-descriptor-file --nvidia --host-command --generate-completions --help"
26+
opts="-v -q -t -f -h --flash-gpu-descriptor --verbose --quiet --versions --version --features --esrt --device --compare-version --power --thermal --sensors --fansetduty --fansetrpm --autofanctrl --pdports --info --meinfo --pd-info --pd-reset --pd-disable --pd-enable --dp-hdmi-info --dp-hdmi-update --audio-card-info --privacy --pd-bin --ec-bin --capsule --dump --h2o-capsule --dump-ec-flash --flash-ec --flash-ro-ec --flash-rw-ec --intrusion --inputdeck --inputdeck-mode --expansion-bay --charge-limit --charge-current-limit --charge-rate-limit --get-gpio --fp-led-level --fp-brightness --kblight --remap-key --rgbkbd --ps2-enable --tablet-mode --touchscreen-enable --stylus-battery --console --reboot-ec --ec-hib-delay --uptimeinfo --s0ix-counter --list-uefi-vars --get-uefi-var --set-uefi-var --hash --driver --pd-addrs --pd-ports --test --test-retimer --boardid --force --dry-run --flash-gpu-descriptor-file --dump-gpu-descriptor-file --nvidia --host-command --generate-completions --help"
2727
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
2828
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
2929
return 0
@@ -173,6 +173,14 @@ _framework_tool() {
173173
COMPREPLY=($(compgen -f "${cur}"))
174174
return 0
175175
;;
176+
--get-uefi-var)
177+
COMPREPLY=($(compgen -f "${cur}"))
178+
return 0
179+
;;
180+
--set-uefi-var)
181+
COMPREPLY=($(compgen -f "${cur}"))
182+
return 0
183+
;;
176184
--hash)
177185
COMPREPLY=($(compgen -f "${cur}"))
178186
return 0

completions/fish/framework_tool.fish

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ jump-rw\t''
5656
cancel-jump\t''
5757
disable-jump\t''"
5858
complete -c framework_tool -l ec-hib-delay -d 'Get or set EC hibernate delay (S5 to G3)' -r
59+
complete -c framework_tool -l get-uefi-var -d 'Get a UEFI variable by name and optionally GUID (GUID auto-resolved for well-known names)' -r
60+
complete -c framework_tool -l set-uefi-var -d 'Set a UEFI variable from file (NAME [GUID] FILEPATH, GUID auto-resolved for well-known names)' -r
5961
complete -c framework_tool -l hash -d 'Hash a file of arbitrary data' -r -F
6062
complete -c framework_tool -l driver -d 'Select which driver is used. By default portio is used' -r -f -a "portio\t''
6163
cros-ec\t''
@@ -91,6 +93,7 @@ complete -c framework_tool -l expansion-bay -d 'Show status of the expansion bay
9193
complete -c framework_tool -l stylus-battery -d 'Check stylus battery level (USI 2.0 stylus only)'
9294
complete -c framework_tool -l uptimeinfo
9395
complete -c framework_tool -l s0ix-counter
96+
complete -c framework_tool -l list-uefi-vars -d 'List all UEFI variables'
9497
complete -c framework_tool -s t -l test -d 'Run self-test to check if interaction with EC is possible'
9598
complete -c framework_tool -l test-retimer -d 'Run self-test to check if interaction with retimers is possible'
9699
complete -c framework_tool -l boardid -d 'Print all board IDs'

completions/zsh/_framework_tool

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ _framework_tool() {
5151
'--console=[Get EC console, choose whether recent or to follow the output]:CONSOLE:(recent follow)' \
5252
'--reboot-ec=[Control EC RO/RW jump]:REBOOT_EC:(reboot jump-ro jump-rw cancel-jump disable-jump)' \
5353
'--ec-hib-delay=[Get or set EC hibernate delay (S5 to G3)]::EC_HIB_DELAY:_default' \
54+
'*--get-uefi-var=[Get a UEFI variable by name and optionally GUID (GUID auto-resolved for well-known names)]:NAME:_default' \
55+
'*--set-uefi-var=[Set a UEFI variable from file (NAME \[GUID\] FILEPATH, GUID auto-resolved for well-known names)]:NAME:_default:NAME:_default' \
5456
'--hash=[Hash a file of arbitrary data]:HASH:_files' \
5557
'--driver=[Select which driver is used. By default portio is used]:DRIVER:(portio cros-ec windows)' \
5658
'*--pd-addrs=[Specify I2C addresses of the PD chips (Advanced)]:PD_ADDRS:_default:PD_ADDRS:_default:PD_ADDRS:_default' \
@@ -82,6 +84,7 @@ _framework_tool() {
8284
'--stylus-battery[Check stylus battery level (USI 2.0 stylus only)]' \
8385
'--uptimeinfo[]' \
8486
'--s0ix-counter[]' \
87+
'--list-uefi-vars[List all UEFI variables]' \
8588
'-t[Run self-test to check if interaction with EC is possible]' \
8689
'--test[Run self-test to check if interaction with EC is possible]' \
8790
'--test-retimer[Run self-test to check if interaction with retimers is possible]' \

framework_lib/src/commandline/clap_std.rs

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,19 @@ struct ClapCli {
254254
#[arg(long)]
255255
s0ix_counter: bool,
256256

257-
/// Set UEFI variable (name, file path)
257+
/// List all UEFI variables
258258
#[arg(long)]
259-
set_uefi_var: Option<std::path::PathBuf>,
259+
list_uefi_vars: bool,
260+
261+
/// Get a UEFI variable by name and optionally GUID (GUID auto-resolved for well-known names)
262+
#[clap(num_args = 1..=2)]
263+
#[arg(long, value_names(["NAME", "GUID"]))]
264+
get_uefi_var: Vec<String>,
265+
266+
/// Set a UEFI variable from file (NAME [GUID] FILEPATH, GUID auto-resolved for well-known names)
267+
#[clap(num_args = 2..=3)]
268+
#[arg(long, value_names(["NAME", "GUID", "FILEPATH"]))]
269+
set_uefi_var: Vec<String>,
260270

261271
/// Hash a file of arbitrary data
262272
#[arg(long)]
@@ -428,6 +438,53 @@ pub fn parse(args: &[String]) -> Cli {
428438
)),
429439
_ => None,
430440
};
441+
let get_uefi_var = match args.get_uefi_var.len() {
442+
2 => Some((args.get_uefi_var[0].clone(), args.get_uefi_var[1].clone())),
443+
1 => {
444+
let name = &args.get_uefi_var[0];
445+
if let Some(guid) = crate::os_specific::known_uefi_guid(name) {
446+
Some((name.clone(), guid.to_string()))
447+
} else {
448+
cli.error(
449+
ErrorKind::MissingRequiredArgument,
450+
format!(
451+
"Unknown UEFI variable '{}'. Please provide the GUID explicitly.",
452+
name
453+
),
454+
)
455+
.exit();
456+
}
457+
}
458+
0 => None,
459+
// Checked by clap
460+
_ => unreachable!(),
461+
};
462+
let set_uefi_var = match args.set_uefi_var.len() {
463+
3 => Some((
464+
args.set_uefi_var[0].clone(),
465+
args.set_uefi_var[1].clone(),
466+
args.set_uefi_var[2].clone(),
467+
)),
468+
2 => {
469+
let name = &args.set_uefi_var[0];
470+
if let Some(guid) = crate::os_specific::known_uefi_guid(name) {
471+
Some((name.clone(), guid.to_string(), args.set_uefi_var[1].clone()))
472+
} else {
473+
cli.error(
474+
ErrorKind::MissingRequiredArgument,
475+
format!(
476+
"Unknown UEFI variable '{}'. Please provide the GUID explicitly.",
477+
name
478+
),
479+
)
480+
.exit();
481+
}
482+
}
483+
0 => None,
484+
// Checked by clap
485+
_ => unreachable!(),
486+
};
487+
431488
let host_command = if args.host_command.len() >= 2 {
432489
let cmd_ver = if let Ok(cmd_ver) = u8::try_from(args.host_command[1]) {
433490
cmd_ver
@@ -532,7 +589,9 @@ pub fn parse(args: &[String]) -> Cli {
532589
ec_hib_delay: args.ec_hib_delay,
533590
uptimeinfo: args.uptimeinfo,
534591
s0ix_counter: args.s0ix_counter,
535-
set_uefi_var: args.set_uefi_var,
592+
list_uefi_vars: args.list_uefi_vars,
593+
get_uefi_var,
594+
set_uefi_var,
536595
hash: args.hash.map(|x| x.into_os_string().into_string().unwrap()),
537596
driver: args.driver,
538597
pd_addrs,

framework_lib/src/commandline/mod.rs

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,9 @@ pub struct Cli {
216216
pub ec_hib_delay: Option<Option<u32>>,
217217
pub uptimeinfo: bool,
218218
pub s0ix_counter: bool,
219-
pub set_uefi_var: Option<std::path::PathBuf>,
219+
pub list_uefi_vars: bool,
220+
pub get_uefi_var: Option<(String, String)>,
221+
pub set_uefi_var: Option<(String, String, String)>,
220222
pub hash: Option<String>,
221223
pub pd_addrs: Option<(u16, u16, u16)>,
222224
pub pd_ports: Option<(u8, u8, u8)>,
@@ -305,6 +307,9 @@ pub fn parse(args: &[String]) -> Cli {
305307
// ec_hib_delay
306308
uptimeinfo: cli.uptimeinfo,
307309
s0ix_counter: cli.s0ix_counter,
310+
// list_uefi_vars
311+
// get_uefi_var
312+
// set_uefi_var
308313
hash: cli.hash,
309314
pd_addrs: cli.pd_addrs,
310315
pd_ports: cli.pd_ports,
@@ -1511,24 +1516,81 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 {
15111516
} else {
15121517
println!("s0ix_counter: Unknown");
15131518
}
1514-
} else if let Some(filepath) = &args.set_uefi_var {
1519+
} else if args.list_uefi_vars {
1520+
if let Some(vars) = os_specific::list_uefi_vars() {
1521+
for (name, guid) in &vars {
1522+
println!("{} {}", name, guid);
1523+
}
1524+
println!("\nTotal: {} variables", vars.len());
1525+
} else {
1526+
println!("Failed to list UEFI variables");
1527+
}
1528+
} else if let Some((name, guid)) = &args.get_uefi_var {
1529+
if let Some((data, attributes)) = os_specific::get_uefi_var(name, guid) {
1530+
println!("Variable: {}", name);
1531+
println!("GUID: {}", guid);
1532+
println!("Attrs: 0x{:08X}", attributes);
1533+
println!("Size: {} B", data.len());
1534+
println!();
1535+
// Print hex dump
1536+
for (i, chunk) in data.chunks(16).enumerate() {
1537+
print!("{:08X} ", i * 16);
1538+
for (j, byte) in chunk.iter().enumerate() {
1539+
print!("{:02X} ", byte);
1540+
if j == 7 {
1541+
print!(" ");
1542+
}
1543+
}
1544+
// Pad if last line is short
1545+
if chunk.len() < 16 {
1546+
for j in chunk.len()..16 {
1547+
print!(" ");
1548+
if j == 7 {
1549+
print!(" ");
1550+
}
1551+
}
1552+
}
1553+
print!(" |");
1554+
for byte in chunk {
1555+
if byte.is_ascii_graphic() || *byte == b' ' {
1556+
print!("{}", *byte as char);
1557+
} else {
1558+
print!(".");
1559+
}
1560+
}
1561+
println!("|");
1562+
}
1563+
} else {
1564+
println!("Failed to read UEFI variable '{}' ({})", name, guid);
1565+
}
1566+
} else if let Some((name, guid, filepath)) = &args.set_uefi_var {
15151567
#[cfg(feature = "uefi")]
15161568
let data = crate::uefi::fs::shell_read_file(filepath);
15171569
#[cfg(not(feature = "uefi"))]
15181570
let data = match fs::read(filepath) {
15191571
Ok(data) => Some(data),
1520-
// TODO: Perhaps a more user-friendly error
15211572
Err(e) => {
1522-
println!("Error {:?}", e);
1573+
println!("Error reading file: {:?}", e);
15231574
None
15241575
}
15251576
};
15261577
if let Some(data) = data {
1527-
println!("File");
1528-
println!(" Size: {:>20} B", data.len());
1529-
println!(" Size: {:>20} KB", data.len() / 1024);
1530-
os_specific::get_dbx();
1531-
//os_specific::set_dbx(&data);
1578+
// Read existing attributes if the variable already exists, otherwise use sensible defaults
1579+
let attrs = if let Some((_, existing_attrs)) = os_specific::get_uefi_var(name, guid) {
1580+
existing_attrs
1581+
} else {
1582+
os_specific::EFI_VARIABLE_NON_VOLATILE
1583+
| os_specific::EFI_VARIABLE_BOOTSERVICE_ACCESS
1584+
| os_specific::EFI_VARIABLE_RUNTIME_ACCESS
1585+
};
1586+
println!("Setting UEFI variable '{}' ({})", name, guid);
1587+
println!(" Attrs: 0x{:08X}", attrs);
1588+
println!(" Data size: {} B", data.len());
1589+
if os_specific::set_uefi_var(name, guid, &data, attrs).is_some() {
1590+
println!(" Success");
1591+
} else {
1592+
println!(" Failed to set UEFI variable");
1593+
}
15321594
}
15331595
} else if args.test {
15341596
println!("Self-Test");

framework_lib/src/commandline/uefi.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ pub fn parse(args: &[String]) -> Cli {
8585
ec_hib_delay: None,
8686
uptimeinfo: false,
8787
s0ix_counter: false,
88+
list_uefi_vars: false,
89+
get_uefi_var: None,
90+
set_uefi_var: None,
8891
hash: None,
8992
// This is the only driver that works on UEFI
9093
driver: Some(CrosEcDriverType::Portio),
@@ -671,6 +674,82 @@ pub fn parse(args: &[String]) -> Cli {
671674
None
672675
};
673676
found_an_option = true;
677+
} else if arg == "--list-uefi-vars" {
678+
cli.list_uefi_vars = true;
679+
found_an_option = true;
680+
} else if arg == "--get-uefi-var" {
681+
cli.get_uefi_var = if args.len() > i + 2 {
682+
// Check if second arg looks like a GUID (contains '-') or is the next flag
683+
if !args[i + 2].starts_with('-') && args[i + 2].contains('-') {
684+
Some((args[i + 1].clone(), args[i + 2].clone()))
685+
} else {
686+
// Only name given, try lookup
687+
let name = &args[i + 1];
688+
if let Some(guid) = crate::os_specific::known_uefi_guid(name) {
689+
Some((name.clone(), guid.into()))
690+
} else {
691+
println!(
692+
"Unknown UEFI variable '{}'. Please provide the GUID explicitly.",
693+
name
694+
);
695+
None
696+
}
697+
}
698+
} else if args.len() > i + 1 {
699+
let name = &args[i + 1];
700+
if let Some(guid) = crate::os_specific::known_uefi_guid(name) {
701+
Some((name.clone(), guid.into()))
702+
} else {
703+
println!(
704+
"Unknown UEFI variable '{}'. Please provide the GUID explicitly.",
705+
name
706+
);
707+
None
708+
}
709+
} else {
710+
println!("--get-uefi-var requires at least one argument: NAME [GUID]");
711+
None
712+
};
713+
found_an_option = true;
714+
} else if arg == "--set-uefi-var" {
715+
cli.set_uefi_var = if args.len() > i + 3 {
716+
// Check if second arg looks like a GUID
717+
if !args[i + 2].starts_with('-') && args[i + 2].contains('-') {
718+
Some((
719+
args[i + 1].clone(),
720+
args[i + 2].clone(),
721+
args[i + 3].clone(),
722+
))
723+
} else {
724+
// Only name + filepath given, try lookup
725+
let name = &args[i + 1];
726+
if let Some(guid) = crate::os_specific::known_uefi_guid(name) {
727+
Some((name.clone(), guid.into(), args[i + 2].clone()))
728+
} else {
729+
println!(
730+
"Unknown UEFI variable '{}'. Please provide the GUID explicitly.",
731+
name
732+
);
733+
None
734+
}
735+
}
736+
} else if args.len() > i + 2 {
737+
// Two args: name + filepath, try GUID lookup
738+
let name = &args[i + 1];
739+
if let Some(guid) = crate::os_specific::known_uefi_guid(name) {
740+
Some((name.clone(), guid.into(), args[i + 2].clone()))
741+
} else {
742+
println!(
743+
"Unknown UEFI variable '{}'. Please provide the GUID explicitly.",
744+
name
745+
);
746+
None
747+
}
748+
} else {
749+
println!("--set-uefi-var requires at least two arguments: NAME [GUID] FILEPATH");
750+
None
751+
};
752+
found_an_option = true;
674753
} else if arg == "--pd-addrs" {
675754
cli.pd_addrs = if args.len() > i + 3 {
676755
let left = args[i + 1].parse::<u16>();

0 commit comments

Comments
 (0)