1 /*
2 * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <assert.h>
8 #include <stdbool.h>
9
10 #include <lib/cassert.h>
11
12 #include "sdei_private.h"
13
14 /* Aliases for SDEI handler states: 'R'unning, 'E'nabled, and re'G'istered */
15 #define r_ 0U
16 #define R_ (1u << SDEI_STATF_RUNNING)
17
18 #define e_ 0U
19 #define E_ (1u << SDEI_STATF_ENABLED)
20
21 #define g_ 0U
22 #define G_ (1u << SDEI_STATF_REGISTERED)
23
24 /* All possible composite handler states */
25 #define reg_ (r_ | e_ | g_)
26 #define reG_ (r_ | e_ | G_)
27 #define rEg_ (r_ | E_ | g_)
28 #define rEG_ (r_ | E_ | G_)
29 #define Reg_ (R_ | e_ | g_)
30 #define ReG_ (R_ | e_ | G_)
31 #define REg_ (R_ | E_ | g_)
32 #define REG_ (R_ | E_ | G_)
33
34 #define MAX_STATES (REG_ + 1u)
35
36 /* Invalid state */
37 #define SDEI_STATE_INVALID ((sdei_state_t) (-1))
38
39 /* No change in state */
40 #define SDEI_STATE_NOP ((sdei_state_t) (-2))
41
42 #define X___ SDEI_STATE_INVALID
43 #define NOP_ SDEI_STATE_NOP
44
45 /* Ensure special states don't overlap with valid ones */
46 CASSERT(X___ > REG_, sdei_state_overlap_invalid);
47 CASSERT(NOP_ > REG_, sdei_state_overlap_nop);
48
49 /*
50 * SDEI handler state machine: refer to sections 6.1 and 6.1.2 of the SDEI v1.0
51 * specification (ARM DEN0054A).
52 *
53 * Not all calls contribute to handler state transition. This table is also used
54 * to validate whether a call is permissible at a given handler state:
55 *
56 * - X___ denotes a forbidden transition;
57 * - NOP_ denotes a permitted transition, but there's no change in state;
58 * - Otherwise, XXX_ gives the new state.
59 *
60 * DISP[atch] is a transition added for the implementation, but is not mentioned
61 * in the spec.
62 *
63 * Those calls that the spec mentions as can be made any time don't picture in
64 * this table.
65 */
66
67 static const sdei_state_t sdei_state_table[MAX_STATES][DO_MAX] = {
68 /*
69 * Action: REG REL ENA DISA UREG ROUT CTX COMP COMPR DISP
70 * Notes: [3] [1] [3] [3][4] [2]
71 */
72 /* Handler unregistered, disabled, and not running. This is the default state. */
73 /* 0 */ [reg_] = { reG_, NOP_, X___, X___, X___, X___, X___, X___, X___, X___, },
74
75 /* Handler unregistered and running */
76 /* 4 */ [Reg_] = { X___, X___, X___, X___, X___, X___, NOP_, reg_, reg_, X___, },
77
78 /* Handler registered */
79 /* 1 */ [reG_] = { X___, X___, rEG_, NOP_, reg_, NOP_, X___, X___, X___, X___, },
80
81 /* Handler registered and running */
82 /* 5 */ [ReG_] = { X___, X___, REG_, NOP_, Reg_, X___, NOP_, reG_, reG_, X___, },
83
84 /* Handler registered and enabled */
85 /* 3 */ [rEG_] = { X___, X___, NOP_, reG_, reg_, X___, X___, X___, X___, REG_, },
86
87 /* Handler registered, enabled, and running */
88 /* 7 */ [REG_] = { X___, X___, NOP_, ReG_, Reg_, X___, NOP_, rEG_, rEG_, X___, },
89
90 /*
91 * Invalid states: no valid transition would leave the handler in these
92 * states; and no transition from these states is possible either.
93 */
94
95 /*
96 * Handler can't be enabled without being registered. I.e., XEg is
97 * impossible.
98 */
99 /* 2 */ [rEg_] = { X___, X___, X___, X___, X___, X___, X___, X___, X___, X___, },
100 /* 6 */ [REg_] = { X___, X___, X___, X___, X___, X___, X___, X___, X___, X___, },
101 };
102
103 /*
104 * [1] Unregister will always also disable the event, so the new state will have
105 * Xeg.
106 * [2] Event is considered for dispatch only when it's both registered and
107 * enabled.
108 * [3] Never causes change in state.
109 * [4] Only allowed when running.
110 */
111
112 /*
113 * Given an action, transition the state of an event by looking up the state
114 * table above:
115 *
116 * - Return false for invalid transition;
117 * - Return true for valid transition that causes no change in state;
118 * - Otherwise, update state and return true.
119 *
120 * This function assumes that the caller holds necessary locks. If the
121 * transition has constrains other than the state table describes, the caller is
122 * expected to restore the previous state. See sdei_event_register() for
123 * example.
124 */
can_sdei_state_trans(sdei_entry_t * se,sdei_action_t act)125 bool can_sdei_state_trans(sdei_entry_t *se, sdei_action_t act)
126 {
127 sdei_state_t next;
128
129 assert(act < DO_MAX);
130 if (se->state >= MAX_STATES) {
131 WARN(" event state invalid: %x\n", se->state);
132 return false;
133 }
134
135 next = sdei_state_table[se->state][act];
136 switch (next) {
137 case SDEI_STATE_INVALID:
138 return false;
139
140 case SDEI_STATE_NOP:
141 return true;
142
143 default:
144 /* Valid transition. Update state. */
145 SDEI_LOG(" event state 0x%x => 0x%x\n", se->state, next);
146 se->state = next;
147
148 return true;
149 }
150 }
151