1 /*
2  * Copyright (c) 2021 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef ZEPHYR_DRIVERS_SYSTEM_MM_DRV_COMMON_H_
8 #define ZEPHYR_DRIVERS_SYSTEM_MM_DRV_COMMON_H_
9 
10 #include <zephyr/kernel.h>
11 #include <zephyr/toolchain.h>
12 
13 #include <zephyr/drivers/mm/system_mm.h>
14 
15 extern struct k_spinlock sys_mm_drv_common_lock;
16 
17 /**
18  * @brief Get the flags of mapped virtual address.
19  *
20  * The function queries the translation tables to find the flags of
21  * a mapped virtual address. This is used internally for remapping.
22  *
23  * Behavior when providing unaligned address is undefined, this
24  * is assumed to be page aligned.
25  *
26  * @param      virt  Page-aligned virtual address
27  * @param[out] flags flags of mapped virtual address
28  *
29  * @retval 0 if mapping is found and valid
30  * @retval -EINVAL if invalid arguments are provided
31  * @retval -EFAULT if virtual address is not mapped
32  */
33 int sys_mm_drv_page_flag_get(void *virt, uint32_t *flags);
34 
35 /**
36  * @brief Test if address is page-aligned
37  *
38  * @param addr address to be tested
39  *
40  * @retval true if page-aligned
41  * @retval false if not page-aligned
42  */
sys_mm_drv_is_addr_aligned(uintptr_t addr)43 static inline bool sys_mm_drv_is_addr_aligned(uintptr_t addr)
44 {
45 	return ((addr & (CONFIG_MM_DRV_PAGE_SIZE - 1)) == 0U);
46 }
47 
48 /**
49  * @brief Test if address is page-aligned
50  *
51  * @param addr address to be tested
52  *
53  * @retval true if page-aligned
54  * @retval false if not page-aligned
55  */
sys_mm_drv_is_virt_addr_aligned(void * virt)56 static inline bool sys_mm_drv_is_virt_addr_aligned(void *virt)
57 {
58 	return sys_mm_drv_is_addr_aligned(POINTER_TO_UINT(virt));
59 }
60 
61 /**
62  * @brief Test if size is page-aligned
63  *
64  * @param addr size to be tested
65  *
66  * @retval true if page-aligned
67  * @retval false if not page-aligned
68  */
sys_mm_drv_is_size_aligned(size_t size)69 static inline bool sys_mm_drv_is_size_aligned(size_t size)
70 {
71 	if ((size & (CONFIG_MM_DRV_PAGE_SIZE - 1)) == 0U) {
72 		return true;
73 	} else {
74 		return false;
75 	}
76 }
77 
78 /**
79  * @brief Test if all physical addresses in array are page-aligned
80  *
81  * @param addr Array of physical addresses
82  * @param cnt Number of elements in the array
83  *
84  * @retval true if all are page-aligned
85  * @retval false if at least one is not page-aligned
86  */
87 bool sys_mm_drv_is_addr_array_aligned(uintptr_t *addr, size_t cnt);
88 
89 /**
90  * @brief Test if the virtual memory region is mapped
91  *
92  * @param virt Page-aligned base virtual address
93  * @param size Size of the virtual memory region
94  *
95  * @retval true if all pages in the region are mapped
96  * @retval false if at least one page is not mapped
97  */
98 bool sys_mm_drv_is_virt_region_mapped(void *virt, size_t size);
99 
100 /**
101  * @brief Test if the virtual memory region is unmapped
102  *
103  * @param virt Page-aligned base virtual address
104  * @param size Size of the virtual memory region
105  *
106  * @retval true if all pages in the region are unmapped
107  * @retval false if at least one page is mapped
108  */
109 bool sys_mm_drv_is_virt_region_unmapped(void *virt, size_t size);
110 
111 /**
112  * @brief Simple implementation of sys_mm_drv_map_region()
113  *
114  * This provides a simple implementation for sys_mm_drv_map_region()
115  * which is marked as a weak alias to sys_mm_drv_map_region().
116  *
117  * Drivers do not have to implement their own sys_mm_drv_map_region()
118  * if this works for them. Or they can override sys_mm_drv_map_region()
119  * and call sys_mm_drv_simple_map_region() with some pre-processing done.
120  * Or the drivers can implement their own sys_mm_drv_map_region(), then
121  * this function will not be used.
122  *
123  * @see sys_mm_drv_map_region
124  *
125  * @param virt Page-aligned destination virtual address to map
126  * @param phys Page-aligned source physical address to map
127  * @param size Page-aligned size of the mapped memory region in bytes
128  * @param flags Caching, access and control flags, see SYS_MM_MEM_* macros
129  *
130  * @retval 0 if successful
131  * @retval -EINVAL if invalid arguments are provided
132  * @retval -EFAULT if any virtual addresses have already been mapped
133  */
134 int sys_mm_drv_simple_map_region(void *virt, uintptr_t phys,
135 				 size_t size, uint32_t flags);
136 
137 /**
138  * @brief Simple implementation of sys_mm_drv_map_array()
139  *
140  * This provides a simple implementation for sys_mm_drv_map_array()
141  * which is marked as a weak alias to sys_mm_drv_map_array().
142  *
143  * Drivers do not have to implement their own sys_mm_drv_map_array()
144  * if this works for them. Or they can override sys_mm_drv_map_array()
145  * and call sys_mm_drv_simple_map_array() with some pre-processing done.
146  * Or the drivers can implement their own sys_mm_drv_map_array(), then
147  * this function will not be used.
148  *
149  * @see sys_mm_drv_map_array
150  *
151  * @param virt Page-aligned destination virtual address to map
152  * @param phys Array of pge-aligned source physical address to map
153  * @param cnt Number of elements in the physical page array
154  * @param flags Caching, access and control flags, see SYS_MM_MEM_* macros
155  *
156  * @retval 0 if successful
157  * @retval -EINVAL if invalid arguments are provided
158  * @retval -EFAULT if any virtual addresses have already been mapped
159  */
160 int sys_mm_drv_simple_map_array(void *virt, uintptr_t *phys,
161 				size_t cnt, uint32_t flags);
162 
163 /**
164  * @brief Simple implementation of sys_mm_drv_unmap_region()
165  *
166  * This provides a simple implementation for sys_mm_drv_unmap_region()
167  * which is marked as a weak alias to sys_mm_drv_unmap_region().
168  *
169  * Drivers do not have to implement their own sys_mm_drv_unmap_region()
170  * if this works for them. Or they can override sys_mm_drv_unmap_region()
171  * and call sys_mm_drv_simple_unmap_region() with some pre-processing done.
172  * Or the drivers can implement their own sys_mm_drv_unmap_region(), then
173  * this function will not be used.
174  *
175  * @see sys_mm_drv_unmap_region
176  *
177  * @param virt Page-aligned base virtual address to un-map
178  * @param size Page-aligned region size
179  *
180  * @retval 0 if successful
181  * @retval -EINVAL if invalid arguments are provided
182  * @retval -EFAULT if virtual addresses have already been mapped
183  */
184 int sys_mm_drv_simple_unmap_region(void *virt, size_t size);
185 
186 /**
187  * @brief Simple implementation of sys_mm_drv_remap_region()
188  *
189  * This provides a simple implementation for sys_mm_drv_remap_region()
190  * which is marked as a weak alias to sys_mm_drv_remap_region().
191  *
192  * Drivers do not have to implement their own sys_mm_drv_remap_region()
193  * if this works for them. Or they can override sys_mm_drv_remap_region()
194  * and call sys_mm_drv_simple_remap_region() with some pre-processing done.
195  * Or the drivers can implement their own sys_mm_drv_remap_region(), then
196  * this function will not be used.
197  *
198  * @see sys_mm_drv_remap_region
199  *
200  * @param virt_old Page-aligned base virtual address of existing memory
201  * @param size Page-aligned size of the mapped memory region in bytes
202  * @param virt_new Page-aligned base virtual address to which to remap
203  *                 the memory
204  *
205  * @retval 0 if successful
206  * @retval -EINVAL if invalid arguments are provided
207  * @retval -EFAULT if old virtual addresses are not all mapped or
208  *                 new virtual addresses are not all unmapped
209  */
210 int sys_mm_drv_simple_remap_region(void *virt_old, size_t size,
211 				   void *virt_new);
212 
213 /**
214  * @brief Simple implementation of sys_mm_drv_move_region()
215  *
216  * This provides a simple implementation for sys_mm_drv_move_region()
217  * which is marked as a weak alias to sys_mm_drv_move_region().
218  *
219  * Drivers do not have to implement their own sys_mm_drv_move_region()
220  * if this works for them. Or they can override sys_mm_drv_move_region()
221  * and call sys_mm_drv_simple_move_region() with some pre-processing done.
222  * Or the drivers can implement their own sys_mm_drv_move_region(), then
223  * this function will not be used.
224  *
225  * @see sys_mm_drv_move_region
226  *
227  * @param virt_old Page-aligned base virtual address of existing memory
228  * @param size Page-aligned size of the mapped memory region in bytes
229  * @param virt_new Page-aligned base virtual address to which to map
230  *                 new physical pages
231  * @param phys_new Page-aligned base physical address to contain
232  *                 the moved memory
233  *
234  * @retval 0 if successful
235  * @retval -EINVAL if invalid arguments are provided
236  * @retval -EFAULT if old virtual addresses are not all mapped or
237  *                 new virtual addresses are not all unmapped
238  */
239 int sys_mm_drv_simple_move_region(void *virt_old, size_t size,
240 				  void *virt_new, uintptr_t phys_new);
241 
242 /**
243  * @brief Simple implementation of sys_mm_drv_move_array()
244  *
245  * This provides a simple implementation for sys_mm_drv_move_array()
246  * which is marked as a weak alias to sys_mm_drv_move_array().
247  *
248  * Drivers do not have to implement their own sys_mm_drv_move_array()
249  * if this works for them. Or they can override sys_mm_drv_move_array()
250  * and call sys_mm_drv_simple_move_array() with some pre-processing done.
251  * Or the drivers can implement their own sys_mm_drv_move_array(), then
252  * this function will not be used.
253  *
254  * @see sys_mm_drv_move_array
255  *
256  * @param virt_old Page-aligned base virtual address of existing memory
257  * @param size Page-aligned size of the mapped memory region in bytes
258  * @param virt_new Page-aligned base virtual address to which to map
259  *                 new physical pages
260  * @param phys_new Array of page-aligned physical address to contain
261  *                 the moved memory
262  * @param phys_cnt Number of elements in the physical page array
263  *
264  * @retval 0 if successful
265  * @retval -EINVAL if invalid arguments are provided
266  * @retval -EFAULT if old virtual addresses are not all mapped or
267  *                 new virtual addresses are not all unmapped
268  */
269 int sys_mm_drv_simple_move_array(void *virt_old, size_t size,
270 				 void *virt_new,
271 				 uintptr_t *phys_new, size_t phys_cnt);
272 
273 /**
274  * @brief Update memory region flags
275  *
276  * This changes the attributes of physical memory which is already
277  * mapped to a virtual address. This is useful when use case of
278  * specific memory region  changes.
279  * E.g. when the library/module code is copied to the memory then
280  * it needs to be read-write and after it has already
281  * been copied and library/module code is ready to be executed then
282  * attributes need to be changed to read-only/executable.
283  * Calling this API must not cause losing memory contents.
284  *
285  * @param virt Page-aligned virtual address to be updated
286  * @param size Page-aligned size of the mapped memory region in bytes
287  * @param flags Caching, access and control flags, see SYS_MM_MEM_* macros
288  *
289  * @retval 0 if successful
290  * @retval -EINVAL if invalid arguments are provided
291  * @retval -EFAULT if virtual addresses is not mapped
292  */
293 
294 int sys_mm_drv_simple_update_region_flags(void *virt, size_t size, uint32_t flags);
295 
296 #endif /* ZEPHYR_DRIVERS_SYSTEM_MM_DRV_COMMON_H_ */
297