tlk: 6/19 update
[3rdparty/ote_partner/tlk.git] / arch / arm / arm / task.c
1 /*
2  * Copyright (c) 2012-2014, 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         if (flags & TM_KERN_SEC_VA) {
97                 if (flags & (TM_NS_MEM | TM_NS_MEM_PRIV))
98                         return NULL;
99         }
100
101         mptr = malloc(sizeof(task_map_t));
102         if (mptr == NULL)
103                 return NULL;
104
105         offset = addr & PAGE_MASK;
106         size = ROUNDUP(offset + size, PAGE_SIZE);
107         npages = size / PAGE_SIZE;
108
109         /* non-secure mapping may require a stricter vaddr alignment */
110         align = (flags & TM_NS_MEM) ? NS_VIRT_ADDR_ALIGN : ALIGN_4KB;
111
112         mptr->vaddr = task_find_address_space(task, size, align);
113         if (!mptr->vaddr) {
114                 free (mptr);
115                 return NULL;
116         }
117
118         mptr->size = size;
119         mptr->flags = flags;
120         mptr->id = 0;
121         mptr->offset = offset;
122
123         task_add_mapping(task, mptr);
124
125         if (npages == 1) {
126                 arm_mmu_translate_range(addr, &mptr->u_phys.contig, mptr);
127
128                 if (mptr->map_attrs == NULL) {
129                         task_delete_mapping (task, mptr);
130                         free (mptr);
131                         return NULL;
132                 }
133                 mptr->flags |= TM_PHYS_CONTIG;
134         } else {
135                 paddr_t *pagelist;
136                 u_int i;
137
138                 /* allocate pagelist, as buffer may not be contiguous */
139                 pagelist = malloc(npages * sizeof(paddr_t));
140                 if (pagelist == NULL) {
141                         task_delete_mapping(task, mptr);
142                         free (mptr);
143                         return NULL;
144                 }
145
146                 arm_mmu_translate_range(addr, pagelist, mptr);
147
148                 if (mptr->map_attrs == NULL) {
149                         free (pagelist);
150                         task_delete_mapping (task, mptr);
151                         free (mptr);
152                         return NULL;
153                 }
154
155                 /* check if the pages ended up being contiguous */
156                 for (i = 1; i < npages; i++) {
157                         if ((pagelist[i-1] + PAGE_SIZE) != pagelist[i])
158                                 break;
159                 }
160                 if (i < npages) {
161                         /* not contiguous */
162                         mptr->u_phys.pagelist = pagelist;
163                 } else {
164                         /* turns out it is (don't need pagelist anymore) */
165                         mptr->flags |= TM_PHYS_CONTIG;
166                         mptr->u_phys.contig = pagelist[0];
167                         free(pagelist);
168                 }
169         }
170
171         arch_task_map(task, mptr);
172         return mptr;
173 }
174
175 task_map_t *arch_task_setup_mmio(task_t *task, u_int id, addr_t paddr, u_int size)
176 {
177         task_map_t *mptr;
178
179         dprintf(SPEW, "%s: id 0x%x paddr 0x%x size 0x%x\n",
180                 __func__, id, (u_int)paddr, size);
181
182         mptr = malloc(sizeof(task_map_t));
183         if (mptr == NULL)
184                 return NULL;
185
186         size = ROUNDUP(size, PAGE_SIZE);
187
188         mptr->vaddr = task_find_address_space(task, size, ALIGN_4KB);
189         if (!mptr->vaddr) {
190                 free (mptr);
191                 return NULL;
192         }
193
194         mptr->size = size;
195         mptr->flags = TM_IO | TM_UW | TM_PHYS_CONTIG;
196         mptr->u_phys.contig = ROUNDDOWN(paddr, PAGE_SIZE);
197         mptr->offset = (paddr & PAGE_MASK);
198
199         mptr->id = id;
200         mptr->map_attrs = NULL;
201
202         task_add_mapping(task, mptr);
203
204         return mptr;
205 }
206
207 void arch_task_unmap_memory(task_t *task, task_map_t *mptr)
208 {
209         /* if non-contig, free the pagelist */
210         if ((mptr->flags & TM_PHYS_CONTIG) == 0)
211                 free(mptr->u_phys.pagelist);
212
213         /* If the task pages are not yet mapped (e.g. error while loading a task)
214          * the page table does not yet exist.
215          */
216         if (task->page_table) {
217                 arch_task_unmap(task, mptr);
218                 ASSERT(mptr->map_attrs);
219         }
220         task_delete_mapping(task, mptr);
221         if (mptr->map_attrs)
222                 free(mptr->map_attrs);
223         free(mptr);
224 }
225
226 bool arch_task_init(thread_t *thread, task_t *task)
227 {
228         static u_int context_id;
229         task_map_t *mptr;
230         uint32_t stack_top_off;
231         vaddr_t arg_base;
232         uint32_t *args;
233         Elf32_auxv_t *auxv;
234
235         /* setup/clean user stack (reduced as libc expects args at sp) */
236         stack_top_off = task->stack_map->size - TASK_ARGS_SPACE;
237         task->sp = task->stack_map->vaddr + stack_top_off;
238         memset((void *)physical_to_virtual(task->stack_map->u_phys.contig), 0, task->stack_map->size);
239
240         platform_clean_invalidate_cache_range(physical_to_virtual(task->stack_map->u_phys.contig),
241                         task->stack_map->size);
242
243         /* initialize libc expected args */
244         arg_base = physical_to_virtual(task->stack_map->u_phys.contig) + stack_top_off;
245         args = (uint32_t *)arg_base;
246
247         *args++ = 0x0;          /* argc */
248         *args++ = 0x0;          /* argv */
249         *args++ = 0x0;          /* envp */
250
251         /* add AT_RANDOM auxv tag/value */
252         auxv = (Elf32_auxv_t *)args;
253         args += 2;
254
255         auxv->a_type = AT_RANDOM;
256         auxv->a_un.a_val = task->sp + ((vaddr_t)args - arg_base);
257
258         *args++ = platform_get_rand32();
259         *args++ = platform_get_rand32();
260         *args++ = platform_get_rand32();
261         *args++ = platform_get_rand32();
262
263 #if ARM_WITH_NEON
264         /* alloc per-thread (and NS world) vfp context */
265         thread->arch.fpctx = calloc(1, sizeof(fpctx_t));
266         if (!ns_vfp_hw_context)
267                 ns_vfp_hw_context = calloc(1, sizeof(fpctx_t));
268 #endif
269
270         thread->arch.task = task;
271         arch_task_set_usermode(thread);
272
273         task->context_id = ++context_id;
274         list_add_tail(&task->thread_node, &thread->task_node);
275
276         /* create pagetable entries for boot time mappings */
277         list_for_every_entry(&task->map_list, mptr, task_map_t, node) {
278                 if (arm_mmu_set_attrs_task_init(mptr) != NO_ERROR)
279                         return false;
280                 arch_task_map(task, mptr);
281         }
282
283         return true;
284 }