blob: 16b337acf011df41695d04dd29ec43663d71b5cc [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
Gerard Marull-Paretascffefc82022-05-06 11:04:23 +02007#include <zephyr/init.h>
8#include <zephyr/kernel.h>
9#include <zephyr/kernel_structs.h>
Ramakrishna Pallala301acb82018-01-31 10:11:47 +053010#include <kernel_internal.h>
Gerard Marull-Paretascffefc82022-05-06 11:04:23 +020011#include <zephyr/sys/__assert.h>
Flavio Ceolin6fdc56d2018-09-18 12:32:27 -070012#include <stdbool.h>
Gerard Marull-Paretascffefc82022-05-06 11:04:23 +020013#include <zephyr/spinlock.h>
14#include <zephyr/sys/check.h>
15#include <zephyr/sys/libc-hooks.h>
16#include <zephyr/logging/log.h>
Krzysztof Chruscinski3ed80832020-11-26 19:32:34 +010017LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
Andrew Boiebc35b0b2020-08-19 13:11:47 -070018
Andrew Boie348a0fd2020-10-06 15:53:43 -070019struct k_spinlock z_mem_domain_lock;
Kumar Galaa1b77fd2020-05-27 11:26:57 -050020static uint8_t max_partitions;
Chunlin Hane9c97022017-07-07 20:29:30 +080021
Andrew Boie9bfc8d82020-08-25 14:42:33 -070022struct k_mem_domain k_mem_domain_default;
23
Andrew Boie72792a52020-08-19 15:06:37 -070024static 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 }
Simon Heinbcd1d192024-03-08 12:00:10 +010046#endif /* CONFIG_EXECUTE_XOR_WRITE */
Leandro Pereiradb094b82018-02-28 14:11:41 -080047
Anas Nashifbbbc38b2021-03-29 10:03:49 -040048 if (part->size == 0U) {
Andrew Boie72792a52020-08-19 15:06:37 -070049 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
Anas Nashifbbbc38b2021-03-29 10:03:49 -040069 if (dpart->size == 0U) {
Andrew Boie72792a52020-08-19 15:06:37 -070070 /* 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
Daniel Leungfb91ce22021-11-11 12:49:58 -080088int k_mem_domain_init(struct k_mem_domain *domain, uint8_t num_parts,
89 struct k_mem_partition *parts[])
Chunlin Hane9c97022017-07-07 20:29:30 +080090{
Andy Ross04382b92018-07-24 11:35:55 -070091 k_spinlock_key_t key;
Daniel Leungfb91ce22021-11-11 12:49:58 -080092 int ret = 0;
Chunlin Hane9c97022017-07-07 20:29:30 +080093
Daniel Leungfb91ce22021-11-11 12:49:58 -080094 CHECKIF(domain == NULL) {
95 ret = -EINVAL;
96 goto out;
97 }
98
99 CHECKIF(!(num_parts == 0U || parts != NULL)) {
100 LOG_ERR("parts array is NULL and num_parts is nonzero");
101 ret = -EINVAL;
102 goto out;
103 }
104
105 CHECKIF(!(num_parts <= max_partitions)) {
106 LOG_ERR("num_parts of %d exceeds maximum allowable partitions (%d)",
107 num_parts, max_partitions);
108 ret = -EINVAL;
109 goto out;
110 }
Chunlin Hane9c97022017-07-07 20:29:30 +0800111
Andrew Boie348a0fd2020-10-06 15:53:43 -0700112 key = k_spin_lock(&z_mem_domain_lock);
Chunlin Hane9c97022017-07-07 20:29:30 +0800113
Patrik Flykt24d71432019-03-26 19:57:45 -0600114 domain->num_partitions = 0U;
Flavio Ceolinda49f2e2018-09-11 19:09:03 -0700115 (void)memset(domain->partitions, 0, sizeof(domain->partitions));
Chunlin Hane9c97022017-07-07 20:29:30 +0800116 sys_dlist_init(&domain->mem_domain_q);
117
Andrew Boiebc35b0b2020-08-19 13:11:47 -0700118#ifdef CONFIG_ARCH_MEM_DOMAIN_DATA
Daniel Leungfb91ce22021-11-11 12:49:58 -0800119 ret = arch_mem_domain_init(domain);
Andrew Boiebc35b0b2020-08-19 13:11:47 -0700120
Andrew Boiebc35b0b2020-08-19 13:11:47 -0700121 if (ret != 0) {
122 LOG_ERR("architecture-specific initialization failed for domain %p with %d",
123 domain, ret);
Daniel Leungfb91ce22021-11-11 12:49:58 -0800124 ret = -ENOMEM;
125 goto unlock_out;
Andrew Boiebc35b0b2020-08-19 13:11:47 -0700126 }
Simon Heinbcd1d192024-03-08 12:00:10 +0100127#endif /* CONFIG_ARCH_MEM_DOMAIN_DATA */
Andrew Boie52bf4822020-10-16 16:49:41 -0700128 if (num_parts != 0U) {
129 uint32_t i;
130
131 for (i = 0U; i < num_parts; i++) {
Daniel Leungfb91ce22021-11-11 12:49:58 -0800132 CHECKIF(!check_add_partition(domain, parts[i])) {
133 LOG_ERR("invalid partition index %d (%p)",
134 i, parts[i]);
135 ret = -EINVAL;
136 goto unlock_out;
137 }
Andrew Boie52bf4822020-10-16 16:49:41 -0700138
139 domain->partitions[i] = *parts[i];
140 domain->num_partitions++;
141#ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API
Daniel Leunga6031712021-11-12 16:10:10 -0800142 int ret2 = arch_mem_domain_partition_add(domain, i);
143
144 ARG_UNUSED(ret2);
145 CHECKIF(ret2 != 0) {
146 ret = ret2;
147 }
Simon Heinbcd1d192024-03-08 12:00:10 +0100148#endif /* CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API */
Andrew Boie52bf4822020-10-16 16:49:41 -0700149 }
150 }
Andrew Boiebc35b0b2020-08-19 13:11:47 -0700151
Daniel Leungfb91ce22021-11-11 12:49:58 -0800152unlock_out:
Andrew Boie348a0fd2020-10-06 15:53:43 -0700153 k_spin_unlock(&z_mem_domain_lock, key);
Daniel Leungfb91ce22021-11-11 12:49:58 -0800154
155out:
156 return ret;
Chunlin Hane9c97022017-07-07 20:29:30 +0800157}
158
Daniel Leungbb595a82021-11-10 11:11:00 -0800159int k_mem_domain_add_partition(struct k_mem_domain *domain,
160 struct k_mem_partition *part)
Chunlin Hane9c97022017-07-07 20:29:30 +0800161{
162 int p_idx;
Andy Ross04382b92018-07-24 11:35:55 -0700163 k_spinlock_key_t key;
Daniel Leungbb595a82021-11-10 11:11:00 -0800164 int ret = 0;
Chunlin Hane9c97022017-07-07 20:29:30 +0800165
Daniel Leungbb595a82021-11-10 11:11:00 -0800166 CHECKIF(domain == NULL) {
167 ret = -EINVAL;
168 goto out;
169 }
170
171 CHECKIF(!check_add_partition(domain, part)) {
172 LOG_ERR("invalid partition %p", part);
173 ret = -EINVAL;
174 goto out;
175 }
Leandro Pereirab007b642017-10-17 17:01:48 -0700176
Andrew Boie348a0fd2020-10-06 15:53:43 -0700177 key = k_spin_lock(&z_mem_domain_lock);
Chunlin Hane9c97022017-07-07 20:29:30 +0800178
179 for (p_idx = 0; p_idx < max_partitions; p_idx++) {
180 /* A zero-sized partition denotes it's a free partition */
Patrik Flykt24d71432019-03-26 19:57:45 -0600181 if (domain->partitions[p_idx].size == 0U) {
Chunlin Hane9c97022017-07-07 20:29:30 +0800182 break;
183 }
184 }
185
Daniel Leungbb595a82021-11-10 11:11:00 -0800186 CHECKIF(!(p_idx < max_partitions)) {
187 LOG_ERR("no free partition slots available");
188 ret = -ENOSPC;
189 goto unlock_out;
190 }
Chunlin Hane9c97022017-07-07 20:29:30 +0800191
Andrew Boied11e3c32020-08-25 16:47:44 -0700192 LOG_DBG("add partition base %lx size %zu to domain %p\n",
193 part->start, part->size, domain);
194
Chunlin Hane9c97022017-07-07 20:29:30 +0800195 domain->partitions[p_idx].start = part->start;
196 domain->partitions[p_idx].size = part->size;
197 domain->partitions[p_idx].attr = part->attr;
198
199 domain->num_partitions++;
200
Andrew Boie00f71b02020-08-25 17:02:38 -0700201#ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API
Daniel Leunga6031712021-11-12 16:10:10 -0800202 ret = arch_mem_domain_partition_add(domain, p_idx);
Simon Heinbcd1d192024-03-08 12:00:10 +0100203#endif /* CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API */
Daniel Leungbb595a82021-11-10 11:11:00 -0800204
205unlock_out:
Andrew Boie348a0fd2020-10-06 15:53:43 -0700206 k_spin_unlock(&z_mem_domain_lock, key);
Daniel Leungbb595a82021-11-10 11:11:00 -0800207
208out:
209 return ret;
Chunlin Hane9c97022017-07-07 20:29:30 +0800210}
211
Daniel Leungbb595a82021-11-10 11:11:00 -0800212int k_mem_domain_remove_partition(struct k_mem_domain *domain,
Chunlin Hane9c97022017-07-07 20:29:30 +0800213 struct k_mem_partition *part)
214{
215 int p_idx;
Andy Ross04382b92018-07-24 11:35:55 -0700216 k_spinlock_key_t key;
Daniel Leungbb595a82021-11-10 11:11:00 -0800217 int ret = 0;
Chunlin Hane9c97022017-07-07 20:29:30 +0800218
Daniel Leungbb595a82021-11-10 11:11:00 -0800219 CHECKIF((domain == NULL) || (part == NULL)) {
220 ret = -EINVAL;
221 goto out;
222 }
Chunlin Hane9c97022017-07-07 20:29:30 +0800223
Andrew Boie348a0fd2020-10-06 15:53:43 -0700224 key = k_spin_lock(&z_mem_domain_lock);
Chunlin Hane9c97022017-07-07 20:29:30 +0800225
226 /* find a partition that matches the given start and size */
227 for (p_idx = 0; p_idx < max_partitions; p_idx++) {
Hess Nathan6d417d52024-04-30 13:26:35 +0200228 if ((domain->partitions[p_idx].start == part->start) &&
229 (domain->partitions[p_idx].size == part->size)) {
Chunlin Hane9c97022017-07-07 20:29:30 +0800230 break;
231 }
232 }
233
Daniel Leungbb595a82021-11-10 11:11:00 -0800234 CHECKIF(!(p_idx < max_partitions)) {
235 LOG_ERR("no matching partition found");
236 ret = -ENOENT;
237 goto unlock_out;
238 }
Chunlin Hane9c97022017-07-07 20:29:30 +0800239
Andrew Boied11e3c32020-08-25 16:47:44 -0700240 LOG_DBG("remove partition base %lx size %zu from domain %p\n",
241 part->start, part->size, domain);
242
Andrew Boie00f71b02020-08-25 17:02:38 -0700243#ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API
Daniel Leunga6031712021-11-12 16:10:10 -0800244 ret = arch_mem_domain_partition_remove(domain, p_idx);
Simon Heinbcd1d192024-03-08 12:00:10 +0100245#endif /* CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API */
Adithya Baglodyeff2ec62017-10-09 11:37:31 +0530246
Ioannis Glaropoulosccf813c2018-12-03 14:21:54 +0100247 /* A zero-sized partition denotes it's a free partition */
Patrik Flykt24d71432019-03-26 19:57:45 -0600248 domain->partitions[p_idx].size = 0U;
Chunlin Hane9c97022017-07-07 20:29:30 +0800249
250 domain->num_partitions--;
251
Daniel Leungbb595a82021-11-10 11:11:00 -0800252unlock_out:
Andrew Boie348a0fd2020-10-06 15:53:43 -0700253 k_spin_unlock(&z_mem_domain_lock, key);
Daniel Leungbb595a82021-11-10 11:11:00 -0800254
255out:
256 return ret;
Chunlin Hane9c97022017-07-07 20:29:30 +0800257}
258
Daniel Leunga6031712021-11-12 16:10:10 -0800259static int add_thread_locked(struct k_mem_domain *domain,
260 k_tid_t thread)
Chunlin Hane9c97022017-07-07 20:29:30 +0800261{
Daniel Leunga6031712021-11-12 16:10:10 -0800262 int ret = 0;
263
Andrew Boie72792a52020-08-19 15:06:37 -0700264 __ASSERT_NO_MSG(domain != NULL);
265 __ASSERT_NO_MSG(thread != NULL);
Chunlin Hane9c97022017-07-07 20:29:30 +0800266
Andrew Boied11e3c32020-08-25 16:47:44 -0700267 LOG_DBG("add thread %p to domain %p\n", thread, domain);
Chunlin Hane9c97022017-07-07 20:29:30 +0800268 sys_dlist_append(&domain->mem_domain_q,
269 &thread->mem_domain_info.mem_domain_q_node);
270 thread->mem_domain_info.mem_domain = domain;
271
Andrew Boie00f71b02020-08-25 17:02:38 -0700272#ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API
Daniel Leunga6031712021-11-12 16:10:10 -0800273 ret = arch_mem_domain_thread_add(thread);
Simon Heinbcd1d192024-03-08 12:00:10 +0100274#endif /* CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API */
Daniel Leunga6031712021-11-12 16:10:10 -0800275
276 return ret;
Andrew Boieb5a71f72020-10-06 13:39:29 -0700277}
Adithya Baglody9cde20a2017-12-06 16:48:28 +0530278
Daniel Leunga6031712021-11-12 16:10:10 -0800279static int remove_thread_locked(struct k_thread *thread)
Andrew Boieb5a71f72020-10-06 13:39:29 -0700280{
Daniel Leunga6031712021-11-12 16:10:10 -0800281 int ret = 0;
282
Andrew Boieb5a71f72020-10-06 13:39:29 -0700283 __ASSERT_NO_MSG(thread != NULL);
284 LOG_DBG("remove thread %p from memory domain %p\n",
285 thread, thread->mem_domain_info.mem_domain);
286 sys_dlist_remove(&thread->mem_domain_info.mem_domain_q_node);
287
288#ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API
Daniel Leunga6031712021-11-12 16:10:10 -0800289 ret = arch_mem_domain_thread_remove(thread);
Simon Heinbcd1d192024-03-08 12:00:10 +0100290#endif /* CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API */
Daniel Leunga6031712021-11-12 16:10:10 -0800291
292 return ret;
Andrew Boieb5a71f72020-10-06 13:39:29 -0700293}
294
295/* Called from thread object initialization */
296void z_mem_domain_init_thread(struct k_thread *thread)
297{
Daniel Leunga6031712021-11-12 16:10:10 -0800298 int ret;
Andrew Boie348a0fd2020-10-06 15:53:43 -0700299 k_spinlock_key_t key = k_spin_lock(&z_mem_domain_lock);
Andrew Boieb5a71f72020-10-06 13:39:29 -0700300
301 /* New threads inherit memory domain configuration from parent */
Daniel Leunga6031712021-11-12 16:10:10 -0800302 ret = add_thread_locked(_current->mem_domain_info.mem_domain, thread);
303 __ASSERT_NO_MSG(ret == 0);
304 ARG_UNUSED(ret);
305
Andrew Boie348a0fd2020-10-06 15:53:43 -0700306 k_spin_unlock(&z_mem_domain_lock, key);
Andrew Boieb5a71f72020-10-06 13:39:29 -0700307}
308
Anas Nashif0d8da5f2024-03-06 15:59:36 -0500309/* Called when thread aborts during teardown tasks. _sched_spinlock is held */
Andrew Boieb5a71f72020-10-06 13:39:29 -0700310void z_mem_domain_exit_thread(struct k_thread *thread)
311{
Daniel Leunga6031712021-11-12 16:10:10 -0800312 int ret;
313
Andrew Boie348a0fd2020-10-06 15:53:43 -0700314 k_spinlock_key_t key = k_spin_lock(&z_mem_domain_lock);
Daniel Leunga6031712021-11-12 16:10:10 -0800315
316 ret = remove_thread_locked(thread);
317 __ASSERT_NO_MSG(ret == 0);
318 ARG_UNUSED(ret);
319
Andrew Boie348a0fd2020-10-06 15:53:43 -0700320 k_spin_unlock(&z_mem_domain_lock, key);
Andrew Boieb5a71f72020-10-06 13:39:29 -0700321}
322
Daniel Leunga6031712021-11-12 16:10:10 -0800323int k_mem_domain_add_thread(struct k_mem_domain *domain, k_tid_t thread)
Andrew Boieb5a71f72020-10-06 13:39:29 -0700324{
Daniel Leunga6031712021-11-12 16:10:10 -0800325 int ret = 0;
Andrew Boieb5a71f72020-10-06 13:39:29 -0700326 k_spinlock_key_t key;
327
Andrew Boie348a0fd2020-10-06 15:53:43 -0700328 key = k_spin_lock(&z_mem_domain_lock);
Andrew Boiefa6db612020-10-20 13:26:19 -0700329 if (thread->mem_domain_info.mem_domain != domain) {
Daniel Leunga6031712021-11-12 16:10:10 -0800330 ret = remove_thread_locked(thread);
331
332 if (ret == 0) {
333 ret = add_thread_locked(domain, thread);
334 }
Andrew Boiefa6db612020-10-20 13:26:19 -0700335 }
Andrew Boie348a0fd2020-10-06 15:53:43 -0700336 k_spin_unlock(&z_mem_domain_lock, key);
Daniel Leunga6031712021-11-12 16:10:10 -0800337
338 return ret;
Chunlin Hane9c97022017-07-07 20:29:30 +0800339}
340
Gerard Marull-Paretasa5fd0d12022-10-19 09:33:44 +0200341static int init_mem_domain_module(void)
Chunlin Hane9c97022017-07-07 20:29:30 +0800342{
Daniel Leungfb91ce22021-11-11 12:49:58 -0800343 int ret;
344
Daniel Leungfb91ce22021-11-11 12:49:58 -0800345 ARG_UNUSED(ret);
Chunlin Hane9c97022017-07-07 20:29:30 +0800346
Andrew Boie4f77c2a2019-11-07 12:43:29 -0800347 max_partitions = arch_mem_domain_max_partitions_get();
Chunlin Hane9c97022017-07-07 20:29:30 +0800348 /*
349 * max_partitions must be less than or equal to
350 * CONFIG_MAX_DOMAIN_PARTITIONS, or would encounter array index
351 * out of bounds error.
352 */
353 __ASSERT(max_partitions <= CONFIG_MAX_DOMAIN_PARTITIONS, "");
354
Daniel Leungfb91ce22021-11-11 12:49:58 -0800355 ret = k_mem_domain_init(&k_mem_domain_default, 0, NULL);
356 __ASSERT(ret == 0, "failed to init default mem domain");
357
Andrew Boie9bfc8d82020-08-25 14:42:33 -0700358#ifdef Z_LIBC_PARTITION_EXISTS
Daniel Leungbb595a82021-11-10 11:11:00 -0800359 ret = k_mem_domain_add_partition(&k_mem_domain_default,
360 &z_libc_partition);
361 __ASSERT(ret == 0, "failed to add default libc mem partition");
Andrew Boie9bfc8d82020-08-25 14:42:33 -0700362#endif /* Z_LIBC_PARTITION_EXISTS */
363
Chunlin Hane9c97022017-07-07 20:29:30 +0800364 return 0;
365}
366
367SYS_INIT(init_mem_domain_module, PRE_KERNEL_1,
368 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);