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