blob: 2463984c3f17f68e86938b31143d3c13db0d3e6f [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;
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +000020use core::mem::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 Tosi20b60962022-10-17 13:35:27 +010024use log::warn;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010025use log::LevelFilter;
Alice Wang4be4dd02023-06-07 07:50:40 +000026use vmbase::util::RangeExt as _;
Alice Wangeacb7382023-06-05 12:53:54 +000027use vmbase::{
Pierre-Clément Tosi8ab7c372024-10-30 20:46:04 +000028 arch::aarch64::min_dcache_line_size,
Pierre-Clément Tosieba83162024-11-02 12:11:48 +000029 configure_heap, console_writeln, layout, limit_stack_size, main,
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +000030 memory::{
Pierre-Clément Tosiae071612024-11-02 13:13:34 +000031 deactivate_dynamic_page_tables, map_image_footer, unshare_all_memory,
32 unshare_all_mmio_except_uart, unshare_uart, MemoryTrackerError, SIZE_128KB, SIZE_4KB,
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +000033 },
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 Tosieba83162024-11-02 12:11:48 +000075limit_stack_size!(SIZE_4KB * 12);
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010076
77/// Entry point for pVM firmware.
78pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
79 // Limitations in this function:
80 // - can't access non-pvmfw memory (only statically-mapped memory)
Pierre-Clément Tosi8e92d1a2024-06-18 16:15:14 +010081 // - can't access MMIO (except the console, already configured by vmbase)
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010082
83 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010084 Ok((entry, bcc)) => jump_to_payload(fdt_address, entry.try_into().unwrap(), bcc),
Pierre-Clément Tosib5a3ab12023-09-15 11:18:38 +010085 Err(e) => {
86 const REBOOT_REASON_CONSOLE: usize = 1;
87 console_writeln!(REBOOT_REASON_CONSOLE, "{}", e.as_avf_reboot_string());
88 reboot()
89 }
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010090 }
91
92 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
93}
94
95/// Sets up the environment for main() and wraps its result for start().
96///
97/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
98/// the assumption that its environment has been properly configured.
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010099fn main_wrapper(
100 fdt: usize,
101 payload: usize,
102 payload_size: usize,
103) -> Result<(usize, Range<usize>), RebootReason> {
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100104 // Limitations in this function:
105 // - only access MMIO once (and while) it has been mapped and configured
106 // - only perform logging once the logger has been initialized
107 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100108
Pierre-Clément Tosid3305482023-06-29 15:03:48 +0000109 log::set_max_level(LevelFilter::Info);
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100110
Pierre-Clément Tosi229dd9d2024-11-02 10:34:27 +0000111 let appended_data = get_appended_data_slice().map_err(|e| {
112 error!("Failed to map the appended data: {e}");
113 RebootReason::InternalError
114 })?;
Alan Stokesc3829f12023-06-02 15:02:23 +0100115
Alan Stokes65618332023-12-15 14:09:25 +0000116 let appended = AppendedPayload::new(appended_data).ok_or_else(|| {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100117 error!("No valid configuration found");
118 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000119 })?;
120
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000121 let config_entries = appended.get_entries();
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100122
Pierre-Clément Tosi92778802024-11-19 17:36:14 +0000123 let slices = memory::MemorySlices::new(fdt, payload, payload_size)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000124
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100125 // This wrapper allows main() to be blissfully ignorant of platform details.
Pierre-Clément Tosi64aff642024-07-31 16:20:21 +0100126 let (next_bcc, debuggable_payload) = crate::main(
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000127 slices.fdt,
128 slices.kernel,
129 slices.ramdisk,
130 config_entries.bcc,
131 config_entries.debug_policy,
Pierre-Clément Tosi92778802024-11-19 17:36:14 +0000132 config_entries.vm_dtbo,
133 config_entries.vm_ref_dt,
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000134 )?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100135
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100136 // Writable-dirty regions will be flushed when MemoryTracker is dropped.
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000137 config_entries.bcc.zeroize();
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100138
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +0000139 unshare_all_mmio_except_uart().map_err(|e| {
Andrew Walbran19690632022-12-07 16:41:30 +0000140 error!("Failed to unshare MMIO ranges: {e}");
141 RebootReason::InternalError
142 })?;
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000143 // Call unshare_all_memory here (instead of relying on the dtor) while UART is still mapped.
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +0000144 unshare_all_memory();
Pierre-Clément Tosi64aff642024-07-31 16:20:21 +0100145
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +0000146 if cfg!(debuggable_vms_improvements) && debuggable_payload {
147 // Keep UART MMIO_GUARD-ed for debuggable payloads, to enable earlycon.
148 } else {
149 unshare_uart().map_err(|e| {
150 error!("Failed to unshare the UART: {e}");
151 RebootReason::InternalError
152 })?;
Pierre-Clément Tosi5ad1e8c2023-06-29 10:36:48 +0000153 }
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100154
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +0000155 deactivate_dynamic_page_tables();
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100156
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100157 Ok((slices.kernel.as_ptr() as usize, next_bcc))
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100158}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100159
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100160fn jump_to_payload(fdt_address: u64, payload_start: u64, bcc: Range<usize>) -> ! {
161 const ASM_STP_ALIGN: usize = size_of::<u64>() * 2;
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000162 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100163 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000164 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100165 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000166 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100167 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000168 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100169
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000170 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 +0100171
Pierre-Clément Tosi0b02a2b2024-11-28 22:48:27 +0000172 let scratch = layout::data_bss_range();
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100173
Alice Wanga3931aa2023-07-05 12:52:09 +0000174 assert_ne!(scratch.end - scratch.start, 0, "scratch memory is empty.");
175 assert_eq!(scratch.start.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
176 assert_eq!(scratch.end.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100177
Alice Wanga3931aa2023-07-05 12:52:09 +0000178 assert!(bcc.is_within(&(scratch.start.0..scratch.end.0)));
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100179 assert_eq!(bcc.start % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
180 assert_eq!(bcc.end % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
181
Pierre-Clément Tosieba83162024-11-02 12:11:48 +0000182 let stack = layout::stack_range();
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100183
Alice Wanga3931aa2023-07-05 12:52:09 +0000184 assert_ne!(stack.end - stack.start, 0, "stack region is empty.");
185 assert_eq!(stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
186 assert_eq!(stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100187
Pierre-Clément Tosi0b02a2b2024-11-28 22:48:27 +0000188 let eh_stack = layout::eh_stack_range();
189
190 assert_ne!(eh_stack.end - eh_stack.start, 0, "EH stack region is empty.");
191 assert_eq!(eh_stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned EH stack region.");
192 assert_eq!(eh_stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned EH stack region.");
193
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100194 // 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 +0000195 // Disable the exception vector, caches and page table and then jump to the payload at the
196 // given address, passing it the given FDT pointer.
197 //
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100198 // 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 +0100199 unsafe {
200 asm!(
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100201 "cmp {scratch}, {bcc}",
202 "b.hs 1f",
203
204 // Zero .data & .bss until BCC.
205 "0: stp xzr, xzr, [{scratch}], 16",
206 "cmp {scratch}, {bcc}",
207 "b.lo 0b",
208
209 "1:",
210 // Skip BCC.
211 "mov {scratch}, {bcc_end}",
212 "cmp {scratch}, {scratch_end}",
213 "b.hs 1f",
214
215 // Keep zeroing .data & .bss.
216 "0: stp xzr, xzr, [{scratch}], 16",
217 "cmp {scratch}, {scratch_end}",
218 "b.lo 0b",
219
220 "1:",
221 // Flush d-cache over .data & .bss (including BCC).
222 "0: dc cvau, {cache_line}",
223 "add {cache_line}, {cache_line}, {dcache_line_size}",
224 "cmp {cache_line}, {scratch_end}",
225 "b.lo 0b",
226
227 "mov {cache_line}, {stack}",
228 // Zero stack region.
229 "0: stp xzr, xzr, [{stack}], 16",
230 "cmp {stack}, {stack_end}",
231 "b.lo 0b",
232
233 // Flush d-cache over stack region.
234 "0: dc cvau, {cache_line}",
235 "add {cache_line}, {cache_line}, {dcache_line_size}",
236 "cmp {cache_line}, {stack_end}",
237 "b.lo 0b",
238
Pierre-Clément Tosi0b02a2b2024-11-28 22:48:27 +0000239 "mov {cache_line}, {eh_stack}",
240 // Zero EH stack region.
241 "0: stp xzr, xzr, [{eh_stack}], 16",
242 "cmp {eh_stack}, {eh_stack_end}",
243 "b.lo 0b",
244
245 // Flush d-cache over EH stack region.
246 "0: dc cvau, {cache_line}",
247 "add {cache_line}, {cache_line}, {dcache_line_size}",
248 "cmp {cache_line}, {eh_stack_end}",
249 "b.lo 0b",
250
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100251 "msr sctlr_el1, {sctlr_el1_val}",
252 "isb",
253 "mov x1, xzr",
254 "mov x2, xzr",
255 "mov x3, xzr",
256 "mov x4, xzr",
257 "mov x5, xzr",
258 "mov x6, xzr",
259 "mov x7, xzr",
260 "mov x8, xzr",
261 "mov x9, xzr",
262 "mov x10, xzr",
263 "mov x11, xzr",
264 "mov x12, xzr",
265 "mov x13, xzr",
266 "mov x14, xzr",
267 "mov x15, xzr",
268 "mov x16, xzr",
269 "mov x17, xzr",
270 "mov x18, xzr",
271 "mov x19, xzr",
272 "mov x20, xzr",
273 "mov x21, xzr",
274 "mov x22, xzr",
275 "mov x23, xzr",
276 "mov x24, xzr",
277 "mov x25, xzr",
278 "mov x26, xzr",
279 "mov x27, xzr",
280 "mov x28, xzr",
281 "mov x29, xzr",
282 "msr ttbr0_el1, xzr",
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100283 // Ensure that CMOs have completed before entering payload.
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100284 "dsb nsh",
285 "br x30",
286 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100287 bcc = in(reg) u64::try_from(bcc.start).unwrap(),
288 bcc_end = in(reg) u64::try_from(bcc.end).unwrap(),
Alice Wanga3931aa2023-07-05 12:52:09 +0000289 cache_line = in(reg) u64::try_from(scratch.start.0).unwrap(),
290 scratch = in(reg) u64::try_from(scratch.start.0).unwrap(),
291 scratch_end = in(reg) u64::try_from(scratch.end.0).unwrap(),
292 stack = in(reg) u64::try_from(stack.start.0).unwrap(),
293 stack_end = in(reg) u64::try_from(stack.end.0).unwrap(),
Pierre-Clément Tosi0b02a2b2024-11-28 22:48:27 +0000294 eh_stack = in(reg) u64::try_from(eh_stack.start.0).unwrap(),
295 eh_stack_end = in(reg) u64::try_from(eh_stack.end.0).unwrap(),
Alice Wang3fa9b802023-06-06 07:52:31 +0000296 dcache_line_size = in(reg) u64::try_from(min_dcache_line_size()).unwrap(),
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100297 in("x0") fdt_address,
298 in("x30") payload_start,
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100299 options(noreturn),
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100300 );
301 };
302}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100303
Pierre-Clément Tosi229dd9d2024-11-02 10:34:27 +0000304fn get_appended_data_slice() -> Result<&'static mut [u8], MemoryTrackerError> {
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +0000305 let range = map_image_footer()?;
Pierre-Clément Tosi229dd9d2024-11-02 10:34:27 +0000306 // SAFETY: This region was just mapped for the first time (as map_image_footer() didn't fail)
307 // and the linker script prevents it from overlapping with other objects.
308 Ok(unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) })
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100309}
310
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100311enum AppendedPayload<'a> {
312 /// Configuration data.
313 Config(config::Config<'a>),
314 /// Deprecated raw BCC, as used in Android T.
315 LegacyBcc(&'a mut [u8]),
316}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100317
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100318impl<'a> AppendedPayload<'a> {
Alan Stokesc3829f12023-06-02 15:02:23 +0100319 fn new(data: &'a mut [u8]) -> Option<Self> {
Pierre-Clément Tosi147addf2024-04-15 15:07:58 +0100320 // The borrow checker gets confused about the ownership of data (see inline comments) so we
321 // intentionally obfuscate it using a raw pointer; see a similar issue (still not addressed
322 // in v1.77) in https://users.rust-lang.org/t/78467.
323 let data_ptr = data as *mut [u8];
324
325 // Config::new() borrows data as mutable ...
326 match config::Config::new(data) {
327 // ... so this branch has a mutable reference to data, from the Ok(Config<'a>). But ...
328 Ok(valid) => Some(Self::Config(valid)),
329 // ... if Config::new(data).is_err(), the Err holds no ref to data. However ...
330 Err(config::Error::InvalidMagic) if cfg!(feature = "legacy") => {
331 // ... the borrow checker still complains about a second mutable ref without this.
332 // SAFETY: Pointer to a valid mut (not accessed elsewhere), 'a lifetime re-used.
333 let data: &'a mut _ = unsafe { &mut *data_ptr };
334
Alice Wangeacb7382023-06-05 12:53:54 +0000335 const BCC_SIZE: usize = SIZE_4KB;
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000336 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
337 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
338 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000339 Err(e) => {
Pierre-Clément Tosi147addf2024-04-15 15:07:58 +0100340 error!("Invalid configuration data at {data_ptr:?}: {e}");
341 None
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000342 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000343 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100344 }
345
Alan Stokes65618332023-12-15 14:09:25 +0000346 fn get_entries(self) -> config::Entries<'a> {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100347 match self {
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000348 Self::Config(cfg) => cfg.get_entries(),
349 Self::LegacyBcc(bcc) => config::Entries { bcc, ..Default::default() },
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000350 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100351 }
352}