blob: f9717d013f8e3e10d554797604103432283a44a1 [file] [log] [blame]
Chunlin Hane9c97022017-07-07 20:29:30 +08001/*
2 * Copyright (c) 2017 Linaro Limited
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7#include <init.h>
8#include <kernel.h>
9#include <kernel_structs.h>
Ramakrishna Pallala301acb82018-01-31 10:11:47 +053010#include <kernel_internal.h>
Anas Nashif5eb90ec2019-06-26 10:33:39 -040011#include <sys/__assert.h>
Flavio Ceolin6fdc56d2018-09-18 12:32:27 -070012#include <stdbool.h>
Andy Ross04382b92018-07-24 11:35:55 -070013#include <spinlock.h>
Andrew Boie9bfc8d82020-08-25 14:42:33 -070014#include <sys/libc-hooks.h>
Andrew Boiebc35b0b2020-08-19 13:11:47 -070015#include <logging/log.h>
Krzysztof Chruscinski3ed80832020-11-26 19:32:34 +010016LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
Andrew Boiebc35b0b2020-08-19 13:11:47 -070017
Andrew Boie348a0fd2020-10-06 15:53:43 -070018struct k_spinlock z_mem_domain_lock;
Kumar Galaa1b77fd2020-05-27 11:26:57 -050019static uint8_t max_partitions;
Chunlin Hane9c97022017-07-07 20:29:30 +080020
Andrew Boie9bfc8d82020-08-25 14:42:33 -070021struct k_mem_domain k_mem_domain_default;
22
Andrew Boie72792a52020-08-19 15:06:37 -070023#if __ASSERT_ON
24static bool check_add_partition(struct k_mem_domain *domain,
25 struct k_mem_partition *part)
Leandro Pereiradb094b82018-02-28 14:11:41 -080026{
Leandro Pereirab007b642017-10-17 17:01:48 -070027
Andrew Boie72792a52020-08-19 15:06:37 -070028 int i;
29 uintptr_t pstart, pend, dstart, dend;
Leandro Pereiradb094b82018-02-28 14:11:41 -080030
Andrew Boie72792a52020-08-19 15:06:37 -070031 if (part == NULL) {
32 LOG_ERR("NULL k_mem_partition provided");
Leandro Pereiradb094b82018-02-28 14:11:41 -080033 return false;
34 }
35
Andrew Boie72792a52020-08-19 15:06:37 -070036#ifdef CONFIG_EXECUTE_XOR_WRITE
37 /* Arches where execution cannot be disabled should always return
38 * false to this check
39 */
40 if (K_MEM_PARTITION_IS_EXECUTABLE(part->attr) &&
41 K_MEM_PARTITION_IS_WRITABLE(part->attr)) {
42 LOG_ERR("partition is writable and executable <start %lx>",
43 part->start);
Ioannis Glaropoulos6c54cac2018-11-13 16:28:55 +010044 return false;
Andrew Boie72792a52020-08-19 15:06:37 -070045 }
Ioannis Glaropoulos6c54cac2018-11-13 16:28:55 +010046#endif
Leandro Pereiradb094b82018-02-28 14:11:41 -080047
Andrew Boie72792a52020-08-19 15:06:37 -070048 if (part->size == 0) {
49 LOG_ERR("zero sized partition at %p with base 0x%lx",
50 part, part->start);
51 return false;
52 }
Leandro Pereiradb094b82018-02-28 14:11:41 -080053
Andrew Boie72792a52020-08-19 15:06:37 -070054 pstart = part->start;
55 pend = part->start + part->size;
56
57 if (pend <= pstart) {
58 LOG_ERR("invalid partition %p, wraparound detected. base 0x%lx size %zu",
59 part, part->start, part->size);
60 return false;
61 }
62
63 /* Check that this partition doesn't overlap any existing ones already
64 * in the domain
65 */
66 for (i = 0; i < domain->num_partitions; i++) {
67 struct k_mem_partition *dpart = &domain->partitions[i];
68
69 if (dpart->size == 0) {
70 /* Unused slot */
71 continue;
72 }
73
74 dstart = dpart->start;
75 dend = dstart + dpart->size;
76
77 if (pend > dstart && dend > pstart) {
78 LOG_ERR("partition %p base %lx (size %zu) overlaps existing base %lx (size %zu)",
79 part, part->start, part->size,
80 dpart->start, dpart->size);
Leandro Pereiradb094b82018-02-28 14:11:41 -080081 return false;
82 }
83 }
84
85 return true;
Leandro Pereirab007b642017-10-17 17:01:48 -070086}
Leandro Pereiradb094b82018-02-28 14:11:41 -080087#endif
88
Kumar Galaa1b77fd2020-05-27 11:26:57 -050089void k_mem_domain_init(struct k_mem_domain *domain, uint8_t num_parts,
Leandro Pereira08de6582018-02-28 14:22:57 -080090 struct k_mem_partition *parts[])
Chunlin Hane9c97022017-07-07 20:29:30 +080091{
Andy Ross04382b92018-07-24 11:35:55 -070092 k_spinlock_key_t key;
Chunlin Hane9c97022017-07-07 20:29:30 +080093
Andrew Boie72792a52020-08-19 15:06:37 -070094 __ASSERT_NO_MSG(domain != NULL);
95 __ASSERT(num_parts == 0U || parts != NULL,
96 "parts array is NULL and num_parts is nonzero");
97 __ASSERT(num_parts <= max_partitions,
98 "num_parts of %d exceeds maximum allowable partitions (%d)",
99 num_parts, max_partitions);
Chunlin Hane9c97022017-07-07 20:29:30 +0800100
Andrew Boie348a0fd2020-10-06 15:53:43 -0700101 key = k_spin_lock(&z_mem_domain_lock);
Chunlin Hane9c97022017-07-07 20:29:30 +0800102
Patrik Flykt24d71432019-03-26 19:57:45 -0600103 domain->num_partitions = 0U;
Flavio Ceolinda49f2e2018-09-11 19:09:03 -0700104 (void)memset(domain->partitions, 0, sizeof(domain->partitions));
Chunlin Hane9c97022017-07-07 20:29:30 +0800105 sys_dlist_init(&domain->mem_domain_q);
106
Andrew Boiebc35b0b2020-08-19 13:11:47 -0700107#ifdef CONFIG_ARCH_MEM_DOMAIN_DATA
108 int ret = arch_mem_domain_init(domain);
109
110 /* TODO propagate return values, see #24609.
111 *
112 * Not using an assertion here as this is a memory allocation error
113 */
114 if (ret != 0) {
115 LOG_ERR("architecture-specific initialization failed for domain %p with %d",
116 domain, ret);
117 k_panic();
118 }
119#endif
Andrew Boie52bf4822020-10-16 16:49:41 -0700120 if (num_parts != 0U) {
121 uint32_t i;
122
123 for (i = 0U; i < num_parts; i++) {
124 __ASSERT(check_add_partition(domain, parts[i]),
125 "invalid partition index %d (%p)",
126 i, parts[i]);
127
128 domain->partitions[i] = *parts[i];
129 domain->num_partitions++;
130#ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API
131 arch_mem_domain_partition_add(domain, i);
132#endif
133 }
134 }
Andrew Boiebc35b0b2020-08-19 13:11:47 -0700135
Andrew Boie348a0fd2020-10-06 15:53:43 -0700136 k_spin_unlock(&z_mem_domain_lock, key);
Chunlin Hane9c97022017-07-07 20:29:30 +0800137}
138
Chunlin Hane9c97022017-07-07 20:29:30 +0800139void k_mem_domain_add_partition(struct k_mem_domain *domain,
Leandro Pereiradb094b82018-02-28 14:11:41 -0800140 struct k_mem_partition *part)
Chunlin Hane9c97022017-07-07 20:29:30 +0800141{
142 int p_idx;
Andy Ross04382b92018-07-24 11:35:55 -0700143 k_spinlock_key_t key;
Chunlin Hane9c97022017-07-07 20:29:30 +0800144
Andrew Boie72792a52020-08-19 15:06:37 -0700145 __ASSERT_NO_MSG(domain != NULL);
146 __ASSERT(check_add_partition(domain, part),
147 "invalid partition %p", part);
Leandro Pereirab007b642017-10-17 17:01:48 -0700148
Andrew Boie348a0fd2020-10-06 15:53:43 -0700149 key = k_spin_lock(&z_mem_domain_lock);
Chunlin Hane9c97022017-07-07 20:29:30 +0800150
151 for (p_idx = 0; p_idx < max_partitions; p_idx++) {
152 /* A zero-sized partition denotes it's a free partition */
Patrik Flykt24d71432019-03-26 19:57:45 -0600153 if (domain->partitions[p_idx].size == 0U) {
Chunlin Hane9c97022017-07-07 20:29:30 +0800154 break;
155 }
156 }
157
Andrew Boie72792a52020-08-19 15:06:37 -0700158 __ASSERT(p_idx < max_partitions,
159 "no free partition slots available");
Chunlin Hane9c97022017-07-07 20:29:30 +0800160
Andrew Boied11e3c32020-08-25 16:47:44 -0700161 LOG_DBG("add partition base %lx size %zu to domain %p\n",
162 part->start, part->size, domain);
163
Chunlin Hane9c97022017-07-07 20:29:30 +0800164 domain->partitions[p_idx].start = part->start;
165 domain->partitions[p_idx].size = part->size;
166 domain->partitions[p_idx].attr = part->attr;
167
168 domain->num_partitions++;
169
Andrew Boie00f71b02020-08-25 17:02:38 -0700170#ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API
Andrew Boie4f77c2a2019-11-07 12:43:29 -0800171 arch_mem_domain_partition_add(domain, p_idx);
Andrew Boie00f71b02020-08-25 17:02:38 -0700172#endif
Andrew Boie348a0fd2020-10-06 15:53:43 -0700173 k_spin_unlock(&z_mem_domain_lock, key);
Chunlin Hane9c97022017-07-07 20:29:30 +0800174}
175
176void k_mem_domain_remove_partition(struct k_mem_domain *domain,
177 struct k_mem_partition *part)
178{
179 int p_idx;
Andy Ross04382b92018-07-24 11:35:55 -0700180 k_spinlock_key_t key;
Chunlin Hane9c97022017-07-07 20:29:30 +0800181
Andrew Boie72792a52020-08-19 15:06:37 -0700182 __ASSERT_NO_MSG(domain != NULL);
183 __ASSERT_NO_MSG(part != NULL);
Chunlin Hane9c97022017-07-07 20:29:30 +0800184
Andrew Boie348a0fd2020-10-06 15:53:43 -0700185 key = k_spin_lock(&z_mem_domain_lock);
Chunlin Hane9c97022017-07-07 20:29:30 +0800186
187 /* find a partition that matches the given start and size */
188 for (p_idx = 0; p_idx < max_partitions; p_idx++) {
189 if (domain->partitions[p_idx].start == part->start &&
190 domain->partitions[p_idx].size == part->size) {
191 break;
192 }
193 }
194
Andrew Boie475d2792019-03-01 18:12:49 -0800195 __ASSERT(p_idx < max_partitions, "no matching partition found");
Chunlin Hane9c97022017-07-07 20:29:30 +0800196
Andrew Boied11e3c32020-08-25 16:47:44 -0700197 LOG_DBG("remove partition base %lx size %zu from domain %p\n",
198 part->start, part->size, domain);
199
Andrew Boie00f71b02020-08-25 17:02:38 -0700200#ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API
Andrew Boie4f77c2a2019-11-07 12:43:29 -0800201 arch_mem_domain_partition_remove(domain, p_idx);
Andrew Boie00f71b02020-08-25 17:02:38 -0700202#endif
Adithya Baglodyeff2ec62017-10-09 11:37:31 +0530203
Ioannis Glaropoulosccf813c2018-12-03 14:21:54 +0100204 /* A zero-sized partition denotes it's a free partition */
Patrik Flykt24d71432019-03-26 19:57:45 -0600205 domain->partitions[p_idx].size = 0U;
Chunlin Hane9c97022017-07-07 20:29:30 +0800206
207 domain->num_partitions--;
208
Andrew Boie348a0fd2020-10-06 15:53:43 -0700209 k_spin_unlock(&z_mem_domain_lock, key);
Chunlin Hane9c97022017-07-07 20:29:30 +0800210}
211
Andrew Boieb5a71f72020-10-06 13:39:29 -0700212static void add_thread_locked(struct k_mem_domain *domain,
213 k_tid_t thread)
Chunlin Hane9c97022017-07-07 20:29:30 +0800214{
Andrew Boie72792a52020-08-19 15:06:37 -0700215 __ASSERT_NO_MSG(domain != NULL);
216 __ASSERT_NO_MSG(thread != NULL);
Chunlin Hane9c97022017-07-07 20:29:30 +0800217
Andrew Boied11e3c32020-08-25 16:47:44 -0700218 LOG_DBG("add thread %p to domain %p\n", thread, domain);
Chunlin Hane9c97022017-07-07 20:29:30 +0800219 sys_dlist_append(&domain->mem_domain_q,
220 &thread->mem_domain_info.mem_domain_q_node);
221 thread->mem_domain_info.mem_domain = domain;
222
Andrew Boie00f71b02020-08-25 17:02:38 -0700223#ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API
Andrew Boie4f77c2a2019-11-07 12:43:29 -0800224 arch_mem_domain_thread_add(thread);
Andrew Boie00f71b02020-08-25 17:02:38 -0700225#endif
Andrew Boieb5a71f72020-10-06 13:39:29 -0700226}
Adithya Baglody9cde20a2017-12-06 16:48:28 +0530227
Andrew Boieb5a71f72020-10-06 13:39:29 -0700228static void remove_thread_locked(struct k_thread *thread)
229{
230 __ASSERT_NO_MSG(thread != NULL);
231 LOG_DBG("remove thread %p from memory domain %p\n",
232 thread, thread->mem_domain_info.mem_domain);
233 sys_dlist_remove(&thread->mem_domain_info.mem_domain_q_node);
234
235#ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API
236 arch_mem_domain_thread_remove(thread);
237#endif
238}
239
240/* Called from thread object initialization */
241void z_mem_domain_init_thread(struct k_thread *thread)
242{
Andrew Boie348a0fd2020-10-06 15:53:43 -0700243 k_spinlock_key_t key = k_spin_lock(&z_mem_domain_lock);
Andrew Boieb5a71f72020-10-06 13:39:29 -0700244
245 /* New threads inherit memory domain configuration from parent */
246 add_thread_locked(_current->mem_domain_info.mem_domain, thread);
Andrew Boie348a0fd2020-10-06 15:53:43 -0700247 k_spin_unlock(&z_mem_domain_lock, key);
Andrew Boieb5a71f72020-10-06 13:39:29 -0700248}
249
250/* Called when thread aborts during teardown tasks. sched_spinlock is held */
251void z_mem_domain_exit_thread(struct k_thread *thread)
252{
Andrew Boie348a0fd2020-10-06 15:53:43 -0700253 k_spinlock_key_t key = k_spin_lock(&z_mem_domain_lock);
Andrew Boieb5a71f72020-10-06 13:39:29 -0700254 remove_thread_locked(thread);
Andrew Boie348a0fd2020-10-06 15:53:43 -0700255 k_spin_unlock(&z_mem_domain_lock, key);
Andrew Boieb5a71f72020-10-06 13:39:29 -0700256}
257
258void k_mem_domain_add_thread(struct k_mem_domain *domain, k_tid_t thread)
259{
260 k_spinlock_key_t key;
261
Andrew Boie348a0fd2020-10-06 15:53:43 -0700262 key = k_spin_lock(&z_mem_domain_lock);
Andrew Boiefa6db612020-10-20 13:26:19 -0700263 if (thread->mem_domain_info.mem_domain != domain) {
264 remove_thread_locked(thread);
265 add_thread_locked(domain, thread);
266 }
Andrew Boie348a0fd2020-10-06 15:53:43 -0700267 k_spin_unlock(&z_mem_domain_lock, key);
Chunlin Hane9c97022017-07-07 20:29:30 +0800268}
269
270void k_mem_domain_remove_thread(k_tid_t thread)
271{
Andrew Boie9bfc8d82020-08-25 14:42:33 -0700272 k_mem_domain_add_thread(&k_mem_domain_default, thread);
Chunlin Hane9c97022017-07-07 20:29:30 +0800273}
274
Andrew Boie9f87dea2020-10-06 15:02:04 -0700275void k_mem_domain_destroy(struct k_mem_domain *domain)
276{
277 k_spinlock_key_t key;
278 sys_dnode_t *node, *next_node;
279
280 __ASSERT_NO_MSG(domain != NULL);
281 __ASSERT(domain != &k_mem_domain_default,
282 "cannot destroy default domain");
283
Andrew Boie348a0fd2020-10-06 15:53:43 -0700284 key = k_spin_lock(&z_mem_domain_lock);
Andrew Boie9f87dea2020-10-06 15:02:04 -0700285
286#ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API
287 arch_mem_domain_destroy(domain);
288#endif
289
290 SYS_DLIST_FOR_EACH_NODE_SAFE(&domain->mem_domain_q, node, next_node) {
291 struct k_thread *thread =
292 CONTAINER_OF(node, struct k_thread, mem_domain_info);
293
294 remove_thread_locked(thread);
295 add_thread_locked(&k_mem_domain_default, thread);
296 }
297
Andrew Boie348a0fd2020-10-06 15:53:43 -0700298 k_spin_unlock(&z_mem_domain_lock, key);
Andrew Boie9f87dea2020-10-06 15:02:04 -0700299}
300
Tomasz Bursztykae18fcbb2020-04-30 20:33:38 +0200301static int init_mem_domain_module(const struct device *arg)
Chunlin Hane9c97022017-07-07 20:29:30 +0800302{
303 ARG_UNUSED(arg);
304
Andrew Boie4f77c2a2019-11-07 12:43:29 -0800305 max_partitions = arch_mem_domain_max_partitions_get();
Chunlin Hane9c97022017-07-07 20:29:30 +0800306 /*
307 * max_partitions must be less than or equal to
308 * CONFIG_MAX_DOMAIN_PARTITIONS, or would encounter array index
309 * out of bounds error.
310 */
311 __ASSERT(max_partitions <= CONFIG_MAX_DOMAIN_PARTITIONS, "");
312
Andrew Boie9bfc8d82020-08-25 14:42:33 -0700313 k_mem_domain_init(&k_mem_domain_default, 0, NULL);
314#ifdef Z_LIBC_PARTITION_EXISTS
315 k_mem_domain_add_partition(&k_mem_domain_default, &z_libc_partition);
316#endif /* Z_LIBC_PARTITION_EXISTS */
317
Chunlin Hane9c97022017-07-07 20:29:30 +0800318 return 0;
319}
320
321SYS_INIT(init_mem_domain_module, PRE_KERNEL_1,
322 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);