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
38struct buddy_page {
39 uint64_t meta;
40};
41static_assert_struct_size_eq(buddy_page, 8);
42
43static inline uint64_t buddy_page_get_order(const struct buddy_page *bp) {
44 return (bp->meta & BUDDY_ORDER_MASK) >> BUDDY_ORDER_SHIFT;
45}
46
47static 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
52static 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
56static 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
61static inline bool buddy_page_is_free(const struct buddy_page *bp) {
62 return (bp->meta & BUDDY_IS_FREE_MASK) != 0;
63}
64
65static 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
70static inline bool buddy_page_is_zeroed(const struct buddy_page *bp) {
71 return (bp->meta & BUDDY_IS_ZEROED_MASK) != 0;
72}
73
74static 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
80static inline bool page_pfn_allocated_in_boot_bitmap(pfn_t pfn) {
81 return test_bit(index: pfn);
82}
83
84static 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
93static inline struct buddy_page *buddy_page_for_pfn(pfn_t pfn) {
94 return PAGE_TO_BUDDY_PAGE(page_for_pfn(pfn));
95}
96
97static inline pfn_t buddy_page_get_pfn(struct buddy_page *bp) {
98 return page_get_pfn(bp: (struct page *) bp);
99}
100
101static inline paddr_t buddy_page_get_paddr(struct buddy_page *bp) {
102 return PFN_TO_PAGE(buddy_page_get_pfn(bp));
103}
104
105static 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
113static 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
121struct buddy_free_link {
122 struct buddy_page *prev;
123};
124
125static inline struct buddy_free_link *
126buddy_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
131static inline struct buddy_page *buddy_page_get_prev(struct buddy_page *bp) {
132 return buddy_page_free_link(bp)->prev;
133}
134
135static 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
140static 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
145static 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
150static 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
156bool buddy_fa_empty(struct buddy_free_area *area);
157struct buddy_page *buddy_fa_get_head(struct buddy_free_area *area);
158struct buddy_page *buddy_fa_get_tail(struct buddy_free_area *area);
159void buddy_fa_push_head(struct buddy_free_area *area, struct buddy_page *page);
160void buddy_fa_push_tail(struct buddy_free_area *area, struct buddy_page *page);
161struct buddy_page *buddy_fa_pop_head(struct buddy_free_area *area);
162struct buddy_page *buddy_fa_pop_tail(struct buddy_free_area *area);
163void buddy_fa_remove(struct buddy_free_area *area, struct buddy_page *page);
164