summaryrefslogtreecommitdiff
path: root/Documentation/fujitsu/frv/kernel-ABI.txt
blob: 0ed9b0a779bcad04025cae00bdf2fa40fe1f4748 (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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
				 =================================
				 INTERNAL KERNEL ABI FOR FR-V ARCH
				 =================================

The internal FRV kernel ABI is not quite the same as the userspace ABI. A number of the registers
are used for special purposed, and the ABI is not consistent between modules vs core, and MMU vs
no-MMU.

This partly stems from the fact that FRV CPUs do not have a separate supervisor stack pointer, and
most of them do not have any scratch registers, thus requiring at least one general purpose
register to be clobbered in such an event. Also, within the kernel core, it is possible to simply
jump or call directly between functions using a relative offset. This cannot be extended to modules
for the displacement is likely to be too far. Thus in modules the address of a function to call
must be calculated in a register and then used, requiring two extra instructions.

This document has the following sections:

 (*) System call register ABI
 (*) CPU operating modes
 (*) Internal kernel-mode register ABI
 (*) Internal debug-mode register ABI
 (*) Virtual interrupt handling


========================
SYSTEM CALL REGISTER ABI
========================

When a system call is made, the following registers are effective:

	REGISTERS	CALL			RETURN
	===============	=======================	=======================
	GR7		System call number	Preserved
	GR8		Syscall arg #1		Return value
	GR9-GR13	Syscall arg #2-6	Preserved


===================
CPU OPERATING MODES
===================

The FR-V CPU has three basic operating modes. In order of increasing capability:

  (1) User mode.

      Basic userspace running mode.

  (2) Kernel mode.

      Normal kernel mode. There are many additional control registers available that may be
      accessed in this mode, in addition to all the stuff available to user mode. This has two
      submodes:

      (a) Exceptions enabled (PSR.T == 1).

      	  Exceptions will invoke the appropriate normal kernel mode handler. On entry to the
      	  handler, the PSR.T bit will be cleared.

      (b) Exceptions disabled (PSR.T == 0).

      	  No exceptions or interrupts may happen. Any mandatory exceptions will cause the CPU to
      	  halt unless the CPU is told to jump into debug mode instead.

  (3) Debug mode.

      No exceptions may happen in this mode. Memory protection and management exceptions will be
      flagged for later consideration, but the exception handler won't be invoked. Debugging traps
      such as hardware breakpoints and watchpoints will be ignored. This mode is entered only by
      debugging events obtained from the other two modes.

      All kernel mode registers may be accessed, plus a few extra debugging specific registers.


=================================
INTERNAL KERNEL-MODE REGISTER ABI
=================================

There are a number of permanent register assignments that are set up by entry.S in the exception
prologue. Note that there is a complete set of exception prologues for each of user->kernel
transition and kernel->kernel transition. There are also user->debug and kernel->debug mode
transition prologues.


	REGISTER	FLAVOUR	USE
	===============	=======	====================================================
	GR1			Supervisor stack pointer
	GR15			Current thread info pointer
	GR16			GP-Rel base register for small data
	GR28			Current exception frame pointer (__frame)
	GR29			Current task pointer (current)
	GR30			Destroyed by kernel mode entry
	GR31		NOMMU	Destroyed by debug mode entry
	GR31		MMU	Destroyed by TLB miss kernel mode entry
	CCR.ICC2		Virtual interrupt disablement tracking
	CCCR.CC3		Cleared by exception prologue (atomic op emulation)
	SCR0		MMU	See mmu-layout.txt.
	SCR1		MMU	See mmu-layout.txt.
	SCR2		MMU	Save for EAR0 (destroyed by icache insns in debug mode)
	SCR3		MMU	Save for GR31 during debug exceptions
	DAMR/IAMR	NOMMU	Fixed memory protection layout.
	DAMR/IAMR	MMU	See mmu-layout.txt.


Certain registers are also used or modified across function calls:

	REGISTER	CALL				RETURN
	===============	===============================	===============================
	GR0		Fixed Zero			-
	GR2		Function call frame pointer
	GR3		Special				Preserved
	GR3-GR7		-				Clobbered
	GR8		Function call arg #1		Return value (or clobbered)
	GR9		Function call arg #2		Return value MSW (or clobbered)
	GR10-GR13	Function call arg #3-#6		Clobbered
	GR14		-				Clobbered
	GR15-GR16	Special				Preserved
	GR17-GR27	-				Preserved
	GR28-GR31	Special				Only accessed explicitly
	LR		Return address after CALL	Clobbered
	CCR/CCCR	-				Mostly Clobbered


================================
INTERNAL DEBUG-MODE REGISTER ABI
================================

This is the same as the kernel-mode register ABI for functions calls. The difference is that in
debug-mode there's a different stack and a different exception frame. Almost all the global
registers from kernel-mode (including the stack pointer) may be changed.

	REGISTER	FLAVOUR	USE
	===============	=======	====================================================
	GR1			Debug stack pointer
	GR16			GP-Rel base register for small data
	GR31			Current debug exception frame pointer (__debug_frame)
	SCR3		MMU	Saved value of GR31


Note that debug mode is able to interfere with the kernel's emulated atomic ops, so it must be
exceedingly careful not to do any that would interact with the main kernel in this regard. Hence
the debug mode code (gdbstub) is almost completely self-contained. The only external code used is
the sprintf family of functions.

Futhermore, break.S is so complicated because single-step mode does not switch off on entry to an
exception. That means unless manually disabled, single-stepping will blithely go on stepping into
things like interrupts. See gdbstub.txt for more information.


==========================
VIRTUAL INTERRUPT HANDLING
==========================

Because accesses to the PSR is so slow, and to disable interrupts we have to access it twice (once
to read and once to write), we don't actually disable interrupts at all if we don't have to. What
we do instead is use the ICC2 condition code flags to note virtual disablement, such that if we
then do take an interrupt, we note the flag, really disable interrupts, set another flag and resume
execution at the point the interrupt happened. Setting condition flags as a side effect of an
arithmetic or logical instruction is really fast. This use of the ICC2 only occurs within the
kernel - it does not affect userspace.

The flags we use are:

 (*) CCR.ICC2.Z [Zero flag]

     Set to virtually disable interrupts, clear when interrupts are virtually enabled. Can be
     modified by logical instructions without affecting the Carry flag.

 (*) CCR.ICC2.C [Carry flag]

     Clear to indicate hardware interrupts are really disabled, set otherwise.


What happens is this:

 (1) Normal kernel-mode operation.

	ICC2.Z is 0, ICC2.C is 1.

 (2) An interrupt occurs. The exception prologue examines ICC2.Z and determines that nothing needs
     doing. This is done simply with an unlikely BEQ instruction.

 (3) The interrupts are disabled (local_irq_disable)

	ICC2.Z is set to 1.

 (4) If interrupts were then re-enabled (local_irq_enable):

	ICC2.Z would be set to 0.

     A TIHI #2 instruction (trap #2 if condition HI - Z==0 && C==0) would be used to trap if
     interrupts were now virtually enabled, but physically disabled - which they're not, so the
     trap isn't taken. The kernel would then be back to state (1).

 (5) An interrupt occurs. The exception prologue examines ICC2.Z and determines that the interrupt
     shouldn't actually have happened. It jumps aside, and there disabled interrupts by setting
     PSR.PIL to 14 and then it clears ICC2.C.

 (6) If interrupts were then saved and disabled again (local_irq_save):

	ICC2.Z would be shifted into the save variable and masked off (giving a 1).

	ICC2.Z would then be set to 1 (thus unchanged), and ICC2.C would be unaffected (ie: 0).

 (7) If interrupts were then restored from state (6) (local_irq_restore):

	ICC2.Z would be set to indicate the result of XOR'ing the saved value (ie: 1) with 1, which
	gives a result of 0 - thus leaving ICC2.Z set.

	ICC2.C would remain unaffected (ie: 0).

     A TIHI #2 instruction would be used to again assay the current state, but this would do
     nothing as Z==1.

 (8) If interrupts were then enabled (local_irq_enable):

	ICC2.Z would be cleared. ICC2.C would be left unaffected. Both flags would now be 0.

     A TIHI #2 instruction again issued to assay the current state would then trap as both Z==0
     [interrupts virtually enabled] and C==0 [interrupts really disabled] would then be true.

 (9) The trap #2 handler would simply enable hardware interrupts (set PSR.PIL to 0), set ICC2.C to
     1 and return.

(10) Immediately upon returning, the pending interrupt would be taken.

(11) The interrupt handler would take the path of actually processing the interrupt (ICC2.Z is
     clear, BEQ fails as per step (2)).

(12) The interrupt handler would then set ICC2.C to 1 since hardware interrupts are definitely
     enabled - or else the kernel wouldn't be here.

(13) On return from the interrupt handler, things would be back to state (1).

This trap (#2) is only available in kernel mode. In user mode it will result in SIGILL.