summaryrefslogtreecommitdiffstats
path: root/pk/ppe42/ppe42_exceptions.S
blob: b3ce355121f57b2ffe4e35852592d6ab6a914484 (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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
//-----------------------------------------------------------------------------
// *! (C) Copyright International Business Machines Corp. 2014
// *! All Rights Reserved -- Property of IBM
// *! *** IBM Confidential ***
//-----------------------------------------------------------------------------
        
/// \file ppe42_exceptions.S
/// \brief PPE42 exception vector area.
///
/// \cond

        .nolist
#include "pk.h"
        .list

## declare and initializes global variables that hold external irq config data
## Each PPE macro type (GPE, CME, and SBE) will have it's own implementation of this macro
## defined in (gpe, cme, sbe)_common.h
        .hwmacro_irq_cfg_bitmaps

### ****************************************************************************
### .vectors - This section contains all ppe42 exception vectors
### 
### ****************************************************************************

        .section .vectors, "ax", @progbits

        .global __vectors

__vectors:

        ############################################################
        # 0x0000 : Machine Check
        ############################################################

        ### Unmaskable interrupts (including program interrupts) are promoted
        ### to machine check interrupts if MSR[UIE] = 0 and MSR[ME] = 1.
        ### If the machine check was caused by a program interrupt it
        ### will be forwarded to the program exception handler.
__machine_check:

        PPE42_MACHINE_CHECK_HANDLER

        ############################################################
        # 0x0040 : System Reset
        ############################################################
        .global __system_reset
        .org __vectors + 0x0040
__system_reset:
        b   __pk_boot

        ############################################################
        # 0x0060 : Data Storage Interrupt
        ############################################################

        .org __vectors + 0x0060
__data_storage:

        PPE42_DATA_STORAGE_HANDLER

        ############################################################
        # 0x0080 : Instruction Storage Interrupt
        ############################################################

        .org __vectors + 0x0080
__instruction_storage:

        PPE42_INSTRUCTION_STORAGE_HANDLER


        ############################################################
        # 0x00A0 : External Interrupt
        ############################################################

        .org __vectors + 0x00A0
__external_interrupt_vector:
        _pk_ctx_push_as_needed __get_ext_irq

        ############################################################
        # 0x00C0 : Alignment Exception
        ############################################################

        .org __vectors + 0x00C0
__alignment_exception:

        PPE42_ALIGNMENT_HANDLER


        ############################################################
        # 0x00E0 : Program Interrupt
        ############################################################

        .org __vectors + 0x00E0

        ### Program exceptions are utilized for emulating the system call
        ### instruction (0x44000002) which is used for doing context
        ### switches between threads.  They can also be used by the code
        ### to signal an exception in an error scenario.
__program_exception:
        _pk_ctx_push_as_needed  program_exception_handler 


        ############################################################
        # 0x0100 : DEC Interrupts
        ############################################################

        .org __vectors + 0x0100
__dec_interrupt:
        _pk_ctx_push_as_needed  dec_handler

        ############################################################
        # 0x0120 : FIT Interrupts
        ############################################################
        .org __vectors + 0x0120
__fit_interrupt:        

        _pk_ctx_push_as_needed  fit_handler

        ############################################################
        # 0x0140 : Watchdog Interrupts
        ############################################################
        .org __vectors + 0x0140
__watchdog_interrupt:   

        _pk_ctx_push_as_needed  watchdog_handler



        ## The idle thread has no permanent register context.  The idle thread
        ## entry point is re-entered whenever the idle thread is scheduled.

        .global __pk_idle_thread
        .global __pk_idle_thread_from_bootloader

__pk_idle_thread:

        ## The idle thread 'uses' the kernel stack.  Any register context
        ## pushed here is redundant and is wiped out/ignored every time the
        ## idle thread is re-scheduled. 
        
        ## The idle thread simply establishes a default machine context and
        ## enters the wait-enable state.  The idle thread is always entered
        ## with interrupts disabled.  
        ##
        ## The kernel context is initialized to indicate that the idle thread
        ## is running - the idle thread priority is PK_THREADS, the
        ## 'thread-mode' bit is asserted and so is the 'discard-ctx" bit.
        ## In addition, the previous kernel context is stored in the lower
        ## 16 bits.
        ##
        ## This loop can also be called from the PK bootloader if main()
        ## returns - in which case we don't muck with the SPRG0 or the stack
        ## pointer. 
        mfsprg0 %r3         
        srwi    %r3, %r3, 16
        oris    %r3, %r3, (PK_THREADS << 8) | PPE42_THREAD_MODE | PPE42_DISCARD_CTX
        mtsprg0        %r3
        _lwzsd  %r1, __pk_kernel_stack
                
__pk_idle_thread_from_bootloader:

        PK_KERN_TRACE_ASM16("ENTER_IDLE_STATE")

        _lwzsd  %r3, __pk_thread_machine_context_default
        _oriwa  %r3, %r3, MSR_WE
        mtmsr   %r3
        b       .

        ## pk_halt() is implemented on the ppe42 by writing a value of 0x3 to
        ## the RST field of the DBCR.
        .global pk_halt
pk_halt:
        lis     %r31, 0x3000
        mtdbcr  %r31
        .long   0


dec_handler:            

        ## The portable timer handler of PK is a full-mode handler with the prototype:
        ## void (*pk_timer_handler)(void).
        ##
        ## To support the portable specification, the kernel clears the
        ## interrupt by writing the DIS back into the TSR before calling the
        ## handler.  The timer handler does not take any arguments.

        li      %r4, PPE42_IRQ_DEC
        _update_kernel_context %r4, %r3
        
        _liwa   %r3, TSR_DIS
        mttsr   %r3

        bl      __pk_timer_handler
        b       check_for_ext_interrupt

program_exception_handler:
        ## first check if exception was caused by an illegal 'sc' instruction
        mfspr       %r3, SPRN_EDR
        _liw        %r4, PPE42_SC_INST
        cmpwbeq     %r3, %r4, __sc_helper
        _pk_panic   PPE42_ILLEGAL_INSTRUCTION

        ## Saved SRR0 is currently pointing to the 'sc' instruction.  We need to advance it
        ## to the next instruction so that we don't end up in an endless loop (something
        ## that the ppc sc instruction does automatically).
__sc_helper:
        mfsrr0      %r4
        _lwzsd      %r3, __pk_saved_sp
        addi        %r4, %r4, 4
        stw         %r4, PK_CTX_SRR0(%r3)

        .global __pk_next_thread_resume
__pk_next_thread_resume:
        
        _lwzsd  %r3, __pk_next_thread
        _stwsd  %r3, __pk_current_thread

        ## Enter the wait enabled state if the thread pointer is null
        bwz     %r3, __pk_idle_thread
        
        ## switch to the new thread stack 
        lwz     %r1, PK_THREAD_OFFSET_SAVED_STACK_POINTER(%r3)

        ## load sprg0 from the stack and update the thread priority
        ## in case it changed.
restore_and_update_sprg0:
        _lbzsd  %r31, __pk_next_priority

        PK_KERN_TRACE_ASM16("RESUME_THREAD(%d)", %r31)

        lwz     %r3, PK_CTX_KERNEL_CTX(%r1)
        rlwimi  %r3, %r31, 24, 2, 7
        mtsprg0 %r3

        b       ctx_pop
 
fit_handler:
        
        ## The FIT handler is user defined. By
        ## convention the kernel clears the interrupt by writing the FIS back
        ## into the TSR.
        
        li      %r4, PPE42_IRQ_FIT
                
        _update_kernel_context %r4, %r3

        _lwzsd  %r3, __ppe42_fit_arg

        _liwa   %r6, TSR_FIS
        mttsr   %r6

        _lwzsd  %r6, __ppe42_fit_routine
        mtlr    %r6
        blrl
        
        b       check_for_ext_interrupt

watchdog_handler:
        ## Watchdog setup is described in the PK Specification. 
        ## The kernel clears TSR[WIS] prior to calling the handler.  

        li      %r4, PPE42_IRQ_WATCHDOG
        
        _update_kernel_context %r4, %r3

        _liwa   %r6, TSR_WIS
        mttsr   %r6

        _lwzsd  %r6, __ppe42_watchdog_routine
        mtlr    %r6
        blrl

        b       check_for_ext_interrupt


        ## Check if we can disard the interrupted context.
        ## This routine expects r3, r4, lr, and cr to already be pushed.
        ## It also expects r3 to hold the address of the function to jump
        ## to after the interrupted context has been pushed (if necessary).

        .align 5
ctx_check_discard:

        ## Prepare to jump to the branch function that was passed in
        mtlr    %r3 

        ## Check if the DISCARD_CTX bit is set in the kernel context
        mfsprg0 %r3
        bb0wi   %r3, PPE42_DISCARD_CTX_BIT, ctx_continue_push

ctx_discard:        
        ## DISCARD_CTX bit was set.  Discard stack and branch to interrupt
        ## handler code
        addi    %r1, %r1, PK_CTX_SIZE
        blr

        ## DISCARD_CTX bit was not set.  Continue saving full context.
        ## (r3, r4, lr, and cr have already been saved for us) and
        ## r3 contains the interrupted kernel context

ctx_continue_push:

        stvd    %d5,    PK_CTX_GPR5(%r1)
        stvd    %d7,    PK_CTX_GPR7(%r1)
        stvd    %d9,    PK_CTX_GPR9(%r1)
        stvd    %d28,   PK_CTX_GPR28(%r1)
        stvd    %d30,   PK_CTX_GPR30(%r1)
        mfxer   %r5
        mfctr   %r6
        stvd    %d5,    PK_CTX_XER(%r1)
        mfsrr0  %r7
        mfsrr1  %r8
        stvd    %d7,    PK_CTX_SRR0(%r1)
        stw     %r0,    PK_CTX_GPR0(%r1)
        stw     %r3,    PK_CTX_KERNEL_CTX(%r1)
        
        ## If the 'processing interrupt' bit is set then we were already
        ## using the kernel stack and don't need to modify or save the current
        ## stack pointer.
        bb1wi   %r3, PPE42_PROC_IRQ_BIT, ctx_push_completed

        ## load the pointer to the current thread control block
        _lwzsd  %r4, __pk_current_thread
        
        ## don't save the stack pointer in the thread control block
        ## if the current thread was the idle thread (null pointer)
        bwz     %r4, switch_to_kernel_stack

        ## we interrupted a bonafide thread, so save off the stack
        ## pointer
        stw     %r1, PK_THREAD_OFFSET_SAVED_STACK_POINTER(%r4)

switch_to_kernel_stack:
        _stwsd  %r1, __pk_saved_sp
        _lwzsd  %r1, __pk_kernel_stack
       
ctx_push_completed:      
        blr

__get_ext_irq:   
        
        ## Entry invariants:
        ## 1. external interupts are disabled;
        ## 2. previous context has ben saved off
        ## 3. r3 contains the kernel context
        ## 4. r1 points to the kernel stack

        ## This is HW Macro specific code that is responsible for finding the
        ## IRQ # and storing it in r4 (phantom IRQ's are assigned a value of EXTERNAL_IRQS).
        
        hwmacro_get_ext_irq        

        ## An active or phantom IRQ was found. 
        ## R3 has the context of the interrupted thread or bottom half
        ## R4 has the IRQ number.
        ## The IRQ is converted into a pointer to an 8-byte handler
        ## structure, and the handler is dispatched. The call is made with the
        ## parameters:

        ## R3 = private data ptr
        ## R4 = irq

call_external_irq_handler:  

        _update_kernel_context %r4, %r3
        slwi    %r3, %r4, 3                 //multiply the irq# by 8
        _liw    %r6, __ppe42_irq_handlers
        lwzx    %r5, %r6, %r3
        addi    %r3, %r3, 4
        lwzx    %r3, %r6, %r3
        mtlr    %r5
        blrl
                
        ## Once the interrupt handler returns, check if any interrupts are
        ## waiting and handle them now.

check_for_ext_interrupt:

        ## Set the CTX_DISCARD bit in the kernel context so that if there is
        ## an interrupt it will not bother saving the full context.
        mfsprg0 %r31
        oris    %r31, %r31, PPE42_DISCARD_CTX
        mtsprg0 %r31

        ###### Enable/Disable External Interrupts #####
        wrteei  1
        wrteei  0

        ## If we made it this far, there must not be any interrupts pending.
        ## If bottom half processing was interrupted we need to restore it
check_interrupted_bh:

        ## If the thread ID is 33 then the bottom half handler was interrupted
        ## and needs to be restored.
        extrwi      %r4, %r31, 6, 2
        cmpwi       %r4, 33
        beq         ctx_pop_with_sprg0

check_for_bh:
        ## if the bottom half queue is pointing to itself then the queue is
        ## empty and there are no bottom halves that need processing.
        _lwzsd      %r4, _pk_bh_queue
        lwz         %r5, 0(%r4)
        cmplwbeq    %r4, %r5, restore_interrupted_sp

process_bottom_halves:
        ## Clear the CTX_DISCARD bit so that interrupted bottom half context
        ## will be saved in case an interrupt occurs after this point.  Also
        ## set the thread ID to 33 so that we know to restore the bottom half
        ## context that was interrupted.
        rlwinm  %r3, %r31, 0, 9, 1   //clear thread id + discard bit
        oris    %r3, %r3, 0x2100    //set thread id to 33
        mtsprg0 %r3                 //set bottom half context
        
        ## branch to a C function that processes bottom halves
        wrteei  1
        bl      _pk_process_bh
        wrteei  0

        ## restore the previous kernel context (with discard bit set)
        mtsprg0 %r31

restore_interrupted_sp:
        ## restore the interrupted thread stack pointer
        _lwzsd  %r1,   __pk_saved_sp

        ## If we are not in thread mode (i.e., we took an interrupt in an
        ## interupt-only configuration of PK or after pk_initialize() but
        ## before pk_start_threads) simply pop the context and RFI - in this
        ## case we'll most likely be returning to main() or the non-thread-mode
        ## idle thread.

check_thread_mode:
        bb0wi   %r31, PPE42_THREAD_MODE_BIT, ctx_pop_with_sprg0

        ## Check if external interrupt activated a delayed context switch.  The
        ## C-level code has taken care of the scheduling decisions - we simply
        ## need to implement them here.
check_for_ctx_switch:

        _lwzsd  %r3, __pk_delayed_switch
        bwz     %r3, check_for_idle_thread

        ## Clear the delayed switch flag and go to the context switch code to
        ## finish the switch. 

        li      %r3, 0
        _stwsd  %r3, __pk_delayed_switch
        
        b       __pk_next_thread_resume

        ## check if we should switch to the wait enabled state (idle)
check_for_idle_thread:
        _lwzsd  %r3, __pk_current_thread
        bwz     %r3, __pk_idle_thread

ctx_pop_with_sprg0:
        ## we must ensure that interrupts are disabled while restoring context
        ##
        ## restore sprg0 from the saved context
        lwz     %r0,    PK_CTX_KERNEL_CTX(%r1)
        mtsprg0 %r0

#if PK_KERNEL_TRACE_ENABLE
        srwi    %r0, %r0, 16
        PK_KERN_TRACE_ASM16("RESUME_CONTEXT(0x%04x)", %r0)
#endif

ctx_pop:
        lwz     %r0,    PK_CTX_GPR0(%r1)
        lvd     %d7,    PK_CTX_SRR0(%r1)
        mtsrr1  %r8
        mtsrr0  %r7
        lvd     %d5,    PK_CTX_XER(%r1)
        mtctr   %r6
        mtxer   %r5
        lvd     %d30,   PK_CTX_GPR30(%r1)
        lvd     %d28,   PK_CTX_GPR28(%r1)
        lvd     %d9,    PK_CTX_GPR9(%r1)
        lvd     %d7,    PK_CTX_GPR7(%r1)
        lvd     %d5,    PK_CTX_GPR5(%r1)
        lvd     %d3,    PK_CTX_CR(%r1)
        mtlr    %r4
        mtcr    %r3
        lvd     %d3,    PK_CTX_GPR3(%r1)
        addi    %r1,    %r1, PK_CTX_SIZE

        rfi
        
/// \endcond
OpenPOWER on IntegriCloud