diff --git a/Cargo.lock b/Cargo.lock index 96d12dd43..571e68b15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1479,7 +1479,6 @@ dependencies = [ "num-traits", "rand_core 0.5.1", "rkyv 0.4.3", - "sha2", "spinor", "susres", "ticktimer-server", diff --git a/services/aes/src/vex.rs b/services/aes/src/vex.rs index bfb5790bb..dbe41e4b7 100644 --- a/services/aes/src/vex.rs +++ b/services/aes/src/vex.rs @@ -406,6 +406,15 @@ macro_rules! define_aes_impl { pub fn key_size(&self) -> usize { $key_bits as usize } + pub fn clear(&mut self) { + for b in self.enc_key.iter_mut() { + *b = 0; + } + for b in self.dec_key.iter_mut() { + *b = 0; + } + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + } } impl NewBlockCipher for $name { diff --git a/services/root-keys/Cargo.toml b/services/root-keys/Cargo.toml index 3f6e18d80..38c2dc3cd 100644 --- a/services/root-keys/Cargo.toml +++ b/services/root-keys/Cargo.toml @@ -39,7 +39,6 @@ engine-25519 = {path="../engine-25519"} # hardware acceleration adaptations are inserted into a fork of the main branch. rand_core = "0.5" hex = {version = "0.4.3", default-features = false, features = []} -sha2 = {version = "0.9.5", default-features = false, features = []} [dependencies.curve25519-dalek] version = "3.1.0" # note this is patched to our fork in ./Cargo.toml default-features = false diff --git a/services/root-keys/locales/i18n.json b/services/root-keys/locales/i18n.json index 365f24378..a66a9a72a 100644 --- a/services/root-keys/locales/i18n.json +++ b/services/root-keys/locales/i18n.json @@ -20,10 +20,50 @@ "en-audio": "This will initialize the root keys. We're going to need two passwords, one for device unlock, and one for software updates." }, "rootkeys.setup_wait": { - "en": "Initializing keys. This may\ntake a couple minutes.\nDo not reset or sleep!", + "en": "Initializing keys.\nThis takes a few minutes.\nDo not reset or sleep!", "zh": "现在正在初始化\n可能还要等一下\n不要重置或睡眠!\n", "en-audio": "Initializing keys. This may take a while, don't reset or sleep the device." }, + "rootkeys.init.signing_loader": { + "en": "Signing loader.\nThis takes a few minutes.\nDo not reset or sleep!", + "zh": "签名引导程序\n可能还要等一下\n不要重置或睡眠!\n", + "en-audio": "Signing loader. This may take a while, don't reset or sleep the device." + }, + "rootkeys.init.signing_kernel": { + "en": "Signing kernel.\nThis takes a few minutes.\nDo not reset or sleep!", + "zh": "签名内核\n可能还要等一下\n不要重置或睡眠!\n", + "en-audio": "Signing kernel. This may take a while, don't reset or sleep the device." + }, + "rootkeys.init.backup_gateware": { + "en": "Backing up gateware.\nThis takes a few minutes.\nDo not reset or sleep!", + "zh": "备份比特流\n可能还要等一下\n不要重置或睡眠!\n", + "en-audio": "Backing up gateware. This may take a while, don't reset or sleep the device." + }, + "rootkeys.init.patching_keys": { + "en": "Injecting new keys.\nThis takes a few minutes.\nDo not reset or sleep!", + "zh": "更换新钥匙\n可能还要等一下\n不要重置或睡眠!\n", + "en-audio": "Injecting new keys. This may take a while, don't reset or sleep the device." + }, + "rootkeys.init.verifying_gateware": { + "en": "Verifying gateware.\nThis takes a few minutes.\nDo not reset or sleep!", + "zh": "验证比特流\n可能还要等一下\n不要重置或睡眠!\n", + "en-audio": "Verifying gateware. This may take a while, don't reset or sleep the device." + }, + "rootkeys.init.finished": { + "en": "Preparing to reboot...", + "zh": "准备重启...", + "en-audio": "Preparing to reboot." + }, + "rootkeys.init.failure": { + "en": "Process failed, aborting!", + "zh": "正在初始化失败,中止!", + "en-audio": "Process failed, aborting" + }, + "rootkeys.init.confirmation": { + "en": "This cannot be undone.\nContinue?", + "zh": "这无法撤消,要继续吗?", + "en-audio": "This cannot be undone. Continue?" + }, "rootkeys.policy_clear": { "en": "Clear after use", "zh": "使用后删除", diff --git a/services/root-keys/src/implementation.rs b/services/root-keys/src/implementation.rs index 86cc63e34..440904e03 100644 --- a/services/root-keys/src/implementation.rs +++ b/services/root-keys/src/implementation.rs @@ -1,11 +1,13 @@ use gam::FONT_TOTAL_LEN; use ticktimer_server::Ticktimer; use utralib::generated::*; +use xous::SOC_STAGING_GW_LEN; use crate::api::*; use core::num::NonZeroUsize; use num_traits::*; use gam::modal::{Modal, Slider}; +use locales::t; use crate::bcrypt::*; use crate::api::PasswordType; @@ -17,6 +19,9 @@ use digest::Digest; use graphics_server::BulkRead; use core::mem::size_of; +use aes_xous::{Aes256, NewBlockCipher, BlockDecrypt, BlockEncrypt}; +use cipher::generic_array::GenericArray; + use root_keys::key2bits::*; // TODO: add hardware acceleration for BCRYPT so we can hit the OWASP target without excessive UX delay @@ -78,6 +83,13 @@ pub(crate) mod keyrom_config { pub const INITIALIZED: KeyField = KeyField::new(1, 27); } +enum RootkeyResult { + AlignmentError, + KeyError, + IntegrityError, + FlashError, +} + /// This structure is mapped into the password cache page and can be zero-ized at any time /// we avoid using fancy Rust structures because everything has to "make sense" after a forced zero-ization /// The "password" here is generated as follows: @@ -90,6 +102,125 @@ struct PasswordCache { hashed_boot_pw_valid: u32, // non-zero for valid hashed_update_pw: [u8; 32], hashed_update_pw_valid: u32, + fpga_key: [u8; 32], + fpga_key_valid: u32, +} + +/// helper routine that will reverse the order of bits. uses a divide-and-conquer approach. +fn bitflip(input: &[u8], output: &mut [u8]) { + assert!((input.len() % 4 == 0) && (output.len() % 4 == 0) && (input.len() == output.len())); + for (src, dst) in + input.chunks(4).into_iter() + .zip(output.chunks_mut(4).into_iter()) { + let mut word = u32::from_le_bytes(src.try_into().unwrap()); // read in as LE + word = ((word >> 1) & 0x5555_5555) | ((word & (0x5555_5555)) << 1); + word = ((word >> 2) & 0x3333_3333) | ((word & (0x3333_3333)) << 2); + word = ((word >> 4) & 0x0F0F_0F0F) | ((word & (0x0F0F_0F0F)) << 4); + // copy out as BE, this performs the final byte-level swap needed by the divde-and-conquer algorithm + for (&s, d) in word.to_be_bytes().iter().zip(dst.iter_mut()) { + *d = s; + } + } +} + +const AES_BLOCKSIZE: usize = 16; +/// This structure encapsulates the tools necessary to create an Oracle that can go from +/// the encrypted bitstream to plaintext and back again, based on the position in the bitstream. +/// It is a partial re-implementation of the Cbc crate from block-ciphers, and the reason we're +/// not just using the stock Cbc crate is that it doesn't seem to support restarting the Cbc +/// stream from an arbitrary position. +struct BitstreamOracle<'a> { + bitstream: &'a [u8], + cipher: Aes256, + iv: [u8; AES_BLOCKSIZE], + /// subslice of the bitstream that contains just the encrypted area of the bitstream + ciphertext: &'a [u8], +} +impl<'a> BitstreamOracle<'a> { + pub fn new(key: &'a[u8], bitstream: &'a[u8]) -> BitstreamOracle<'a> { + let mut position: usize = 0; + + // Search through the bitstream for the key words that define the IV and ciphertetx length. + // This is done so that if extra headers are added or modified, the code doesn't break (versus coding in static offsets). + let mut iv_pos = 0; + while position < bitstream.len() { + let cwd = u32::from_be_bytes(bitstream[position..position+4].try_into().unwrap()); + if cwd == 0x3001_6004 { + iv_pos = position + 4 + } + if cwd == 0x3003_4001 { + break; + } + position += 1; + } + + let position = position + 4; + let ciphertext_len = 4 * u32::from_be_bytes(bitstream[position..position+4].try_into().unwrap()); + let ciphertext_start = position + 4; + log::info!("ciphertext len: {} bytes, start: 0x{:08x}", ciphertext_len, ciphertext_start); + let ciphertext = &bitstream[ciphertext_start..ciphertext_start + ciphertext_len as usize]; + + let mut iv_bytes: [u8; AES_BLOCKSIZE] = [0; AES_BLOCKSIZE]; + bitflip(&bitstream[iv_pos..iv_pos + AES_BLOCKSIZE], &mut iv_bytes); + log::info!("recovered iv (pre-flip): {:x?}", &bitstream[iv_pos..iv_pos + AES_BLOCKSIZE]); + log::info!("recovered iv : {:x?}", &iv_bytes); + + let cipher = Aes256::new(key.try_into().unwrap()); + + BitstreamOracle { + bitstream, + cipher, + iv: iv_bytes, + ciphertext, + } + } + pub fn clear(&mut self) { + self.cipher.clear(); + } + /// decrypts a portion of the bitstream starting at "from", of length output + pub fn decrypt(&self, from: usize, output: &mut [u8]) { + assert!(from & (AES_BLOCKSIZE - 1) == 0); // all requests must be an even multiple of an AES block size + + let mut index = from; + let mut temp_block = [0; AES_BLOCKSIZE]; + let mut chain: [u8; AES_BLOCKSIZE] = [0; AES_BLOCKSIZE]; + for block in output.chunks_mut(AES_BLOCKSIZE).into_iter() { + if index == 0 { + chain = self.iv; + } else { + bitflip(&self.ciphertext[index - AES_BLOCKSIZE..index], &mut chain); + }; + // copy the ciphertext into the temp_block, with a bitflip + bitflip(&self.ciphertext[index..index + AES_BLOCKSIZE], &mut temp_block); + + // replaces the ciphertext with "plaintext" + let mut d = GenericArray::clone_from_slice(&mut temp_block); + self.cipher.decrypt_block(&mut d); + for (&src, dst) in d.iter().zip(temp_block.iter_mut()) { + *dst = src; + } + + // now XOR against the IV into the final output block. We use a "temp" block so we + // guarantee an even block size for AES, but in fact, the output block does not + // have to be an even multiple of our block size! + for (dst, (&src, &iv)) in block.iter_mut().zip(temp_block.iter().zip(chain.iter())) { + *dst = src ^ iv; + } + index += AES_BLOCKSIZE; + } + } + pub fn ciphertext_len(&self) -> usize { + self.ciphertext.len() + } +} +impl<'a> Drop for BitstreamOracle<'a> { + fn drop(&mut self) { + self.cipher.clear(); + for b in self.iv.iter_mut() { + *b = 0; + } + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + } } pub(crate) struct RootKeys { @@ -110,6 +241,7 @@ pub(crate) struct RootKeys { gam: gam::Gam, // for raising UX elements directly gfx: graphics_server::Gfx, // for reading out font planes for signing verification spinor: spinor::Spinor, + ticktimer: ticktimer_server::Ticktimer, } impl RootKeys { @@ -179,7 +311,8 @@ impl RootKeys { trng: trng::Trng::new(&xns).expect("couldn't connect to TRNG server"), gam: gam::Gam::new(&xns).expect("couldn't connect to GAM"), gfx: graphics_server::Gfx::new(&xns).expect("couldn't connect to gfx"), - spinor + spinor, + ticktimer: ticktimer_server::Ticktimer::new().expect("couldn't connect to ticktimer"), }; keys @@ -200,6 +333,11 @@ impl RootKeys { *p = 0; } (*pcache_ptr).hashed_update_pw_valid = 0; + + for p in (*pcache_ptr).fpga_key.iter_mut() { + *p = 0; + } + (*pcache_ptr).fpga_key_valid = 0; } } } @@ -418,13 +556,13 @@ impl RootKeys { // sensitive_slice is our staging area for the new keyrom contents let sensitive_slice = self.sensitive_data.as_slice_mut::(); for (src, dst) in public_key.chunks(4).into_iter() - .zip(sensitive_slice[KeyRomLocs::SELFSIGN_PUBKEY as usize..KeyRomLocs::SELFSIGN_PUBKEY as usize + 256/(size_of::()*8)].iter_mut()) { + .zip(sensitive_slice[KeyRomLocs::SELFSIGN_PUBKEY as usize..KeyRomLocs::SELFSIGN_PUBKEY as usize + 256/(size_of::()*8)].iter_mut()) { *dst = u32::from_be_bytes(src.try_into().unwrap()) } } // extract the update password key from the cache, and apply it to the private key - let pcache: &PasswordCache = unsafe{&*(self.pass_cache.as_mut_ptr() as *mut PasswordCache)}; + let pcache: &mut PasswordCache = unsafe{&mut *(self.pass_cache.as_mut_ptr() as *mut PasswordCache)}; #[cfg(feature = "hazardous-debug")] { log::info!("cached boot passwords {:x?}", pcache.hashed_boot_pw); @@ -434,22 +572,22 @@ impl RootKeys { let mut private_key_enc: [u8; ed25519_dalek::SECRET_KEY_LENGTH] = [0; ed25519_dalek::SECRET_KEY_LENGTH]; // we do this from to try and avoid making as few copies of the hashed password as possible for (dst, (plain, key)) in - private_key_enc.iter_mut() - .zip(keypair.secret.to_bytes().iter() - .zip(pcache.hashed_update_pw.iter())) { - *dst = plain ^ key; + private_key_enc.iter_mut() + .zip(keypair.secret.to_bytes().iter() + .zip(pcache.hashed_update_pw.iter())) { + *dst = plain ^ key; } // store the private key to the keyrom staging area { let sensitive_slice = self.sensitive_data.as_slice_mut::(); for (src, dst) in private_key_enc.chunks(4).into_iter() - .zip(sensitive_slice[KeyRomLocs::SELFSIGN_PRIVKEY as usize..KeyRomLocs::SELFSIGN_PRIVKEY as usize + 256/(size_of::()*8)].iter_mut()) { + .zip(sensitive_slice[KeyRomLocs::SELFSIGN_PRIVKEY as usize..KeyRomLocs::SELFSIGN_PRIVKEY as usize + 256/(size_of::()*8)].iter_mut()) { *dst = u32::from_be_bytes(src.try_into().unwrap()) } } - update_progress(10, progress_modal, progress_action); + update_progress(5, progress_modal, progress_action); // generate and store a root key (aka boot key), this is what is unlocked by the "boot password" // ironically, it's a "lower security" key because it just acts as a gatekeeper to further @@ -457,50 +595,61 @@ impl RootKeys { // think of this more as a user PIN login confirmation, than as a significant cryptographic event let mut boot_key_enc: [u8; 32] = [0; 32]; for (dst, key) in - boot_key_enc.chunks_mut(4).into_iter() - .zip(pcache.hashed_boot_pw.chunks(4).into_iter()) { - let key_word = self.trng.get_u32().unwrap().to_be_bytes(); - // just unroll this loop, it's fast and easy enough - (*dst)[0] = key[0] ^ key_word[0]; - (*dst)[1] = key[1] ^ key_word[1]; - (*dst)[2] = key[2] ^ key_word[2]; - (*dst)[3] = key[3] ^ key_word[3]; - // also note that interestingly, we don't have to XOR it with the hashed boot password -- - // this key isn't used by this routine, just initialized, so really, it only matters to - // XOR it with the password when you use it the first time to encrypt something. + boot_key_enc.chunks_mut(4).into_iter() + .zip(pcache.hashed_boot_pw.chunks(4).into_iter()) { + let key_word = self.trng.get_u32().unwrap().to_be_bytes(); + // just unroll this loop, it's fast and easy enough + (*dst)[0] = key[0] ^ key_word[0]; + (*dst)[1] = key[1] ^ key_word[1]; + (*dst)[2] = key[2] ^ key_word[2]; + (*dst)[3] = key[3] ^ key_word[3]; + // also note that interestingly, we don't have to XOR it with the hashed boot password -- + // this key isn't used by this routine, just initialized, so really, it only matters to + // XOR it with the password when you use it the first time to encrypt something. } // store the boot key to the keyrom staging area { let sensitive_slice = self.sensitive_data.as_slice_mut::(); for (src, dst) in private_key_enc.chunks(4).into_iter() - .zip(sensitive_slice[KeyRomLocs::USER_KEY as usize..KeyRomLocs::USER_KEY as usize + 256/(size_of::()*8)].iter_mut()) { + .zip(sensitive_slice[KeyRomLocs::USER_KEY as usize..KeyRomLocs::USER_KEY as usize + 256/(size_of::()*8)].iter_mut()) { *dst = u32::from_be_bytes(src.try_into().unwrap()) } } - update_progress(20, progress_modal, progress_action); - // sign the loader + progress_modal.modify(None, Some(t!("rootkeys.init.signing_loader", xous::LANG)), false, None, false, None); + update_progress(10, progress_modal, progress_action); let (loader_sig, loader_len) = self.sign_loader(&keypair); - update_progress(30, progress_modal, progress_action); // sign the kernel + progress_modal.modify(None, Some(t!("rootkeys.init.signing_kernel", xous::LANG)), false, None, false, None); + update_progress(20, progress_modal, progress_action); let (kernel_sig, kernel_len) = self.sign_kernel(&keypair); - update_progress(40, progress_modal, progress_action); + update_progress(25, progress_modal, progress_action); // encrypt the FPGA key using the update password. in an un-init system, it is provided to us in plaintext format // e.g. in the case that we're doing a BBRAM boot (eFuse flow would give us a 0's key and we'd later on set it) #[cfg(feature = "hazardous-debug")] self.debug_print_key(KeyRomLocs::FPGA_KEY as usize, 256, "FPGA key before encryption: "); { let sensitive_slice = self.sensitive_data.as_slice_mut::(); + // before we encrypt it, stash a copy in our password cache, as we'll need it later on to encrypt the bitstream + for (word, key) in sensitive_slice[KeyRomLocs::FPGA_KEY as usize..KeyRomLocs::FPGA_KEY as usize + 256/(size_of::()*8)].iter() + .zip(pcache.fpga_key.chunks_mut(4).into_iter()) { + for (&s, d) in word.to_be_bytes().iter().zip(key.iter_mut()) { + *d = s; + } + } + pcache.fpga_key_valid = 1; + + // now encrypt it in the staging area for (word, key_word) in sensitive_slice[KeyRomLocs::FPGA_KEY as usize..KeyRomLocs::FPGA_KEY as usize + 256/(size_of::()*8)].iter_mut() - .zip(pcache.hashed_update_pw.chunks(4).into_iter()) { + .zip(pcache.hashed_update_pw.chunks(4).into_iter()) { *word = *word ^ u32::from_be_bytes(key_word.try_into().unwrap()); } } - update_progress(50, progress_modal, progress_action); + update_progress(30, progress_modal, progress_action); // set the "init" bit in the staging area { @@ -509,7 +658,6 @@ impl RootKeys { let config = self.keyrom.rf(utra::keyrom::DATA_DATA); sensitive_slice[KeyRomLocs::CONFIG as usize] |= keyrom_config::INITIALIZED.ms(1); } - update_progress(60, progress_modal, progress_action); #[cfg(feature = "hazardous-debug")] { @@ -521,19 +669,221 @@ impl RootKeys { // Because we're initializing keys for the *first* time, make a backup copy of the bitstream to // the staging area. Note that if we're doing an update, the update target would already be // in the staging area, so this step should be skipped. - self.make_gateware_backup(60, 70, progress_modal, progress_action); + progress_modal.modify(None, Some(t!("rootkeys.init.backup_gateware", xous::LANG)), false, None, false, None); + update_progress(40, progress_modal, progress_action); + if self.make_gateware_backup(40, 60, progress_modal, progress_action).is_err() { + log::error!("error occured in make_gateware_backup."); + return false; + }; // compute the keyrom patch set for the bitstream // at this point the KEYROM as replicated in sensitive_slice should have all its assets in place + progress_modal.modify(None, Some(t!("rootkeys.init.patching_keys", xous::LANG)), false, None, false, None); + update_progress(60, progress_modal, progress_action); + + let gateware = self.gateware.as_slice::(); + assert!(pcache.fpga_key_valid == 1); + // note to self: BitstreamOracle implements Drop which will clear the key schedule and iv + let oracle = BitstreamOracle::new(&pcache.fpga_key, gateware); + + if self.patch_keys(&oracle, 60, 70, progress_modal, progress_action).is_err() { + log::error!("error occured in patch_keys."); + return false; + } patch::should_patch(42); + // verify that the patch worked + progress_modal.modify(None, Some(t!("rootkeys.init.verifying_gateware", xous::LANG)), false, None, false, None); + update_progress(90, progress_modal, progress_action); + + // --> put the verify call here + + // sign the image, commit the signature + // --> add something to sign the gateware region here + // --> format the signatures and write them to FLASH + // finalize the progress bar on exit -- always leave at 100% + progress_modal.modify(None, Some(t!("rootkeys.init.finished", xous::LANG)), false, None, false, None); + update_progress(100, progress_modal, progress_action); + + true + } + + pub fn test(&mut self, progress_modal: &mut Modal, progress_action: &mut Slider) -> bool { + let pcache: &mut PasswordCache = unsafe{&mut *(self.pass_cache.as_mut_ptr() as *mut PasswordCache)}; + + // setup the local cache + let sensitive_slice = self.sensitive_data.as_slice_mut::(); + for addr in 0..256 { + self.keyrom.wfo(utra::keyrom::ADDRESS_ADDRESS, addr); + sensitive_slice[addr as usize] = self.keyrom.rf(utra::keyrom::DATA_DATA); + } + + for (word, key) in sensitive_slice[KeyRomLocs::FPGA_KEY as usize..KeyRomLocs::FPGA_KEY as usize + 256/(size_of::()*8)].iter() + .zip(pcache.fpga_key.chunks_mut(4).into_iter()) { + for (&s, d) in word.to_be_bytes().iter().zip(key.iter_mut()) { + *d = s; + } + } + pcache.fpga_key_valid = 1; + + // make the oracle + let gateware = self.gateware.as_slice::(); + assert!(pcache.fpga_key_valid == 1); + let oracle = BitstreamOracle::new(&pcache.fpga_key, gateware); + + // TEST + if self.patch_keys(&oracle, 60, 80, progress_modal, progress_action).is_err() { + log::error!("error occured in patch_keys."); + return false; + } + + if self.verify_gateware(&oracle, 80, 100, progress_modal, progress_action).is_err() { + log::error!("error occurred in gateware verification"); + return false; + } + // finalize the progress bar on exit -- always leave at 100% + progress_modal.modify(None, Some(t!("rootkeys.init.finished", xous::LANG)), false, None, false, None); update_progress(100, progress_modal, progress_action); true } - fn make_gateware_backup(&mut self, prog_start: u32, prog_end: u32, progress_modal: &mut Modal, progress_action: &mut Slider) { - + fn patch_keys(&self, oracle: &BitstreamOracle, prog_start: u32, prog_end: u32, progress_modal: &mut Modal, progress_action: &mut Slider) -> Result<(), RootkeyResult> { + let mut last_prog_update = 0; + + // extract the HMAC header + let mut hmac_header: [u8; 64] = [0; 64]; + oracle.decrypt(0, &mut hmac_header); + + // check that the header has a well-known sequence in it -- sanity check the AES key + let mut pass = true; + for &b in hmac_header[32..].iter() { + if b != 0x6C { + pass = false; + } + } + if !pass { + log::error!("hmac_header did not decrypt correctly: {:x?}", hmac_header); + return Err(RootkeyResult::KeyError); + } + + // patch the keyrom frames + for frame in patch::PATCH_FRAMES.iter() { + + } + + // now seek to the various frames and patch their data... + Ok(()) + } + + fn verify_gateware(&self, oracle: &BitstreamOracle, prog_start: u32, prog_end: u32, progress_modal: &mut Modal, progress_action: &mut Slider) -> Result<(), RootkeyResult> { + let mut last_prog_update = 0; + + let mut hmac_area = [0; 64]; + oracle.decrypt(0, &mut hmac_area); + let mut hmac_code: [u8; 32] = [0; 32]; + for (dst, (&hm1, &mask)) in + hmac_code.iter_mut() + .zip(hmac_area[0..32].iter().zip(hmac_area[32..64].iter())) { + *dst = hm1 ^ mask; + } + log::trace!("hmac code: {:x?}", hmac_code); + + log::debug!("verifying gateware"); + let mut hasher = engine_sha512::Sha256::new(); + // magic number alert: + // 160 = reserved space for the 2nd hash + // 320 = some padding that is built into the file format, for whatever reason xilinx picked + let tot_len = oracle.ciphertext_len() - 320 - 160; + // slow but steady. we can optimize later. + // for now, very tiny optimization - use blocksize * 2 because that happens to be divisible by the bitstream parameters + let mut decrypt = [0; AES_BLOCKSIZE*2]; + let mut flipped = [0; AES_BLOCKSIZE*2]; + for index in (0..tot_len).step_by(AES_BLOCKSIZE*2) { + oracle.decrypt(index, &mut decrypt); + bitflip(&decrypt, &mut flipped); + hasher.update(&flipped); + + let progress = ((prog_end - prog_start) * index as u32) / tot_len as u32; + if progress != last_prog_update { + log::debug!("progress: {}", progress); + last_prog_update = progress; + update_progress(progress + prog_start, progress_modal, progress_action); + } + } + let h1_digest: [u8; 32] = hasher.finalize().try_into().unwrap(); + log::trace!("computed hash: {:x?}", h1_digest); + + let mut hasher2 = engine_sha512::Sha256::new(); + let footer_mask: [u8; 32] = [0x3A; 32]; + let mut masked_footer: [u8; 32] = [0; 32]; + for (dst, (&hm2, &mask)) in + masked_footer.iter_mut() + .zip(hmac_code.iter().zip(footer_mask.iter())) { + *dst = hm2 ^ mask; + } + log::debug!("masked_footer: {:x?}", masked_footer); + log::debug!("footer_mask: {:x?}", footer_mask); + let mut masked_footer_flipped: [u8; 32] = [0; 32]; + bitflip(&masked_footer, &mut masked_footer_flipped); + let mut footer_mask_flipped: [u8; 32] = [0; 32]; + bitflip(&footer_mask, &mut footer_mask_flipped); + hasher2.update(masked_footer_flipped); + hasher2.update(footer_mask_flipped); + hasher2.update(h1_digest); + let h2_digest: [u8; 32] = hasher2.finalize().try_into().unwrap(); + + log::debug!("h2 hash: {:x?}", h2_digest); + let mut ref_digest_flipped: [u8; 32] = [0; 32]; + oracle.decrypt(oracle.ciphertext_len() - 32, &mut ref_digest_flipped); + let mut ref_digest: [u8; 32] = [0; 32]; + log::debug!("ref digest (flipped): {:x?}", ref_digest_flipped); + bitflip(&ref_digest_flipped, &mut ref_digest); + log::debug!("ref digest : {:x?}", ref_digest); + + let mut matching = true; + for (&l, &r) in ref_digest.iter().zip(h2_digest.iter()) { + if l != r { + matching = false; + } + } + + if matching { + Ok(()) + } else { + Err(RootkeyResult::IntegrityError) + } + } + + + fn make_gateware_backup(&mut self, prog_start: u32, prog_end: u32, progress_modal: &mut Modal, progress_action: &mut Slider) -> Result<(), RootkeyResult> { + let gateware_dest = self.staging.as_slice::(); + let gateware_src = self.gateware.as_slice::(); + + log::trace!("src: {:x?}", &gateware_src[0..32]); + log::trace!("dst: {:x?}", &gateware_dest[0..32]); + + let mut last_prog_update = 0; + const PATCH_CHUNK: usize = 65536; // this controls the granularity of the erase operation + let mut dest_patch_addr = xous::SOC_STAGING_GW_LOC; + for (dst, src) in + gateware_dest.chunks(PATCH_CHUNK).into_iter() + .zip(gateware_src.chunks(PATCH_CHUNK)) { + log::debug!("writing {} backup bytes to address 0x{:08x}", src.len(), dest_patch_addr); + self.spinor.patch(dst, dest_patch_addr, src, 0) + .map_err(|_| RootkeyResult::FlashError)?; + dest_patch_addr += PATCH_CHUNK as u32; + + let progress = ((prog_end - prog_start) * (dest_patch_addr - xous::SOC_STAGING_GW_LOC)) / xous::SOC_STAGING_GW_LEN; + if progress != last_prog_update { + last_prog_update = progress; + update_progress(progress + prog_start, progress_modal, progress_action); + } + } + + log::trace!("src: {:x?}", &gateware_src[0..32]); + log::trace!("dst: {:x?}", &gateware_dest[0..32]); + Ok(()) } #[cfg(feature = "hazardous-debug")] @@ -622,11 +972,23 @@ impl RootKeys { /// Called by the UX layer at the epilogue of the initialization run. Allows suspend/resume to resume, /// and zero-izes any sensitive data that was created in the process. pub fn finish_key_init(&mut self) { - let sensitive_slice = self.sensitive_data.as_slice_mut::(); - // zeroize the RAM-backed data - for data in sensitive_slice.iter_mut() { - *data = 0; + // purge the password cache, if the policy calls for it + match self.boot_password_policy { + PasswordRetentionPolicy::AlwaysPurge => { + self.purge_password(PasswordType::Boot); + }, + _ => () } + match self.update_password_policy { + PasswordRetentionPolicy::AlwaysPurge => { + self.purge_password(PasswordType::Update); + }, + _ => () + } + + // now purge the keyrom copy and other temporaries + self.purge_sensitive_data(); + // re-allow suspend/resume ops self.susres.set_suspendable(true).expect("couldn't re-allow suspend/resume"); } diff --git a/services/root-keys/src/key2bits/key2bits.py b/services/root-keys/src/key2bits/key2bits.py index b1199cd0d..f3c8137ed 100755 --- a/services/root-keys/src/key2bits/key2bits.py +++ b/services/root-keys/src/key2bits/key2bits.py @@ -361,7 +361,15 @@ def main(): //! - entirely different FPGA target (in which case, the db/ files also need to be regenerated //! (this is a long process if you're not targting a prjxray pre-build target)) //! - bug fixes and enhancements to these routines +""".format(str(datetime.now()))) + f.write(""" +pub const PATCH_FRAMES: [u32; {}] = [\n""".format(len(patchdata_sorted))) + for frame_rec in patchdata_sorted: + f.write(" 0x{:x},\n".format(frame_rec[0])) + f.write("];\n") + + f.write(""" #[derive(Copy,Clone)] pub struct PatchBit {{ adr: u8, @@ -399,7 +407,7 @@ pub struct PatchFrame {{ /// returns a tuple of the value and its inverse; this is done to reduce the timing /// sidechannel. The inverse value needs to be consumed to prevent the compiler /// from optimizing out that path. -pub fn patch_frame(frame: u32, offset: u32, rom: &[u32; 256]) -> (Option, Option) {{\n""".format(str(datetime.now()), patchlen)) +pub fn patch_frame(frame: u32, offset: u32, rom: &[u32; 256]) -> (Option, Option) {{\n""".format(patchlen)) for frame_rec in patchdata_sorted: frame = frame_rec[1] diff --git a/services/root-keys/src/key2bits/patch.rs b/services/root-keys/src/key2bits/patch.rs old mode 100644 new mode 100755 index 2acb0ec3b..0f3c93a98 --- a/services/root-keys/src/key2bits/patch.rs +++ b/services/root-keys/src/key2bits/patch.rs @@ -1,11 +1,22 @@ -//! this file was auto-generated by key2bits.py on 2021-08-03 16:15:39.923623 +//! this file was auto-generated by key2bits.py on 2021-08-05 04:52:22.858205 //! manual regeneration is needed for the following cases: //! - rom.db changes (that is, the KEYROM pcells have been moved to a new location) //! - entirely different FPGA target (in which case, the db/ files also need to be regenerated //! (this is a long process if you're not targting a prjxray pre-build target)) //! - bug fixes and enhancements to these routines +pub const PATCH_FRAMES: [u32; 8] = [ + 0x35e, + 0x35f, + 0x360, + 0x361, + 0x364, + 0x365, + 0x366, + 0x367, +]; + #[derive(Copy,Clone)] pub struct PatchBit { adr: u8, @@ -9335,520 +9346,520 @@ mod tests { #[test] fn check_frames() { const ROM: [u32; 256] = [ - 0xc23cd219, - 0xeebbf79c, - 0xdaf1f2ff, - 0xed8609c3, - 0xd5688aed, - 0x0939a665, - 0x4549bf16, - 0x3353137b, - 0x0a4274c3, - 0x3b9c898e, - 0x94dfa9b7, - 0x261ac3de, - 0xe4687dc7, - 0x676d1ac0, - 0x0ee10953, - 0x1208bc5b, - 0x08b62484, - 0x7c67adef, - 0x074a98c5, - 0x043e6b6d, - 0x04a86ff9, - 0x8c8e6250, - 0x8218cfcc, - 0xbad3a4dc, - 0xd9623186, - 0x2804b7b9, - 0xb7e75c91, - 0xd02ab38c, - 0x8a861d9e, - 0xeac12339, - 0xf8aa7647, - 0x02ee199d, - 0x5446d14b, - 0xbd0b3b01, - 0x3fd72bb7, - 0xe1d9769c, - 0x04f7a783, - 0x5b156183, - 0x93528479, - 0xe7d63a36, - 0x9fb032d4, - 0x65561a59, - 0xa28277ed, - 0x24f8f228, - 0x6d694b43, - 0x11603f34, - 0xa939ce16, - 0x05fcf1b0, - 0x6482f3a4, - 0x72912737, - 0x5073e531, - 0x93697df3, - 0x0ef52af5, - 0x90343eb5, - 0xcf28f806, - 0x8bb749b9, - 0x1a8aac71, - 0xaac7ab09, - 0xd8f19b74, - 0x994208fd, - 0xc622999d, - 0xcaa9869a, - 0x4fb53039, - 0xf88548db, - 0xda8f2f3c, - 0x1cb25728, - 0xaa6a9b7d, - 0x229df41a, - 0xf74817fc, - 0x6e6c9920, - 0xe8461f64, - 0xd0b2aac3, - 0x3f177ac1, - 0x887a4564, - 0x087fcc95, - 0xc52b9b76, - 0x60747542, - 0xcb570e6a, - 0xacc738f2, - 0x824aea27, - 0xbb6cc798, - 0xf2b546c0, - 0xe0b6839c, - 0xccc79b4f, - 0x1407a8bc, - 0x3c76262b, - 0x2e435605, - 0xa238ea3f, - 0x564c9caa, - 0xd1deca3e, - 0xe0153fd3, - 0x13a17d60, - 0x727005bf, - 0x8cc3a65a, - 0xe36f48f4, - 0xd57e6e66, - 0x5a8cdbf6, - 0x055395b2, - 0x883bf07f, - 0x4bcb2b3f, - 0x12f1a33a, - 0x67742fa2, - 0x2ae6e3e6, - 0xa73d1532, - 0xf173734f, - 0xacd0279c, - 0x0b0df403, - 0xe67f393d, - 0x726644c5, - 0xd4a66213, - 0x66a0848a, - 0xdc4030d7, - 0x31d96202, - 0x38aab86a, - 0xc37c217b, - 0xb6d10075, - 0x146033eb, - 0x16155841, - 0x1e6370cd, - 0xe241573d, - 0x5ea78e74, - 0x7ee81251, - 0x80b883a3, - 0x02298026, - 0x12a64bcc, - 0x368b70f2, - 0x66dc373a, - 0x631ee448, - 0xfaaf4d7e, - 0x397b71e7, - 0x82592ea2, - 0xef11c334, - 0x65b2396b, - 0x62b5f950, - 0xddcc7818, - 0x30e580fe, - 0x2ab6bd79, - 0x0c7c22d7, - 0xe6bd0846, - 0xddbf6058, - 0xfd6344d6, - 0xc41e3e9f, - 0xe63e67df, - 0x3a1a85d9, - 0x94cb515e, - 0xae539804, - 0x1a547a4e, - 0xfe1b50b1, - 0x6a090592, - 0xca13aeef, - 0x2d954ffe, - 0x3bfa9d0e, - 0x34a66be6, - 0xfedc23db, - 0x30f46c28, - 0x58cd2f17, - 0xe22b7101, - 0x6569d851, - 0xf2bfe006, - 0xe5ebd3ac, - 0x1a7109d4, - 0xe418b45e, - 0x9e8a528b, - 0x73e5e002, - 0x93c36778, - 0x9b60978d, - 0xac6a1ad4, - 0x5775c4d8, - 0x363ec681, - 0xed5f9e92, - 0xa8a1a2c1, - 0xc266c020, - 0xe90c05a8, - 0x95e0e00e, - 0xa1e1a21d, - 0xaeaaac55, - 0x47c542df, - 0x891a6bca, - 0x1e4fe6e1, - 0xf62e99e5, - 0x23f920e0, - 0x48d45589, - 0x471ad55d, - 0x4c7017f2, - 0xb9585aa9, - 0xfcd79c54, - 0x997082d9, - 0x17b404e7, - 0xd03ca229, - 0xc0046a9d, - 0xa54a1522, - 0x3eff6a7e, - 0x638b0000, - 0xf7e15ec4, - 0x64332e25, - 0xd34f0f3f, - 0x615e7cc7, - 0xd2cde466, - 0xfc67e679, - 0x4fdce5c1, - 0x20326b03, - 0xf6d6a2cb, - 0xb986cafb, - 0x290cbaed, - 0x99d6f213, - 0x9e4db1ef, - 0xa09194f8, - 0xf85eaad0, - 0x52a1220e, - 0x34b29f13, - 0x58fe6240, - 0x85f4ce4e, - 0x22ddd5c4, - 0x274963fe, - 0x3e12942d, - 0x218683f2, - 0x733bde40, - 0xa7186d81, - 0x038dfdd2, - 0x7bb54892, - 0xab9da85d, - 0x6466470d, - 0x1e919fa4, - 0x7ab9e350, - 0xacc3fda7, - 0x41d51883, - 0x49858e45, - 0xbfb4824c, - 0xd72f0250, - 0xd3471d9b, - 0xa576978f, - 0xd5642a7d, - 0xd6e24e7e, - 0x4b13e297, - 0x91631f43, - 0x03ee16fd, - 0x909a39cf, - 0x985d82f4, - 0x4a26eead, - 0x5f5dc23f, - 0xc032d342, - 0xf1f803aa, - 0xd88be416, - 0x4e44b059, - 0x2776d97a, - 0x8820c5dc, - 0xf282a8f0, - 0xade41cb2, - 0x276b3d78, - 0xb99b5f9e, - 0x6d1d86d5, - 0x24c25ba5, - 0x035dce00, - 0xdeda1f93, - 0x6ec91a9c, - 0x47406a45, + 0x60dcb193, + 0x45ec46e3, + 0x8a603456, + 0x18be6e6a, + 0x758b9825, + 0x744c054a, + 0xde45b1ee, + 0xa31c7a2d, + 0xa17eb4ec, + 0xd3b82a54, + 0xc1bd97d8, + 0xe44ec5a7, + 0x256a20a2, + 0xc9915790, + 0x1b7c3c8c, + 0x19cae7df, + 0x22742779, + 0xb6370eb5, + 0x2b9505bb, + 0x19882bf0, + 0x43f0739a, + 0xc8e96614, + 0x444fd1a3, + 0x6dfe38ab, + 0xa35a461f, + 0xe15e9c9d, + 0x51ee1e39, + 0xa6226fcb, + 0x0ac1a97c, + 0xad3f88db, + 0x731d69c4, + 0xcec18e49, + 0xa2323a99, + 0xc395d5a9, + 0x57fc4e69, + 0x75fb524f, + 0xc2e3510a, + 0x059a65cd, + 0xfc559fd2, + 0x2f5876e1, + 0xd9c64259, + 0xb7245db6, + 0x7966d52e, + 0x084f424d, + 0x50b6e873, + 0x506b4533, + 0x1a003ad1, + 0x8014c795, + 0x041eb824, + 0x871fbccf, + 0xbdaf532a, + 0x4c59ec86, + 0x9d489e66, + 0x549b3d1d, + 0xcbc67524, + 0xa2ccc7f4, + 0x81ef25a4, + 0x4cc754b9, + 0xae5ff5d3, + 0x3864eedc, + 0x5d9c9961, + 0x628ffd74, + 0xe155d9ad, + 0x5f10cf99, + 0x5ce6cef2, + 0x8880d25b, + 0xc3e394ea, + 0xdc1408dc, + 0x89ddb484, + 0x87671ca1, + 0xc3f89512, + 0x3123d2ba, + 0xfd5b4646, + 0x432af9d4, + 0xe8e6d76f, + 0x126ab69e, + 0x4781f5a4, + 0xd05d5491, + 0x24d768a6, + 0xe0ad5938, + 0xf6891af7, + 0x307c7e49, + 0x3c20e9b8, + 0xdffdbba7, + 0x6ab2f819, + 0x4122f8ee, + 0x2ce7b263, + 0xd62ad4fe, + 0x92a9056f, + 0xa4299bab, + 0xf576e971, + 0x50a995a2, + 0x8fdafea8, + 0x9f486305, + 0xf84d6d8d, + 0xa557f32c, + 0x942e4e6b, + 0x8a04e58c, + 0xeb007188, + 0x73802f73, + 0x1766027d, + 0xf896edd5, + 0x287e06d7, + 0x42270ef8, + 0x5e431aa2, + 0xb41835eb, + 0x82cba690, + 0x55a52940, + 0xf21903e7, + 0x8555f4f7, + 0x83e2d1ad, + 0x5ec4af4a, + 0x05382947, + 0xa7ebfc3f, + 0x0b9a918a, + 0xa5cbcecd, + 0x71614505, + 0xa5a972f9, + 0x4e6340a0, + 0x8b51d8fa, + 0xe57da393, + 0x11422dae, + 0x1e77996d, + 0x06fa9308, + 0xe94af229, + 0x1eff351c, + 0x12aee9c9, + 0x0e0d7f0c, + 0x97518f90, + 0xd7ce029c, + 0x2356463d, + 0xb8f110ff, + 0x7d646be7, + 0x85bffaed, + 0x8d50d2eb, + 0x0cc81e20, + 0x358e8940, + 0x85386e8e, + 0xd88c7723, + 0xf4b24b3d, + 0xb5c02fbe, + 0xe0985489, + 0x2a912488, + 0x22a32f98, + 0xc4665957, + 0xe642354b, + 0x4205be10, + 0xeed77f26, + 0x08bfcf79, + 0x3dd0a539, + 0xf6a645d6, + 0x8d0121af, + 0xd8e862ab, + 0xf4602c60, + 0x0ce78535, + 0x2a4d8543, + 0x4f8e3912, + 0x5cdfb50d, + 0x415dbafc, + 0x332bbe9a, + 0xce3ad2d0, + 0xb0bee3b8, + 0xf612332a, + 0x5dfa55b1, + 0xec706a23, + 0xbf85ecc3, + 0xaf2abac8, + 0xfdbb14fa, + 0xcc032e53, + 0xae703c79, + 0x2a23461c, + 0x147d4d55, + 0xc54c8736, + 0xb71d1a5b, + 0x3eb770d7, + 0x29508711, + 0xdb2e9e6a, + 0x760079c4, + 0x8c597021, + 0x0ff36411, + 0xa331bf6f, + 0x456fb279, + 0x1066f0e4, + 0x91690f2c, + 0x64eab33a, + 0x0c1f7865, + 0x5fecb278, + 0x6b037dad, + 0xe1b83785, + 0x1ab19abc, + 0x4f4cf7e2, + 0x02a8f3b8, + 0x4eb8d4fc, + 0x2485966e, + 0x3b2d416c, + 0x85fc9c8e, + 0x88a7abdd, + 0x2258028d, + 0x6a3966db, + 0xaa124f67, + 0x58458991, + 0x99f42695, + 0x65a062eb, + 0xcf4c5337, + 0x8c279702, + 0x0b916053, + 0x0f233f8c, + 0x8b7a01ea, + 0xa8c8ca23, + 0xd707da1b, + 0x98134842, + 0x72130ce5, + 0xfe10ca30, + 0x302cf4b9, + 0xf0192fd9, + 0x4f30bc2f, + 0xf5aff866, + 0x1518d44f, + 0x19a95afe, + 0x5afc5c44, + 0xab9e2766, + 0xe017f98d, + 0x85564136, + 0x2f143c8c, + 0xe3f58f94, + 0xb84480b6, + 0xf76ba411, + 0x064aae97, + 0x870c2c16, + 0xdc547221, + 0x34242221, + 0x619107f1, + 0x97a73bb3, + 0x691bfbda, + 0x0294adc1, + 0xb9f111b5, + 0x69a7e467, + 0x0695d95b, + 0x96c6d960, + 0x8ee932c4, + 0xc637b877, + 0xa8a6ea9b, + 0xa268d9c9, + 0x203b3e16, + 0xecfdb4b4, + 0x8fc44005, + 0xcbd8fc91, + 0x90446dcf, + 0x55404748, + 0xd80b686c, + 0x811250ba, + 0xbe118105, + 0x4dc55915, + 0x69b3fb34, + 0x918d9997, + 0x161d0c30, ]; - assert_eq!(crate::patch_frame(862, 0, ROM), (Some(0x289eef48), Some(!0x289eef48))); - assert_eq!(crate::patch_frame(862, 1, ROM), (Some(0x00fe50e1), Some(!0x00fe50e1))); - assert_eq!(crate::patch_frame(862, 2, ROM), (Some(0x1912bd69), Some(!0x1912bd69))); - assert_eq!(crate::patch_frame(862, 3, ROM), (Some(0xee67e390), Some(!0xee67e390))); - assert_eq!(crate::patch_frame(862, 4, ROM), (Some(0x5183e126), Some(!0x5183e126))); - assert_eq!(crate::patch_frame(862, 5, ROM), (Some(0xf2f6625e), Some(!0xf2f6625e))); - assert_eq!(crate::patch_frame(862, 6, ROM), (Some(0x229143de), Some(!0x229143de))); - assert_eq!(crate::patch_frame(862, 7, ROM), (Some(0x2e936f6a), Some(!0x2e936f6a))); - assert_eq!(crate::patch_frame(862, 8, ROM), (Some(0x5c78457c), Some(!0x5c78457c))); - assert_eq!(crate::patch_frame(862, 9, ROM), (Some(0xfdbaf36a), Some(!0xfdbaf36a))); - assert_eq!(crate::patch_frame(862, 10, ROM), (Some(0x60c3f590), Some(!0x60c3f590))); - assert_eq!(crate::patch_frame(862, 11, ROM), (Some(0xd2803743), Some(!0xd2803743))); - assert_eq!(crate::patch_frame(862, 12, ROM), (Some(0x7c857426), Some(!0x7c857426))); - assert_eq!(crate::patch_frame(862, 13, ROM), (Some(0x827f5a6f), Some(!0x827f5a6f))); - assert_eq!(crate::patch_frame(862, 14, ROM), (Some(0x13df0005), Some(!0x13df0005))); - assert_eq!(crate::patch_frame(862, 15, ROM), (Some(0x4ef0f5bd), Some(!0x4ef0f5bd))); - assert_eq!(crate::patch_frame(862, 16, ROM), (Some(0xf5bfa875), Some(!0xf5bfa875))); - assert_eq!(crate::patch_frame(862, 17, ROM), (Some(0xd7510cfc), Some(!0xd7510cfc))); - assert_eq!(crate::patch_frame(862, 18, ROM), (Some(0xa624da57), Some(!0xa624da57))); - assert_eq!(crate::patch_frame(862, 19, ROM), (Some(0xe8ccb701), Some(!0xe8ccb701))); - assert_eq!(crate::patch_frame(862, 20, ROM), (Some(0x5c3aa092), Some(!0x5c3aa092))); - assert_eq!(crate::patch_frame(862, 21, ROM), (Some(0x4c77ea27), Some(!0x4c77ea27))); - assert_eq!(crate::patch_frame(862, 22, ROM), (Some(0x8ec5b96a), Some(!0x8ec5b96a))); - assert_eq!(crate::patch_frame(862, 23, ROM), (Some(0x84b84a6a), Some(!0x84b84a6a))); - assert_eq!(crate::patch_frame(862, 24, ROM), (Some(0x8b23c6ef), Some(!0x8b23c6ef))); - assert_eq!(crate::patch_frame(862, 25, ROM), (Some(0xe9b5c553), Some(!0xe9b5c553))); - assert_eq!(crate::patch_frame(862, 26, ROM), (Some(0x15c497d4), Some(!0x15c497d4))); - assert_eq!(crate::patch_frame(862, 27, ROM), (Some(0xd9d14843), Some(!0xd9d14843))); - assert_eq!(crate::patch_frame(862, 28, ROM), (Some(0xf393a312), Some(!0xf393a312))); - assert_eq!(crate::patch_frame(862, 29, ROM), (Some(0x7d180048), Some(!0x7d180048))); - assert_eq!(crate::patch_frame(862, 30, ROM), (Some(0x10bdd870), Some(!0x10bdd870))); - assert_eq!(crate::patch_frame(862, 31, ROM), (Some(0xfc44e111), Some(!0xfc44e111))); - assert_eq!(crate::patch_frame(863, 0, ROM), (Some(0x6fa993c9), Some(!0x6fa993c9))); - assert_eq!(crate::patch_frame(863, 1, ROM), (Some(0x57f8583c), Some(!0x57f8583c))); - assert_eq!(crate::patch_frame(863, 2, ROM), (Some(0x467e13ba), Some(!0x467e13ba))); - assert_eq!(crate::patch_frame(863, 3, ROM), (Some(0xc7499d41), Some(!0xc7499d41))); - assert_eq!(crate::patch_frame(863, 4, ROM), (Some(0x6319d605), Some(!0x6319d605))); - assert_eq!(crate::patch_frame(863, 5, ROM), (Some(0xa3fd3c1f), Some(!0xa3fd3c1f))); - assert_eq!(crate::patch_frame(863, 6, ROM), (Some(0x93ab963f), Some(!0x93ab963f))); - assert_eq!(crate::patch_frame(863, 7, ROM), (Some(0x18a0c967), Some(!0x18a0c967))); - assert_eq!(crate::patch_frame(863, 8, ROM), (Some(0xcf584229), Some(!0xcf584229))); - assert_eq!(crate::patch_frame(863, 9, ROM), (Some(0x9f61b6da), Some(!0x9f61b6da))); - assert_eq!(crate::patch_frame(863, 10, ROM), (Some(0xccb12b0c), Some(!0xccb12b0c))); - assert_eq!(crate::patch_frame(863, 11, ROM), (Some(0x356a4c97), Some(!0x356a4c97))); - assert_eq!(crate::patch_frame(863, 12, ROM), (Some(0x3214a2c8), Some(!0x3214a2c8))); - assert_eq!(crate::patch_frame(863, 13, ROM), (Some(0x5368affe), Some(!0x5368affe))); - assert_eq!(crate::patch_frame(863, 14, ROM), (Some(0x3d467bf4), Some(!0x3d467bf4))); - assert_eq!(crate::patch_frame(863, 15, ROM), (Some(0x7588a900), Some(!0x7588a900))); - assert_eq!(crate::patch_frame(863, 16, ROM), (Some(0x49208f0c), Some(!0x49208f0c))); - assert_eq!(crate::patch_frame(863, 17, ROM), (Some(0x96c8df91), Some(!0x96c8df91))); - assert_eq!(crate::patch_frame(863, 18, ROM), (Some(0x7208858c), Some(!0x7208858c))); - assert_eq!(crate::patch_frame(863, 19, ROM), (Some(0x6158a6c4), Some(!0x6158a6c4))); - assert_eq!(crate::patch_frame(863, 20, ROM), (Some(0x8c5bb175), Some(!0x8c5bb175))); - assert_eq!(crate::patch_frame(863, 21, ROM), (Some(0xbb38ac07), Some(!0xbb38ac07))); - assert_eq!(crate::patch_frame(863, 22, ROM), (Some(0xbdc93142), Some(!0xbdc93142))); - assert_eq!(crate::patch_frame(863, 23, ROM), (Some(0xdc4cc359), Some(!0xdc4cc359))); - assert_eq!(crate::patch_frame(863, 24, ROM), (Some(0xf2646f74), Some(!0xf2646f74))); - assert_eq!(crate::patch_frame(863, 25, ROM), (Some(0x6977913d), Some(!0x6977913d))); - assert_eq!(crate::patch_frame(863, 26, ROM), (Some(0x1047cf2b), Some(!0x1047cf2b))); - assert_eq!(crate::patch_frame(863, 27, ROM), (Some(0xa648eba1), Some(!0xa648eba1))); - assert_eq!(crate::patch_frame(863, 28, ROM), (Some(0x8b49fdc4), Some(!0x8b49fdc4))); - assert_eq!(crate::patch_frame(863, 29, ROM), (Some(0x6b3dd9d8), Some(!0x6b3dd9d8))); - assert_eq!(crate::patch_frame(863, 30, ROM), (Some(0xe47b4eac), Some(!0xe47b4eac))); - assert_eq!(crate::patch_frame(863, 31, ROM), (Some(0x1d15c3d7), Some(!0x1d15c3d7))); - assert_eq!(crate::patch_frame(864, 0, ROM), (Some(0xa49aaca5), Some(!0xa49aaca5))); - assert_eq!(crate::patch_frame(864, 1, ROM), (Some(0x7b36d003), Some(!0x7b36d003))); - assert_eq!(crate::patch_frame(864, 2, ROM), (Some(0xe2587923), Some(!0xe2587923))); - assert_eq!(crate::patch_frame(864, 3, ROM), (Some(0x2ac1dfcf), Some(!0x2ac1dfcf))); - assert_eq!(crate::patch_frame(864, 4, ROM), (Some(0x60740145), Some(!0x60740145))); - assert_eq!(crate::patch_frame(864, 5, ROM), (Some(0xfd460a74), Some(!0xfd460a74))); - assert_eq!(crate::patch_frame(864, 6, ROM), (Some(0xfceeb986), Some(!0xfceeb986))); - assert_eq!(crate::patch_frame(864, 7, ROM), (Some(0x0092ed17), Some(!0x0092ed17))); - assert_eq!(crate::patch_frame(864, 8, ROM), (Some(0xd3ffad83), Some(!0xd3ffad83))); - assert_eq!(crate::patch_frame(864, 9, ROM), (Some(0x7ba86eea), Some(!0x7ba86eea))); - assert_eq!(crate::patch_frame(864, 10, ROM), (Some(0x5c0f269b), Some(!0x5c0f269b))); - assert_eq!(crate::patch_frame(864, 11, ROM), (Some(0x7d40b1ad), Some(!0x7d40b1ad))); - assert_eq!(crate::patch_frame(864, 12, ROM), (Some(0xf981ec33), Some(!0xf981ec33))); - assert_eq!(crate::patch_frame(864, 13, ROM), (Some(0x17f31e78), Some(!0x17f31e78))); - assert_eq!(crate::patch_frame(864, 14, ROM), (Some(0xf1b013f8), Some(!0xf1b013f8))); - assert_eq!(crate::patch_frame(864, 15, ROM), (Some(0x5a05dc5a), Some(!0x5a05dc5a))); - assert_eq!(crate::patch_frame(864, 16, ROM), (Some(0x92ce71d9), Some(!0x92ce71d9))); - assert_eq!(crate::patch_frame(864, 17, ROM), (Some(0xfb63458c), Some(!0xfb63458c))); - assert_eq!(crate::patch_frame(864, 18, ROM), (Some(0x797aff91), Some(!0x797aff91))); - assert_eq!(crate::patch_frame(864, 19, ROM), (Some(0xd94ff552), Some(!0xd94ff552))); - assert_eq!(crate::patch_frame(864, 20, ROM), (Some(0x0740c375), Some(!0x0740c375))); - assert_eq!(crate::patch_frame(864, 21, ROM), (Some(0xc56c2572), Some(!0xc56c2572))); - assert_eq!(crate::patch_frame(864, 22, ROM), (Some(0x854e4d3d), Some(!0x854e4d3d))); - assert_eq!(crate::patch_frame(864, 23, ROM), (Some(0x0eaa835b), Some(!0x0eaa835b))); - assert_eq!(crate::patch_frame(864, 24, ROM), (Some(0xadd31855), Some(!0xadd31855))); - assert_eq!(crate::patch_frame(864, 25, ROM), (Some(0x344ff30a), Some(!0x344ff30a))); - assert_eq!(crate::patch_frame(864, 26, ROM), (Some(0x75badc99), Some(!0x75badc99))); - assert_eq!(crate::patch_frame(864, 27, ROM), (Some(0xa2988a0f), Some(!0xa2988a0f))); - assert_eq!(crate::patch_frame(864, 28, ROM), (Some(0xdf0c1b99), Some(!0xdf0c1b99))); - assert_eq!(crate::patch_frame(864, 29, ROM), (Some(0x00cbeac9), Some(!0x00cbeac9))); - assert_eq!(crate::patch_frame(864, 30, ROM), (Some(0xb82a69fa), Some(!0xb82a69fa))); - assert_eq!(crate::patch_frame(864, 31, ROM), (Some(0xfbf0060f), Some(!0xfbf0060f))); - assert_eq!(crate::patch_frame(865, 0, ROM), (Some(0xe4e07901), Some(!0xe4e07901))); - assert_eq!(crate::patch_frame(865, 1, ROM), (Some(0x3ed5fb30), Some(!0x3ed5fb30))); - assert_eq!(crate::patch_frame(865, 2, ROM), (Some(0x52b9943e), Some(!0x52b9943e))); - assert_eq!(crate::patch_frame(865, 3, ROM), (Some(0x0a930243), Some(!0x0a930243))); - assert_eq!(crate::patch_frame(865, 4, ROM), (Some(0x51988c2b), Some(!0x51988c2b))); - assert_eq!(crate::patch_frame(865, 5, ROM), (Some(0x1b0d404d), Some(!0x1b0d404d))); - assert_eq!(crate::patch_frame(865, 6, ROM), (Some(0x553538ec), Some(!0x553538ec))); - assert_eq!(crate::patch_frame(865, 7, ROM), (Some(0xdf36eec2), Some(!0xdf36eec2))); - assert_eq!(crate::patch_frame(865, 8, ROM), (Some(0xe9d718de), Some(!0xe9d718de))); - assert_eq!(crate::patch_frame(865, 9, ROM), (Some(0x848f01f4), Some(!0x848f01f4))); - assert_eq!(crate::patch_frame(865, 10, ROM), (Some(0xcffbcc08), Some(!0xcffbcc08))); - assert_eq!(crate::patch_frame(865, 11, ROM), (Some(0xdd0a763e), Some(!0xdd0a763e))); - assert_eq!(crate::patch_frame(865, 12, ROM), (Some(0xa6389f52), Some(!0xa6389f52))); - assert_eq!(crate::patch_frame(865, 13, ROM), (Some(0xb4c1e9c9), Some(!0xb4c1e9c9))); - assert_eq!(crate::patch_frame(865, 14, ROM), (Some(0x7f1681d6), Some(!0x7f1681d6))); - assert_eq!(crate::patch_frame(865, 15, ROM), (Some(0x485c401e), Some(!0x485c401e))); - assert_eq!(crate::patch_frame(865, 16, ROM), (Some(0xe8f8bb81), Some(!0xe8f8bb81))); - assert_eq!(crate::patch_frame(865, 17, ROM), (Some(0xd1aacf4a), Some(!0xd1aacf4a))); - assert_eq!(crate::patch_frame(865, 18, ROM), (Some(0x0e2f53ab), Some(!0x0e2f53ab))); - assert_eq!(crate::patch_frame(865, 19, ROM), (Some(0x49456138), Some(!0x49456138))); - assert_eq!(crate::patch_frame(865, 20, ROM), (Some(0x88d8ffd6), Some(!0x88d8ffd6))); - assert_eq!(crate::patch_frame(865, 21, ROM), (Some(0x63be3db7), Some(!0x63be3db7))); - assert_eq!(crate::patch_frame(865, 22, ROM), (Some(0x77a1cd50), Some(!0x77a1cd50))); - assert_eq!(crate::patch_frame(865, 23, ROM), (Some(0x101f57cd), Some(!0x101f57cd))); - assert_eq!(crate::patch_frame(865, 24, ROM), (Some(0x0f9bd380), Some(!0x0f9bd380))); - assert_eq!(crate::patch_frame(865, 25, ROM), (Some(0x8b7b96cb), Some(!0x8b7b96cb))); - assert_eq!(crate::patch_frame(865, 26, ROM), (Some(0x6315a06c), Some(!0x6315a06c))); - assert_eq!(crate::patch_frame(865, 27, ROM), (Some(0xd0489bbd), Some(!0xd0489bbd))); - assert_eq!(crate::patch_frame(865, 28, ROM), (Some(0xda0dfff9), Some(!0xda0dfff9))); - assert_eq!(crate::patch_frame(865, 29, ROM), (Some(0xb7b12570), Some(!0xb7b12570))); - assert_eq!(crate::patch_frame(865, 30, ROM), (Some(0x72e0737f), Some(!0x72e0737f))); - assert_eq!(crate::patch_frame(865, 31, ROM), (Some(0x15846fd6), Some(!0x15846fd6))); - assert_eq!(crate::patch_frame(868, 0, ROM), (Some(0x71d0204d), Some(!0x71d0204d))); - assert_eq!(crate::patch_frame(868, 1, ROM), (Some(0x4147e6f6), Some(!0x4147e6f6))); - assert_eq!(crate::patch_frame(868, 2, ROM), (Some(0x6bd48d99), Some(!0x6bd48d99))); - assert_eq!(crate::patch_frame(868, 3, ROM), (Some(0xf7d17d4b), Some(!0xf7d17d4b))); - assert_eq!(crate::patch_frame(868, 4, ROM), (Some(0x10279bb9), Some(!0x10279bb9))); - assert_eq!(crate::patch_frame(868, 5, ROM), (Some(0xeee4d256), Some(!0xeee4d256))); - assert_eq!(crate::patch_frame(868, 6, ROM), (Some(0x366badbf), Some(!0x366badbf))); - assert_eq!(crate::patch_frame(868, 7, ROM), (Some(0x70d76792), Some(!0x70d76792))); - assert_eq!(crate::patch_frame(868, 8, ROM), (Some(0x029aaba1), Some(!0x029aaba1))); - assert_eq!(crate::patch_frame(868, 9, ROM), (Some(0xfcb613ec), Some(!0xfcb613ec))); - assert_eq!(crate::patch_frame(868, 10, ROM), (Some(0x73d4c325), Some(!0x73d4c325))); - assert_eq!(crate::patch_frame(868, 11, ROM), (Some(0xb9001b34), Some(!0xb9001b34))); - assert_eq!(crate::patch_frame(868, 12, ROM), (Some(0x239a3c51), Some(!0x239a3c51))); - assert_eq!(crate::patch_frame(868, 13, ROM), (Some(0x71c3d489), Some(!0x71c3d489))); - assert_eq!(crate::patch_frame(868, 14, ROM), (Some(0x368e9d6d), Some(!0x368e9d6d))); - assert_eq!(crate::patch_frame(868, 15, ROM), (Some(0x09d9c38d), Some(!0x09d9c38d))); - assert_eq!(crate::patch_frame(868, 16, ROM), (Some(0xdae4cbae), Some(!0xdae4cbae))); - assert_eq!(crate::patch_frame(868, 17, ROM), (Some(0x83695066), Some(!0x83695066))); - assert_eq!(crate::patch_frame(868, 18, ROM), (Some(0x3672950c), Some(!0x3672950c))); - assert_eq!(crate::patch_frame(868, 19, ROM), (Some(0x9e9488e2), Some(!0x9e9488e2))); - assert_eq!(crate::patch_frame(868, 20, ROM), (Some(0x671a6583), Some(!0x671a6583))); - assert_eq!(crate::patch_frame(868, 21, ROM), (Some(0x046cc976), Some(!0x046cc976))); - assert_eq!(crate::patch_frame(868, 22, ROM), (Some(0x36925cbe), Some(!0x36925cbe))); - assert_eq!(crate::patch_frame(868, 23, ROM), (Some(0x793f74f6), Some(!0x793f74f6))); - assert_eq!(crate::patch_frame(868, 24, ROM), (Some(0xa072312b), Some(!0xa072312b))); - assert_eq!(crate::patch_frame(868, 25, ROM), (Some(0x280c3451), Some(!0x280c3451))); - assert_eq!(crate::patch_frame(868, 26, ROM), (Some(0x51b2395d), Some(!0x51b2395d))); - assert_eq!(crate::patch_frame(868, 27, ROM), (Some(0x230336eb), Some(!0x230336eb))); - assert_eq!(crate::patch_frame(868, 28, ROM), (Some(0x1d259ce4), Some(!0x1d259ce4))); - assert_eq!(crate::patch_frame(868, 29, ROM), (Some(0xaaab60d4), Some(!0xaaab60d4))); - assert_eq!(crate::patch_frame(868, 30, ROM), (Some(0xfc6db209), Some(!0xfc6db209))); - assert_eq!(crate::patch_frame(868, 31, ROM), (Some(0xb484f08d), Some(!0xb484f08d))); - assert_eq!(crate::patch_frame(869, 0, ROM), (Some(0x58b48626), Some(!0x58b48626))); - assert_eq!(crate::patch_frame(869, 1, ROM), (Some(0x17477caf), Some(!0x17477caf))); - assert_eq!(crate::patch_frame(869, 2, ROM), (Some(0xe652dba4), Some(!0xe652dba4))); - assert_eq!(crate::patch_frame(869, 3, ROM), (Some(0x0545ad5a), Some(!0x0545ad5a))); - assert_eq!(crate::patch_frame(869, 4, ROM), (Some(0x4b377491), Some(!0x4b377491))); - assert_eq!(crate::patch_frame(869, 5, ROM), (Some(0x41d5935f), Some(!0x41d5935f))); - assert_eq!(crate::patch_frame(869, 6, ROM), (Some(0xb756b29d), Some(!0xb756b29d))); - assert_eq!(crate::patch_frame(869, 7, ROM), (Some(0x1c0e7f04), Some(!0x1c0e7f04))); - assert_eq!(crate::patch_frame(869, 8, ROM), (Some(0x5b2ae12f), Some(!0x5b2ae12f))); - assert_eq!(crate::patch_frame(869, 9, ROM), (Some(0xa4f1dcad), Some(!0xa4f1dcad))); - assert_eq!(crate::patch_frame(869, 10, ROM), (Some(0xfc2303b3), Some(!0xfc2303b3))); - assert_eq!(crate::patch_frame(869, 11, ROM), (Some(0xcab1a94e), Some(!0xcab1a94e))); - assert_eq!(crate::patch_frame(869, 12, ROM), (Some(0x88a5ada7), Some(!0x88a5ada7))); - assert_eq!(crate::patch_frame(869, 13, ROM), (Some(0xe49b90d6), Some(!0xe49b90d6))); - assert_eq!(crate::patch_frame(869, 14, ROM), (Some(0xb602e45a), Some(!0xb602e45a))); - assert_eq!(crate::patch_frame(869, 15, ROM), (Some(0xc9038665), Some(!0xc9038665))); - assert_eq!(crate::patch_frame(869, 16, ROM), (Some(0xe2a0fe50), Some(!0xe2a0fe50))); - assert_eq!(crate::patch_frame(869, 17, ROM), (Some(0x4cd7b9ed), Some(!0x4cd7b9ed))); - assert_eq!(crate::patch_frame(869, 18, ROM), (Some(0x75f53056), Some(!0x75f53056))); - assert_eq!(crate::patch_frame(869, 19, ROM), (Some(0x6e324e33), Some(!0x6e324e33))); - assert_eq!(crate::patch_frame(869, 20, ROM), (Some(0x1cc8ef9b), Some(!0x1cc8ef9b))); - assert_eq!(crate::patch_frame(869, 21, ROM), (Some(0xdbb6b57b), Some(!0xdbb6b57b))); - assert_eq!(crate::patch_frame(869, 22, ROM), (Some(0xf6bd9973), Some(!0xf6bd9973))); - assert_eq!(crate::patch_frame(869, 23, ROM), (Some(0x26e51954), Some(!0x26e51954))); - assert_eq!(crate::patch_frame(869, 24, ROM), (Some(0xd7f9c178), Some(!0xd7f9c178))); - assert_eq!(crate::patch_frame(869, 25, ROM), (Some(0x00f070f5), Some(!0x00f070f5))); - assert_eq!(crate::patch_frame(869, 26, ROM), (Some(0x9e554c95), Some(!0x9e554c95))); - assert_eq!(crate::patch_frame(869, 27, ROM), (Some(0xa6b6ce90), Some(!0xa6b6ce90))); - assert_eq!(crate::patch_frame(869, 28, ROM), (Some(0xe8789574), Some(!0xe8789574))); - assert_eq!(crate::patch_frame(869, 29, ROM), (Some(0x9a0e19ae), Some(!0x9a0e19ae))); - assert_eq!(crate::patch_frame(869, 30, ROM), (Some(0xf0bc66d7), Some(!0xf0bc66d7))); - assert_eq!(crate::patch_frame(869, 31, ROM), (Some(0x3c61c878), Some(!0x3c61c878))); - assert_eq!(crate::patch_frame(870, 0, ROM), (Some(0xead7be16), Some(!0xead7be16))); - assert_eq!(crate::patch_frame(870, 1, ROM), (Some(0x10781b8d), Some(!0x10781b8d))); - assert_eq!(crate::patch_frame(870, 2, ROM), (Some(0x62fda53f), Some(!0x62fda53f))); - assert_eq!(crate::patch_frame(870, 3, ROM), (Some(0xd9d4c524), Some(!0xd9d4c524))); - assert_eq!(crate::patch_frame(870, 4, ROM), (Some(0x15fafe9b), Some(!0x15fafe9b))); - assert_eq!(crate::patch_frame(870, 5, ROM), (Some(0x4afa5bb7), Some(!0x4afa5bb7))); - assert_eq!(crate::patch_frame(870, 6, ROM), (Some(0xf161da1d), Some(!0xf161da1d))); - assert_eq!(crate::patch_frame(870, 7, ROM), (Some(0xe71b7085), Some(!0xe71b7085))); - assert_eq!(crate::patch_frame(870, 8, ROM), (Some(0x2b0e1d00), Some(!0x2b0e1d00))); - assert_eq!(crate::patch_frame(870, 9, ROM), (Some(0xc4c0cf38), Some(!0xc4c0cf38))); - assert_eq!(crate::patch_frame(870, 10, ROM), (Some(0x0a4a349c), Some(!0x0a4a349c))); - assert_eq!(crate::patch_frame(870, 11, ROM), (Some(0xa7811822), Some(!0xa7811822))); - assert_eq!(crate::patch_frame(870, 12, ROM), (Some(0x604e2388), Some(!0x604e2388))); - assert_eq!(crate::patch_frame(870, 13, ROM), (Some(0x445a3df0), Some(!0x445a3df0))); - assert_eq!(crate::patch_frame(870, 14, ROM), (Some(0x0f9d4363), Some(!0x0f9d4363))); - assert_eq!(crate::patch_frame(870, 15, ROM), (Some(0x9d234051), Some(!0x9d234051))); - assert_eq!(crate::patch_frame(870, 16, ROM), (Some(0x25b84789), Some(!0x25b84789))); - assert_eq!(crate::patch_frame(870, 17, ROM), (Some(0x6646220b), Some(!0x6646220b))); - assert_eq!(crate::patch_frame(870, 18, ROM), (Some(0xf670eccf), Some(!0xf670eccf))); - assert_eq!(crate::patch_frame(870, 19, ROM), (Some(0x2961a999), Some(!0x2961a999))); - assert_eq!(crate::patch_frame(870, 20, ROM), (Some(0x9dbaf88d), Some(!0x9dbaf88d))); - assert_eq!(crate::patch_frame(870, 21, ROM), (Some(0xa9c1c0d0), Some(!0xa9c1c0d0))); - assert_eq!(crate::patch_frame(870, 22, ROM), (Some(0xb2778fe9), Some(!0xb2778fe9))); - assert_eq!(crate::patch_frame(870, 23, ROM), (Some(0xbbd823fc), Some(!0xbbd823fc))); - assert_eq!(crate::patch_frame(870, 24, ROM), (Some(0x4cd943a4), Some(!0x4cd943a4))); - assert_eq!(crate::patch_frame(870, 25, ROM), (Some(0x6d01a0b4), Some(!0x6d01a0b4))); - assert_eq!(crate::patch_frame(870, 26, ROM), (Some(0xaa17ebbd), Some(!0xaa17ebbd))); - assert_eq!(crate::patch_frame(870, 27, ROM), (Some(0x43fa60d0), Some(!0x43fa60d0))); - assert_eq!(crate::patch_frame(870, 28, ROM), (Some(0xb53a5c2d), Some(!0xb53a5c2d))); - assert_eq!(crate::patch_frame(870, 29, ROM), (Some(0x0d3a9425), Some(!0x0d3a9425))); - assert_eq!(crate::patch_frame(870, 30, ROM), (Some(0x97936fca), Some(!0x97936fca))); - assert_eq!(crate::patch_frame(870, 31, ROM), (Some(0x69792683), Some(!0x69792683))); - assert_eq!(crate::patch_frame(871, 0, ROM), (Some(0xe27492de), Some(!0xe27492de))); - assert_eq!(crate::patch_frame(871, 1, ROM), (Some(0xc6e4f56b), Some(!0xc6e4f56b))); - assert_eq!(crate::patch_frame(871, 2, ROM), (Some(0x03b57910), Some(!0x03b57910))); - assert_eq!(crate::patch_frame(871, 3, ROM), (Some(0x43aa6bd6), Some(!0x43aa6bd6))); - assert_eq!(crate::patch_frame(871, 4, ROM), (Some(0x768db014), Some(!0x768db014))); - assert_eq!(crate::patch_frame(871, 5, ROM), (Some(0x5709569f), Some(!0x5709569f))); - assert_eq!(crate::patch_frame(871, 6, ROM), (Some(0x5eecf844), Some(!0x5eecf844))); - assert_eq!(crate::patch_frame(871, 7, ROM), (Some(0xb5aab1ec), Some(!0xb5aab1ec))); - assert_eq!(crate::patch_frame(871, 8, ROM), (Some(0x85689a21), Some(!0x85689a21))); - assert_eq!(crate::patch_frame(871, 9, ROM), (Some(0x26877a66), Some(!0x26877a66))); - assert_eq!(crate::patch_frame(871, 10, ROM), (Some(0x1ddeb4a1), Some(!0x1ddeb4a1))); - assert_eq!(crate::patch_frame(871, 11, ROM), (Some(0x6e79a758), Some(!0x6e79a758))); - assert_eq!(crate::patch_frame(871, 12, ROM), (Some(0x3d698209), Some(!0x3d698209))); - assert_eq!(crate::patch_frame(871, 13, ROM), (Some(0xbcc1afc7), Some(!0xbcc1afc7))); - assert_eq!(crate::patch_frame(871, 14, ROM), (Some(0xec923f88), Some(!0xec923f88))); - assert_eq!(crate::patch_frame(871, 15, ROM), (Some(0xe1e2a570), Some(!0xe1e2a570))); - assert_eq!(crate::patch_frame(871, 16, ROM), (Some(0x1f4f6350), Some(!0x1f4f6350))); - assert_eq!(crate::patch_frame(871, 17, ROM), (Some(0xd5c85435), Some(!0xd5c85435))); - assert_eq!(crate::patch_frame(871, 18, ROM), (Some(0x6616dda2), Some(!0x6616dda2))); - assert_eq!(crate::patch_frame(871, 19, ROM), (Some(0xfd6b4601), Some(!0xfd6b4601))); - assert_eq!(crate::patch_frame(871, 20, ROM), (Some(0xbb26d58e), Some(!0xbb26d58e))); - assert_eq!(crate::patch_frame(871, 21, ROM), (Some(0xe6854095), Some(!0xe6854095))); - assert_eq!(crate::patch_frame(871, 22, ROM), (Some(0x20cb241d), Some(!0x20cb241d))); - assert_eq!(crate::patch_frame(871, 23, ROM), (Some(0x7ba1fc24), Some(!0x7ba1fc24))); - assert_eq!(crate::patch_frame(871, 24, ROM), (Some(0x6e4e203d), Some(!0x6e4e203d))); - assert_eq!(crate::patch_frame(871, 25, ROM), (Some(0x81c00cb1), Some(!0x81c00cb1))); - assert_eq!(crate::patch_frame(871, 26, ROM), (Some(0x018d7881), Some(!0x018d7881))); - assert_eq!(crate::patch_frame(871, 27, ROM), (Some(0x981974a3), Some(!0x981974a3))); - assert_eq!(crate::patch_frame(871, 28, ROM), (Some(0x69e02d8e), Some(!0x69e02d8e))); - assert_eq!(crate::patch_frame(871, 29, ROM), (Some(0x8aaa4d8c), Some(!0x8aaa4d8c))); - assert_eq!(crate::patch_frame(871, 30, ROM), (Some(0x08957322), Some(!0x08957322))); - assert_eq!(crate::patch_frame(871, 31, ROM), (Some(0x2fb92927), Some(!0x2fb92927))); + assert_eq!(crate::patch_frame(862, 0, ROM), (Some(0x1c28396a), Some(!0x1c28396a))); + assert_eq!(crate::patch_frame(862, 1, ROM), (Some(0xd99cd736), Some(!0xd99cd736))); + assert_eq!(crate::patch_frame(862, 2, ROM), (Some(0xf104525a), Some(!0xf104525a))); + assert_eq!(crate::patch_frame(862, 3, ROM), (Some(0x46e41ee4), Some(!0x46e41ee4))); + assert_eq!(crate::patch_frame(862, 4, ROM), (Some(0xca1a726f), Some(!0xca1a726f))); + assert_eq!(crate::patch_frame(862, 5, ROM), (Some(0xcda13d4f), Some(!0xcda13d4f))); + assert_eq!(crate::patch_frame(862, 6, ROM), (Some(0xb187b191), Some(!0xb187b191))); + assert_eq!(crate::patch_frame(862, 7, ROM), (Some(0xec559790), Some(!0xec559790))); + assert_eq!(crate::patch_frame(862, 8, ROM), (Some(0x3b90f6fa), Some(!0x3b90f6fa))); + assert_eq!(crate::patch_frame(862, 9, ROM), (Some(0x89b00ad6), Some(!0x89b00ad6))); + assert_eq!(crate::patch_frame(862, 10, ROM), (Some(0x2fadae3a), Some(!0x2fadae3a))); + assert_eq!(crate::patch_frame(862, 11, ROM), (Some(0x8e8820da), Some(!0x8e8820da))); + assert_eq!(crate::patch_frame(862, 12, ROM), (Some(0x317b2477), Some(!0x317b2477))); + assert_eq!(crate::patch_frame(862, 13, ROM), (Some(0x2748da89), Some(!0x2748da89))); + assert_eq!(crate::patch_frame(862, 14, ROM), (Some(0xaacf969b), Some(!0xaacf969b))); + assert_eq!(crate::patch_frame(862, 15, ROM), (Some(0xf704b11a), Some(!0xf704b11a))); + assert_eq!(crate::patch_frame(862, 16, ROM), (Some(0x24484bd9), Some(!0x24484bd9))); + assert_eq!(crate::patch_frame(862, 17, ROM), (Some(0xc3b521ad), Some(!0xc3b521ad))); + assert_eq!(crate::patch_frame(862, 18, ROM), (Some(0xd967029c), Some(!0xd967029c))); + assert_eq!(crate::patch_frame(862, 19, ROM), (Some(0x389ca14e), Some(!0x389ca14e))); + assert_eq!(crate::patch_frame(862, 20, ROM), (Some(0xf0de2bbb), Some(!0xf0de2bbb))); + assert_eq!(crate::patch_frame(862, 21, ROM), (Some(0xd7bb4ae4), Some(!0xd7bb4ae4))); + assert_eq!(crate::patch_frame(862, 22, ROM), (Some(0xa8830300), Some(!0xa8830300))); + assert_eq!(crate::patch_frame(862, 23, ROM), (Some(0xfb04a665), Some(!0xfb04a665))); + assert_eq!(crate::patch_frame(862, 24, ROM), (Some(0xd2edc5da), Some(!0xd2edc5da))); + assert_eq!(crate::patch_frame(862, 25, ROM), (Some(0x5a655ee1), Some(!0x5a655ee1))); + assert_eq!(crate::patch_frame(862, 26, ROM), (Some(0xfe0332bc), Some(!0xfe0332bc))); + assert_eq!(crate::patch_frame(862, 27, ROM), (Some(0xa7555417), Some(!0xa7555417))); + assert_eq!(crate::patch_frame(862, 28, ROM), (Some(0x5bd66172), Some(!0x5bd66172))); + assert_eq!(crate::patch_frame(862, 29, ROM), (Some(0x0f52ac94), Some(!0x0f52ac94))); + assert_eq!(crate::patch_frame(862, 30, ROM), (Some(0x2fef99fe), Some(!0x2fef99fe))); + assert_eq!(crate::patch_frame(862, 31, ROM), (Some(0x78c050b7), Some(!0x78c050b7))); + assert_eq!(crate::patch_frame(863, 0, ROM), (Some(0xd9cd4d30), Some(!0xd9cd4d30))); + assert_eq!(crate::patch_frame(863, 1, ROM), (Some(0x9749e14c), Some(!0x9749e14c))); + assert_eq!(crate::patch_frame(863, 2, ROM), (Some(0xeb09eb93), Some(!0xeb09eb93))); + assert_eq!(crate::patch_frame(863, 3, ROM), (Some(0xdb9f71ea), Some(!0xdb9f71ea))); + assert_eq!(crate::patch_frame(863, 4, ROM), (Some(0x97b077d3), Some(!0x97b077d3))); + assert_eq!(crate::patch_frame(863, 5, ROM), (Some(0x375bdd91), Some(!0x375bdd91))); + assert_eq!(crate::patch_frame(863, 6, ROM), (Some(0x66d9e1f8), Some(!0x66d9e1f8))); + assert_eq!(crate::patch_frame(863, 7, ROM), (Some(0x77b78dbd), Some(!0x77b78dbd))); + assert_eq!(crate::patch_frame(863, 8, ROM), (Some(0xb87cb483), Some(!0xb87cb483))); + assert_eq!(crate::patch_frame(863, 9, ROM), (Some(0x9c56de51), Some(!0x9c56de51))); + assert_eq!(crate::patch_frame(863, 10, ROM), (Some(0x5d4d3429), Some(!0x5d4d3429))); + assert_eq!(crate::patch_frame(863, 11, ROM), (Some(0x6e7d5d0e), Some(!0x6e7d5d0e))); + assert_eq!(crate::patch_frame(863, 12, ROM), (Some(0x036d2fae), Some(!0x036d2fae))); + assert_eq!(crate::patch_frame(863, 13, ROM), (Some(0x0eea573e), Some(!0x0eea573e))); + assert_eq!(crate::patch_frame(863, 14, ROM), (Some(0xcbc822a2), Some(!0xcbc822a2))); + assert_eq!(crate::patch_frame(863, 15, ROM), (Some(0x97ad008d), Some(!0x97ad008d))); + assert_eq!(crate::patch_frame(863, 16, ROM), (Some(0x1c4cacd6), Some(!0x1c4cacd6))); + assert_eq!(crate::patch_frame(863, 17, ROM), (Some(0x333c496a), Some(!0x333c496a))); + assert_eq!(crate::patch_frame(863, 18, ROM), (Some(0x6244b0d3), Some(!0x6244b0d3))); + assert_eq!(crate::patch_frame(863, 19, ROM), (Some(0x0d0ef77f), Some(!0x0d0ef77f))); + assert_eq!(crate::patch_frame(863, 20, ROM), (Some(0x430c60d7), Some(!0x430c60d7))); + assert_eq!(crate::patch_frame(863, 21, ROM), (Some(0x3f1acb40), Some(!0x3f1acb40))); + assert_eq!(crate::patch_frame(863, 22, ROM), (Some(0xc01af6f4), Some(!0xc01af6f4))); + assert_eq!(crate::patch_frame(863, 23, ROM), (Some(0x846ec7e3), Some(!0x846ec7e3))); + assert_eq!(crate::patch_frame(863, 24, ROM), (Some(0x3d428c2c), Some(!0x3d428c2c))); + assert_eq!(crate::patch_frame(863, 25, ROM), (Some(0x25d91899), Some(!0x25d91899))); + assert_eq!(crate::patch_frame(863, 26, ROM), (Some(0x11aa5774), Some(!0x11aa5774))); + assert_eq!(crate::patch_frame(863, 27, ROM), (Some(0xc4a14714), Some(!0xc4a14714))); + assert_eq!(crate::patch_frame(863, 28, ROM), (Some(0xb69c4eb8), Some(!0xb69c4eb8))); + assert_eq!(crate::patch_frame(863, 29, ROM), (Some(0x186e3951), Some(!0x186e3951))); + assert_eq!(crate::patch_frame(863, 30, ROM), (Some(0x58abedb1), Some(!0x58abedb1))); + assert_eq!(crate::patch_frame(863, 31, ROM), (Some(0xe5af1a89), Some(!0xe5af1a89))); + assert_eq!(crate::patch_frame(864, 0, ROM), (Some(0x78a08520), Some(!0x78a08520))); + assert_eq!(crate::patch_frame(864, 1, ROM), (Some(0x4cb856a0), Some(!0x4cb856a0))); + assert_eq!(crate::patch_frame(864, 2, ROM), (Some(0x1ba8f3a7), Some(!0x1ba8f3a7))); + assert_eq!(crate::patch_frame(864, 3, ROM), (Some(0x599f1f4d), Some(!0x599f1f4d))); + assert_eq!(crate::patch_frame(864, 4, ROM), (Some(0x504b488f), Some(!0x504b488f))); + assert_eq!(crate::patch_frame(864, 5, ROM), (Some(0x1da840aa), Some(!0x1da840aa))); + assert_eq!(crate::patch_frame(864, 6, ROM), (Some(0x93d0b107), Some(!0x93d0b107))); + assert_eq!(crate::patch_frame(864, 7, ROM), (Some(0xeca87e9d), Some(!0xeca87e9d))); + assert_eq!(crate::patch_frame(864, 8, ROM), (Some(0xc092d133), Some(!0xc092d133))); + assert_eq!(crate::patch_frame(864, 9, ROM), (Some(0x4b15b555), Some(!0x4b15b555))); + assert_eq!(crate::patch_frame(864, 10, ROM), (Some(0x07abd9ee), Some(!0x07abd9ee))); + assert_eq!(crate::patch_frame(864, 11, ROM), (Some(0x98598f87), Some(!0x98598f87))); + assert_eq!(crate::patch_frame(864, 12, ROM), (Some(0xa39a9b8d), Some(!0xa39a9b8d))); + assert_eq!(crate::patch_frame(864, 13, ROM), (Some(0xc3fb9406), Some(!0xc3fb9406))); + assert_eq!(crate::patch_frame(864, 14, ROM), (Some(0x0aa60713), Some(!0x0aa60713))); + assert_eq!(crate::patch_frame(864, 15, ROM), (Some(0xcd345b17), Some(!0xcd345b17))); + assert_eq!(crate::patch_frame(864, 16, ROM), (Some(0x128a530c), Some(!0x128a530c))); + assert_eq!(crate::patch_frame(864, 17, ROM), (Some(0xc10e5e6a), Some(!0xc10e5e6a))); + assert_eq!(crate::patch_frame(864, 18, ROM), (Some(0x5c99a769), Some(!0x5c99a769))); + assert_eq!(crate::patch_frame(864, 19, ROM), (Some(0xfe87da62), Some(!0xfe87da62))); + assert_eq!(crate::patch_frame(864, 20, ROM), (Some(0x9452d9c3), Some(!0x9452d9c3))); + assert_eq!(crate::patch_frame(864, 21, ROM), (Some(0xdc4686a4), Some(!0xdc4686a4))); + assert_eq!(crate::patch_frame(864, 22, ROM), (Some(0xa4727203), Some(!0xa4727203))); + assert_eq!(crate::patch_frame(864, 23, ROM), (Some(0x1456b10a), Some(!0x1456b10a))); + assert_eq!(crate::patch_frame(864, 24, ROM), (Some(0x753515a7), Some(!0x753515a7))); + assert_eq!(crate::patch_frame(864, 25, ROM), (Some(0xc2178583), Some(!0xc2178583))); + assert_eq!(crate::patch_frame(864, 26, ROM), (Some(0xf5de069e), Some(!0xf5de069e))); + assert_eq!(crate::patch_frame(864, 27, ROM), (Some(0x0213334d), Some(!0x0213334d))); + assert_eq!(crate::patch_frame(864, 28, ROM), (Some(0x03c67db4), Some(!0x03c67db4))); + assert_eq!(crate::patch_frame(864, 29, ROM), (Some(0x19804e86), Some(!0x19804e86))); + assert_eq!(crate::patch_frame(864, 30, ROM), (Some(0xd25ce8a0), Some(!0xd25ce8a0))); + assert_eq!(crate::patch_frame(864, 31, ROM), (Some(0x3ba0ef90), Some(!0x3ba0ef90))); + assert_eq!(crate::patch_frame(865, 0, ROM), (Some(0x6fa56ab9), Some(!0x6fa56ab9))); + assert_eq!(crate::patch_frame(865, 1, ROM), (Some(0xd8a82864), Some(!0xd8a82864))); + assert_eq!(crate::patch_frame(865, 2, ROM), (Some(0x540c394c), Some(!0x540c394c))); + assert_eq!(crate::patch_frame(865, 3, ROM), (Some(0x4b17dec1), Some(!0x4b17dec1))); + assert_eq!(crate::patch_frame(865, 4, ROM), (Some(0x4fb46d2d), Some(!0x4fb46d2d))); + assert_eq!(crate::patch_frame(865, 5, ROM), (Some(0x7eb6a66b), Some(!0x7eb6a66b))); + assert_eq!(crate::patch_frame(865, 6, ROM), (Some(0xd4c53913), Some(!0xd4c53913))); + assert_eq!(crate::patch_frame(865, 7, ROM), (Some(0x33f9f11d), Some(!0x33f9f11d))); + assert_eq!(crate::patch_frame(865, 8, ROM), (Some(0x768869ef), Some(!0x768869ef))); + assert_eq!(crate::patch_frame(865, 9, ROM), (Some(0xc2ea4c90), Some(!0xc2ea4c90))); + assert_eq!(crate::patch_frame(865, 10, ROM), (Some(0x9cd3a380), Some(!0x9cd3a380))); + assert_eq!(crate::patch_frame(865, 11, ROM), (Some(0x17851733), Some(!0x17851733))); + assert_eq!(crate::patch_frame(865, 12, ROM), (Some(0x5ae07b9f), Some(!0x5ae07b9f))); + assert_eq!(crate::patch_frame(865, 13, ROM), (Some(0x374bb33c), Some(!0x374bb33c))); + assert_eq!(crate::patch_frame(865, 14, ROM), (Some(0xa871852d), Some(!0xa871852d))); + assert_eq!(crate::patch_frame(865, 15, ROM), (Some(0x665fc267), Some(!0x665fc267))); + assert_eq!(crate::patch_frame(865, 16, ROM), (Some(0x3bb486d8), Some(!0x3bb486d8))); + assert_eq!(crate::patch_frame(865, 17, ROM), (Some(0xd6d7acec), Some(!0xd6d7acec))); + assert_eq!(crate::patch_frame(865, 18, ROM), (Some(0x0e01cb2f), Some(!0x0e01cb2f))); + assert_eq!(crate::patch_frame(865, 19, ROM), (Some(0x8b6bfd0e), Some(!0x8b6bfd0e))); + assert_eq!(crate::patch_frame(865, 20, ROM), (Some(0x7ca00c5e), Some(!0x7ca00c5e))); + assert_eq!(crate::patch_frame(865, 21, ROM), (Some(0x4c1df468), Some(!0x4c1df468))); + assert_eq!(crate::patch_frame(865, 22, ROM), (Some(0x4ef3fe1e), Some(!0x4ef3fe1e))); + assert_eq!(crate::patch_frame(865, 23, ROM), (Some(0x7a5146aa), Some(!0x7a5146aa))); + assert_eq!(crate::patch_frame(865, 24, ROM), (Some(0x12d01255), Some(!0x12d01255))); + assert_eq!(crate::patch_frame(865, 25, ROM), (Some(0x2af51b14), Some(!0x2af51b14))); + assert_eq!(crate::patch_frame(865, 26, ROM), (Some(0xb6225ed5), Some(!0xb6225ed5))); + assert_eq!(crate::patch_frame(865, 27, ROM), (Some(0xc38612d6), Some(!0xc38612d6))); + assert_eq!(crate::patch_frame(865, 28, ROM), (Some(0x4a20b05a), Some(!0x4a20b05a))); + assert_eq!(crate::patch_frame(865, 29, ROM), (Some(0xd52aa945), Some(!0xd52aa945))); + assert_eq!(crate::patch_frame(865, 30, ROM), (Some(0x2b9568a2), Some(!0x2b9568a2))); + assert_eq!(crate::patch_frame(865, 31, ROM), (Some(0xcf7ac88d), Some(!0xcf7ac88d))); + assert_eq!(crate::patch_frame(868, 0, ROM), (Some(0x395d7a26), Some(!0x395d7a26))); + assert_eq!(crate::patch_frame(868, 1, ROM), (Some(0x0bbaadc0), Some(!0x0bbaadc0))); + assert_eq!(crate::patch_frame(868, 2, ROM), (Some(0xe0aa6903), Some(!0xe0aa6903))); + assert_eq!(crate::patch_frame(868, 3, ROM), (Some(0x283a700b), Some(!0x283a700b))); + assert_eq!(crate::patch_frame(868, 4, ROM), (Some(0xb3ebcf80), Some(!0xb3ebcf80))); + assert_eq!(crate::patch_frame(868, 5, ROM), (Some(0x9e30ce90), Some(!0x9e30ce90))); + assert_eq!(crate::patch_frame(868, 6, ROM), (Some(0xf50c3b9b), Some(!0xf50c3b9b))); + assert_eq!(crate::patch_frame(868, 7, ROM), (Some(0xc9b85852), Some(!0xc9b85852))); + assert_eq!(crate::patch_frame(868, 8, ROM), (Some(0x6184ab42), Some(!0x6184ab42))); + assert_eq!(crate::patch_frame(868, 9, ROM), (Some(0x144e9f35), Some(!0x144e9f35))); + assert_eq!(crate::patch_frame(868, 10, ROM), (Some(0x91e3c70a), Some(!0x91e3c70a))); + assert_eq!(crate::patch_frame(868, 11, ROM), (Some(0xf0924c53), Some(!0xf0924c53))); + assert_eq!(crate::patch_frame(868, 12, ROM), (Some(0x800f1cdf), Some(!0x800f1cdf))); + assert_eq!(crate::patch_frame(868, 13, ROM), (Some(0x7b44f3bf), Some(!0x7b44f3bf))); + assert_eq!(crate::patch_frame(868, 14, ROM), (Some(0xde057ba5), Some(!0xde057ba5))); + assert_eq!(crate::patch_frame(868, 15, ROM), (Some(0x86c30365), Some(!0x86c30365))); + assert_eq!(crate::patch_frame(868, 16, ROM), (Some(0x75ca8606), Some(!0x75ca8606))); + assert_eq!(crate::patch_frame(868, 17, ROM), (Some(0x69033534), Some(!0x69033534))); + assert_eq!(crate::patch_frame(868, 18, ROM), (Some(0x60ba6f09), Some(!0x60ba6f09))); + assert_eq!(crate::patch_frame(868, 19, ROM), (Some(0xa1b09d5d), Some(!0xa1b09d5d))); + assert_eq!(crate::patch_frame(868, 20, ROM), (Some(0x978bd2e6), Some(!0x978bd2e6))); + assert_eq!(crate::patch_frame(868, 21, ROM), (Some(0x321c8ed8), Some(!0x321c8ed8))); + assert_eq!(crate::patch_frame(868, 22, ROM), (Some(0x08c7f825), Some(!0x08c7f825))); + assert_eq!(crate::patch_frame(868, 23, ROM), (Some(0xf133db73), Some(!0xf133db73))); + assert_eq!(crate::patch_frame(868, 24, ROM), (Some(0x40e1f01a), Some(!0x40e1f01a))); + assert_eq!(crate::patch_frame(868, 25, ROM), (Some(0x706e2647), Some(!0x706e2647))); + assert_eq!(crate::patch_frame(868, 26, ROM), (Some(0x827ab9f4), Some(!0x827ab9f4))); + assert_eq!(crate::patch_frame(868, 27, ROM), (Some(0x8da9315e), Some(!0x8da9315e))); + assert_eq!(crate::patch_frame(868, 28, ROM), (Some(0x4750a149), Some(!0x4750a149))); + assert_eq!(crate::patch_frame(868, 29, ROM), (Some(0x8ca23056), Some(!0x8ca23056))); + assert_eq!(crate::patch_frame(868, 30, ROM), (Some(0x93cb2de8), Some(!0x93cb2de8))); + assert_eq!(crate::patch_frame(868, 31, ROM), (Some(0xda43b371), Some(!0xda43b371))); + assert_eq!(crate::patch_frame(869, 0, ROM), (Some(0x3f7b6b66), Some(!0x3f7b6b66))); + assert_eq!(crate::patch_frame(869, 1, ROM), (Some(0xac6e99fa), Some(!0xac6e99fa))); + assert_eq!(crate::patch_frame(869, 2, ROM), (Some(0xf5c7e509), Some(!0xf5c7e509))); + assert_eq!(crate::patch_frame(869, 3, ROM), (Some(0x47ac1a6f), Some(!0x47ac1a6f))); + assert_eq!(crate::patch_frame(869, 4, ROM), (Some(0x0adcc2d6), Some(!0x0adcc2d6))); + assert_eq!(crate::patch_frame(869, 5, ROM), (Some(0xd17b0e03), Some(!0xd17b0e03))); + assert_eq!(crate::patch_frame(869, 6, ROM), (Some(0x9411683a), Some(!0x9411683a))); + assert_eq!(crate::patch_frame(869, 7, ROM), (Some(0xcb77e479), Some(!0xcb77e479))); + assert_eq!(crate::patch_frame(869, 8, ROM), (Some(0x10110fc9), Some(!0x10110fc9))); + assert_eq!(crate::patch_frame(869, 9, ROM), (Some(0x04e024a3), Some(!0x04e024a3))); + assert_eq!(crate::patch_frame(869, 10, ROM), (Some(0xd7551e75), Some(!0xd7551e75))); + assert_eq!(crate::patch_frame(869, 11, ROM), (Some(0x29fceabf), Some(!0x29fceabf))); + assert_eq!(crate::patch_frame(869, 12, ROM), (Some(0xcb247c5a), Some(!0xcb247c5a))); + assert_eq!(crate::patch_frame(869, 13, ROM), (Some(0xbf0b11da), Some(!0xbf0b11da))); + assert_eq!(crate::patch_frame(869, 14, ROM), (Some(0x1a2b24ec), Some(!0x1a2b24ec))); + assert_eq!(crate::patch_frame(869, 15, ROM), (Some(0x9bafd2f5), Some(!0x9bafd2f5))); + assert_eq!(crate::patch_frame(869, 16, ROM), (Some(0x8c146537), Some(!0x8c146537))); + assert_eq!(crate::patch_frame(869, 17, ROM), (Some(0x341f0ace), Some(!0x341f0ace))); + assert_eq!(crate::patch_frame(869, 18, ROM), (Some(0xcaaba4a2), Some(!0xcaaba4a2))); + assert_eq!(crate::patch_frame(869, 19, ROM), (Some(0x6cb0f989), Some(!0x6cb0f989))); + assert_eq!(crate::patch_frame(869, 20, ROM), (Some(0x753466d4), Some(!0x753466d4))); + assert_eq!(crate::patch_frame(869, 21, ROM), (Some(0x4c2159fe), Some(!0x4c2159fe))); + assert_eq!(crate::patch_frame(869, 22, ROM), (Some(0x60e3de47), Some(!0x60e3de47))); + assert_eq!(crate::patch_frame(869, 23, ROM), (Some(0x2c0da355), Some(!0x2c0da355))); + assert_eq!(crate::patch_frame(869, 24, ROM), (Some(0x4912a377), Some(!0x4912a377))); + assert_eq!(crate::patch_frame(869, 25, ROM), (Some(0x364f95f8), Some(!0x364f95f8))); + assert_eq!(crate::patch_frame(869, 26, ROM), (Some(0xc962bf7e), Some(!0xc962bf7e))); + assert_eq!(crate::patch_frame(869, 27, ROM), (Some(0x650ea97e), Some(!0x650ea97e))); + assert_eq!(crate::patch_frame(869, 28, ROM), (Some(0x0ea1c2f9), Some(!0x0ea1c2f9))); + assert_eq!(crate::patch_frame(869, 29, ROM), (Some(0x5d606c42), Some(!0x5d606c42))); + assert_eq!(crate::patch_frame(869, 30, ROM), (Some(0x0d308c5a), Some(!0x0d308c5a))); + assert_eq!(crate::patch_frame(869, 31, ROM), (Some(0x4770a3c6), Some(!0x4770a3c6))); + assert_eq!(crate::patch_frame(870, 0, ROM), (Some(0xea6466fc), Some(!0xea6466fc))); + assert_eq!(crate::patch_frame(870, 1, ROM), (Some(0x2aa05f79), Some(!0x2aa05f79))); + assert_eq!(crate::patch_frame(870, 2, ROM), (Some(0xcf5ec24e), Some(!0xcf5ec24e))); + assert_eq!(crate::patch_frame(870, 3, ROM), (Some(0xc32bd8d6), Some(!0xc32bd8d6))); + assert_eq!(crate::patch_frame(870, 4, ROM), (Some(0xe0e351f3), Some(!0xe0e351f3))); + assert_eq!(crate::patch_frame(870, 5, ROM), (Some(0xf022babf), Some(!0xf022babf))); + assert_eq!(crate::patch_frame(870, 6, ROM), (Some(0x3cb80ce8), Some(!0x3cb80ce8))); + assert_eq!(crate::patch_frame(870, 7, ROM), (Some(0x80f09746), Some(!0x80f09746))); + assert_eq!(crate::patch_frame(870, 8, ROM), (Some(0x52e65655), Some(!0x52e65655))); + assert_eq!(crate::patch_frame(870, 9, ROM), (Some(0x9fdf74b3), Some(!0x9fdf74b3))); + assert_eq!(crate::patch_frame(870, 10, ROM), (Some(0x8d01bfd4), Some(!0x8d01bfd4))); + assert_eq!(crate::patch_frame(870, 11, ROM), (Some(0x64bb7dbf), Some(!0x64bb7dbf))); + assert_eq!(crate::patch_frame(870, 12, ROM), (Some(0x4ff223af), Some(!0x4ff223af))); + assert_eq!(crate::patch_frame(870, 13, ROM), (Some(0xfda7288a), Some(!0xfda7288a))); + assert_eq!(crate::patch_frame(870, 14, ROM), (Some(0x6eaae04d), Some(!0x6eaae04d))); + assert_eq!(crate::patch_frame(870, 15, ROM), (Some(0xb32174ff), Some(!0xb32174ff))); + assert_eq!(crate::patch_frame(870, 16, ROM), (Some(0x22ff176e), Some(!0x22ff176e))); + assert_eq!(crate::patch_frame(870, 17, ROM), (Some(0x3d63236a), Some(!0x3d63236a))); + assert_eq!(crate::patch_frame(870, 18, ROM), (Some(0xc7210668), Some(!0xc7210668))); + assert_eq!(crate::patch_frame(870, 19, ROM), (Some(0x31734ade), Some(!0x31734ade))); + assert_eq!(crate::patch_frame(870, 20, ROM), (Some(0xbfe7e2fa), Some(!0xbfe7e2fa))); + assert_eq!(crate::patch_frame(870, 21, ROM), (Some(0x21a6aa11), Some(!0x21a6aa11))); + assert_eq!(crate::patch_frame(870, 22, ROM), (Some(0xd4500ed0), Some(!0xd4500ed0))); + assert_eq!(crate::patch_frame(870, 23, ROM), (Some(0x633e596c), Some(!0x633e596c))); + assert_eq!(crate::patch_frame(870, 24, ROM), (Some(0xf9c28134), Some(!0xf9c28134))); + assert_eq!(crate::patch_frame(870, 25, ROM), (Some(0x8368ba81), Some(!0x8368ba81))); + assert_eq!(crate::patch_frame(870, 26, ROM), (Some(0x4935cae8), Some(!0x4935cae8))); + assert_eq!(crate::patch_frame(870, 27, ROM), (Some(0x0bf74789), Some(!0x0bf74789))); + assert_eq!(crate::patch_frame(870, 28, ROM), (Some(0x8c4d4b62), Some(!0x8c4d4b62))); + assert_eq!(crate::patch_frame(870, 29, ROM), (Some(0x66da90a5), Some(!0x66da90a5))); + assert_eq!(crate::patch_frame(870, 30, ROM), (Some(0x468a6a04), Some(!0x468a6a04))); + assert_eq!(crate::patch_frame(870, 31, ROM), (Some(0xb450e92b), Some(!0xb450e92b))); + assert_eq!(crate::patch_frame(871, 0, ROM), (Some(0xc0e34c92), Some(!0xc0e34c92))); + assert_eq!(crate::patch_frame(871, 1, ROM), (Some(0x4d3f0cb7), Some(!0x4d3f0cb7))); + assert_eq!(crate::patch_frame(871, 2, ROM), (Some(0x1f232572), Some(!0x1f232572))); + assert_eq!(crate::patch_frame(871, 3, ROM), (Some(0xf9349b49), Some(!0xf9349b49))); + assert_eq!(crate::patch_frame(871, 4, ROM), (Some(0x858727fc), Some(!0x858727fc))); + assert_eq!(crate::patch_frame(871, 5, ROM), (Some(0x04484eb4), Some(!0x04484eb4))); + assert_eq!(crate::patch_frame(871, 6, ROM), (Some(0x4e788195), Some(!0x4e788195))); + assert_eq!(crate::patch_frame(871, 7, ROM), (Some(0xcc25c3b6), Some(!0xcc25c3b6))); + assert_eq!(crate::patch_frame(871, 8, ROM), (Some(0xb3dbe62b), Some(!0xb3dbe62b))); + assert_eq!(crate::patch_frame(871, 9, ROM), (Some(0x6d3d434f), Some(!0x6d3d434f))); + assert_eq!(crate::patch_frame(871, 10, ROM), (Some(0x326874e3), Some(!0x326874e3))); + assert_eq!(crate::patch_frame(871, 11, ROM), (Some(0xeb40dc4c), Some(!0xeb40dc4c))); + assert_eq!(crate::patch_frame(871, 12, ROM), (Some(0x3c97431f), Some(!0x3c97431f))); + assert_eq!(crate::patch_frame(871, 13, ROM), (Some(0x6296d457), Some(!0x6296d457))); + assert_eq!(crate::patch_frame(871, 14, ROM), (Some(0x4d3e4851), Some(!0x4d3e4851))); + assert_eq!(crate::patch_frame(871, 15, ROM), (Some(0xf71309e5), Some(!0xf71309e5))); + assert_eq!(crate::patch_frame(871, 16, ROM), (Some(0xbca315d0), Some(!0xbca315d0))); + assert_eq!(crate::patch_frame(871, 17, ROM), (Some(0xb9ec430d), Some(!0xb9ec430d))); + assert_eq!(crate::patch_frame(871, 18, ROM), (Some(0xabf3c735), Some(!0xabf3c735))); + assert_eq!(crate::patch_frame(871, 19, ROM), (Some(0x550dd5ef), Some(!0x550dd5ef))); + assert_eq!(crate::patch_frame(871, 20, ROM), (Some(0x03441112), Some(!0x03441112))); + assert_eq!(crate::patch_frame(871, 21, ROM), (Some(0x962cd927), Some(!0x962cd927))); + assert_eq!(crate::patch_frame(871, 22, ROM), (Some(0x811a2d2d), Some(!0x811a2d2d))); + assert_eq!(crate::patch_frame(871, 23, ROM), (Some(0xd7debecd), Some(!0xd7debecd))); + assert_eq!(crate::patch_frame(871, 24, ROM), (Some(0x5fafa327), Some(!0x5fafa327))); + assert_eq!(crate::patch_frame(871, 25, ROM), (Some(0xa61afdcb), Some(!0xa61afdcb))); + assert_eq!(crate::patch_frame(871, 26, ROM), (Some(0x799aa6bd), Some(!0x799aa6bd))); + assert_eq!(crate::patch_frame(871, 27, ROM), (Some(0xb68c2006), Some(!0xb68c2006))); + assert_eq!(crate::patch_frame(871, 28, ROM), (Some(0x8c99e814), Some(!0x8c99e814))); + assert_eq!(crate::patch_frame(871, 29, ROM), (Some(0x8da515f2), Some(!0x8da515f2))); + assert_eq!(crate::patch_frame(871, 30, ROM), (Some(0xc82a4baf), Some(!0xc82a4baf))); + assert_eq!(crate::patch_frame(871, 31, ROM), (Some(0xe5aa45e3), Some(!0xe5aa45e3))); // also test the null case, frame 0 should typically have no mappings. assert_eq!(crate::patch_frame(0x0, 0, ROM), (None, None) ); diff --git a/services/root-keys/src/main.rs b/services/root-keys/src/main.rs index 84897d80b..5590e1198 100644 --- a/services/root-keys/src/main.rs +++ b/services/root-keys/src/main.rs @@ -179,38 +179,51 @@ fn xmain() -> ! { // UX flow opcodes Some(Opcode::UxTryInitKeys) => msg_scalar_unpack!(msg, _, _, _, _, { - // overall flow: - // - setup the init - // - prompt for root password - // - prompt for boot password - // - create the keys - // - write the keys - // - clear the passwords - // - reboot - // the following keys should be provisioned: - // - self-signing private key - // - self-signing public key - // - user root key - // - pepper - if keys.is_initialized() { - dismiss_modal_action.set_action_opcode(Opcode::UxGutter.to_u32().unwrap()); + if true { // short-circuit for testing subroutines rootkeys_modal.modify( - Some(ActionType::Notification(dismiss_modal_action)), - Some(t!("rootkeys.already_init", xous::LANG)), false, + Some(ActionType::Slider(progress_action)), + Some(t!("rootkeys.setup_wait", xous::LANG)), false, None, true, None); - keys.set_ux_password_type(None); + rootkeys_modal.activate(); + + xous::yield_slice(); // give some time to the GAM to render + + let success = keys.test(&mut rootkeys_modal, &mut progress_action); } else { - dismiss_modal_action.set_action_opcode(Opcode::UxInitRequestPassword.to_u32().unwrap()); - rootkeys_modal.modify( - Some(ActionType::Notification(dismiss_modal_action)), - Some(t!("rootkeys.setup", xous::LANG)), false, - None, true, None); - // setup_key_init() prepares the salt and other items necessary to receive a password safely - keys.setup_key_init(); - // request the boot password first - keys.set_ux_password_type(Some(PasswordType::Boot)); + // overall flow: + // - setup the init + // -------> TODO: add an abort option here, in case someone clicked on this item out of curiosity + // - prompt for root password + // - prompt for boot password + // - create the keys + // - write the keys + // - clear the passwords + // - reboot + // the following keys should be provisioned: + // - self-signing private key + // - self-signing public key + // - user root key + // - pepper + if keys.is_initialized() { + dismiss_modal_action.set_action_opcode(Opcode::UxGutter.to_u32().unwrap()); + rootkeys_modal.modify( + Some(ActionType::Notification(dismiss_modal_action)), + Some(t!("rootkeys.already_init", xous::LANG)), false, + None, true, None); + keys.set_ux_password_type(None); + } else { + dismiss_modal_action.set_action_opcode(Opcode::UxInitRequestPassword.to_u32().unwrap()); + rootkeys_modal.modify( + Some(ActionType::Notification(dismiss_modal_action)), + Some(t!("rootkeys.setup", xous::LANG)), false, + None, true, None); + // setup_key_init() prepares the salt and other items necessary to receive a password safely + keys.setup_key_init(); + // request the boot password first + keys.set_ux_password_type(Some(PasswordType::Boot)); + } + rootkeys_modal.activate(); } - rootkeys_modal.activate(); }), Some(Opcode::UxInitRequestPassword) => { password_action.set_action_opcode(Opcode::UxInitPasswordReturn.to_u32().unwrap()); @@ -275,7 +288,23 @@ fn xmain() -> ! { // at this point, we may want to pop up a modal indicating we are going to reboot? // we also need to include a command that does the reboot. if success { - // do a reboot + dismiss_modal_action.set_action_opcode(Opcode::UxGutter.to_u32().unwrap()); + rootkeys_modal.modify( + Some(ActionType::Notification(dismiss_modal_action)), + Some(t!("rootkeys.init.finished", xous::LANG)), false, + None, false, None); + rootkeys_modal.activate(); + xous::yield_slice(); + + // TODO: insert the reboot code here + } else { + dismiss_modal_action.set_action_opcode(Opcode::UxGutter.to_u32().unwrap()); + rootkeys_modal.modify( + Some(ActionType::Notification(dismiss_modal_action)), + Some(t!("rootkeys.init.failure", xous::LANG)), false, + None, false, None); + rootkeys_modal.activate(); + xous::yield_slice(); } } } diff --git a/services/spinor/src/lib.rs b/services/spinor/src/lib.rs index c992cd3ea..db82bd175 100644 --- a/services/spinor/src/lib.rs +++ b/services/spinor/src/lib.rs @@ -107,7 +107,8 @@ impl Spinor { /// `patch` is an extremely low-level function that can patch data on FLASH. Access control must be enforced by higher level /// abstractions. However, there is a single sanity check on the server side that prevents rogue writes to the SoC gateware area. /// Aside from that, it's the wild west! Think of this as `dd` into a raw disk device node, and not a filesystem-abstracted `write` - /// `region` is a slice that points to the ostensible region that we have access to, and wish to patch + /// `region` is a slice that points to the target region that we wish to patch -- ostensibly we should have access to it, so this should + /// just be the MemoryRange turned into a slice. /// `region_base` is the base address of `region`, given as an offset from base of FLASH (that is, physical address minus 0x2000_0000) /// this *must* be aligned to an erase sector. /// `patch_data` is a slice that contains exactly the data we want to have patched into FLASH. If you're lazy and you just send a large diff --git a/services/status/src/main.rs b/services/status/src/main.rs index cde264286..232c4d608 100644 --- a/services/status/src/main.rs +++ b/services/status/src/main.rs @@ -180,7 +180,7 @@ fn xmain() -> ! { let mut stats_phase: usize = 0; let dt_pump_interval = 15; - let charger_pump_interval = 60; + let charger_pump_interval = 180; let stats_interval; let batt_interval; if cfg!(feature = "slowstatus") { @@ -234,13 +234,13 @@ fn xmain() -> ! { } if (stats_phase % charger_pump_interval) == 1 { // stagger periodic tasks - // once a minute confirm that the charger is in the right state. + // confirm that the charger is in the right state. if stats.soc < 95 || stats.remaining_capacity < 1000 { // only request if we aren't fully charged, either by SOC or capacity metrics if (llio.adc_vbus().unwrap() as f64) * 0.005033 > 4.45 { // 4.45V is our threshold for deciding if a cable is present // charging cable is present if !com.is_charging().expect("couldn't check charging state") { // not charging, but cable is present - log::info!("Charger present, but not currently charging. Automatically requesting charge start."); + log::debug!("Charger present, but not currently charging. Automatically requesting charge start."); com.request_charging().expect("couldn't send charge request"); } }