• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crate::{
16     gbl_avb::{
17         ops::{GblAvbOps, AVB_DIGEST_KEY},
18         state::{BootStateColor, KeyValidationStatus},
19     },
20     gbl_print, gbl_println, GblOps, Result,
21 };
22 use abr::SlotIndex;
23 use arrayvec::ArrayVec;
24 use avb::{slot_verify, HashtreeErrorMode, Ops as _, SlotVerifyFlags};
25 use bootparams::{bootconfig::BootConfigBuilder, entry::CommandlineParser};
26 use core::{ffi::CStr, fmt::Write};
27 use liberror::Error;
28 
29 // Maximum number of partition allowed for verification.
30 //
31 // The value is randomly chosen for now. We can update it as we see more usecases.
32 const MAX_NUM_PARTITION: usize = 16;
33 
34 // Type alias for ArrayVec of size `MAX_NUM_PARTITION`:
35 type ArrayMaxParts<T> = ArrayVec<T, MAX_NUM_PARTITION>;
36 
37 /// A container holding partitions for libavb verification
38 pub(crate) struct PartitionsToVerify<'a> {
39     partitions: ArrayMaxParts<&'a CStr>,
40     preloaded: ArrayMaxParts<(&'a str, &'a [u8])>,
41 }
42 
43 impl<'a> PartitionsToVerify<'a> {
44     /// Appends a partition to verify
45     #[cfg(test)]
try_push(&mut self, name: &'a CStr) -> Result<()>46     pub fn try_push(&mut self, name: &'a CStr) -> Result<()> {
47         self.partitions.try_push(name).or(Err(Error::TooManyPartitions(MAX_NUM_PARTITION)))?;
48         Ok(())
49     }
50 
51     /// Appends a partition, along with its preloaded data
try_push_preloaded(&mut self, name: &'a CStr, data: &'a [u8]) -> Result<()>52     pub fn try_push_preloaded(&mut self, name: &'a CStr, data: &'a [u8]) -> Result<()> {
53         let err = Err(Error::TooManyPartitions(MAX_NUM_PARTITION));
54         self.partitions.try_push(name).or(err)?;
55         self.preloaded.try_push((name.to_str().unwrap(), data)).or(err)?;
56         Ok(())
57     }
58 
59     /// Appends partitions, along with preloaded data
try_extend_preloaded(&mut self, partitions: &PartitionsToVerify<'a>) -> Result<()>60     pub fn try_extend_preloaded(&mut self, partitions: &PartitionsToVerify<'a>) -> Result<()> {
61         let err = Err(Error::TooManyPartitions(MAX_NUM_PARTITION));
62         self.partitions.try_extend_from_slice(partitions.partitions()).or(err)?;
63         self.preloaded.try_extend_from_slice(partitions.preloaded()).or(err)?;
64         Ok(())
65     }
66 
partitions(&self) -> &[&'a CStr]67     fn partitions(&self) -> &[&'a CStr] {
68         &self.partitions
69     }
70 
preloaded(&self) -> &[(&'a str, &'a [u8])]71     fn preloaded(&self) -> &[(&'a str, &'a [u8])] {
72         &self.preloaded
73     }
74 }
75 
76 impl<'a> Default for PartitionsToVerify<'a> {
default() -> Self77     fn default() -> Self {
78         Self { partitions: ArrayMaxParts::new(), preloaded: ArrayMaxParts::new() }
79     }
80 }
81 
82 /// Android verified boot flow.
83 ///
84 /// All relevant images from disk must be preloaded and provided as `partitions`; in its final
85 /// state `ops` will provide the necessary callbacks for where the images should go in RAM and
86 /// which ones are preloaded.
87 ///
88 /// # Arguments
89 /// * `ops`: [GblOps] providing device-specific backend.
90 /// * `slot`: The slot index.
91 /// * `partitions`: [PartitionsToVerify] providing pre-loaded partitions.
92 /// * `bootconfig_builder`: object to write the bootconfig data into.
93 ///
94 /// # Returns
95 /// `()` on success. Returns an error if verification process failed and boot cannot
96 /// continue, or if parsing the command line or updating the boot configuration fail.
avb_verify_slot<'a, 'b, 'c>( ops: &mut impl GblOps<'a, 'b>, slot: u8, partitions: &PartitionsToVerify<'c>, bootconfig_builder: &mut BootConfigBuilder, ) -> Result<()>97 pub(crate) fn avb_verify_slot<'a, 'b, 'c>(
98     ops: &mut impl GblOps<'a, 'b>,
99     slot: u8,
100     partitions: &PartitionsToVerify<'c>,
101     bootconfig_builder: &mut BootConfigBuilder,
102 ) -> Result<()> {
103     let slot = match slot {
104         0 => SlotIndex::A,
105         1 => SlotIndex::B,
106         _ => {
107             gbl_println!(ops, "AVB: Invalid slot index: {slot}");
108             return Err(Error::InvalidInput.into());
109         }
110     };
111 
112     let mut avb_ops = GblAvbOps::new(ops, Some(slot), partitions.preloaded(), false);
113     let unlocked = avb_ops.read_is_device_unlocked()?;
114     let verify_result = slot_verify(
115         &mut avb_ops,
116         partitions.partitions(),
117         Some(slot.into()),
118         // TODO(b/337846185): Pass AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION in
119         // case verity corruption is detected by HLOS.
120         match unlocked {
121             true => SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
122             _ => SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
123         },
124         // TODO(b/337846185): For demo, we use the same setting as Cuttlefish u-boot.
125         // Pass AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO and handle EIO.
126         HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
127     );
128     let (color, verify_data) = match verify_result {
129         Ok(ref verify_data) => {
130             let color = match unlocked {
131                 false
132                     if avb_ops.key_validation_status()? == KeyValidationStatus::ValidCustomKey =>
133                 {
134                     BootStateColor::Yellow
135                 }
136                 false => BootStateColor::Green,
137                 true => BootStateColor::Orange,
138             };
139 
140             gbl_println!(
141                 avb_ops.gbl_ops,
142                 "AVB verification passed. Device is unlocked: {unlocked}. Color: {color}"
143             );
144 
145             (color, Some(verify_data))
146         }
147         // Non-fatal error, can continue booting since verify_data is available.
148         Err(ref e) if e.verification_data().is_some() && unlocked => {
149             let color = BootStateColor::Orange;
150 
151             gbl_println!(
152                 avb_ops.gbl_ops,
153                 "AVB verification failed with {e}. Device is unlocked: {unlocked}. Color: {color}. \
154                 Continue current boot attempt."
155             );
156 
157             (color, Some(e.verification_data().unwrap()))
158         }
159         // Fatal error. Cannot boot.
160         Err(ref e) => {
161             let color = BootStateColor::Red;
162 
163             gbl_println!(
164                 avb_ops.gbl_ops,
165                 "AVB verification failed with {e}. Device is unlocked: {unlocked}. Color: {color}. \
166                 Cannot continue boot."
167             );
168 
169             (color, None)
170         }
171     };
172 
173     // Gets digest from the result command line.
174     let mut digest = None;
175     if let Some(ref verify_data) = verify_data {
176         for entry in CommandlineParser::new(verify_data.cmdline().to_str().unwrap()) {
177             let entry = entry?;
178             if entry.key == AVB_DIGEST_KEY {
179                 digest = entry.value;
180             }
181             write!(bootconfig_builder, "{}\n", entry).or(Err(Error::BufferTooSmall(None)))?;
182         }
183     }
184 
185     // Allowes FW to handle verification result.
186     avb_ops.handle_verification_result(verify_data, color, digest)?;
187 
188     match color {
189         BootStateColor::Red => Err(verify_result.unwrap_err().without_verify_data().into()),
190         _ => {
191             write!(bootconfig_builder, "androidboot.verifiedbootstate={}\n", color)
192                 .or(Err(Error::BufferTooSmall(None)))?;
193 
194             Ok(())
195         }
196     }
197 }
198 
199 #[cfg(test)]
200 mod test {
201     use super::*;
202     use crate::{
203         android_boot::load::tests::{
204             dump_bootconfig, make_bootconfig, read_test_data, read_test_data_as_str,
205             AvbResultBootconfigBuilder, TEST_PUBLIC_KEY_DIGEST,
206         },
207         ops::test::{FakeGblOps, FakeGblOpsStorage},
208         IntegrationError::AvbIoError,
209     };
210     use avb::{IoError, SlotVerifyError};
211     use std::{collections::HashMap, ffi::CStr};
212 
213     /// Helper for testing avb_verify_slot
test_avb_verify_slot<'a>( partitions: &[(&CStr, &str)], partitions_to_verify: &PartitionsToVerify<'a>, device_unlocked: std::result::Result<bool, avb::IoError>, rollback_result: std::result::Result<u64, avb::IoError>, slot: u8, expected_reported_color: Option<BootStateColor>, expected_bootconfig: &[u8], ) -> Result<()>214     fn test_avb_verify_slot<'a>(
215         partitions: &[(&CStr, &str)],
216         partitions_to_verify: &PartitionsToVerify<'a>,
217         device_unlocked: std::result::Result<bool, avb::IoError>,
218         rollback_result: std::result::Result<u64, avb::IoError>,
219         slot: u8,
220         expected_reported_color: Option<BootStateColor>,
221         expected_bootconfig: &[u8],
222     ) -> Result<()> {
223         let mut storage = FakeGblOpsStorage::default();
224         for (part, file) in partitions {
225             storage.add_raw_device(part, read_test_data(file));
226         }
227         let mut ops = FakeGblOps::new(&storage);
228         ops.avb_ops.unlock_state = device_unlocked;
229         ops.avb_ops.rollbacks = HashMap::from([(1, rollback_result)]);
230         let mut out_color = None;
231         let mut handler = |color,
232                            _: Option<&CStr>,
233                            _: Option<&[u8]>,
234                            _: Option<&[u8]>,
235                            _: Option<&[u8]>,
236                            _: Option<&[u8]>,
237                            _: Option<&[u8]>,
238                            _: Option<&[u8]>| {
239             out_color = Some(color);
240             Ok(())
241         };
242         ops.avb_handle_verification_result = Some(&mut handler);
243         ops.avb_key_validation_status = Some(Ok(KeyValidationStatus::Valid));
244 
245         let mut bootconfig_buffer = vec![0u8; 512 * 1024];
246         let mut bootconfig_builder = BootConfigBuilder::new(&mut bootconfig_buffer).unwrap();
247         let verify_result =
248             avb_verify_slot(&mut ops, slot, partitions_to_verify, &mut bootconfig_builder);
249         let bootconfig_bytes = bootconfig_builder.config_bytes();
250 
251         assert_eq!(out_color, expected_reported_color);
252         assert_eq!(
253             bootconfig_bytes,
254             expected_bootconfig,
255             "\nexpect: \n{}\nactual: \n{}\n",
256             dump_bootconfig(expected_bootconfig),
257             dump_bootconfig(bootconfig_bytes),
258         );
259 
260         verify_result
261     }
262 
263     #[test]
test_avb_verify_slot_success()264     fn test_avb_verify_slot_success() {
265         let mut partitions_to_verify = PartitionsToVerify::default();
266         partitions_to_verify.try_push(c"boot").unwrap();
267         partitions_to_verify.try_push(c"init_boot").unwrap();
268         partitions_to_verify.try_push(c"vendor_boot").unwrap();
269         let partitions_data = [
270             (c"boot_a", "boot_no_ramdisk_v4_a.img"),
271             (c"init_boot_a", "init_boot_a.img"),
272             (c"vendor_boot_a", "vendor_boot_v4_a.img"),
273             (c"vbmeta_a", "vbmeta_v4_v4_init_boot_a.img"),
274         ];
275         let expected_bootconfig = AvbResultBootconfigBuilder::new()
276             .vbmeta_size(read_test_data("vbmeta_v4_v4_init_boot_a.img").len())
277             .digest(
278                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.digest.txt")
279                     .strip_suffix("\n")
280                     .unwrap(),
281             )
282             .partition_digest(
283                 "boot",
284                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.boot.digest.txt")
285                     .strip_suffix("\n")
286                     .unwrap(),
287             )
288             .partition_digest(
289                 "init_boot",
290                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.init_boot.digest.txt")
291                     .strip_suffix("\n")
292                     .unwrap(),
293             )
294             .partition_digest(
295                 "vendor_boot",
296                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.vendor_boot.digest.txt")
297                     .strip_suffix("\n")
298                     .unwrap(),
299             )
300             .public_key_digest(TEST_PUBLIC_KEY_DIGEST)
301             .build();
302 
303         assert_eq!(
304             test_avb_verify_slot(
305                 &partitions_data,
306                 &partitions_to_verify,
307                 // Unlocked result
308                 Ok(false),
309                 // Rollback index result
310                 Ok(0),
311                 // Slot
312                 0,
313                 // Expected color
314                 Some(BootStateColor::Green),
315                 // Expected bootcofnig
316                 &expected_bootconfig,
317             ),
318             Ok(()),
319         );
320     }
321 
322     #[test]
test_avb_verify_slot_from_preloaded_success()323     fn test_avb_verify_slot_from_preloaded_success() {
324         let boot = read_test_data("boot_no_ramdisk_v4_a.img");
325         let init_boot = read_test_data("init_boot_a.img");
326         let vendor_boot = read_test_data("vendor_boot_v4_a.img");
327 
328         let mut partitions_to_verify = PartitionsToVerify::default();
329         partitions_to_verify.try_push_preloaded(c"boot", &boot).unwrap();
330         partitions_to_verify.try_push_preloaded(c"init_boot", &init_boot).unwrap();
331         partitions_to_verify.try_push_preloaded(c"vendor_boot", &vendor_boot).unwrap();
332         let partitions_data = [
333             // Required images aren't presented. Have to rely on preloaded.
334             (c"vbmeta_a", "vbmeta_v4_v4_init_boot_a.img"),
335         ];
336         let expected_bootconfig = AvbResultBootconfigBuilder::new()
337             .vbmeta_size(read_test_data("vbmeta_v4_v4_init_boot_a.img").len())
338             .digest(
339                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.digest.txt")
340                     .strip_suffix("\n")
341                     .unwrap(),
342             )
343             .partition_digest(
344                 "boot",
345                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.boot.digest.txt")
346                     .strip_suffix("\n")
347                     .unwrap(),
348             )
349             .partition_digest(
350                 "init_boot",
351                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.init_boot.digest.txt")
352                     .strip_suffix("\n")
353                     .unwrap(),
354             )
355             .partition_digest(
356                 "vendor_boot",
357                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.vendor_boot.digest.txt")
358                     .strip_suffix("\n")
359                     .unwrap(),
360             )
361             .public_key_digest(TEST_PUBLIC_KEY_DIGEST)
362             .build();
363 
364         assert_eq!(
365             test_avb_verify_slot(
366                 &partitions_data,
367                 &partitions_to_verify,
368                 // Unlocked result
369                 Ok(false),
370                 // Rollback index result
371                 Ok(0),
372                 // Slot
373                 0,
374                 // Expected color
375                 Some(BootStateColor::Green),
376                 // Expected bootcofnig
377                 &expected_bootconfig,
378             ),
379             Ok(()),
380         );
381     }
382 
383     #[test]
test_avb_verify_slot_success_unlocked()384     fn test_avb_verify_slot_success_unlocked() {
385         let mut partitions_to_verify = PartitionsToVerify::default();
386         partitions_to_verify.try_push(c"boot").unwrap();
387         partitions_to_verify.try_push(c"init_boot").unwrap();
388         partitions_to_verify.try_push(c"vendor_boot").unwrap();
389         let partitions_data = [
390             (c"boot_a", "boot_no_ramdisk_v4_a.img"),
391             (c"init_boot_a", "init_boot_a.img"),
392             (c"vendor_boot_a", "vendor_boot_v4_a.img"),
393             (c"vbmeta_a", "vbmeta_v4_v4_init_boot_a.img"),
394         ];
395         let expected_bootconfig = AvbResultBootconfigBuilder::new()
396             .vbmeta_size(read_test_data("vbmeta_v4_v4_init_boot_a.img").len())
397             .digest(
398                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.digest.txt")
399                     .strip_suffix("\n")
400                     .unwrap(),
401             )
402             .partition_digest(
403                 "boot",
404                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.boot.digest.txt")
405                     .strip_suffix("\n")
406                     .unwrap(),
407             )
408             .partition_digest(
409                 "init_boot",
410                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.init_boot.digest.txt")
411                     .strip_suffix("\n")
412                     .unwrap(),
413             )
414             .partition_digest(
415                 "vendor_boot",
416                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.vendor_boot.digest.txt")
417                     .strip_suffix("\n")
418                     .unwrap(),
419             )
420             .public_key_digest(TEST_PUBLIC_KEY_DIGEST)
421             .color(BootStateColor::Orange)
422             .unlocked(true)
423             .build();
424 
425         assert_eq!(
426             test_avb_verify_slot(
427                 &partitions_data,
428                 &partitions_to_verify,
429                 // Unlocked result
430                 Ok(true),
431                 // Rollback index result
432                 Ok(0),
433                 // Slot
434                 0,
435                 // Expected color
436                 Some(BootStateColor::Orange),
437                 // Expected bootconfig
438                 &expected_bootconfig,
439             ),
440             Ok(()),
441         );
442     }
443 
444     #[test]
test_avb_verify_slot_verification_failed_unlocked()445     fn test_avb_verify_slot_verification_failed_unlocked() {
446         let mut partitions_to_verify = PartitionsToVerify::default();
447         partitions_to_verify.try_push(c"boot").unwrap();
448         partitions_to_verify.try_push(c"init_boot").unwrap();
449         partitions_to_verify.try_push(c"vendor_boot").unwrap();
450         let partitions_data = [
451             (c"boot_a", "boot_no_ramdisk_v4_a.img"),
452             (c"init_boot_a", "init_boot_a.img"),
453             (c"vendor_boot_a", "vendor_boot_v4_a.img"),
454             (c"vbmeta_a", "vbmeta_v4_v4_init_boot_a.img"),
455         ];
456         let expected_bootconfig = AvbResultBootconfigBuilder::new()
457             .vbmeta_size(read_test_data("vbmeta_v4_v4_init_boot_a.img").len())
458             .digest(
459                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.digest.txt")
460                     .strip_suffix("\n")
461                     .unwrap(),
462             )
463             .partition_digest(
464                 "boot",
465                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.boot.digest.txt")
466                     .strip_suffix("\n")
467                     .unwrap(),
468             )
469             .partition_digest(
470                 "init_boot",
471                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.init_boot.digest.txt")
472                     .strip_suffix("\n")
473                     .unwrap(),
474             )
475             .partition_digest(
476                 "vendor_boot",
477                 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.vendor_boot.digest.txt")
478                     .strip_suffix("\n")
479                     .unwrap(),
480             )
481             .public_key_digest(TEST_PUBLIC_KEY_DIGEST)
482             .color(BootStateColor::Orange)
483             .unlocked(true)
484             .build();
485 
486         assert_eq!(
487             test_avb_verify_slot(
488                 &partitions_data,
489                 &partitions_to_verify,
490                 // Unlocked result
491                 Ok(true),
492                 // Rollback index result
493                 Ok(0),
494                 // Slot
495                 0,
496                 // Expected color
497                 Some(BootStateColor::Orange),
498                 // Expected bootconfig
499                 &expected_bootconfig,
500             ),
501             // Device is unlocked, so can continue boot
502             Ok(()),
503         );
504     }
505 
506     #[test]
test_avb_verify_slot_verification_fatal_failed_unlocked()507     fn test_avb_verify_slot_verification_fatal_failed_unlocked() {
508         let mut partitions_to_verify = PartitionsToVerify::default();
509         partitions_to_verify.try_push(c"boot").unwrap();
510         partitions_to_verify.try_push(c"init_boot").unwrap();
511         partitions_to_verify.try_push(c"vendor_boot").unwrap();
512         let partitions_data = [
513             (c"boot_a", "boot_no_ramdisk_v4_a.img"),
514             (c"init_boot_a", "init_boot_a.img"),
515             (c"vendor_boot_a", "vendor_boot_v4_a.img"),
516             (c"vbmeta_a", "vbmeta_v4_v4_init_boot_a.img"),
517         ];
518         let expected_bootconfig = make_bootconfig("");
519 
520         assert_eq!(
521             test_avb_verify_slot(
522                 &partitions_data,
523                 &partitions_to_verify,
524                 // Unlocked result
525                 Ok(true),
526                 // Get rollback index is failed
527                 Err(IoError::NoSuchValue),
528                 // Slot
529                 0,
530                 // Expected color
531                 Some(BootStateColor::Red),
532                 // Expected bootconfig
533                 &expected_bootconfig,
534             ),
535             // Fatal error, so cannot continue boot
536             Err(SlotVerifyError::Io.into()),
537         );
538     }
539 
540     #[test]
test_avb_verify_slot_verification_failed_locked()541     fn test_avb_verify_slot_verification_failed_locked() {
542         let mut partitions_to_verify = PartitionsToVerify::default();
543         partitions_to_verify.try_push(c"boot").unwrap();
544         partitions_to_verify.try_push(c"init_boot").unwrap();
545         partitions_to_verify.try_push(c"vendor_boot").unwrap();
546         let partitions_data = [
547             // Wrong boot image, expect verification to fail.
548             (c"boot_a", "boot_v0_a.img"),
549             (c"init_boot_a", "init_boot_a.img"),
550             (c"vendor_boot_a", "vendor_boot_v4_a.img"),
551             (c"vbmeta_a", "vbmeta_v4_v4_init_boot_a.img"),
552         ];
553         let expected_bootconfig = make_bootconfig("");
554 
555         assert_eq!(
556             test_avb_verify_slot(
557                 &partitions_data,
558                 &partitions_to_verify,
559                 // Unlocked result
560                 Ok(false),
561                 // Rollback index result
562                 Ok(0),
563                 // Slot
564                 0,
565                 // Expected color
566                 Some(BootStateColor::Red),
567                 // Expected bootconfig
568                 &expected_bootconfig,
569             ),
570             // Cannot continue boot
571             Err(SlotVerifyError::Verification(None).into()),
572         );
573     }
574 
575     #[test]
test_avb_verify_slot_verification_failed_obtain_lock_status()576     fn test_avb_verify_slot_verification_failed_obtain_lock_status() {
577         let partitions_to_verify = PartitionsToVerify::default();
578         let expected_bootconfig = make_bootconfig("");
579 
580         assert_eq!(
581             test_avb_verify_slot(
582                 &[],
583                 &partitions_to_verify,
584                 // Unlocked result
585                 Err(avb::IoError::NoSuchValue),
586                 // Rollback index result
587                 Ok(0),
588                 // Slot
589                 0,
590                 // Expected color
591                 None,
592                 // Expected bootconfig
593                 &expected_bootconfig,
594             ),
595             // Cannot continue boot
596             Err(AvbIoError(IoError::NoSuchValue)),
597         );
598     }
599 }
600