mmtk/util/metadata/side_metadata/
side_metadata_tests.rs

1#[cfg(all(test, debug_assertions))]
2mod tests {
3    use atomic::Ordering;
4
5    use crate::util::constants;
6    use crate::util::heap::layout::vm_layout;
7    use crate::util::heap::layout::vm_layout::vm_layout;
8    use crate::util::metadata::side_metadata::SideMetadataContext;
9    use crate::util::metadata::side_metadata::SideMetadataSpec;
10    use crate::util::metadata::side_metadata::*;
11    use crate::util::test_util::{serial_test, with_cleanup};
12    use crate::util::Address;
13
14    #[test]
15    fn test_side_metadata_address_to_meta_address() {
16        let mut gspec = SideMetadataSpec {
17            name: "gspec",
18            is_global: true,
19            offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
20            log_num_of_bits: 0,
21            log_bytes_in_region: 0,
22        };
23        #[cfg(target_pointer_width = "64")]
24        let mut lspec = SideMetadataSpec {
25            name: "lspec",
26            is_global: false,
27            offset: SideMetadataOffset::addr(LOCAL_SIDE_METADATA_BASE_ADDRESS),
28            log_num_of_bits: 0,
29            log_bytes_in_region: 0,
30        };
31
32        #[cfg(target_pointer_width = "32")]
33        let mut lspec = SideMetadataSpec {
34            name: "lspec",
35            is_global: false,
36            offset: SideMetadataOffset::rel(0),
37            log_num_of_bits: 0,
38            log_bytes_in_region: 0,
39        };
40
41        assert_eq!(
42            address_to_meta_address(&gspec, unsafe { Address::from_usize(0) }),
43            GLOBAL_SIDE_METADATA_BASE_ADDRESS
44        );
45        assert_eq!(
46            address_to_meta_address(&lspec, unsafe { Address::from_usize(0) }),
47            LOCAL_SIDE_METADATA_BASE_ADDRESS
48        );
49
50        assert_eq!(
51            address_to_meta_address(&gspec, unsafe { Address::from_usize(7) }),
52            GLOBAL_SIDE_METADATA_BASE_ADDRESS
53        );
54        assert_eq!(
55            address_to_meta_address(&lspec, unsafe { Address::from_usize(7) }),
56            LOCAL_SIDE_METADATA_BASE_ADDRESS
57        );
58
59        assert_eq!(
60            address_to_meta_address(&gspec, unsafe { Address::from_usize(27) }),
61            GLOBAL_SIDE_METADATA_BASE_ADDRESS + 3usize
62        );
63        assert_eq!(
64            address_to_meta_address(&lspec, unsafe { Address::from_usize(129) }),
65            LOCAL_SIDE_METADATA_BASE_ADDRESS + 16usize
66        );
67
68        gspec.log_bytes_in_region = 2;
69        lspec.log_bytes_in_region = 1;
70
71        assert_eq!(
72            address_to_meta_address(&gspec, unsafe { Address::from_usize(0) }),
73            GLOBAL_SIDE_METADATA_BASE_ADDRESS
74        );
75        assert_eq!(
76            address_to_meta_address(&lspec, unsafe { Address::from_usize(0) }),
77            LOCAL_SIDE_METADATA_BASE_ADDRESS
78        );
79
80        assert_eq!(
81            address_to_meta_address(&gspec, unsafe { Address::from_usize(32) }),
82            GLOBAL_SIDE_METADATA_BASE_ADDRESS + 1usize
83        );
84        assert_eq!(
85            address_to_meta_address(&lspec, unsafe { Address::from_usize(32) }),
86            LOCAL_SIDE_METADATA_BASE_ADDRESS + 2usize
87        );
88
89        assert_eq!(
90            address_to_meta_address(&gspec, unsafe { Address::from_usize(316) }),
91            GLOBAL_SIDE_METADATA_BASE_ADDRESS + 9usize
92        );
93        assert_eq!(
94            address_to_meta_address(&lspec, unsafe { Address::from_usize(316) }),
95            LOCAL_SIDE_METADATA_BASE_ADDRESS + 19usize
96        );
97
98        gspec.log_num_of_bits = 1;
99        lspec.log_num_of_bits = 3;
100
101        assert_eq!(
102            address_to_meta_address(&gspec, unsafe { Address::from_usize(0) }),
103            GLOBAL_SIDE_METADATA_BASE_ADDRESS
104        );
105        assert_eq!(
106            address_to_meta_address(&lspec, unsafe { Address::from_usize(0) }),
107            LOCAL_SIDE_METADATA_BASE_ADDRESS
108        );
109
110        assert_eq!(
111            address_to_meta_address(&gspec, unsafe { Address::from_usize(32) }),
112            GLOBAL_SIDE_METADATA_BASE_ADDRESS + 2usize
113        );
114        assert_eq!(
115            address_to_meta_address(&lspec, unsafe { Address::from_usize(32) }),
116            LOCAL_SIDE_METADATA_BASE_ADDRESS + 16usize
117        );
118
119        assert_eq!(
120            address_to_meta_address(&gspec, unsafe { Address::from_usize(316) }),
121            GLOBAL_SIDE_METADATA_BASE_ADDRESS + 19usize
122        );
123        assert_eq!(
124            address_to_meta_address(&lspec, unsafe { Address::from_usize(318) }),
125            LOCAL_SIDE_METADATA_BASE_ADDRESS + 159usize
126        );
127    }
128
129    #[test]
130    fn test_side_metadata_meta_byte_mask() {
131        let mut spec = SideMetadataSpec {
132            name: "test_spec",
133            is_global: true,
134            offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
135            log_num_of_bits: 0,
136            log_bytes_in_region: 0,
137        };
138
139        assert_eq!(meta_byte_mask(&spec), 1);
140
141        spec.log_num_of_bits = 1;
142        assert_eq!(meta_byte_mask(&spec), 3);
143        spec.log_num_of_bits = 2;
144        assert_eq!(meta_byte_mask(&spec), 15);
145        spec.log_num_of_bits = 3;
146        assert_eq!(meta_byte_mask(&spec), 255);
147    }
148
149    #[test]
150    fn test_side_metadata_meta_byte_lshift() {
151        let mut spec = SideMetadataSpec {
152            name: "test_spec",
153            is_global: true,
154            offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
155            log_num_of_bits: 0,
156            log_bytes_in_region: 0,
157        };
158
159        assert_eq!(
160            meta_byte_lshift(&spec, unsafe { Address::from_usize(0) }),
161            0
162        );
163        assert_eq!(
164            meta_byte_lshift(&spec, unsafe { Address::from_usize(5) }),
165            5
166        );
167        assert_eq!(
168            meta_byte_lshift(&spec, unsafe { Address::from_usize(15) }),
169            7
170        );
171
172        spec.log_num_of_bits = 2;
173
174        assert_eq!(
175            meta_byte_lshift(&spec, unsafe { Address::from_usize(0) }),
176            0
177        );
178        assert_eq!(
179            meta_byte_lshift(&spec, unsafe { Address::from_usize(5) }),
180            4
181        );
182        assert_eq!(
183            meta_byte_lshift(&spec, unsafe { Address::from_usize(15) }),
184            4
185        );
186        assert_eq!(
187            meta_byte_lshift(&spec, unsafe { Address::from_usize(0x10010) }),
188            0
189        );
190    }
191
192    #[test]
193    fn test_side_metadata_try_mmap_metadata() {
194        let heap_start = vm_layout().heap_start;
195        serial_test(|| {
196            with_cleanup(
197                || {
198                    // We need to do this because of the static NO_METADATA
199                    // sanity::reset();
200                    let mut gspec = SideMetadataSpec {
201                        name: "gspec",
202                        is_global: true,
203                        offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
204                        log_num_of_bits: 1,
205                        log_bytes_in_region: 1,
206                    };
207                    #[cfg(target_pointer_width = "64")]
208                    let mut lspec = SideMetadataSpec {
209                        name: "lspec",
210                        is_global: false,
211                        offset: SideMetadataOffset::addr(LOCAL_SIDE_METADATA_BASE_ADDRESS),
212                        log_num_of_bits: 1,
213                        log_bytes_in_region: 1,
214                    };
215                    #[cfg(target_pointer_width = "32")]
216                    let mut lspec = SideMetadataSpec {
217                        name: "lspec",
218                        is_global: false,
219                        offset: SideMetadataOffset::rel(0),
220                        log_num_of_bits: 1,
221                        log_bytes_in_region: 1,
222                    };
223
224                    let metadata = SideMetadataContext {
225                        global: vec![gspec],
226                        local: vec![lspec],
227                    };
228
229                    let mut metadata_sanity = SideMetadataSanity::new();
230                    metadata_sanity.verify_metadata_context("NoPolicy", &metadata);
231
232                    assert!(metadata
233                        .try_map_metadata_space(heap_start, constants::BYTES_IN_PAGE, "test_space")
234                        .is_ok());
235
236                    gspec.assert_metadata_mapped(heap_start);
237                    lspec.assert_metadata_mapped(heap_start);
238                    gspec.assert_metadata_mapped(heap_start + constants::BYTES_IN_PAGE - 1);
239                    lspec.assert_metadata_mapped(heap_start + constants::BYTES_IN_PAGE - 1);
240
241                    metadata.ensure_unmap_metadata_space(heap_start, constants::BYTES_IN_PAGE);
242
243                    gspec.log_bytes_in_region = 4;
244                    gspec.log_num_of_bits = 4;
245                    lspec.log_bytes_in_region = 4;
246                    lspec.log_num_of_bits = 4;
247
248                    metadata_sanity.reset();
249
250                    let metadata = SideMetadataContext {
251                        global: vec![gspec],
252                        local: vec![lspec],
253                    };
254
255                    metadata_sanity.verify_metadata_context("NoPolicy", &metadata);
256                    metadata_sanity.reset();
257
258                    assert!(metadata
259                        .try_map_metadata_space(
260                            heap_start + vm_layout::BYTES_IN_CHUNK,
261                            vm_layout::BYTES_IN_CHUNK,
262                            "test_space",
263                        )
264                        .is_ok());
265
266                    gspec.assert_metadata_mapped(heap_start + vm_layout::BYTES_IN_CHUNK);
267                    lspec.assert_metadata_mapped(heap_start + vm_layout::BYTES_IN_CHUNK);
268                    gspec.assert_metadata_mapped(heap_start + vm_layout::BYTES_IN_CHUNK * 2 - 8);
269                    lspec.assert_metadata_mapped(heap_start + vm_layout::BYTES_IN_CHUNK * 2 - 16);
270
271                    metadata.ensure_unmap_metadata_space(
272                        heap_start + vm_layout::BYTES_IN_CHUNK,
273                        vm_layout::BYTES_IN_CHUNK,
274                    );
275                },
276                || {
277                    sanity::reset();
278                },
279            );
280        })
281    }
282
283    #[test]
284    fn test_side_metadata_atomic_fetch_add_sub_ge8bits() {
285        serial_test(|| {
286            with_cleanup(
287                || {
288                    // We need to do this because of the static NO_METADATA
289                    // sanity::reset();
290                    let data_addr = vm_layout().heap_start;
291
292                    let metadata_1_spec = SideMetadataSpec {
293                        name: "metadata_1_spec",
294                        is_global: true,
295                        offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
296                        log_num_of_bits: 4,
297                        log_bytes_in_region: 6,
298                    };
299
300                    let metadata_2_spec = SideMetadataSpec {
301                        name: "metadata_2_spec",
302                        is_global: true,
303                        offset: SideMetadataOffset::layout_after(&metadata_1_spec),
304                        log_num_of_bits: 3,
305                        log_bytes_in_region: 7,
306                    };
307
308                    let metadata = SideMetadataContext {
309                        global: vec![metadata_1_spec, metadata_2_spec],
310                        local: vec![],
311                    };
312
313                    let mut metadata_sanity = SideMetadataSanity::new();
314                    metadata_sanity.verify_metadata_context("NoPolicy", &metadata);
315
316                    assert!(metadata
317                        .try_map_metadata_space(data_addr, constants::BYTES_IN_PAGE, "test_space",)
318                        .is_ok());
319
320                    let zero =
321                        metadata_1_spec.fetch_add_atomic::<u16>(data_addr, 5, Ordering::SeqCst);
322                    assert_eq!(zero, 0);
323
324                    let five = metadata_1_spec.load_atomic::<u16>(data_addr, Ordering::SeqCst);
325                    assert_eq!(five, 5);
326
327                    let zero =
328                        metadata_2_spec.fetch_add_atomic::<u8>(data_addr, 5, Ordering::SeqCst);
329                    assert_eq!(zero, 0);
330
331                    let five = metadata_2_spec.load_atomic::<u8>(data_addr, Ordering::SeqCst);
332                    assert_eq!(five, 5);
333
334                    let another_five =
335                        metadata_1_spec.fetch_sub_atomic::<u16>(data_addr, 2, Ordering::SeqCst);
336                    assert_eq!(another_five, 5);
337
338                    let three = metadata_1_spec.load_atomic::<u16>(data_addr, Ordering::SeqCst);
339                    assert_eq!(three, 3);
340
341                    let another_five =
342                        metadata_2_spec.fetch_sub_atomic::<u8>(data_addr, 2, Ordering::SeqCst);
343                    assert_eq!(another_five, 5);
344
345                    let three = metadata_2_spec.load_atomic::<u8>(data_addr, Ordering::SeqCst);
346                    assert_eq!(three, 3);
347
348                    metadata.ensure_unmap_metadata_space(data_addr, constants::BYTES_IN_PAGE);
349                    metadata_sanity.reset();
350                },
351                || {
352                    sanity::reset();
353                },
354            );
355        });
356    }
357
358    #[test]
359    fn test_side_metadata_atomic_fetch_add_sub_2bits() {
360        serial_test(|| {
361            with_cleanup(
362                || {
363                    // We need to do this because of the static NO_METADATA
364                    // sanity::reset();
365                    let data_addr = vm_layout().heap_start + (vm_layout::BYTES_IN_CHUNK << 1) * 2;
366
367                    let metadata_1_spec = SideMetadataSpec {
368                        name: "metadata_1_spec",
369                        is_global: true,
370                        offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
371                        log_num_of_bits: 1,
372                        log_bytes_in_region: constants::LOG_BYTES_IN_WORD as usize,
373                    };
374
375                    let metadata = SideMetadataContext {
376                        global: vec![metadata_1_spec],
377                        local: vec![],
378                    };
379
380                    let mut metadata_sanity = SideMetadataSanity::new();
381                    metadata_sanity.verify_metadata_context("NoPolicy", &metadata);
382
383                    assert!(metadata
384                        .try_map_metadata_space(data_addr, constants::BYTES_IN_PAGE, "test_space",)
385                        .is_ok());
386
387                    let zero =
388                        metadata_1_spec.fetch_add_atomic::<u8>(data_addr, 2, Ordering::SeqCst);
389                    assert_eq!(zero, 0);
390
391                    let two = metadata_1_spec.load_atomic::<u8>(data_addr, Ordering::SeqCst);
392                    assert_eq!(two, 2);
393
394                    let another_two =
395                        metadata_1_spec.fetch_sub_atomic::<u8>(data_addr, 1, Ordering::SeqCst);
396                    assert_eq!(another_two, 2);
397
398                    let one = metadata_1_spec.load_atomic::<u8>(data_addr, Ordering::SeqCst);
399                    assert_eq!(one, 1);
400
401                    metadata_1_spec.store_atomic::<u8>(data_addr, 0, Ordering::SeqCst);
402
403                    metadata.ensure_unmap_metadata_space(data_addr, constants::BYTES_IN_PAGE);
404
405                    metadata_sanity.reset();
406                },
407                || {
408                    sanity::reset();
409                },
410            );
411        });
412    }
413
414    #[test]
415    fn test_side_metadata_atomic_fetch_and_or_2bits() {
416        serial_test(|| {
417            with_cleanup(
418                || {
419                    // We need to do this because of the static NO_METADATA
420                    // sanity::reset();
421                    let data_addr =
422                        vm_layout::vm_layout().heap_start + (vm_layout::BYTES_IN_CHUNK << 1);
423
424                    let metadata_1_spec = SideMetadataSpec {
425                        name: "metadata_1_spec",
426                        is_global: true,
427                        offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
428                        log_num_of_bits: 1,
429                        log_bytes_in_region: constants::LOG_BYTES_IN_WORD as usize,
430                    };
431
432                    let metadata = SideMetadataContext {
433                        global: vec![metadata_1_spec],
434                        local: vec![],
435                    };
436
437                    let mut metadata_sanity = SideMetadataSanity::new();
438                    metadata_sanity.verify_metadata_context("NoPolicy", &metadata);
439
440                    assert!(metadata
441                        .try_map_metadata_space(data_addr, constants::BYTES_IN_PAGE, "test_space",)
442                        .is_ok());
443
444                    let zero =
445                        metadata_1_spec.fetch_or_atomic::<u8>(data_addr, 0b11, Ordering::SeqCst);
446                    assert_eq!(zero, 0);
447
448                    let value_11 = metadata_1_spec.load_atomic::<u8>(data_addr, Ordering::SeqCst);
449                    assert_eq!(value_11, 0b11);
450
451                    let another_value_11 =
452                        metadata_1_spec.fetch_and_atomic::<u8>(data_addr, 0b01, Ordering::SeqCst);
453                    assert_eq!(another_value_11, 0b11);
454
455                    let value_01 = metadata_1_spec.load_atomic::<u8>(data_addr, Ordering::SeqCst);
456                    assert_eq!(value_01, 0b01);
457
458                    metadata_1_spec.store_atomic::<u8>(data_addr, 0, Ordering::SeqCst);
459
460                    metadata.ensure_unmap_metadata_space(data_addr, constants::BYTES_IN_PAGE);
461
462                    metadata_sanity.reset();
463                },
464                || {
465                    sanity::reset();
466                },
467            );
468        });
469    }
470
471    #[test]
472    fn test_side_metadata_bzero_metadata() {
473        serial_test(|| {
474            with_cleanup(
475                || {
476                    // We need to do this because of the static NO_METADATA
477                    // sanity::reset();
478                    let data_addr = vm_layout().heap_start + (vm_layout::BYTES_IN_CHUNK << 2);
479
480                    #[cfg(target_pointer_width = "64")]
481                    let metadata_1_spec = SideMetadataSpec {
482                        name: "metadata_1_spec",
483                        is_global: false,
484                        offset: SideMetadataOffset::addr(LOCAL_SIDE_METADATA_BASE_ADDRESS),
485                        log_num_of_bits: 4,
486                        log_bytes_in_region: 9,
487                    };
488                    #[cfg(target_pointer_width = "64")]
489                    let metadata_2_spec = SideMetadataSpec {
490                        name: "metadata_2_spec",
491                        is_global: false,
492                        offset: SideMetadataOffset::layout_after(&metadata_1_spec),
493                        log_num_of_bits: 3,
494                        log_bytes_in_region: 7,
495                    };
496
497                    #[cfg(target_pointer_width = "32")]
498                    let metadata_1_spec = SideMetadataSpec {
499                        name: "metadata_1_spec",
500                        is_global: false,
501                        offset: SideMetadataOffset::rel(0),
502                        log_num_of_bits: 4,
503                        log_bytes_in_region: 9,
504                    };
505                    #[cfg(target_pointer_width = "32")]
506                    let metadata_2_spec = SideMetadataSpec {
507                        name: "metadata_2_spec",
508                        is_global: false,
509                        offset: SideMetadataOffset::layout_after(&metadata_1_spec),
510                        log_num_of_bits: 3,
511                        log_bytes_in_region: 7,
512                    };
513
514                    let metadata = SideMetadataContext {
515                        global: vec![],
516                        local: vec![metadata_1_spec, metadata_2_spec],
517                    };
518
519                    let mut metadata_sanity = SideMetadataSanity::new();
520                    metadata_sanity.verify_metadata_context("NoPolicy", &metadata);
521
522                    assert!(metadata
523                        .try_map_metadata_space(data_addr, constants::BYTES_IN_PAGE, "test_space",)
524                        .is_ok());
525
526                    let zero =
527                        metadata_1_spec.fetch_add_atomic::<u16>(data_addr, 5, Ordering::SeqCst);
528                    assert_eq!(zero, 0);
529
530                    let five = metadata_1_spec.load_atomic::<u16>(data_addr, Ordering::SeqCst);
531                    assert_eq!(five, 5);
532
533                    let zero =
534                        metadata_2_spec.fetch_add_atomic::<u8>(data_addr, 5, Ordering::SeqCst);
535                    assert_eq!(zero, 0);
536
537                    let five = metadata_2_spec.load_atomic::<u8>(data_addr, Ordering::SeqCst);
538                    assert_eq!(five, 5);
539
540                    metadata_2_spec.bzero_metadata(data_addr, constants::BYTES_IN_PAGE);
541
542                    let five = metadata_1_spec.load_atomic::<u16>(data_addr, Ordering::SeqCst);
543                    assert_eq!(five, 5);
544                    let five = metadata_2_spec.load_atomic::<u8>(data_addr, Ordering::SeqCst);
545                    assert_eq!(five, 0);
546
547                    metadata_1_spec.bzero_metadata(data_addr, constants::BYTES_IN_PAGE);
548
549                    let five = metadata_1_spec.load_atomic::<u16>(data_addr, Ordering::SeqCst);
550                    assert_eq!(five, 0);
551                    let five = metadata_2_spec.load_atomic::<u8>(data_addr, Ordering::SeqCst);
552                    assert_eq!(five, 0);
553
554                    metadata.ensure_unmap_metadata_space(data_addr, constants::BYTES_IN_PAGE);
555
556                    metadata_sanity.reset();
557                },
558                || {
559                    sanity::reset();
560                },
561            );
562        });
563    }
564
565    #[test]
566    fn test_side_metadata_bzero_by_bytes() {
567        serial_test(|| {
568            with_cleanup(
569                || {
570                    let data_addr = vm_layout::vm_layout().heap_start;
571
572                    // 1 bit per 8 bytes
573                    let spec = SideMetadataSpec {
574                        name: "test spec",
575                        is_global: true,
576                        offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
577                        log_num_of_bits: 0,
578                        log_bytes_in_region: 3,
579                    };
580                    let region_size: usize = 1 << spec.log_bytes_in_region;
581
582                    let metadata = SideMetadataContext {
583                        global: vec![spec],
584                        local: vec![],
585                    };
586
587                    let mut metadata_sanity = SideMetadataSanity::new();
588                    metadata_sanity.verify_metadata_context("NoPolicy", &metadata);
589
590                    assert!(metadata
591                        .try_map_metadata_space(data_addr, constants::BYTES_IN_PAGE, "test_space",)
592                        .is_ok());
593
594                    // First 9 regions
595                    let regions = (0..9)
596                        .map(|i| data_addr + (region_size * i))
597                        .collect::<Vec<Address>>();
598                    // Set metadata for the regions
599                    regions
600                        .iter()
601                        .for_each(|addr| unsafe { spec.store::<u8>(*addr, 1) });
602                    regions
603                        .iter()
604                        .for_each(|addr| assert!(unsafe { spec.load::<u8>(*addr) } == 1));
605
606                    // bulk zero the 8 regions (1 bit for each, in total 1 byte)
607                    spec.bzero_metadata(regions[0], region_size * 8);
608                    // Check if the first 8 regions are set to 0
609                    regions[0..8]
610                        .iter()
611                        .for_each(|addr| assert!(unsafe { spec.load::<u8>(*addr) } == 0));
612                    // Check if the 9th region is still 1
613                    assert!(unsafe { spec.load::<u8>(regions[8]) } == 1);
614                },
615                || {
616                    sanity::reset();
617                },
618            )
619        })
620    }
621
622    #[test]
623    fn test_side_metadata_bzero_by_fraction_of_bytes() {
624        serial_test(|| {
625            with_cleanup(
626                || {
627                    let data_addr = vm_layout::vm_layout().heap_start;
628
629                    // 1 bit per 8 bytes
630                    let spec = SideMetadataSpec {
631                        name: "test spec",
632                        is_global: true,
633                        offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
634                        log_num_of_bits: 0,
635                        log_bytes_in_region: 3,
636                    };
637                    let region_size: usize = 1 << spec.log_bytes_in_region;
638
639                    let metadata = SideMetadataContext {
640                        global: vec![spec],
641                        local: vec![],
642                    };
643
644                    let mut metadata_sanity = SideMetadataSanity::new();
645                    metadata_sanity.verify_metadata_context("NoPolicy", &metadata);
646
647                    assert!(metadata
648                        .try_map_metadata_space(data_addr, constants::BYTES_IN_PAGE, "test_space",)
649                        .is_ok());
650
651                    // First 9 regions
652                    let regions = (0..9)
653                        .map(|i| data_addr + (region_size * i))
654                        .collect::<Vec<Address>>();
655                    // Set metadata for the regions
656                    regions
657                        .iter()
658                        .for_each(|addr| unsafe { spec.store::<u8>(*addr, 1) });
659                    regions
660                        .iter()
661                        .for_each(|addr| assert!(unsafe { spec.load::<u8>(*addr) } == 1));
662
663                    // bulk zero the first 4 regions (1 bit for each, in total 4 bits)
664                    spec.bzero_metadata(regions[0], region_size * 4);
665                    // Check if the first 4 regions are set to 0
666                    regions[0..4]
667                        .iter()
668                        .for_each(|addr| assert!(unsafe { spec.load::<u8>(*addr) } == 0));
669                    // Check if the rest regions is still 1
670                    regions[4..9]
671                        .iter()
672                        .for_each(|addr| assert!(unsafe { spec.load::<u8>(*addr) } == 1));
673                },
674                || {
675                    sanity::reset();
676                },
677            )
678        })
679    }
680
681    #[test]
682    fn test_side_metadata_zero_meta_bits() {
683        let size = 4usize;
684        let allocate_u32 = || -> Address {
685            let ptr = unsafe {
686                std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align(size, 4).unwrap())
687            };
688            Address::from_mut_ptr(ptr)
689        };
690        let fill_1 = |addr: Address| unsafe {
691            addr.store(u32::MAX);
692        };
693
694        let start = allocate_u32();
695        let end = start + size;
696
697        fill_1(start);
698        // zero the word
699        SideMetadataSpec::zero_meta_bits(start, 0, end, 0);
700        assert_eq!(unsafe { start.load::<u32>() }, 0);
701
702        fill_1(start);
703        // zero first 2 bits
704        SideMetadataSpec::zero_meta_bits(start, 0, start, 2);
705        assert_eq!(unsafe { start.load::<u32>() }, 0xFFFF_FFFC); // ....1100
706
707        fill_1(start);
708        // zero last 2 bits
709        SideMetadataSpec::zero_meta_bits(end - 1, 6, end, 0);
710        assert_eq!(unsafe { start.load::<u32>() }, 0x3FFF_FFFF); // 0011....
711
712        fill_1(start);
713        // zero everything except first 2 bits and last 2 bits
714        SideMetadataSpec::zero_meta_bits(start, 2, end - 1, 6);
715        assert_eq!(unsafe { start.load::<u32>() }, 0xC000_0003); // 1100....0011
716    }
717
718    #[test]
719    fn test_side_metadata_bcopy_metadata_contiguous() {
720        serial_test(|| {
721            with_cleanup(
722                || {
723                    let data_addr = vm_layout().heap_start;
724
725                    let log_num_of_bits = 0;
726                    let log_bytes_in_region = 3;
727                    let num_regions = 0x400; // 1024
728                    let bytes_per_region = 1 << log_bytes_in_region;
729                    let total_size = num_regions * bytes_per_region; // 8192
730
731                    let metadata_1_spec = SideMetadataSpec {
732                        name: "metadata_1_spec",
733                        is_global: true,
734                        offset: SideMetadataOffset::addr(GLOBAL_SIDE_METADATA_BASE_ADDRESS),
735                        log_num_of_bits,
736                        log_bytes_in_region,
737                    };
738
739                    let metadata_2_spec = SideMetadataSpec {
740                        name: "metadata_2_spec",
741                        is_global: true,
742                        offset: SideMetadataOffset::layout_after(&metadata_1_spec),
743                        log_num_of_bits,
744                        log_bytes_in_region,
745                    };
746
747                    // Currently global metadata are contiguous.
748                    let metadata = SideMetadataContext {
749                        global: vec![metadata_1_spec, metadata_2_spec],
750                        local: vec![],
751                    };
752
753                    let mut metadata_sanity = SideMetadataSanity::new();
754                    metadata_sanity.verify_metadata_context("NoPolicy", &metadata);
755
756                    metadata
757                        .try_map_metadata_space(data_addr, total_size, "test_space")
758                        .unwrap();
759
760                    metadata_1_spec.bzero_metadata(data_addr, total_size);
761                    metadata_2_spec.bzero_metadata(data_addr, total_size);
762
763                    for i in 0..num_regions {
764                        metadata_1_spec.store_atomic::<u8>(
765                            data_addr + i * bytes_per_region,
766                            (i % 2) as u8,
767                            Ordering::Relaxed,
768                        );
769                    }
770
771                    let test_copy_region = |begin: usize, end: usize| {
772                        // Test copying whole bytes.
773                        metadata_2_spec.bcopy_metadata_contiguous(
774                            data_addr + begin * bytes_per_region,
775                            (end - begin) * bytes_per_region,
776                            &metadata_1_spec,
777                        );
778
779                        for i in 0..num_regions {
780                            let bit = metadata_2_spec.load_atomic::<u8>(
781                                data_addr + i * bytes_per_region,
782                                Ordering::Relaxed,
783                            );
784
785                            let expected = if begin <= i && i < end {
786                                (i % 2) as u8
787                            } else {
788                                0
789                            };
790                            assert_eq!(
791                                bit, expected,
792                                "Expected: {expected}, actual: {bit}, i: {i}, begin: {begin}, end: {end}"
793                            );
794                        }
795
796                        metadata_2_spec.bzero_metadata(data_addr, total_size);
797                    };
798
799                    // Whole bytes
800                    test_copy_region(0x100, 0x200);
801
802                    // End unaligned
803                    test_copy_region(0x18, 0xcc);
804
805                    // Start unaligned
806                    test_copy_region(0x263, 0x3f0);
807
808                    // Start and end unaligned
809                    test_copy_region(0x82, 0x1fd);
810
811                    metadata_1_spec.bzero_metadata(data_addr, total_size);
812                    metadata_2_spec.bzero_metadata(data_addr, total_size);
813
814                    metadata_sanity.reset();
815                },
816                || {
817                    sanity::reset();
818                },
819            );
820        });
821    }
822}