First version
[3rdparty/ote_partner/tlk.git] / arch / arm / arm / task.c
1 /*
2  * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 #include <assert.h>
24 #include <sys/types.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <arch.h>
28 #include <err.h>
29 #include <arch/arm.h>
30 #include <arch/arm/mmu.h>
31 #include <kernel/task.h>
32 #include <kernel/thread.h>
33 #include <platform.h>
34 #include <platform/platform_p.h>
35
36 #define TASK_ARGS_SPACE         (3 * 16)
37
38 static void arch_task_set_usermode(thread_t *thread)
39 {
40         struct context_switch_frame *frame;
41         task_t *task = thread->arch.task;
42
43         frame = (struct context_switch_frame *)thread->arch.sp;
44         frame->psr = MODE_USR;
45         frame->sp = task->sp;
46 }
47
48 void arch_task_set_context(thread_t *thread)
49 {
50         arch_thread_set_context(thread);
51         arch_task_set_usermode(thread);
52 }
53
54 static void arch_task_map(task_t *taskp, task_map_t *mptr)
55 {
56         vaddr_t vaddr;
57         paddr_t paddr;
58         u_int pg;
59
60         if ((mptr->vaddr + mptr->size) > MAX_TASK_SIZE) {
61                 dprintf(CRITICAL, "task address space exceeds max: 0x%lx\n",
62                         MAX_TASK_SIZE);
63                 halt();
64         }
65
66         ASSERT(!(mptr->size & PAGE_MASK));
67
68         for (pg = 0; pg < (mptr->size / PAGE_SIZE); pg++) {
69                 if (mptr->flags & TM_PHYS_CONTIG)
70                         paddr = mptr->u_phys.contig + (pg * PAGE_SIZE);
71                 else
72                         paddr = mptr->u_phys.pagelist[pg];
73
74                 ASSERT(!(paddr & PAGE_MASK));
75                 vaddr = mptr->vaddr + (pg * PAGE_SIZE);
76                 arm_mmu_map_upage(taskp, vaddr, paddr, mptr);
77         }
78 }
79
80 static void arch_task_unmap(task_t *taskp, task_map_t *mptr)
81 {
82         addr_t vaddr;
83         u_int pg;
84
85         for (pg = 0; pg < (mptr->size / PAGE_SIZE); pg++) {
86                 vaddr = mptr->vaddr + (pg * PAGE_SIZE);
87                 arm_mmu_unmap_upage(taskp, vaddr);
88         }
89 }
90
91 task_map_t *arch_task_map_memory(task_t *task, addr_t addr, u_int size, u_int flags)
92 {
93         task_map_t *mptr;
94         u_int npages, align, offset;
95
96         mptr = malloc(sizeof(task_map_t));
97         if (mptr == NULL)
98                 return NULL;
99
100         offset = addr & PAGE_MASK;
101         size = ROUNDUP(offset + size, PAGE_SIZE);
102         npages = size / PAGE_SIZE;
103
104         /* non-secure mapping may require a stricter vaddr alignment */
105         align = (flags & TM_NS_MEM) ? NS_VIRT_ADDR_ALIGN : ALIGN_4KB;
106
107         mptr->vaddr = task_find_address_space(task, size, align);
108         if (!mptr->vaddr) {
109                 free (mptr);
110                 return NULL;
111         }
112
113         mptr->size = size;
114         mptr->flags = flags;
115         mptr->id = 0;
116         mptr->offset = offset;
117
118         task_add_mapping(task, mptr);
119
120         if (npages == 1) {
121                 arm_mmu_translate_range(addr, &mptr->u_phys.contig, mptr);
122
123                 if (mptr->map_attrs == NULL) {
124                         task_delete_mapping (task, mptr);
125                         free (mptr);
126                         return NULL;
127                 }
128                 mptr->flags |= TM_PHYS_CONTIG;
129         } else {
130                 paddr_t *pagelist;
131                 u_int i;
132
133                 /* allocate pagelist, as buffer may not be contiguous */
134                 pagelist = malloc(npages * sizeof(paddr_t));
135                 if (pagelist == NULL) {
136                         task_delete_mapping(task, mptr);
137                         free (mptr);
138                         return NULL;
139                 }
140
141                 arm_mmu_translate_range(addr, pagelist, mptr);
142
143                 if (mptr->map_attrs == NULL) {
144                         free (pagelist);
145                         task_delete_mapping (task, mptr);
146                         free (mptr);
147                         return NULL;
148                 }
149
150                 /* check if the pages ended up being contiguous */
151                 for (i = 1; i < npages; i++) {
152                         if ((pagelist[i-1] + PAGE_SIZE) != pagelist[i])
153                                 break;
154                 }
155                 if (i < npages) {
156                         /* not contiguous */
157                         mptr->u_phys.pagelist = pagelist;
158                 } else {
159                         /* turns out it is (don't need pagelist anymore) */
160                         mptr->flags |= TM_PHYS_CONTIG;
161                         mptr->u_phys.contig = pagelist[0];
162                         free(pagelist);
163                 }
164         }
165
166         arch_task_map(task, mptr);
167         return mptr;
168 }
169
170 task_map_t *arch_task_setup_mmio(task_t *task, u_int id, addr_t paddr, u_int size)
171 {
172         task_map_t *mptr;
173
174         dprintf(SPEW, "%s: id 0x%x paddr 0x%x size 0x%x\n",
175                 __func__, id, (u_int)paddr, size);
176
177         mptr = malloc(sizeof(task_map_t));
178         if (mptr == NULL)
179                 return NULL;
180
181         size = ROUNDUP(size, PAGE_SIZE);
182
183         mptr->vaddr = task_find_address_space(task, size, ALIGN_4KB);
184         if (!mptr->vaddr) {
185                 free (mptr);
186                 return NULL;
187         }
188
189         mptr->size = size;
190         mptr->flags = TM_IO | TM_UW | TM_PHYS_CONTIG;
191         mptr->u_phys.contig = ROUNDDOWN(paddr, PAGE_SIZE);
192         mptr->offset = (paddr & PAGE_MASK);
193
194         mptr->id = id;
195
196         task_add_mapping(task, mptr);
197
198         return mptr;
199 }
200
201 void arch_task_unmap_memory(task_t *task, task_map_t *mptr)
202 {
203         /* if non-contig, free the pagelist */
204         if ((mptr->flags & TM_PHYS_CONTIG) == 0)
205                 free(mptr->u_phys.pagelist);
206
207         arch_task_unmap(task, mptr);
208         task_delete_mapping(task, mptr);
209         free(mptr->map_attrs);
210         free(mptr);
211 }
212
213 bool arch_task_init(thread_t *thread, task_t *task)
214 {
215         static u_int context_id;
216         task_map_t *mptr;
217         uint32_t stack_top_off;
218         vaddr_t arg_base;
219         uint32_t *args;
220         Elf32_auxv_t *auxv;
221
222         /* setup/clean user stack (reduced as libc expects args at sp) */
223         stack_top_off = task->stack_map->size - TASK_ARGS_SPACE;
224         task->sp = task->stack_map->vaddr + stack_top_off;
225         memset((void *)physical_to_virtual(task->stack_map->u_phys.contig), 0, task->stack_map->size);
226
227         platform_clean_invalidate_cache_range(physical_to_virtual(task->stack_map->u_phys.contig),
228                         task->stack_map->size);
229
230         /* initialize libc expected args */
231         arg_base = physical_to_virtual(task->stack_map->u_phys.contig) + stack_top_off;
232         args = (uint32_t *)arg_base;
233
234         *args++ = 0x0;          /* argc */
235         *args++ = 0x0;          /* argv */
236         *args++ = 0x0;          /* envp */
237
238         /* add AT_RANDOM auxv tag/value */
239         auxv = (Elf32_auxv_t *)args;
240         args += 2;
241
242         auxv->a_type = AT_RANDOM;
243         auxv->a_un.a_val = task->sp + ((vaddr_t)args - arg_base);
244
245         *args++ = platform_get_rand32();
246         *args++ = platform_get_rand32();
247         *args++ = platform_get_rand32();
248         *args++ = platform_get_rand32();
249
250 #if ARM_WITH_NEON
251         /* alloc per-thread (and NS world) vfp context */
252         thread->arch.fpctx = calloc(1, sizeof(fpctx_t));
253         if (!ns_vfp_hw_context)
254                 ns_vfp_hw_context = calloc(1, sizeof(fpctx_t));
255 #endif
256
257         thread->arch.task = task;
258         arch_task_set_usermode(thread);
259
260         task->context_id = ++context_id;
261         list_add_tail(&task->thread_node, &thread->task_node);
262
263         /* create pagetable entries for boot time mappings */
264         list_for_every_entry(&task->map_list, mptr, task_map_t, node) {
265                 if (arm_mmu_set_attrs_task_init(mptr) != NO_ERROR)
266                         return false;
267                 arch_task_map(task, mptr);
268         }
269
270         return true;
271 }