| 1 | #pragma once |
| 2 | #include <compiler.h> |
| 3 | #include <math/min_max.h> |
| 4 | #include <mem/bitmap.h> |
| 5 | #include <mem/buddy.h> |
| 6 | #include <mem/page.h> |
| 7 | #include <stdbool.h> |
| 8 | #include <stdint.h> |
| 9 | #include <sync/spinlock.h> |
| 10 | #include <types/types.h> |
| 11 | |
| 12 | #define PAGE_TO_BUDDY_PAGE(p) ((struct buddy_page *) (p)) |
| 13 | #define BUDDY_PAGE_TO_PAGE(p) ((struct page *) (p)) |
| 14 | |
| 15 | /* |
| 16 | * Layout of buddy_page::meta |
| 17 | * bits [0, 2] : page tag (PAGE_TAG_*) |
| 18 | * bit [3] : is_free |
| 19 | * bit [4] : is_zeroed |
| 20 | * bits [5, 11] : order (7 bits) |
| 21 | * bits [12, 63] : next_pfn (52 bits) |
| 22 | */ |
| 23 | #define BUDDY_IS_FREE_SHIFT 3 |
| 24 | #define BUDDY_IS_FREE_MASK (1ULL << BUDDY_IS_FREE_SHIFT) |
| 25 | |
| 26 | #define BUDDY_IS_ZEROED_SHIFT 4 |
| 27 | #define BUDDY_IS_ZEROED_MASK (1ULL << BUDDY_IS_ZEROED_SHIFT) |
| 28 | |
| 29 | #define BUDDY_ORDER_SHIFT 5 |
| 30 | #define BUDDY_ORDER_BITS 7 |
| 31 | #define BUDDY_ORDER_MASK (((1ULL << BUDDY_ORDER_BITS) - 1) << BUDDY_ORDER_SHIFT) |
| 32 | |
| 33 | #define BUDDY_NEXT_PFN_SHIFT PAGE_4K_SHIFT |
| 34 | #define BUDDY_NEXT_PFN_BITS (64 - PAGE_4K_SHIFT) |
| 35 | #define BUDDY_NEXT_PFN_MASK \ |
| 36 | (((1ULL << BUDDY_NEXT_PFN_BITS) - 1) << BUDDY_NEXT_PFN_SHIFT) |
| 37 | |
| 38 | struct buddy_page { |
| 39 | uint64_t meta; |
| 40 | }; |
| 41 | static_assert_struct_size_eq(buddy_page, 8); |
| 42 | |
| 43 | static inline uint64_t buddy_page_get_order(const struct buddy_page *bp) { |
| 44 | return (bp->meta & BUDDY_ORDER_MASK) >> BUDDY_ORDER_SHIFT; |
| 45 | } |
| 46 | |
| 47 | static inline void buddy_page_set_order(struct buddy_page *bp, uint64_t order) { |
| 48 | bp->meta = (bp->meta & ~BUDDY_ORDER_MASK) | |
| 49 | ((order << BUDDY_ORDER_SHIFT) & BUDDY_ORDER_MASK); |
| 50 | } |
| 51 | |
| 52 | static inline pfn_t buddy_page_get_next_pfn(const struct buddy_page *bp) { |
| 53 | return (pfn_t) ((bp->meta & BUDDY_NEXT_PFN_MASK) >> BUDDY_NEXT_PFN_SHIFT); |
| 54 | } |
| 55 | |
| 56 | static inline void buddy_page_set_next_pfn(struct buddy_page *bp, pfn_t pfn) { |
| 57 | bp->meta = (bp->meta & ~BUDDY_NEXT_PFN_MASK) | |
| 58 | (((uint64_t) pfn << BUDDY_NEXT_PFN_SHIFT) & BUDDY_NEXT_PFN_MASK); |
| 59 | } |
| 60 | |
| 61 | static inline bool buddy_page_is_free(const struct buddy_page *bp) { |
| 62 | return (bp->meta & BUDDY_IS_FREE_MASK) != 0; |
| 63 | } |
| 64 | |
| 65 | static inline void buddy_page_set_free(struct buddy_page *bp, bool is_free) { |
| 66 | bp->meta = |
| 67 | (bp->meta & ~BUDDY_IS_FREE_MASK) | (is_free ? BUDDY_IS_FREE_MASK : 0); |
| 68 | } |
| 69 | |
| 70 | static inline bool buddy_page_is_zeroed(const struct buddy_page *bp) { |
| 71 | return (bp->meta & BUDDY_IS_ZEROED_MASK) != 0; |
| 72 | } |
| 73 | |
| 74 | static inline void buddy_page_set_zeroed(struct buddy_page *bp, |
| 75 | bool is_zeroed) { |
| 76 | bp->meta = (bp->meta & ~BUDDY_IS_ZEROED_MASK) | |
| 77 | (is_zeroed ? BUDDY_IS_ZEROED_MASK : 0); |
| 78 | } |
| 79 | |
| 80 | static inline bool page_pfn_allocated_in_boot_bitmap(pfn_t pfn) { |
| 81 | return test_bit(index: pfn); |
| 82 | } |
| 83 | |
| 84 | static inline bool buddy_page_pfn_free(pfn_t pfn) { |
| 85 | if (pfn >= global.last_pfn) |
| 86 | return false; |
| 87 | |
| 88 | struct page *p = &global.page_array[pfn]; |
| 89 | |
| 90 | return buddy_page_is_free(bp: (struct buddy_page *) p); |
| 91 | } |
| 92 | |
| 93 | static inline struct buddy_page *buddy_page_for_pfn(pfn_t pfn) { |
| 94 | return PAGE_TO_BUDDY_PAGE(page_for_pfn(pfn)); |
| 95 | } |
| 96 | |
| 97 | static inline pfn_t buddy_page_get_pfn(struct buddy_page *bp) { |
| 98 | return page_get_pfn(bp: (struct page *) bp); |
| 99 | } |
| 100 | |
| 101 | static inline paddr_t buddy_page_get_paddr(struct buddy_page *bp) { |
| 102 | return PFN_TO_PAGE(buddy_page_get_pfn(bp)); |
| 103 | } |
| 104 | |
| 105 | static inline void buddy_page_set_next(struct buddy_page *bp, |
| 106 | struct buddy_page *next) { |
| 107 | if (!next) |
| 108 | return buddy_page_set_next_pfn(bp, pfn: 0); |
| 109 | |
| 110 | buddy_page_set_next_pfn(bp, pfn: buddy_page_get_pfn(bp: next)); |
| 111 | } |
| 112 | |
| 113 | static inline struct buddy_page *buddy_page_get_next(struct buddy_page *bp) { |
| 114 | pfn_t pfn = buddy_page_get_next_pfn(bp); |
| 115 | if (pfn == 0) |
| 116 | return NULL; |
| 117 | |
| 118 | return buddy_page_for_pfn(pfn); |
| 119 | } |
| 120 | |
| 121 | struct buddy_free_link { |
| 122 | struct buddy_page *prev; |
| 123 | }; |
| 124 | |
| 125 | static inline struct buddy_free_link * |
| 126 | buddy_page_free_link(struct buddy_page *bp) { |
| 127 | return (struct buddy_free_link *) hhdm_paddr_to_ptr( |
| 128 | p: buddy_page_get_paddr(bp)); |
| 129 | } |
| 130 | |
| 131 | static inline struct buddy_page *buddy_page_get_prev(struct buddy_page *bp) { |
| 132 | return buddy_page_free_link(bp)->prev; |
| 133 | } |
| 134 | |
| 135 | static inline void buddy_page_set_prev(struct buddy_page *bp, |
| 136 | struct buddy_page *prev) { |
| 137 | buddy_page_free_link(bp)->prev = prev; |
| 138 | } |
| 139 | |
| 140 | static inline void buddy_page_tag(struct buddy_page *page) { |
| 141 | if (page) |
| 142 | page_set_tag(BUDDY_PAGE_TO_PAGE(page), tag: PAGE_TAG_BUDDY); |
| 143 | } |
| 144 | |
| 145 | static inline void buddy_page_untag(struct buddy_page *page) { |
| 146 | if (page) |
| 147 | page_set_tag(BUDDY_PAGE_TO_PAGE(page), tag: PAGE_TAG_NONE); |
| 148 | } |
| 149 | |
| 150 | static inline void buddy_page_assert_tag(struct buddy_page *page, |
| 151 | enum page_tag tag) { |
| 152 | if (page) |
| 153 | page_assert_tag(BUDDY_PAGE_TO_PAGE(page), expected: tag); |
| 154 | } |
| 155 | |
| 156 | bool buddy_fa_empty(struct buddy_free_area *area); |
| 157 | struct buddy_page *buddy_fa_get_head(struct buddy_free_area *area); |
| 158 | struct buddy_page *buddy_fa_get_tail(struct buddy_free_area *area); |
| 159 | void buddy_fa_push_head(struct buddy_free_area *area, struct buddy_page *page); |
| 160 | void buddy_fa_push_tail(struct buddy_free_area *area, struct buddy_page *page); |
| 161 | struct buddy_page *buddy_fa_pop_head(struct buddy_free_area *area); |
| 162 | struct buddy_page *buddy_fa_pop_tail(struct buddy_free_area *area); |
| 163 | void buddy_fa_remove(struct buddy_free_area *area, struct buddy_page *page); |
| 164 | |