blob: 3b8d1af23edda6c0bba92c4207842ad012fd9150 [file] [log] [blame]
Ronald G. Minnich62997e02013-08-23 11:22:42 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2013 Google, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Ronald G. Minnich62997e02013-08-23 11:22:42 -070014 */
15#include <console/console.h>
16#include <thread.h>
17
18/* The stack frame looks like the following. */
19struct pushed_regs {
20 u32 r4;
21 u32 r5;
22 u32 r6;
23 u32 r7;
24 u32 r8;
25 u32 r9;
26 u32 r10;
27 u32 r11;
28 u32 lr;
29};
30
31static inline uintptr_t push_stack(uintptr_t cur_stack, uintptr_t value)
32{
33 uintptr_t *addr;
34
35 cur_stack -= sizeof(value);
36 addr = (uintptr_t *)cur_stack;
37 *addr = value;
38 return cur_stack;
39}
40
41void arch_prepare_thread(struct thread *t,
Logan Carlson50522252017-05-31 12:01:01 -060042 void asmlinkage(*thread_entry)(void *), void *arg)
Ronald G. Minnich62997e02013-08-23 11:22:42 -070043{
44 uintptr_t stack = t->stack_current;
45 int i;
46 uintptr_t poison = 0xdeadbeef;
47
48 /* Push the LR. thread_entry()
49 * is assumed to never return.
50 */
51 stack = push_stack(stack, (uintptr_t)thread_entry);
52 /* Make room for the registers.
53 * Poison the initial stack. This is good hygiene and finds bugs.
54 * Poisoning the stack with different values helps when you're
55 * hunting for (e.g.) misaligned stacks or other such
56 * weirdness. The -1 is because we already pushed lr.
57 */
Elyes HAOUAS6b727872016-09-03 08:28:48 +020058 for (i = 0; i < sizeof(struct pushed_regs) / sizeof(u32) - 1; i++)
Ronald G. Minnich62997e02013-08-23 11:22:42 -070059 stack = push_stack(stack, poison++);
60
61 t->stack_current = stack;
62}
63
64/* We could write this as a .S and the first time around that's how we
65 * did it. But there's always the question of matching our ARM
66 * directives in the .S with how gcc is doing things. It seems best
67 * to follow the pattern of the rest of the ARM port and just use
68 * inline assembly and let gcc get all the ELF magic right.
69 */
70void __attribute__((naked))
71switch_to_thread(uintptr_t new_stack, uintptr_t *saved_stack)
72{
73 /* Defintions for those of us not totally familiar with ARM:
74 * R15 -- PC, R14 -- LR, R13 -- SP
75 * R0-R3 need not be saved, nor R12.
76 * on entry, the only saved state is in LR -- the old PC.
77 * The args are in R0,R1.
78 * R0 is the new stack
79 * R1 is a pointer to the old stack save location
80 * Push R4-R11 and LR
81 * then switch stacks
82 * then pop R0-R12 and LR
83 * then mov PC,LR
84 *
85 * stack layout
86 * +------------+
87 * | LR | <-- sp + 0x20
88 * +------------+
89 * | R11 | <-- sp + 0x1c
90 * +------------+
91 * | R10 | <-- sp + 0x18
92 * +------------+
93 * | R9 | <-- sp + 0x14
94 * +------------+
95 * | R8 | <-- sp + 0x10
96 * +------------+
97 * | R7 | <-- sp + 0x0c
98 * +------------+
99 * | R6 | <-- sp + 0x08
100 * +------------+
101 * | R5 | <-- sp + 0x04
102 * +------------+
103 * | R4 | <-- sp + 0x00
104 * +------------+
105 */
Elyes HAOUAS6b727872016-09-03 08:28:48 +0200106 asm volatile (
Ronald G. Minnich62997e02013-08-23 11:22:42 -0700107 /* save context. */
108 "push {r4-r11,lr}\n\t"
109 /* Save the current stack */
110 "str sp,[r1]\n\t"
111 /* switch to the new stack */
112 "mov sp,r0\n\t"
113 /* restore the registers */
114 "pop {r4-r11,lr}\n\t"
115 /* resume other thread. */
116 "mov pc,lr\n\t"
117 );
118}
119
120void *arch_get_thread_stackbase(void)
121{
122 return (void *)CONFIG_STACK_BOTTOM;
123}