summaryrefslogtreecommitdiff
path: root/libc/sysdeps/linux/xtensa/makecontext.c
blob: da26a0130325be413c14980d8c88c252a39441fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/* Copyright (C) 2018 - 2022 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

#include <sysdep.h>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
#include <ucontext.h>

extern void __start_context (void);

#if defined(__XTENSA_CALL0_ABI__)
/*
 * makecontext sets up new stack like this:
 *
 *    +------------------ stack top (uc_stack.ss_sp + uc_stack.ss_size)
 *    | optional alignment
 *    +------------------ CFA of __start_context, initial sp points here
 *    | optional padding
 *    +------------------ Optional arguments 6..argc - 1
 *    | func arg argc - 1
 *    | func arg argc - 2
 *    |  ...
 *    | func arg 6
 *    +------------------ Optional arguments 2..5
 *    | func arg 5
 * 16 | func arg 4
 *    | func arg 3
 *    | func arg 2
 *    +------------------ Optional arguments 0..1
 *    | func arg 1
 * 16 | func arg 0
 *    | padding
 *    | padding
 *    +------------------ CFA of pseudo getcontext
 *    |
 *    +------------------ stack bottom (uc_stack.ss_sp)
 *
 * When argc is 0 arguments areas are not allocated,
 * when 1 <= argc < 3 only area for arguments 0..1 is allocated,
 * when 3 <= argc < 7 areas for arguments 0..1 and 2..5 is allocated,
 * when argc >= 7 all three arguments areas are allocated.
 * Arguments 0..5 area is deallocated by the __start_context after
 * arguments are loaded into registers.
 * uc_mcontext registers are set as if __start_context made call0
 * to getcontext, sp points to that pseudo getcontext CFA.
 * setcontext/swapcontext will arrange for restoring regiters
 * a1, a12..a15 of __start_context.
 */

void
__makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
{
  unsigned long sp = ((unsigned long) ucp->uc_stack.ss_sp
    + ucp->uc_stack.ss_size) & -16;
  int i;

  if (argc > 0)
    sp -= 4 * (argc + 2);
  sp &= -16;

  ucp->uc_mcontext.sc_pc = (unsigned long) __start_context;
  ucp->uc_mcontext.sc_a[1] = sp;
  ucp->uc_mcontext.sc_a[12] = (unsigned long) func;
  ucp->uc_mcontext.sc_a[13] = (unsigned long) ucp->uc_link;
  ucp->uc_mcontext.sc_a[14] = argc;

  if (argc)
    {
      va_list ap;

      va_start (ap, argc);
      for (i = 0; i < argc; ++i)
	((int *) sp)[i + 2] = va_arg (ap, int);
      va_end (ap);
    }
}
#elif defined(__XTENSA_WINDOWED_ABI__)
/*
 * makecontext sets up new stack like this:
 *
 *    +------------------ stack top (uc_stack.ss_sp + uc_stack.ss_size)
 *    | optional alignment
 *    +------------------ CFA of __start_context
 * 16 | Outermost caller spill area
 *    +------------------
 * 16 | __start_context overflow area
 *    +------------------ initial sp points here
 *    | optional padding
 *    +------------------ Optional arguments 6..argc - 1
 *    | func arg argc - 1
 *    | func arg argc - 2
 *    |  ...
 *    | func arg 6
 *    +------------------ Optional arguments 3..5
 *    | func arg 5
 * 16 | func arg 4
 *    | func arg 3
 *    | padding
 *    +------------------ CFA of pseudo getcontext
 * 16 | __start_context caller spill area
 *    +------------------
 *    |
 *    +------------------ stack bottom (uc_stack.ss_sp)
 *
 * When argc < 4 both arguments areas are not allocated,
 * when 4 <= argc < 7 only area for arguments 3..5 is allocated,
 * when argc >= 7 both arguments areas are allocated.
 * Arguments 3..5 area is deallocated by the __start_context after
 * arguments are loaded into registers.
 * uc_mcontext registers are set as if __start_context made call8
 * to getcontext, sp points to that pseudo getcontext CFA, spill
 * area under that sp has a1 pointing to the __start_context CFA
 * at the top of the stack. setcontext/swapcontext will arrange for
 * restoring regiters a0..a7 of __start_context.
 */

void
__makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
{
  unsigned long sp = ((unsigned long) ucp->uc_stack.ss_sp
    + ucp->uc_stack.ss_size - 32) & -16;
  unsigned long spill0[4] =
    {
      0, sp + 32, 0, 0,
    };
  int i;

  memset ((void *) sp, 0, 32);

  if (argc > 2)
    sp -= 4 * (argc - 2);
  sp &= -16;

  ucp->uc_mcontext.sc_pc =
    ((unsigned long) __start_context & 0x3fffffff) + 0x80000000;
  ucp->uc_mcontext.sc_a[0] = 0;
  ucp->uc_mcontext.sc_a[1] = sp;
  ucp->uc_mcontext.sc_a[2] = (unsigned long) func;
  ucp->uc_mcontext.sc_a[3] = (unsigned long) ucp->uc_link;
  ucp->uc_mcontext.sc_a[4] = argc;

  if (argc)
    {
      va_list ap;

      va_start (ap, argc);
      for (i = 0; i < argc; ++i)
	{
	  if (i < 3)
	    ucp->uc_mcontext.sc_a[5 + i] = va_arg (ap, int);
	  else
	    ((int *) sp)[i - 2] = va_arg (ap, int);
	}
      va_end (ap);
    }

  sp -= 16;
  memcpy ((void *) sp, spill0, sizeof (spill0));
}
#else
#error Unsupported Xtensa ABI
#endif

weak_alias (__makecontext, makecontext)