blob: 039cdae71fb63297523e5b7956ab45d0606084cc [file] [log] [blame]
Imre Kis703482d2023-11-30 15:51:26 +01001// 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
4//! Memory descriptor
5
6use packed_struct::prelude::*;
7
8use core::cell::UnsafeCell;
9use core::ptr;
10
11use crate::kernel_space::KernelSpace;
12use crate::MemoryAttributesIndex;
13
14/// Memory shareability
15#[derive(PrimitiveEnum_u8, Clone, Copy, Debug, PartialEq, Eq, Default)]
16pub enum Shareability {
17 #[default]
18 NonShareable = 0b00,
19 Outer = 0b10,
20 Inner = 0b11,
21}
22
23/// Data access permission
24#[derive(PrimitiveEnum_u8, Clone, Copy, Debug, PartialEq, Eq, Default)]
25pub enum DataAccessPermissions {
26 #[default]
27 ReadWrite_None = 0b00,
28 ReadWrite_ReadWrite = 0b01,
29 ReadOnly_None = 0b10,
30 ReadOnly_ReadOnly = 0b11,
31}
32
33/// Memory attributes
34#[derive(PackedStruct, Clone, Debug, PartialEq, Eq, Default)]
35#[packed_struct(size_bytes = "8", bit_numbering = "lsb0")]
36pub struct Attributes {
37 #[packed_field(bits = "54")]
38 pub uxn: bool,
39 #[packed_field(bits = "53")]
40 pub pxn: bool,
41 #[packed_field(bits = "52")]
42 pub contiguous: bool,
43 #[packed_field(bits = "11")]
44 pub not_global: bool,
45 #[packed_field(bits = "10")]
46 pub access_flag: bool,
47 #[packed_field(bits = "9..=8", ty = "enum")]
48 pub shareability: Shareability,
49 #[packed_field(bits = "7..=6", ty = "enum")]
50 pub data_access_permissions: DataAccessPermissions,
51 #[packed_field(bits = "5")]
52 pub non_secure: bool,
53 #[packed_field(bits = "4..=2", ty = "enum")]
54 pub mem_attr_index: MemoryAttributesIndex,
55}
56
57impl From<Attributes> for u64 {
58 fn from(attributes: Attributes) -> Self {
59 u64::from_be_bytes(attributes.pack().unwrap())
60 }
61}
62
63impl From<u64> for Attributes {
64 fn from(bits: u64) -> Self {
65 Self::unpack(&bits.to_be_bytes()).unwrap()
66 }
67}
68
69/// Next level attributes
70#[derive(PackedStruct, Clone, Debug, PartialEq, Eq, Default)]
71#[packed_struct(size_bytes = "8", bit_numbering = "lsb0")]
72pub struct NextLevelAttributes {
73 #[packed_field(bits = "63")]
74 ns_table: bool,
75 #[packed_field(bits = "62..=61")]
76 ap_table: Integer<u8, packed_bits::Bits<2>>,
77 #[packed_field(bits = "60")]
78 xn_table: bool,
79 #[packed_field(bits = "59")]
80 pxn_table: bool,
81}
82
83impl From<NextLevelAttributes> for u64 {
84 fn from(attributes: NextLevelAttributes) -> Self {
85 u64::from_be_bytes(attributes.pack().unwrap())
86 }
87}
88
89impl From<u64> for NextLevelAttributes {
90 fn from(bits: u64) -> Self {
91 Self::unpack(&bits.to_be_bytes()).unwrap()
92 }
93}
94
95/// Memory descriptor type
96#[derive(PartialEq, Eq, Debug)]
97pub enum DescriptorType {
98 Invalid,
99 Block,
100 Table,
101}
102
103/// Memory descriptor of a memory translation table
104#[repr(C)]
105pub struct Descriptor {
106 cell: UnsafeCell<u64>,
107}
108
109impl Descriptor {
110 const ATTR_MASK: u64 = 0xfff8_0000_0000_0ffc;
111 const DESCRIPTOR_TYPE_MASK: u64 = 0b11;
112 pub const GRANULE_SIZES: [usize; 4] = [0, 0x4000_0000, 0x0020_0000, 0x0000_1000];
113 const INVALID_DESCRIPTOR_VALUE: u64 = 0x0;
114 const NEXT_ATTR_MASK: u64 = 0xf800_0000_0000_0000;
115 const OA_MASK: u64 = 0x0000_ffff_ffff_f000;
116 const TABLE_BIT: u64 = 0b10;
117 const TABLE_ENTRY_COUNT: usize = 512;
118 const TA_MASK: u64 = 0x0000_ffff_ffff_f000;
119 const VALID_BIT: u64 = 0b01;
120
121 /// Query descriptor type
122 pub fn get_descriptor_type(&self, level: usize) -> DescriptorType {
123 assert!(level <= 3);
124
125 let desc_type_bits = unsafe { self.get() } & Self::DESCRIPTOR_TYPE_MASK;
126 if desc_type_bits & Self::VALID_BIT != 0 {
127 if level == 3 {
128 assert_eq!(Self::TABLE_BIT, desc_type_bits & Self::TABLE_BIT);
129 DescriptorType::Block
130 } else if desc_type_bits & Self::TABLE_BIT != 0 {
131 DescriptorType::Table
132 } else {
133 DescriptorType::Block
134 }
135 } else {
136 DescriptorType::Invalid
137 }
138 }
139
140 // Invalid descriptor functions
141
142 /// Check if it is a valid descriptor
143 pub fn is_valid(&self) -> bool {
144 unsafe { self.get() & Self::VALID_BIT != 0 }
145 }
146
147 // Block descriptor functions
148
149 /// Set block descriptor
150 pub fn set_block_descriptor(
151 &mut self,
152 level: usize,
153 output_address: usize,
154 attributes: Attributes,
155 ) {
156 let attr: u64 = attributes.into();
157
158 assert!(level <= 3);
159 assert!(self.get_descriptor_type(level) != DescriptorType::Table);
160 assert_eq!(0, output_address & !Self::get_oa_mask(level));
161 assert_eq!(0, attr & !Self::ATTR_MASK);
162
163 let table_bit = if level < 3 { 0 } else { Self::TABLE_BIT };
164
165 unsafe {
166 self.set(Self::VALID_BIT | table_bit | output_address as u64 | attr);
167 }
168 }
169
170 /// Get output address from the block descriptor
171 pub fn get_block_output_address(&self, level: usize) -> usize {
172 assert!(level <= 3);
173 assert_eq!(DescriptorType::Block, self.get_descriptor_type(level));
174
175 ((unsafe { self.get() }) & Self::OA_MASK) as usize
176 }
177
178 /// Set the attributes of the block descriptor
179 pub fn set_block_attributes(&mut self, level: usize, attributes: Attributes) {
180 assert!(level <= 3);
181 let attr: u64 = attributes.into();
182 assert_eq!(0, attr & !Self::ATTR_MASK);
183 assert_eq!(DescriptorType::Block, self.get_descriptor_type(level));
184
185 unsafe { self.modify(|d| (d & !Self::ATTR_MASK) | attr) };
186 }
187
188 /// Get the attributes of the block descriptor
189 pub fn get_block_attributes(&self, level: usize) -> Attributes {
190 assert!(level <= 3);
191 assert_eq!(DescriptorType::Block, self.get_descriptor_type(level));
192
193 Attributes::from((unsafe { self.get() }) & Self::ATTR_MASK)
194 }
195
196 /// Set block descriptor to invalid
197 pub fn set_block_descriptor_to_invalid(&mut self, level: usize) {
198 assert!(level <= 3);
199 assert_eq!(DescriptorType::Block, self.get_descriptor_type(level));
200
201 unsafe { self.set(Self::INVALID_DESCRIPTOR_VALUE) }
202 }
203
204 /// Set table descriptor
205 ///
206 /// **Unsafe**: The caller has to ensure that the passed next level table has the same life as
207 /// the descriptor.
208 pub unsafe fn set_table_descriptor(
209 &mut self,
210 level: usize,
211 next_level_table: &mut [Descriptor],
212 next_level_attributes: Option<NextLevelAttributes>,
213 ) {
214 assert!(level <= 2);
215 assert_eq!(Self::TABLE_ENTRY_COUNT, next_level_table.len());
216 assert!(self.get_descriptor_type(level) != DescriptorType::Table);
217
218 let table_addr = KernelSpace::kernel_to_pa(next_level_table.as_ptr() as u64);
219 assert_eq!(0, table_addr & !Self::TA_MASK);
220
221 let mut raw_desc_value = Self::VALID_BIT | Self::TABLE_BIT | table_addr;
222
223 if let Some(next_attr) = next_level_attributes {
224 let next_attr_bits: u64 = next_attr.into();
225 assert_eq!(0, next_attr_bits & !Self::NEXT_ATTR_MASK);
226 raw_desc_value |= next_attr_bits;
227 }
228
229 self.set(raw_desc_value);
230 }
231
232 /// Get next level table
233 ///
234 /// **Unsafe**: The returned next level table is based on the address read from the descriptor.
235 /// The caller has to ensure that no other references are being used of the table.
236 pub unsafe fn get_next_level_table(&self, level: usize) -> &[Descriptor] {
237 assert!(level <= 2);
238 assert_eq!(DescriptorType::Table, self.get_descriptor_type(level));
239
240 let table_address =
241 KernelSpace::pa_to_kernel(self.get() & Self::TA_MASK) as *const Descriptor;
242 core::slice::from_raw_parts(table_address, Self::TABLE_ENTRY_COUNT)
243 }
244
245 /// Get mutable next level table
246 ///
247 /// **Unsafe**: The returned next level table is based on the address read from the descriptor.
248 /// The caller has to ensure that no other references are being used of the table.
249 pub unsafe fn get_next_level_table_mut(&mut self, level: usize) -> &mut [Descriptor] {
250 assert!(level <= 2);
251 assert_eq!(DescriptorType::Table, self.get_descriptor_type(level));
252
253 let table_address =
254 KernelSpace::pa_to_kernel(self.get() & Self::TA_MASK) as *mut Descriptor;
255 core::slice::from_raw_parts_mut(table_address, Self::TABLE_ENTRY_COUNT)
256 }
257
258 /// Get next level attributes
259 pub fn get_next_level_attributes(&self, level: usize) -> NextLevelAttributes {
260 assert!(level <= 2);
261 assert_eq!(DescriptorType::Table, self.get_descriptor_type(level));
262
263 NextLevelAttributes::from((unsafe { self.get() }) & Self::NEXT_ATTR_MASK)
264 }
265
266 /// Set table descriptor to invalid
267 ///
268 /// **Unsafe:** The returned descriptor reference must be released by the caller, i.e. release
269 /// to `PagePool`
270 pub unsafe fn set_table_descriptor_to_invalid(&mut self, level: usize) -> &mut [Descriptor] {
271 assert!(level <= 2);
272 assert_eq!(DescriptorType::Table, self.get_descriptor_type(level));
273
274 let table_address =
275 KernelSpace::pa_to_kernel(self.get() & Self::TA_MASK) as *mut Descriptor;
276 self.set(Self::INVALID_DESCRIPTOR_VALUE);
277 core::slice::from_raw_parts_mut(table_address, Self::TABLE_ENTRY_COUNT)
278 }
279
280 /// Get raw descriptor value
281 unsafe fn get(&self) -> u64 {
282 ptr::read_volatile(self.cell.get())
283 }
284
285 /// Set raw descriptor value
286 unsafe fn set(&mut self, value: u64) {
287 ptr::write_volatile(self.cell.get(), value)
288 }
289
290 /// Modify raw descriptor value
291 unsafe fn modify<F>(&mut self, f: F)
292 where
293 F: Fn(u64) -> u64,
294 {
295 self.set(f(self.get()))
296 }
297
298 /// Get output address mask
299 fn get_oa_mask(level: usize) -> usize {
300 Self::OA_MASK as usize & !(Self::GRANULE_SIZES[level] - 1)
301 }
302}
303
304#[test]
305fn test_attributes() {
306 let attributes = Attributes::default();
307 assert_eq!(0u64, attributes.into());
308
309 let attributes = Attributes {
310 uxn: true,
311 ..Default::default()
312 };
313 assert_eq!(1u64 << 54, attributes.into());
314
315 let attributes = Attributes {
316 pxn: true,
317 ..Default::default()
318 };
319 assert_eq!(1u64 << 53, attributes.into());
320
321 let attributes = Attributes {
322 contiguous: true,
323 ..Default::default()
324 };
325 assert_eq!(1u64 << 52, attributes.into());
326
327 let attributes = Attributes {
328 not_global: true,
329 ..Default::default()
330 };
331 assert_eq!(1u64 << 11, attributes.into());
332
333 let attributes = Attributes {
334 access_flag: true,
335 ..Default::default()
336 };
337 assert_eq!(1u64 << 10, attributes.into());
338
339 let attributes = Attributes {
340 non_secure: true,
341 ..Default::default()
342 };
343 assert_eq!(1u64 << 5, attributes.into());
344
345 let attributes = Attributes {
346 mem_attr_index: MemoryAttributesIndex::Normal_IWBWA_OWBWA,
347 ..Default::default()
348 };
349 assert_eq!(
350 (MemoryAttributesIndex::Normal_IWBWA_OWBWA as u64) << 2,
351 attributes.into()
352 );
353
354 let attributes: Attributes = 0.into();
355 assert!(!attributes.uxn);
356 assert!(!attributes.pxn);
357 assert!(!attributes.contiguous);
358 assert!(!attributes.not_global);
359 assert!(!attributes.access_flag);
360 assert_eq!(Shareability::NonShareable, attributes.shareability);
361 assert_eq!(
362 DataAccessPermissions::ReadWrite_None,
363 attributes.data_access_permissions
364 );
365 assert!(!attributes.non_secure);
366 assert_eq!(
367 MemoryAttributesIndex::Device_nGnRnE,
368 attributes.mem_attr_index
369 );
370}
371
372#[test]
373fn test_next_level_attributes() {
374 let next_level_attributes = NextLevelAttributes::default();
375 assert_eq!(0u64, next_level_attributes.into());
376
377 let next_level_attributes = NextLevelAttributes {
378 ns_table: true,
379 ..Default::default()
380 };
381 assert_eq!(1u64 << 63, next_level_attributes.into());
382
383 let next_level_attributes = NextLevelAttributes {
384 ap_table: 3.into(),
385 ..Default::default()
386 };
387 assert_eq!(3u64 << 61, next_level_attributes.into());
388
389 let next_level_attributes = NextLevelAttributes {
390 xn_table: true,
391 ..Default::default()
392 };
393 assert_eq!(1u64 << 60, next_level_attributes.into());
394
395 let next_level_attributes = NextLevelAttributes {
396 pxn_table: true,
397 ..Default::default()
398 };
399 assert_eq!(1u64 << 59, next_level_attributes.into());
400
401 let next_level_attributes: NextLevelAttributes = 0.into();
402 assert!(!next_level_attributes.ns_table);
403 assert_eq!(0u8, next_level_attributes.ap_table.into());
404 assert!(!next_level_attributes.xn_table);
405 assert!(!next_level_attributes.pxn_table);
406
407 let next_level_attributes: NextLevelAttributes = u64::MAX.into();
408 assert!(next_level_attributes.ns_table);
409 assert_eq!(3u8, next_level_attributes.ap_table.into());
410 assert!(next_level_attributes.xn_table);
411 assert!(next_level_attributes.pxn_table);
412}
413
414#[test]
415fn test_descriptor_get_type() {
416 let descriptor = Descriptor {
417 cell: UnsafeCell::new(0),
418 };
419 assert_eq!(DescriptorType::Invalid, descriptor.get_descriptor_type(1));
420
421 let descriptor = Descriptor {
422 cell: UnsafeCell::new(1),
423 };
424 assert_eq!(DescriptorType::Block, descriptor.get_descriptor_type(1));
425
426 let descriptor = Descriptor {
427 cell: UnsafeCell::new(3),
428 };
429 assert_eq!(DescriptorType::Table, descriptor.get_descriptor_type(1));
430
431 let descriptor = Descriptor {
432 cell: UnsafeCell::new(0),
433 };
434 assert_eq!(DescriptorType::Invalid, descriptor.get_descriptor_type(3));
435
436 let descriptor = Descriptor {
437 cell: UnsafeCell::new(3),
438 };
439 assert_eq!(DescriptorType::Block, descriptor.get_descriptor_type(3));
440}
441
442#[test]
443fn test_descriptor_is_valid() {
444 let descriptor = Descriptor {
445 cell: UnsafeCell::new(0),
446 };
447 assert!(!descriptor.is_valid());
448
449 let descriptor = Descriptor {
450 cell: UnsafeCell::new(1),
451 };
452 assert!(descriptor.is_valid());
453}
454
455#[test]
456fn test_descriptor_set_block_to_block_again() {
457 let mut descriptor = Descriptor {
458 cell: UnsafeCell::new(1),
459 };
460
461 descriptor.set_block_descriptor(1, 0, Attributes::default());
462 assert_eq!(0x1, unsafe { descriptor.get() });
463}
464
465#[test]
466#[should_panic]
467fn test_descriptor_set_block_invalid_oa() {
468 let mut descriptor = Descriptor {
469 cell: UnsafeCell::new(0),
470 };
471
472 descriptor.set_block_descriptor(1, 1 << 63, Attributes::default());
473}
474
475#[test]
476fn test_descriptor_block() {
477 let mut descriptor = Descriptor {
478 cell: UnsafeCell::new(0),
479 };
480
481 descriptor.set_block_descriptor(
482 1,
483 0x0000000f_c0000000,
484 Attributes {
485 uxn: true,
486 ..Default::default()
487 },
488 );
489 assert_eq!(0x0040000f_c0000001, unsafe { descriptor.get() });
490
491 let mut descriptor = Descriptor {
492 cell: UnsafeCell::new(0),
493 };
494
495 descriptor.set_block_descriptor(
496 3,
497 0x0000000f_fffff000,
498 Attributes {
499 uxn: true,
500 ..Default::default()
501 },
502 );
503 assert_eq!(0x0040000f_fffff003, unsafe { descriptor.get() });
504
505 assert_eq!(0x0000000f_fffff000, descriptor.get_block_output_address(3));
506 assert_eq!(
507 Attributes {
508 uxn: true,
509 ..Default::default()
510 },
511 descriptor.get_block_attributes(3)
512 );
513
514 descriptor.set_block_attributes(
515 3,
516 Attributes {
517 pxn: true,
518 ..Default::default()
519 },
520 );
521 assert_eq!(
522 Attributes {
523 pxn: true,
524 ..Default::default()
525 },
526 descriptor.get_block_attributes(3)
527 );
528}
529
530#[test]
531#[should_panic]
532fn test_descriptor_invalid_block_to_invalid() {
533 let mut descriptor = Descriptor {
534 cell: UnsafeCell::new(0),
535 };
536
537 descriptor.set_block_descriptor_to_invalid(0);
538}
539
540#[test]
541fn test_descriptor_block_to_invalid() {
542 let mut descriptor = Descriptor {
543 cell: UnsafeCell::new(3),
544 };
545
546 descriptor.set_block_descriptor_to_invalid(3);
547 assert_eq!(0, unsafe { descriptor.get() });
548}
549
550#[test]
551#[should_panic]
552fn test_descriptor_level3_to_table() {
553 let mut next_level_table = [Descriptor {
554 cell: UnsafeCell::new(0),
555 }];
556 let mut descriptor = Descriptor {
557 cell: UnsafeCell::new(0),
558 };
559
560 unsafe {
561 descriptor.set_table_descriptor(3, &mut next_level_table, None);
562 }
563}
564
565#[test]
566fn test_descriptor_block_to_table() {
567 let next_level_table =
568 unsafe { core::slice::from_raw_parts_mut(0x1000 as *mut Descriptor, 512) };
569 let mut descriptor = Descriptor {
570 cell: UnsafeCell::new(1),
571 };
572
573 unsafe {
574 descriptor.set_table_descriptor(0, next_level_table, None);
575 }
576 assert_eq!(0x1003, unsafe { descriptor.get() });
577}
578
579#[test]
580#[should_panic]
581fn test_descriptor_table_invalid_count() {
582 let next_level_table =
583 unsafe { core::slice::from_raw_parts_mut(0x800 as *mut Descriptor, 511) };
584 let mut descriptor = Descriptor {
585 cell: UnsafeCell::new(0),
586 };
587
588 unsafe {
589 descriptor.set_table_descriptor(0, next_level_table, None);
590 }
591}
592
593#[test]
594#[should_panic]
595fn test_descriptor_table_non_aligned() {
596 let next_level_table =
597 unsafe { core::slice::from_raw_parts_mut(0x800 as *mut Descriptor, 512) };
598 let mut descriptor = Descriptor {
599 cell: UnsafeCell::new(0),
600 };
601
602 unsafe {
603 descriptor.set_table_descriptor(0, next_level_table, None);
604 }
605}
606
607#[test]
608fn test_descriptor_table() {
609 let next_level_table =
610 unsafe { core::slice::from_raw_parts_mut(0x0000_000c_ba98_7000 as *mut Descriptor, 512) };
611 let mut descriptor = Descriptor {
612 cell: UnsafeCell::new(0),
613 };
614
615 unsafe {
616 descriptor.set_table_descriptor(0, next_level_table, None);
617 }
618 assert_eq!(0x0000_000c_ba98_7003, unsafe { descriptor.get() });
619}
620
621#[test]
622fn test_descriptor_table_next_level_attr() {
623 const NEXT_LEVEL_ADDR: u64 = 0x0000_000c_ba98_7000;
624 let next_level_table =
625 unsafe { core::slice::from_raw_parts_mut(NEXT_LEVEL_ADDR as *mut Descriptor, 512) };
626 let mut descriptor = Descriptor {
627 cell: UnsafeCell::new(0),
628 };
629
630 unsafe {
631 descriptor.set_table_descriptor(
632 0,
633 next_level_table,
634 Some(NextLevelAttributes {
635 ns_table: true,
636 ..Default::default()
637 }),
638 );
639 }
640 assert_eq!(NEXT_LEVEL_ADDR | 0x8000_0000_0000_0003, unsafe {
641 descriptor.get()
642 });
643}
644
645#[test]
646fn test_descriptor_table_get_next_level_table() {
647 const NEXT_LEVEL_ADDR: u64 = 0x0000_000c_ba98_7000;
648 let descriptor = Descriptor {
649 cell: UnsafeCell::new(NEXT_LEVEL_ADDR | 0x8000_0000_0000_0003),
650 };
651 assert_eq!(KernelSpace::pa_to_kernel(NEXT_LEVEL_ADDR), unsafe {
652 descriptor.get_next_level_table(0).as_ptr() as u64
653 });
654}
655
656#[test]
657fn test_descriptor_table_get_next_level_table_mut() {
658 const NEXT_LEVEL_ADDR: u64 = 0x0000_000c_ba98_7000;
659 let mut descriptor = Descriptor {
660 cell: UnsafeCell::new(NEXT_LEVEL_ADDR | 0x8000_0000_0000_0003),
661 };
662 assert_eq!(KernelSpace::pa_to_kernel(NEXT_LEVEL_ADDR), unsafe {
663 descriptor.get_next_level_table_mut(0).as_ptr() as *mut Descriptor as u64
664 });
665}
666
667#[test]
668fn test_descriptor_table_get_next_level_attr() {
669 const NEXT_LEVEL_ADDR: u64 = 0x0000_000c_ba98_7000;
670 let descriptor = Descriptor {
671 cell: UnsafeCell::new(NEXT_LEVEL_ADDR | 0x8000_0000_0000_0003),
672 };
673 assert_eq!(
674 NextLevelAttributes {
675 ns_table: true,
676 ..Default::default()
677 },
678 descriptor.get_next_level_attributes(0)
679 );
680}
681
682#[test]
683fn test_descriptor_table_set_to_invalid() {
684 const NEXT_LEVEL_ADDR: u64 = 0x0000_000c_ba98_7000;
685 let mut descriptor = Descriptor {
686 cell: UnsafeCell::new(NEXT_LEVEL_ADDR | 0x8000_0000_0000_0003),
687 };
688 assert_eq!(KernelSpace::pa_to_kernel(NEXT_LEVEL_ADDR), unsafe {
689 descriptor.set_table_descriptor_to_invalid(0).as_ptr() as *mut Descriptor as u64
690 });
691 assert_eq!(0, unsafe { descriptor.get() });
692}
693
694#[test]
695fn test_descriptor_raw_interface() {
696 let cell_value = 0x01234567_89abcdefu64;
697 let cell_new_value = 0x12345678_9abcdef0u64;
698
699 let mut descriptor = Descriptor {
700 cell: UnsafeCell::new(cell_value),
701 };
702
703 unsafe {
704 assert_eq!(cell_value, descriptor.get());
705
706 descriptor.set(cell_new_value);
707 assert_eq!(cell_new_value, descriptor.get());
708
709 descriptor.modify(|d| d + 1);
710 assert_eq!(cell_new_value + 1, descriptor.get());
711 }
712}