blob: dc6b5fa5c437626c9553619b9bd37532b409f534 [file] [log] [blame]
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +01001// Copyright 2022, 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//! Low-level entry and exit points of pvmfw.
16
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010017use crate::config;
Alice Wang93ee98a2023-06-08 08:20:39 +000018use crate::memory;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +010019use core::arch::asm;
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +010020use core::mem::{drop, size_of};
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010021use core::ops::Range;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010022use core::slice;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010023use log::error;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000024use log::info;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010025use log::warn;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010026use log::LevelFilter;
Alice Wang4be4dd02023-06-07 07:50:40 +000027use vmbase::util::RangeExt as _;
Alice Wangeacb7382023-06-05 12:53:54 +000028use vmbase::{
Pierre-Clément Tosib5a3ab12023-09-15 11:18:38 +010029 configure_heap, console_writeln,
Pierre-Clément Tosi462bdf42024-10-30 17:46:23 +000030 hyp::get_mmio_guard,
Pierre-Clément Tosi38a36212024-06-06 11:30:39 +010031 layout::{self, crosvm, UART_PAGE_ADDR},
Pierre-Clément Tosid3305482023-06-29 15:03:48 +000032 main,
Pierre-Clément Tosif3681e82023-06-22 11:38:22 +000033 memory::{min_dcache_line_size, MemoryTracker, MEMORY, SIZE_128KB, SIZE_4KB},
Alice Wangeacb7382023-06-05 12:53:54 +000034 power::reboot,
35};
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010036use zeroize::Zeroize;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010037
38#[derive(Debug, Clone)]
Andrew Walbran19690632022-12-07 16:41:30 +000039pub enum RebootReason {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010040 /// A malformed BCC was received.
41 InvalidBcc,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010042 /// An invalid configuration was appended to pvmfw.
43 InvalidConfig,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010044 /// An unexpected internal error happened.
45 InternalError,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000046 /// The provided FDT was invalid.
47 InvalidFdt,
48 /// The provided payload was invalid.
49 InvalidPayload,
50 /// The provided ramdisk was invalid.
51 InvalidRamdisk,
Alice Wang28cbcf12022-12-01 07:58:28 +000052 /// Failed to verify the payload.
53 PayloadVerificationError,
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000054 /// DICE layering process failed.
55 SecretDerivationError,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010056}
57
Pierre-Clément Tosib5a3ab12023-09-15 11:18:38 +010058impl RebootReason {
59 pub fn as_avf_reboot_string(&self) -> &'static str {
60 match self {
61 Self::InvalidBcc => "PVM_FIRMWARE_INVALID_BCC",
62 Self::InvalidConfig => "PVM_FIRMWARE_INVALID_CONFIG_DATA",
63 Self::InternalError => "PVM_FIRMWARE_INTERNAL_ERROR",
64 Self::InvalidFdt => "PVM_FIRMWARE_INVALID_FDT",
65 Self::InvalidPayload => "PVM_FIRMWARE_INVALID_PAYLOAD",
66 Self::InvalidRamdisk => "PVM_FIRMWARE_INVALID_RAMDISK",
67 Self::PayloadVerificationError => "PVM_FIRMWARE_PAYLOAD_VERIFICATION_FAILED",
68 Self::SecretDerivationError => "PVM_FIRMWARE_SECRET_DERIVATION_FAILED",
69 }
70 }
71}
72
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010073main!(start);
Pierre-Clément Tosi6a4808c2023-06-29 09:19:38 +000074configure_heap!(SIZE_128KB);
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010075
76/// Entry point for pVM firmware.
77pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
78 // Limitations in this function:
79 // - can't access non-pvmfw memory (only statically-mapped memory)
Pierre-Clément Tosi8e92d1a2024-06-18 16:15:14 +010080 // - can't access MMIO (except the console, already configured by vmbase)
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010081
82 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010083 Ok((entry, bcc)) => jump_to_payload(fdt_address, entry.try_into().unwrap(), bcc),
Pierre-Clément Tosib5a3ab12023-09-15 11:18:38 +010084 Err(e) => {
85 const REBOOT_REASON_CONSOLE: usize = 1;
86 console_writeln!(REBOOT_REASON_CONSOLE, "{}", e.as_avf_reboot_string());
87 reboot()
88 }
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010089 }
90
91 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
92}
93
94/// Sets up the environment for main() and wraps its result for start().
95///
96/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
97/// the assumption that its environment has been properly configured.
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010098fn main_wrapper(
99 fdt: usize,
100 payload: usize,
101 payload_size: usize,
102) -> Result<(usize, Range<usize>), RebootReason> {
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100103 // Limitations in this function:
104 // - only access MMIO once (and while) it has been mapped and configured
105 // - only perform logging once the logger has been initialized
106 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100107
Pierre-Clément Tosid3305482023-06-29 15:03:48 +0000108 log::set_max_level(LevelFilter::Info);
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100109
Alice Wang807fa592023-06-02 09:54:43 +0000110 let page_table = memory::init_page_table().map_err(|e| {
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000111 error!("Failed to set up the dynamic page tables: {e}");
112 RebootReason::InternalError
113 })?;
Alan Stokesc3829f12023-06-02 15:02:23 +0100114
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100115 // SAFETY: We only get the appended payload from here, once. The region was statically mapped,
Alan Stokesc3829f12023-06-02 15:02:23 +0100116 // then remapped by `init_page_table()`.
117 let appended_data = unsafe { get_appended_data_slice() };
118
Alan Stokes65618332023-12-15 14:09:25 +0000119 let appended = AppendedPayload::new(appended_data).ok_or_else(|| {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100120 error!("No valid configuration found");
121 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000122 })?;
123
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000124 let config_entries = appended.get_entries();
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100125
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100126 // Up to this point, we were using the built-in static (from .rodata) page tables.
Alice Wang4c70d142023-06-06 11:52:33 +0000127 MEMORY.lock().replace(MemoryTracker::new(
128 page_table,
Alice Wang63f4c9e2023-06-12 09:36:43 +0000129 crosvm::MEM_START..layout::MAX_VIRT_ADDR,
Alice Wang89d29592023-06-12 09:41:29 +0000130 crosvm::MMIO_RANGE,
Alice Wang5bb79502023-06-12 09:25:07 +0000131 Some(memory::appended_payload_range()),
Alice Wang4c70d142023-06-06 11:52:33 +0000132 ));
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100133
Pierre-Clément Tosi462bdf42024-10-30 17:46:23 +0000134 let slices = memory::MemorySlices::new(
Seungjae Yoo013f4c42024-01-02 13:04:19 +0900135 fdt,
136 payload,
137 payload_size,
138 config_entries.vm_dtbo,
Seungjae Yoof0af81d2024-01-17 13:48:36 +0900139 config_entries.vm_ref_dt,
Seungjae Yoo013f4c42024-01-02 13:04:19 +0900140 )?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000141
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100142 // This wrapper allows main() to be blissfully ignorant of platform details.
Pierre-Clément Tosi64aff642024-07-31 16:20:21 +0100143 let (next_bcc, debuggable_payload) = crate::main(
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000144 slices.fdt,
145 slices.kernel,
146 slices.ramdisk,
147 config_entries.bcc,
148 config_entries.debug_policy,
149 )?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100150
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100151 // Writable-dirty regions will be flushed when MemoryTracker is dropped.
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000152 config_entries.bcc.zeroize();
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100153
Andrew Walbran19690632022-12-07 16:41:30 +0000154 info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
Pierre-Clément Tosi6b867532024-04-29 02:29:42 +0100155 MEMORY.lock().as_mut().unwrap().unshare_all_mmio().map_err(|e| {
Andrew Walbran19690632022-12-07 16:41:30 +0000156 error!("Failed to unshare MMIO ranges: {e}");
157 RebootReason::InternalError
158 })?;
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000159 // Call unshare_all_memory here (instead of relying on the dtor) while UART is still mapped.
160 MEMORY.lock().as_mut().unwrap().unshare_all_memory();
Pierre-Clément Tosi64aff642024-07-31 16:20:21 +0100161
Pierre-Clément Tosid643cfe2023-06-29 09:30:51 +0000162 if let Some(mmio_guard) = get_mmio_guard() {
Nikita Ioffeb4268b32024-09-03 10:23:14 +0000163 if cfg!(debuggable_vms_improvements) && debuggable_payload {
164 // Keep UART MMIO_GUARD-ed for debuggable payloads, to enable earlycon.
165 } else {
Pierre-Clément Tosi64aff642024-07-31 16:20:21 +0100166 mmio_guard.unmap(UART_PAGE_ADDR).map_err(|e| {
167 error!("Failed to unshare the UART: {e}");
168 RebootReason::InternalError
169 })?;
170 }
Pierre-Clément Tosi5ad1e8c2023-06-29 10:36:48 +0000171 }
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100172
173 // Drop MemoryTracker and deactivate page table.
174 drop(MEMORY.lock().take());
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100175
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100176 Ok((slices.kernel.as_ptr() as usize, next_bcc))
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100177}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100178
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100179fn jump_to_payload(fdt_address: u64, payload_start: u64, bcc: Range<usize>) -> ! {
180 const ASM_STP_ALIGN: usize = size_of::<u64>() * 2;
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000181 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100182 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000183 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100184 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000185 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100186 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000187 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100188
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000189 const SCTLR_EL1_VAL: u64 = SCTLR_EL1_RES1 | SCTLR_EL1_ITD | SCTLR_EL1_SED | SCTLR_EL1_I;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100190
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100191 let scratch = layout::scratch_range();
192
Alice Wanga3931aa2023-07-05 12:52:09 +0000193 assert_ne!(scratch.end - scratch.start, 0, "scratch memory is empty.");
194 assert_eq!(scratch.start.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
195 assert_eq!(scratch.end.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100196
Alice Wanga3931aa2023-07-05 12:52:09 +0000197 assert!(bcc.is_within(&(scratch.start.0..scratch.end.0)));
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100198 assert_eq!(bcc.start % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
199 assert_eq!(bcc.end % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
200
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000201 let stack = memory::stack_range();
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100202
Alice Wanga3931aa2023-07-05 12:52:09 +0000203 assert_ne!(stack.end - stack.start, 0, "stack region is empty.");
204 assert_eq!(stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
205 assert_eq!(stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100206
207 // Zero all memory that could hold secrets and that can't be safely written to from Rust.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000208 // Disable the exception vector, caches and page table and then jump to the payload at the
209 // given address, passing it the given FDT pointer.
210 //
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100211 // SAFETY: We're exiting pvmfw by passing the register values we need to a noreturn asm!().
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100212 unsafe {
213 asm!(
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100214 "cmp {scratch}, {bcc}",
215 "b.hs 1f",
216
217 // Zero .data & .bss until BCC.
218 "0: stp xzr, xzr, [{scratch}], 16",
219 "cmp {scratch}, {bcc}",
220 "b.lo 0b",
221
222 "1:",
223 // Skip BCC.
224 "mov {scratch}, {bcc_end}",
225 "cmp {scratch}, {scratch_end}",
226 "b.hs 1f",
227
228 // Keep zeroing .data & .bss.
229 "0: stp xzr, xzr, [{scratch}], 16",
230 "cmp {scratch}, {scratch_end}",
231 "b.lo 0b",
232
233 "1:",
234 // Flush d-cache over .data & .bss (including BCC).
235 "0: dc cvau, {cache_line}",
236 "add {cache_line}, {cache_line}, {dcache_line_size}",
237 "cmp {cache_line}, {scratch_end}",
238 "b.lo 0b",
239
240 "mov {cache_line}, {stack}",
241 // Zero stack region.
242 "0: stp xzr, xzr, [{stack}], 16",
243 "cmp {stack}, {stack_end}",
244 "b.lo 0b",
245
246 // Flush d-cache over stack region.
247 "0: dc cvau, {cache_line}",
248 "add {cache_line}, {cache_line}, {dcache_line_size}",
249 "cmp {cache_line}, {stack_end}",
250 "b.lo 0b",
251
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100252 "msr sctlr_el1, {sctlr_el1_val}",
253 "isb",
254 "mov x1, xzr",
255 "mov x2, xzr",
256 "mov x3, xzr",
257 "mov x4, xzr",
258 "mov x5, xzr",
259 "mov x6, xzr",
260 "mov x7, xzr",
261 "mov x8, xzr",
262 "mov x9, xzr",
263 "mov x10, xzr",
264 "mov x11, xzr",
265 "mov x12, xzr",
266 "mov x13, xzr",
267 "mov x14, xzr",
268 "mov x15, xzr",
269 "mov x16, xzr",
270 "mov x17, xzr",
271 "mov x18, xzr",
272 "mov x19, xzr",
273 "mov x20, xzr",
274 "mov x21, xzr",
275 "mov x22, xzr",
276 "mov x23, xzr",
277 "mov x24, xzr",
278 "mov x25, xzr",
279 "mov x26, xzr",
280 "mov x27, xzr",
281 "mov x28, xzr",
282 "mov x29, xzr",
283 "msr ttbr0_el1, xzr",
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100284 // Ensure that CMOs have completed before entering payload.
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100285 "dsb nsh",
286 "br x30",
287 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100288 bcc = in(reg) u64::try_from(bcc.start).unwrap(),
289 bcc_end = in(reg) u64::try_from(bcc.end).unwrap(),
Alice Wanga3931aa2023-07-05 12:52:09 +0000290 cache_line = in(reg) u64::try_from(scratch.start.0).unwrap(),
291 scratch = in(reg) u64::try_from(scratch.start.0).unwrap(),
292 scratch_end = in(reg) u64::try_from(scratch.end.0).unwrap(),
293 stack = in(reg) u64::try_from(stack.start.0).unwrap(),
294 stack_end = in(reg) u64::try_from(stack.end.0).unwrap(),
Alice Wang3fa9b802023-06-06 07:52:31 +0000295 dcache_line_size = in(reg) u64::try_from(min_dcache_line_size()).unwrap(),
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100296 in("x0") fdt_address,
297 in("x30") payload_start,
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100298 options(noreturn),
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100299 );
300 };
301}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100302
Alan Stokesc3829f12023-06-02 15:02:23 +0100303/// # Safety
304///
305/// This must only be called once, since we are returning a mutable reference.
306/// The appended data region must be mapped.
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100307unsafe fn get_appended_data_slice() -> &'static mut [u8] {
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000308 let range = memory::appended_payload_range();
Alan Stokesa0e42962023-04-14 17:59:50 +0100309 // SAFETY: This region is mapped and the linker script prevents it from overlapping with other
310 // objects.
Alice Wanga3931aa2023-07-05 12:52:09 +0000311 unsafe { slice::from_raw_parts_mut(range.start.0 as *mut u8, range.end - range.start) }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100312}
313
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100314enum AppendedPayload<'a> {
315 /// Configuration data.
316 Config(config::Config<'a>),
317 /// Deprecated raw BCC, as used in Android T.
318 LegacyBcc(&'a mut [u8]),
319}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100320
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100321impl<'a> AppendedPayload<'a> {
Alan Stokesc3829f12023-06-02 15:02:23 +0100322 fn new(data: &'a mut [u8]) -> Option<Self> {
Pierre-Clément Tosi147addf2024-04-15 15:07:58 +0100323 // The borrow checker gets confused about the ownership of data (see inline comments) so we
324 // intentionally obfuscate it using a raw pointer; see a similar issue (still not addressed
325 // in v1.77) in https://users.rust-lang.org/t/78467.
326 let data_ptr = data as *mut [u8];
327
328 // Config::new() borrows data as mutable ...
329 match config::Config::new(data) {
330 // ... so this branch has a mutable reference to data, from the Ok(Config<'a>). But ...
331 Ok(valid) => Some(Self::Config(valid)),
332 // ... if Config::new(data).is_err(), the Err holds no ref to data. However ...
333 Err(config::Error::InvalidMagic) if cfg!(feature = "legacy") => {
334 // ... the borrow checker still complains about a second mutable ref without this.
335 // SAFETY: Pointer to a valid mut (not accessed elsewhere), 'a lifetime re-used.
336 let data: &'a mut _ = unsafe { &mut *data_ptr };
337
Alice Wangeacb7382023-06-05 12:53:54 +0000338 const BCC_SIZE: usize = SIZE_4KB;
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000339 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
340 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
341 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000342 Err(e) => {
Pierre-Clément Tosi147addf2024-04-15 15:07:58 +0100343 error!("Invalid configuration data at {data_ptr:?}: {e}");
344 None
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000345 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000346 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100347 }
348
Alan Stokes65618332023-12-15 14:09:25 +0000349 fn get_entries(self) -> config::Entries<'a> {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100350 match self {
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000351 Self::Config(cfg) => cfg.get_entries(),
352 Self::LegacyBcc(bcc) => config::Entries { bcc, ..Default::default() },
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000353 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100354 }
355}