From f8b5f58cbd115c8721c9b1f3b0aa124ddfc4ad08 Mon Sep 17 00:00:00 2001 From: Kieran Levin Date: Thu, 5 Jun 2025 01:38:07 -0700 Subject: [PATCH 1/7] fw16: add command to write expansion bay eeprom from a file Example invocation: framework_tool.efi --flash-gpu-descriptor-file pcie_4x2.bin TEST=fail: if the write fails we will error correctly TEST=pass: the command will succeed. TEST=dump contents using ec console: i2c read I2C_PORT5 0x50 0x0000 16 00000000: 32 ac 00 00 30 00 00 00 00 00 01 00 08 00 00 00 |2...0... ........| And compare to xxd dump of source file to ensure they match. Signed-off-by: Kieran Levin --- framework_lib/src/chromium_ec/mod.rs | 45 ++++++++++++++++++++++- framework_lib/src/commandline/clap_std.rs | 7 ++++ framework_lib/src/commandline/mod.rs | 30 +++++++++++++++ framework_lib/src/commandline/uefi.rs | 9 +++++ 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 02631e50..c768b33c 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1177,7 +1177,50 @@ impl CrosEc { Ok(result.valid) } - /// Requests console output from EC and constantly asks for more + pub fn write_ec_gpu_chunk(&self, offset: u16, data: &[u8]) -> EcResult<()> { + let result = i2c_passthrough::i2c_write(self, 5, 0x50, offset, data)?; + result.is_successful() + } + + /// Writes EC GPU descriptor to the GPU EEPROM. + pub fn set_gpu_descriptor(&self, data: &[u8]) -> EcResult<()> { + // Need to program the EEPROM 32 bytes at a time. + let chunk_size = 32; + + let chunks = data.len() / chunk_size; + for chunk_no in 0..chunks { + let offset = chunk_no * chunk_size; + // Current chunk might be smaller if it's the last + let cur_chunk_size = std::cmp::min(chunk_size, data.len() - chunk_no * chunk_size); + + if chunk_no % 100 == 0 { + println!(); + print!( + "Writing chunk {:>4}/{:>4} ({:>6}/{:>6}): X", + chunk_no, + chunks, + offset, + cur_chunk_size * chunks + ); + } else { + print!("X"); + } + + let chunk = &data[offset..offset + cur_chunk_size]; + let offset_le = (((offset as u16) << 8) & 0xff00) | (((offset as u16) >> 8) & 0x00ff); + let res = self.write_ec_gpu_chunk(offset_le, chunk); + // Don't read too fast, wait 100ms before writing more to allow for page erase/write cycle. + os_specific::sleep(100_000); + if let Err(err) = res { + println!(" Failed to write chunk: {:?}", err); + return Err(err); + } + } + println!(); + Ok(()) + } + + /// Requests recent console output from EC and constantly asks for more /// Prints the output and returns it when an error is encountered pub fn console_read(&self) -> EcResult<()> { EcRequestConsoleSnapshot {}.send_command(self)?; diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 52eddbeb..bd65139e 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -270,6 +270,10 @@ struct ClapCli { /// Simulate execution of a command (e.g. --flash-ec) #[arg(long)] dry_run: bool, + + /// File to write to the gpu EEPROM + #[arg(long)] + flash_gpu_descriptor_file: Option, } /// Parse a list of commandline arguments and return the struct @@ -457,6 +461,9 @@ pub fn parse(args: &[String]) -> Cli { paginate: false, info: args.info, flash_gpu_descriptor, + flash_gpu_descriptor_file: args + .flash_gpu_descriptor_file + .map(|x| x.into_os_string().into_string().unwrap()), raw_command: vec![], } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ca92266a..80831d83 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -201,6 +201,7 @@ pub struct Cli { pub help: bool, pub info: bool, pub flash_gpu_descriptor: Option<(u8, String)>, + pub flash_gpu_descriptor_file: Option, // UEFI only pub allupdate: bool, pub paginate: bool, @@ -1220,6 +1221,34 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { Ok(x) => println!("GPU Descriptor write failed with status code: {}", x), Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), } + } else if let Some(gpu_descriptor_file) = &args.flash_gpu_descriptor_file { + if let Some(PlatformFamily::Framework16) = + smbios::get_platform().and_then(Platform::which_family) + { + #[cfg(feature = "uefi")] + let data: Option> = crate::uefi::fs::shell_read_file(gpu_descriptor_file); + #[cfg(not(feature = "uefi"))] + let data = match fs::read(gpu_descriptor_file) { + Ok(data) => Some(data), + // TODO: Perhaps a more user-friendly error + Err(e) => { + println!("Error {:?}", e); + None + } + }; + if let Some(data) = data { + println!("File"); + println!(" Size: {:>20} B", data.len()); + println!(" Size: {:>20} KB", data.len() / 1024); + let res = ec.set_gpu_descriptor(&data); + match res { + Ok(()) => println!("GPU Descriptor successfully written"), + Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), + } + } + } else { + println!("Unsupported on this platform"); + } } 0 @@ -1275,6 +1304,7 @@ Options: --console Get EC console, choose whether recent or to follow the output [possible values: recent, follow] --hash Hash a file of arbitrary data --flash-gpu-descriptor <18 DIGIT SN> Overwrite the GPU bay descriptor SN and type. + --flash-gpu-descriptor-file Write the GPU bay descriptor with a descriptor file. -f, --force Force execution of an unsafe command - may render your hardware unbootable! --dry-run Simulate execution of a command (e.g. --flash-ec) -t, --test Run self-test to check if interaction with EC is possible diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index d21938ae..a5e8c4c5 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -116,6 +116,7 @@ pub fn parse(args: &[String]) -> Cli { force: false, help: false, flash_gpu_descriptor: None, + flash_gpu_descriptor_file: None, allupdate: false, info: false, raw_command: vec![], @@ -764,6 +765,14 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; + } else if arg == "--flash-gpu-descriptor-file" { + cli.flash_gpu_descriptor_file = if args.len() > i + 1 { + Some(args[i + 1].clone()) + } else { + println!("Need to provide a value for --flash_gpu_descriptor_file. PATH"); + None + }; + found_an_option = true; } } From e7fc0d96abd8ec641a0f6a8749cb001e5f8b8944 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 17:23:06 +0800 Subject: [PATCH 2/7] --flash-gpu-descriptor-file: Allow --dry-run Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 9 ++++++++- framework_lib/src/commandline/mod.rs | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index c768b33c..e6247e26 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1183,7 +1183,11 @@ impl CrosEc { } /// Writes EC GPU descriptor to the GPU EEPROM. - pub fn set_gpu_descriptor(&self, data: &[u8]) -> EcResult<()> { + pub fn set_gpu_descriptor(&self, data: &[u8], dry_run: bool) -> EcResult<()> { + println!( + "Writing GPU EEPROM {}", + if dry_run { " (DRY RUN)" } else { "" } + ); // Need to program the EEPROM 32 bytes at a time. let chunk_size = 32; @@ -1205,6 +1209,9 @@ impl CrosEc { } else { print!("X"); } + if dry_run { + continue; + } let chunk = &data[offset..offset + cur_chunk_size]; let offset_le = (((offset as u16) << 8) & 0xff00) | (((offset as u16) >> 8) & 0x00ff); diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 80831d83..08c755e8 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -1240,7 +1240,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { println!("File"); println!(" Size: {:>20} B", data.len()); println!(" Size: {:>20} KB", data.len() / 1024); - let res = ec.set_gpu_descriptor(&data); + let res = ec.set_gpu_descriptor(&data, args.dry_run); match res { Ok(()) => println!("GPU Descriptor successfully written"), Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), From 0d65135be31076776750e7a104f63d2712cb758f Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 17:24:05 +0800 Subject: [PATCH 3/7] --flash-gpu-descriptor-file: Don't block if unknown system Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 08c755e8..ae6c1f08 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -1222,9 +1222,10 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), } } else if let Some(gpu_descriptor_file) = &args.flash_gpu_descriptor_file { - if let Some(PlatformFamily::Framework16) = - smbios::get_platform().and_then(Platform::which_family) - { + if matches!( + smbios::get_family(), + Some(PlatformFamily::Framework16) | None + ) { #[cfg(feature = "uefi")] let data: Option> = crate::uefi::fs::shell_read_file(gpu_descriptor_file); #[cfg(not(feature = "uefi"))] From 74042d268e8f1f526ae6ef1427b2822bc8e8bb05 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 17:36:04 +0800 Subject: [PATCH 4/7] write_ec_gpu_chunk: Use std to swap byte order X64 is LE natively, so BE must be intended. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index e6247e26..a6a2fed7 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1214,8 +1214,7 @@ impl CrosEc { } let chunk = &data[offset..offset + cur_chunk_size]; - let offset_le = (((offset as u16) << 8) & 0xff00) | (((offset as u16) >> 8) & 0x00ff); - let res = self.write_ec_gpu_chunk(offset_le, chunk); + let res = self.write_ec_gpu_chunk((offset as u16).to_be(), chunk); // Don't read too fast, wait 100ms before writing more to allow for page erase/write cycle. os_specific::sleep(100_000); if let Err(err) = res { From 85c4b2f6a89e81465bdd1fe751d1e12f639f5957 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 18:58:20 +0800 Subject: [PATCH 5/7] EXAMPLES: Document flashing eeprom Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index e69817ee..6882c761 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -79,3 +79,19 @@ This command has not been thoroughly tested on all Framework Computer systems # EC will boot back into RO if the system turned off for 30s > framework_tool --reboot-ec jump-rw ``` + +## Flashing Expansion Bay EEPROM (Framework 16) + +This will render your dGPU unsuable if you flash the wrong file! +It's intended for advanced users who build their own expansion bay module. +The I2C address of the EEPROM is hardcoded to 0x50. + +``` +# Update just the serial number +> framework_tool --flash_gpu_descriptor GPU FRAKMQCP41500ASSY1 +> framework_tool --flash_gpu_descriptor 13 FRAKMQCP41500ASSY1 +> framework_tool --flash_gpu_descriptor 0x0D FRAKMQCP41500ASSY1 + +# Update everything from a file +> framework_tool --flash-gpu-descriptor-file pcie_4x2.bin +``` From e8acfa7fac3ad7a5b9c8a65897987d8cbde4202d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 18:58:43 +0800 Subject: [PATCH 6/7] --expansion-bay: Read eeprom directly and dump extra details Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 5 +++ framework_lib/src/chromium_ec/mod.rs | 62 ++++++++++++++++++++++++++++ framework_lib/src/commandline/mod.rs | 20 +++++++++ 3 files changed, 87 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 1ea7f82c..61b4aac8 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -317,8 +317,13 @@ Expansion Bay Serial Number: FRAXXXXXXXXXXXXXXX Config: Pcie4x2 Vendor: SsdHolder + Expansion Bay EEPROM + Valid: true + HW Version: 8.0 ``` +Add `-vv` for more verbose details. + ## Check charger and battery status (Framework 12/13/16) ``` diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index a6a2fed7..a596c7cd 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1177,6 +1177,34 @@ impl CrosEc { Ok(result.valid) } + pub fn read_ec_gpu_chunk(&self, addr: u16, len: u16) -> EcResult> { + let eeprom_port = 0x05; + let eeprom_addr = 0x50; + let mut data: Vec = Vec::with_capacity(len.into()); + + while data.len() < len.into() { + let remaining = len - data.len() as u16; + let chunk_len = std::cmp::min(i2c_passthrough::MAX_I2C_CHUNK, remaining.into()); + let offset = addr + data.len() as u16; + let i2c_response = i2c_passthrough::i2c_read( + self, + eeprom_port, + eeprom_addr, + offset, + chunk_len as u16, + )?; + if let Err(EcError::DeviceError(err)) = i2c_response.is_successful() { + return Err(EcError::DeviceError(format!( + "I2C read was not successful: {:?}", + err + ))); + } + data.extend(i2c_response.data); + } + + Ok(data) + } + pub fn write_ec_gpu_chunk(&self, offset: u16, data: &[u8]) -> EcResult<()> { let result = i2c_passthrough::i2c_write(self, 5, 0x50, offset, data)?; result.is_successful() @@ -1226,6 +1254,15 @@ impl CrosEc { Ok(()) } + pub fn read_gpu_desc_header(&self) -> EcResult { + let bytes = + self.read_ec_gpu_chunk(0x00, core::mem::size_of::() as u16)?; + let header: *const GpuCfgDescriptor = unsafe { std::mem::transmute(bytes.as_ptr()) }; + let header = unsafe { *header }; + + Ok(header) + } + /// Requests recent console output from EC and constantly asks for more /// Prints the output and returns it when an error is encountered pub fn console_read(&self) -> EcResult<()> { @@ -1633,3 +1670,28 @@ pub struct IntrusionStatus { /// That means we only know if it was opened at least once, while off, not how many times. pub vtr_open_count: u8, } + +#[derive(Clone, Debug, Copy, PartialEq)] +#[repr(C, packed)] +pub struct GpuCfgDescriptor { + /// Expansion bay card magic value that is unique + pub magic: [u8; 4], + /// Length of header following this field + pub length: u32, + /// descriptor version, if EC max version is lower than this, ec cannot parse + pub desc_ver_major: u16, + pub desc_ver_minor: u16, + /// Hardware major version + pub hardware_version: u16, + /// Hardware minor revision + pub hardware_revision: u16, + /// 18 digit Framework Serial that starts with FRA + /// the first 10 digits must be allocated by framework + pub serial: [u8; 20], + /// Length of descriptor following heade + pub descriptor_length: u32, + /// CRC of descriptor + pub descriptor_crc32: u32, + /// CRC of header before this value + pub crc32: u32, +} diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ae6c1f08..a7a10111 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -884,6 +884,26 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { if let Err(err) = ec.check_bay_status() { error!("{:?}", err); } + if let Ok(header) = ec.read_gpu_desc_header() { + println!(" Expansion Bay EEPROM"); + println!( + " Valid: {:?}", + header.magic == [0x32, 0xAC, 0x00, 0x00] + ); + println!(" HW Version: {}.{}", { header.hardware_version }, { + header.hardware_revision + }); + if log_enabled!(Level::Info) { + println!(" Hdr Length {} B", { header.length }); + println!(" Desc Ver: {}.{}", { header.desc_ver_major }, { + header.desc_ver_minor + }); + println!(" Serialnumber:{:X?}", { header.serial }); + println!(" Desc Length: {} B", { header.descriptor_length }); + println!(" Desc CRC: {:X}", { header.descriptor_crc32 }); + println!(" Hdr CRC: {:X}", { header.crc32 }); + } + } } else if let Some(maybe_limit) = args.charge_limit { print_err(handle_charge_limit(&ec, maybe_limit)); } else if let Some((limit, soc)) = args.charge_current_limit { From d13b41b3fa6154eb93899c53638c5b9a3180ffb1 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 19:12:11 +0800 Subject: [PATCH 7/7] Add --dump-gpu-descriptor-file Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 5 +++++ framework_lib/src/chromium_ec/mod.rs | 10 ++++++++++ framework_lib/src/commandline/clap_std.rs | 7 +++++++ framework_lib/src/commandline/mod.rs | 22 ++++++++++++++++++++++ framework_lib/src/commandline/uefi.rs | 9 +++++++++ 5 files changed, 53 insertions(+) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index 6882c761..f8fb2172 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -87,6 +87,11 @@ It's intended for advanced users who build their own expansion bay module. The I2C address of the EEPROM is hardcoded to 0x50. ``` +# Dump current descriptor (e.g. for backup) +> framework_tool --dump-gpu-descriptor-file foo.bin +Dumping to foo.bin +Wrote 153 bytes to foo.bin + # Update just the serial number > framework_tool --flash_gpu_descriptor GPU FRAKMQCP41500ASSY1 > framework_tool --flash_gpu_descriptor 13 FRAKMQCP41500ASSY1 diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index a596c7cd..22e7134e 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1254,6 +1254,16 @@ impl CrosEc { Ok(()) } + pub fn read_gpu_descriptor(&self) -> EcResult> { + let header = self.read_gpu_desc_header()?; + if header.magic != [0x32, 0xAC, 0x00, 0x00] { + return Err(EcError::DeviceError( + "Invalid descriptor hdr magic".to_string(), + )); + } + self.read_ec_gpu_chunk(0x00, header.descriptor_length as u16) + } + pub fn read_gpu_desc_header(&self) -> EcResult { let bytes = self.read_ec_gpu_chunk(0x00, core::mem::size_of::() as u16)?; diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index bd65139e..148042b2 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -274,6 +274,10 @@ struct ClapCli { /// File to write to the gpu EEPROM #[arg(long)] flash_gpu_descriptor_file: Option, + + /// File to dump the gpu EEPROM to + #[arg(long)] + dump_gpu_descriptor_file: Option, } /// Parse a list of commandline arguments and return the struct @@ -464,6 +468,9 @@ pub fn parse(args: &[String]) -> Cli { flash_gpu_descriptor_file: args .flash_gpu_descriptor_file .map(|x| x.into_os_string().into_string().unwrap()), + dump_gpu_descriptor_file: args + .dump_gpu_descriptor_file + .map(|x| x.into_os_string().into_string().unwrap()), raw_command: vec![], } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index a7a10111..11a64cce 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -202,6 +202,7 @@ pub struct Cli { pub info: bool, pub flash_gpu_descriptor: Option<(u8, String)>, pub flash_gpu_descriptor_file: Option, + pub dump_gpu_descriptor_file: Option, // UEFI only pub allupdate: bool, pub paginate: bool, @@ -680,6 +681,24 @@ fn dump_ec_flash(ec: &CrosEc, dump_path: &str) { } } +fn dump_dgpu_eeprom(ec: &CrosEc, dump_path: &str) { + let flash_bin = ec.read_gpu_descriptor().unwrap(); + + #[cfg(not(feature = "uefi"))] + { + let mut file = fs::File::create(dump_path).unwrap(); + file.write_all(&flash_bin).unwrap(); + } + #[cfg(feature = "uefi")] + { + let ret = crate::uefi::fs::shell_write_file(dump_path, &flash_bin); + if ret.is_err() { + println!("Failed to dump EC FW image."); + } + } + println!("Wrote {} bytes to {}", flash_bin.len(), dump_path); +} + fn compare_version(device: Option, version: String, ec: &CrosEc) -> i32 { println!("Target Version {:?}", version); @@ -1270,6 +1289,9 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else { println!("Unsupported on this platform"); } + } else if let Some(dump_path) = &args.dump_gpu_descriptor_file { + println!("Dumping to {}", dump_path); + dump_dgpu_eeprom(&ec, dump_path); } 0 diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index a5e8c4c5..82d6c253 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -117,6 +117,7 @@ pub fn parse(args: &[String]) -> Cli { help: false, flash_gpu_descriptor: None, flash_gpu_descriptor_file: None, + dump_gpu_descriptor_file: None, allupdate: false, info: false, raw_command: vec![], @@ -773,6 +774,14 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; + } else if arg == "--dump-gpu-descriptor-file" { + cli.dump_gpu_descriptor_file = if args.len() > i + 1 { + Some(args[i + 1].clone()) + } else { + println!("Need to provide a value for --dump_gpu_descriptor_file. PATH"); + None + }; + found_an_option = true; } }