blob: d8ce729425e510629c70626723367cbcfea27136 [file] [log] [blame]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
Balint Dobszaya5846852025-02-26 15:38:53 +01004//! Implementation of FF-A partition discovery data structures.
5
Balint Dobszay2338bd32024-12-19 19:00:14 +01006use thiserror::Error;
Balint Dobszay5bf492f2024-07-29 17:21:32 +02007use uuid::Uuid;
Balint Dobszay2338bd32024-12-19 19:00:14 +01008use zerocopy::{FromBytes, IntoBytes};
Balint Dobszay5bf492f2024-07-29 17:21:32 +02009
Balint Dobszayde0dc802025-02-28 14:16:52 +010010// This module uses FF-A v1.1 types by default.
11// FF-A v1.2 specified some previously reserved bits in the partition info properties field, but
12// this doesn't change the descriptor format.
Imre Kis61c34092025-04-10 14:14:38 +020013use crate::{ffa_v1_1::partition_info_descriptor, PartitionInfoGetFlags, SuccessArgs, Version};
Balint Dobszayde0dc802025-02-28 14:16:52 +010014
15// Sanity check to catch if the descriptor format is changed.
16const _: () = assert!(
17 size_of::<crate::ffa_v1_1::partition_info_descriptor>()
18 == size_of::<crate::ffa_v1_2::partition_info_descriptor>()
19);
20
Balint Dobszaya5846852025-02-26 15:38:53 +010021/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
22/// with the `FFA_ERROR` interface.
Balint Dobszay2338bd32024-12-19 19:00:14 +010023#[derive(Debug, Error)]
24pub enum Error {
25 #[error("Invalid buffer size")]
26 InvalidBufferSize,
27 #[error("Malformed descriptor")]
28 MalformedDescriptor,
29}
30
31impl From<Error> for crate::FfaError {
32 fn from(_value: Error) -> Self {
33 Self::InvalidParameters
34 }
35}
36
Balint Dobszaya5846852025-02-26 15:38:53 +010037/// Type of partition identified by the partition ID.
Balint Dobszay2338bd32024-12-19 19:00:14 +010038#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +020039pub enum PartitionIdType {
Balint Dobszaya5846852025-02-26 15:38:53 +010040 /// Partition ID is a PE endpoint ID. Contains the number of execution contexts implemented by
41 /// this partition.
Balint Dobszay2338bd32024-12-19 19:00:14 +010042 PeEndpoint { execution_ctx_count: u16 },
Balint Dobszaya5846852025-02-26 15:38:53 +010043 /// Partition ID is a SEPID for an independent peripheral device.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020044 SepidIndep,
Balint Dobszaya5846852025-02-26 15:38:53 +010045 /// Partition ID is a SEPID for an dependent peripheral device. Contains the ID of the proxy
46 /// endpoint for a dependent peripheral device.
Balint Dobszay2338bd32024-12-19 19:00:14 +010047 SepidDep { proxy_endpoint_id: u16 },
Balint Dobszaya5846852025-02-26 15:38:53 +010048 /// Partition ID is an auxiliary ID.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020049 Aux,
50}
51
Balint Dobszay2338bd32024-12-19 19:00:14 +010052impl PartitionIdType {
53 const SHIFT: usize = 4;
54 const MASK: u32 = 0b11;
55 const PE_ENDPOINT: u32 = 0b00;
56 const SEPID_INDEP: u32 = 0b01;
57 const SEPID_DEP: u32 = 0b10;
58 const AUX: u32 = 0b11;
59}
60
Balint Dobszaya5846852025-02-26 15:38:53 +010061/// Properties of a partition.
Balint Dobszay2338bd32024-12-19 19:00:14 +010062#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
63pub struct PartitionProperties {
Balint Dobszaya5846852025-02-26 15:38:53 +010064 /// The partition supports receipt of direct requests.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020065 pub support_direct_req_rec: bool,
Balint Dobszaya5846852025-02-26 15:38:53 +010066 /// The partition can send direct requests.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020067 pub support_direct_req_send: bool,
Balint Dobszayde0dc802025-02-28 14:16:52 +010068 /// The partition supports receipt of direct requests via the FFA_MSG_SEND_DIRECT_REQ2 ABI.
69 /// Added in FF-A v1.2
70 pub support_direct_req2_rec: Option<bool>,
71 /// The partition can send direct requests via the FFA_MSG_SEND_DIRECT_REQ2 ABI.
72 /// Added in FF-A v1.2
73 pub support_direct_req2_send: Option<bool>,
Balint Dobszaya5846852025-02-26 15:38:53 +010074 /// The partition can send and receive indirect messages.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020075 pub support_indirect_msg: bool,
Balint Dobszaya5846852025-02-26 15:38:53 +010076 /// The partition supports receipt of notifications.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020077 pub support_notif_rec: bool,
Balint Dobszaya5846852025-02-26 15:38:53 +010078 /// The partition must be informed about each VM that is created by the Hypervisor.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020079 pub subscribe_vm_created: bool,
Balint Dobszaya5846852025-02-26 15:38:53 +010080 /// The partition must be informed about each VM that is destroyed by the Hypervisor.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020081 pub subscribe_vm_destroyed: bool,
Balint Dobszaya5846852025-02-26 15:38:53 +010082 /// The partition runs in the AArch64 execution state.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020083 pub is_aarch64: bool,
84}
85
Balint Dobszay2338bd32024-12-19 19:00:14 +010086impl PartitionProperties {
87 const SUPPORT_DIRECT_REQ_REC_SHIFT: usize = 0;
88 const SUPPORT_DIRECT_REQ_SEND_SHIFT: usize = 1;
89 const SUPPORT_INDIRECT_MSG_SHIFT: usize = 2;
90 const SUPPORT_NOTIF_REC_SHIFT: usize = 3;
91 const SUBSCRIBE_VM_CREATED_SHIFT: usize = 6;
92 const SUBSCRIBE_VM_DESTROYED_SHIFT: usize = 7;
93 const IS_AARCH64_SHIFT: usize = 8;
Balint Dobszayde0dc802025-02-28 14:16:52 +010094 const SUPPORT_DIRECT_REQ2_REC_SHIFT: usize = 9;
95 const SUPPORT_DIRECT_REQ2_SEND_SHIFT: usize = 10;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020096}
97
Balint Dobszayde0dc802025-02-28 14:16:52 +010098fn create_partition_properties(
99 version: Version,
100 id_type: PartitionIdType,
101 properties: PartitionProperties,
102) -> (u32, u16) {
103 let exec_ctx_count_or_proxy_id = match id_type {
104 PartitionIdType::PeEndpoint {
105 execution_ctx_count,
106 } => execution_ctx_count,
107 PartitionIdType::SepidIndep => 0,
108 PartitionIdType::SepidDep { proxy_endpoint_id } => proxy_endpoint_id,
109 PartitionIdType::Aux => 0,
110 };
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200111
Balint Dobszayde0dc802025-02-28 14:16:52 +0100112 let mut prop_bits = match id_type {
113 PartitionIdType::PeEndpoint { .. } => {
114 let mut p = PartitionIdType::PE_ENDPOINT << PartitionIdType::SHIFT;
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200115
Balint Dobszayde0dc802025-02-28 14:16:52 +0100116 if properties.support_direct_req_rec {
117 p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ_REC_SHIFT;
118 if properties.subscribe_vm_created {
119 // TODO: how to handle if ABI is invoked at NS phys instance?
120 p |= 1 << PartitionProperties::SUBSCRIBE_VM_CREATED_SHIFT
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200121 }
Balint Dobszayde0dc802025-02-28 14:16:52 +0100122 if properties.subscribe_vm_destroyed {
123 // TODO: how to handle if ABI is invoked at NS phys instance?
124 p |= 1 << PartitionProperties::SUBSCRIBE_VM_DESTROYED_SHIFT
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200125 }
Balint Dobszay2338bd32024-12-19 19:00:14 +0100126 }
Balint Dobszay2338bd32024-12-19 19:00:14 +0100127
Balint Dobszayde0dc802025-02-28 14:16:52 +0100128 if properties.support_direct_req_send {
129 p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ_SEND_SHIFT
130 }
131
132 // For v1.2 and later it's mandatory to specify these properties
133 if version >= Version(1, 2) {
134 if properties.support_direct_req2_rec.unwrap() {
135 p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ2_REC_SHIFT
136 }
137
138 if properties.support_direct_req2_send.unwrap() {
139 p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ2_SEND_SHIFT
140 }
141 }
142
143 if properties.support_indirect_msg {
144 p |= 1 << PartitionProperties::SUPPORT_INDIRECT_MSG_SHIFT
145 }
146
147 if properties.support_notif_rec {
148 p |= 1 << PartitionProperties::SUPPORT_NOTIF_REC_SHIFT
149 }
150
151 p
Balint Dobszay2338bd32024-12-19 19:00:14 +0100152 }
Balint Dobszayde0dc802025-02-28 14:16:52 +0100153 PartitionIdType::SepidIndep => PartitionIdType::SEPID_INDEP << PartitionIdType::SHIFT,
154 PartitionIdType::SepidDep { .. } => PartitionIdType::SEPID_DEP << PartitionIdType::SHIFT,
155 PartitionIdType::Aux => PartitionIdType::AUX << PartitionIdType::SHIFT,
156 };
Balint Dobszay2338bd32024-12-19 19:00:14 +0100157
Balint Dobszayde0dc802025-02-28 14:16:52 +0100158 if properties.is_aarch64 {
159 prop_bits |= 1 << PartitionProperties::IS_AARCH64_SHIFT
Balint Dobszay2338bd32024-12-19 19:00:14 +0100160 }
Balint Dobszayde0dc802025-02-28 14:16:52 +0100161
162 (prop_bits, exec_ctx_count_or_proxy_id)
Balint Dobszay2338bd32024-12-19 19:00:14 +0100163}
164
Balint Dobszayde0dc802025-02-28 14:16:52 +0100165fn parse_partition_properties(
166 version: Version,
167 prop_bits: u32,
168 id_type: u16,
169) -> (PartitionIdType, PartitionProperties) {
170 let part_id_type = match (prop_bits >> PartitionIdType::SHIFT) & PartitionIdType::MASK {
171 PartitionIdType::PE_ENDPOINT => PartitionIdType::PeEndpoint {
172 execution_ctx_count: id_type,
173 },
174 PartitionIdType::SEPID_INDEP => PartitionIdType::SepidIndep,
175 PartitionIdType::SEPID_DEP => PartitionIdType::SepidDep {
176 proxy_endpoint_id: id_type,
177 },
178 PartitionIdType::AUX => PartitionIdType::Aux,
179 _ => panic!(), // The match is exhaustive for a 2-bit value
180 };
Balint Dobszay2338bd32024-12-19 19:00:14 +0100181
Balint Dobszayde0dc802025-02-28 14:16:52 +0100182 let mut part_props = PartitionProperties::default();
Balint Dobszay2338bd32024-12-19 19:00:14 +0100183
Balint Dobszayde0dc802025-02-28 14:16:52 +0100184 if matches!(part_id_type, PartitionIdType::PeEndpoint { .. }) {
185 if (prop_bits >> PartitionProperties::SUPPORT_DIRECT_REQ_REC_SHIFT) & 0b1 == 1 {
186 part_props.support_direct_req_rec = true;
Balint Dobszay2338bd32024-12-19 19:00:14 +0100187
Balint Dobszayde0dc802025-02-28 14:16:52 +0100188 if (prop_bits >> PartitionProperties::SUBSCRIBE_VM_CREATED_SHIFT) & 0b1 == 1 {
189 part_props.subscribe_vm_created = true;
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200190 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200191
Balint Dobszayde0dc802025-02-28 14:16:52 +0100192 if (prop_bits >> PartitionProperties::SUBSCRIBE_VM_DESTROYED_SHIFT) & 0b1 == 1 {
193 part_props.subscribe_vm_destroyed = true;
Balint Dobszay2338bd32024-12-19 19:00:14 +0100194 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200195 }
196
Balint Dobszayde0dc802025-02-28 14:16:52 +0100197 if (prop_bits >> PartitionProperties::SUPPORT_DIRECT_REQ_SEND_SHIFT) & 0b1 == 1 {
198 part_props.support_direct_req_send = true;
Balint Dobszay2338bd32024-12-19 19:00:14 +0100199 }
200
Balint Dobszayde0dc802025-02-28 14:16:52 +0100201 if version >= Version(1, 2) {
202 part_props.support_direct_req2_rec =
203 Some((prop_bits >> PartitionProperties::SUPPORT_DIRECT_REQ2_REC_SHIFT) & 0b1 == 1);
204 part_props.support_direct_req2_send =
205 Some((prop_bits >> PartitionProperties::SUPPORT_DIRECT_REQ2_SEND_SHIFT) & 0b1 == 1);
206 }
207
208 if (prop_bits >> PartitionProperties::SUPPORT_INDIRECT_MSG_SHIFT) & 0b1 == 1 {
209 part_props.support_indirect_msg = true;
210 }
211
212 if (prop_bits >> PartitionProperties::SUPPORT_NOTIF_REC_SHIFT) & 0b1 == 1 {
213 part_props.support_notif_rec = true;
214 }
Balint Dobszay2338bd32024-12-19 19:00:14 +0100215 }
Balint Dobszayde0dc802025-02-28 14:16:52 +0100216
217 if (prop_bits >> PartitionProperties::IS_AARCH64_SHIFT) & 0b1 == 1 {
218 part_props.is_aarch64 = true;
219 }
220
221 (part_id_type, part_props)
Balint Dobszay2338bd32024-12-19 19:00:14 +0100222}
223
Balint Dobszaya5846852025-02-26 15:38:53 +0100224/// Partition information descriptor, returned by the `FFA_PARTITION_INFO_GET` interface.
Balint Dobszay2338bd32024-12-19 19:00:14 +0100225#[derive(Clone, Copy, Debug, PartialEq, Eq)]
226pub struct PartitionInfo {
227 pub uuid: Uuid,
228 pub partition_id: u16,
229 pub partition_id_type: PartitionIdType,
230 pub props: PartitionProperties,
231}
232
233impl PartitionInfo {
Balint Dobszayde0dc802025-02-28 14:16:52 +0100234 pub const DESC_SIZE: usize = size_of::<partition_info_descriptor>();
Balint Dobszay2338bd32024-12-19 19:00:14 +0100235
Balint Dobszaya5846852025-02-26 15:38:53 +0100236 /// Serialize a list of partition information descriptors into a buffer. The `fill_uuid`
237 /// parameter controls whether the UUID field of the descriptor will be filled.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100238 pub fn pack(version: Version, descriptors: &[PartitionInfo], buf: &mut [u8], fill_uuid: bool) {
239 assert!((Version(1, 1)..=Version(1, 2)).contains(&version));
240
Balint Dobszay2338bd32024-12-19 19:00:14 +0100241 let mut offset = 0;
242
243 for desc in descriptors {
244 let mut desc_raw = partition_info_descriptor {
245 partition_id: desc.partition_id,
246 ..Default::default()
247 };
248
249 (
250 desc_raw.partition_props,
251 desc_raw.exec_ctx_count_or_proxy_id,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100252 ) = create_partition_properties(version, desc.partition_id_type, desc.props);
Balint Dobszay2338bd32024-12-19 19:00:14 +0100253
254 if fill_uuid {
255 desc_raw.uuid.copy_from_slice(desc.uuid.as_bytes());
256 }
257
258 desc_raw.write_to_prefix(&mut buf[offset..]).unwrap();
259 offset += Self::DESC_SIZE;
260 }
261 }
262}
263
Imre Kis61c34092025-04-10 14:14:38 +0200264/// `FFA_PARTITION_INFO_GET` specific success args structure.
265#[derive(Debug, Eq, PartialEq, Clone, Copy)]
266pub struct SuccessArgsPartitionInfoGet {
267 pub count: u32,
268 pub size: Option<u32>,
269}
270
271impl From<SuccessArgsPartitionInfoGet> for SuccessArgs {
272 fn from(value: SuccessArgsPartitionInfoGet) -> Self {
273 SuccessArgs::Args32([value.count, value.size.unwrap_or(0), 0, 0, 0, 0])
274 }
275}
276
277impl TryFrom<(PartitionInfoGetFlags, SuccessArgs)> for SuccessArgsPartitionInfoGet {
278 type Error = crate::Error;
279
280 fn try_from(value: (PartitionInfoGetFlags, SuccessArgs)) -> Result<Self, Self::Error> {
281 let (flags, value) = value;
282 let args = value.try_get_args32()?;
283
284 let size = if !flags.count_only {
285 Some(args[1])
286 } else {
287 None
288 };
289
290 Ok(Self {
291 count: args[0],
292 size,
293 })
294 }
295}
296
Balint Dobszaya5846852025-02-26 15:38:53 +0100297/// Iterator of partition information descriptors.
Balint Dobszay2338bd32024-12-19 19:00:14 +0100298pub struct PartitionInfoIterator<'a> {
Balint Dobszayde0dc802025-02-28 14:16:52 +0100299 version: Version,
Balint Dobszay2338bd32024-12-19 19:00:14 +0100300 buf: &'a [u8],
301 offset: usize,
302 count: usize,
303}
304
305impl<'a> PartitionInfoIterator<'a> {
Balint Dobszaya5846852025-02-26 15:38:53 +0100306 /// Create an iterator of partition information descriptors from a buffer.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100307 pub fn new(version: Version, buf: &'a [u8], count: usize) -> Result<Self, Error> {
308 assert!((Version(1, 1)..=Version(1, 2)).contains(&version));
309
Balint Dobszay2338bd32024-12-19 19:00:14 +0100310 let Some(total_size) = count.checked_mul(PartitionInfo::DESC_SIZE) else {
311 return Err(Error::InvalidBufferSize);
312 };
313
314 if buf.len() < total_size {
315 return Err(Error::InvalidBufferSize);
316 }
317
318 Ok(Self {
Balint Dobszayde0dc802025-02-28 14:16:52 +0100319 version,
Balint Dobszay2338bd32024-12-19 19:00:14 +0100320 buf,
321 offset: 0,
322 count,
323 })
324 }
325}
326
327impl Iterator for PartitionInfoIterator<'_> {
328 type Item = Result<PartitionInfo, Error>;
329
330 fn next(&mut self) -> Option<Self::Item> {
331 if self.count > 0 {
332 let offset = self.offset;
333 self.offset += PartitionInfo::DESC_SIZE;
334 self.count -= 1;
335
336 let Ok(desc_raw) = partition_info_descriptor::ref_from_bytes(
337 &self.buf[offset..offset + PartitionInfo::DESC_SIZE],
338 ) else {
339 return Some(Err(Error::MalformedDescriptor));
340 };
341
342 let partition_id = desc_raw.partition_id;
343
Balint Dobszayde0dc802025-02-28 14:16:52 +0100344 let (partition_id_type, props) = parse_partition_properties(
345 self.version,
Balint Dobszay2338bd32024-12-19 19:00:14 +0100346 desc_raw.partition_props,
347 desc_raw.exec_ctx_count_or_proxy_id,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100348 );
Balint Dobszay2338bd32024-12-19 19:00:14 +0100349
350 let uuid = Uuid::from_bytes(desc_raw.uuid);
351
352 let desc = PartitionInfo {
353 uuid,
354 partition_id,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100355 partition_id_type,
356 props,
Balint Dobszay2338bd32024-12-19 19:00:14 +0100357 };
358
359 return Some(Ok(desc));
360 }
361
362 None
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200369 use uuid::uuid;
370
Balint Dobszay2338bd32024-12-19 19:00:14 +0100371 // TODO: add tests with a known correct partition info blob
372
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200373 #[test]
374 fn part_info() {
Balint Dobszay2338bd32024-12-19 19:00:14 +0100375 let desc1 = PartitionInfo {
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200376 uuid: uuid!("12345678-1234-1234-1234-123456789abc"),
Balint Dobszay2338bd32024-12-19 19:00:14 +0100377 partition_id: 0x8001,
378 partition_id_type: PartitionIdType::PeEndpoint {
379 execution_ctx_count: 1,
380 },
381 props: PartitionProperties {
382 support_direct_req_rec: true,
383 support_direct_req_send: true,
384 support_indirect_msg: false,
385 support_notif_rec: false,
386 subscribe_vm_created: true,
387 subscribe_vm_destroyed: true,
388 is_aarch64: true,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100389 support_direct_req2_rec: Some(true),
390 support_direct_req2_send: Some(true),
Balint Dobszay2338bd32024-12-19 19:00:14 +0100391 },
392 };
393
394 let desc2 = PartitionInfo {
395 uuid: uuid!("abcdef00-abcd-dcba-1234-abcdef012345"),
396 partition_id: 0x8002,
397 partition_id_type: PartitionIdType::SepidIndep,
398 props: PartitionProperties {
399 support_direct_req_rec: false,
400 support_direct_req_send: false,
401 support_indirect_msg: false,
402 support_notif_rec: false,
403 subscribe_vm_created: false,
404 subscribe_vm_destroyed: false,
405 is_aarch64: true,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100406 support_direct_req2_rec: None,
407 support_direct_req2_send: None,
Balint Dobszay2338bd32024-12-19 19:00:14 +0100408 },
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200409 };
410
411 let mut buf = [0u8; 0xff];
Balint Dobszayde0dc802025-02-28 14:16:52 +0100412 PartitionInfo::pack(Version(1, 2), &[desc1, desc2], &mut buf, true);
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200413
Balint Dobszayde0dc802025-02-28 14:16:52 +0100414 let mut descriptors = PartitionInfoIterator::new(Version(1, 2), &buf, 2).unwrap();
Balint Dobszay2338bd32024-12-19 19:00:14 +0100415 let desc1_check = descriptors.next().unwrap().unwrap();
416 let desc2_check = descriptors.next().unwrap().unwrap();
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200417
Balint Dobszay2338bd32024-12-19 19:00:14 +0100418 assert_eq!(desc1, desc1_check);
419 assert_eq!(desc2, desc2_check);
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200420 }
421}