@@ -19,7 +19,8 @@ struct gicv3_data {
19
19
unsigned int nr_spis ;
20
20
};
21
21
22
- #define sgi_base_from_redist (redist_base ) (redist_base + SZ_64K)
22
+ #define sgi_base_from_redist (redist_base ) (redist_base + SZ_64K)
23
+ #define DIST_BIT (1U << 31)
23
24
24
25
enum gicv3_intid_range {
25
26
SGI_RANGE ,
@@ -50,6 +51,14 @@ static void gicv3_gicr_wait_for_rwp(void *redist_base)
50
51
}
51
52
}
52
53
54
+ static void gicv3_wait_for_rwp (uint32_t cpu_or_dist )
55
+ {
56
+ if (cpu_or_dist & DIST_BIT )
57
+ gicv3_gicd_wait_for_rwp ();
58
+ else
59
+ gicv3_gicr_wait_for_rwp (gicv3_data .redist_base [cpu_or_dist ]);
60
+ }
61
+
53
62
static enum gicv3_intid_range get_intid_range (unsigned int intid )
54
63
{
55
64
switch (intid ) {
@@ -81,39 +90,108 @@ static void gicv3_write_eoir(uint32_t irq)
81
90
isb ();
82
91
}
83
92
84
- static void
85
- gicv3_config_irq (unsigned int intid , unsigned int offset )
93
+ uint32_t gicv3_reg_readl (uint32_t cpu_or_dist , uint64_t offset )
94
+ {
95
+ void * base = cpu_or_dist & DIST_BIT ? gicv3_data .dist_base
96
+ : sgi_base_from_redist (gicv3_data .redist_base [cpu_or_dist ]);
97
+ return readl (base + offset );
98
+ }
99
+
100
+ void gicv3_reg_writel (uint32_t cpu_or_dist , uint64_t offset , uint32_t reg_val )
101
+ {
102
+ void * base = cpu_or_dist & DIST_BIT ? gicv3_data .dist_base
103
+ : sgi_base_from_redist (gicv3_data .redist_base [cpu_or_dist ]);
104
+ writel (reg_val , base + offset );
105
+ }
106
+
107
+ uint32_t gicv3_getl_fields (uint32_t cpu_or_dist , uint64_t offset , uint32_t mask )
108
+ {
109
+ return gicv3_reg_readl (cpu_or_dist , offset ) & mask ;
110
+ }
111
+
112
+ void gicv3_setl_fields (uint32_t cpu_or_dist , uint64_t offset ,
113
+ uint32_t mask , uint32_t reg_val )
114
+ {
115
+ uint32_t tmp = gicv3_reg_readl (cpu_or_dist , offset ) & ~mask ;
116
+
117
+ tmp |= (reg_val & mask );
118
+ gicv3_reg_writel (cpu_or_dist , offset , tmp );
119
+ }
120
+
121
+ /*
122
+ * We use a single offset for the distributor and redistributor maps as they
123
+ * have the same value in both. The only exceptions are registers that only
124
+ * exist in one and not the other, like GICR_WAKER that doesn't exist in the
125
+ * distributor map. Such registers are conveniently marked as reserved in the
126
+ * map that doesn't implement it; like GICR_WAKER's offset of 0x0014 being
127
+ * marked as "Reserved" in the Distributor map.
128
+ */
129
+ static void gicv3_access_reg (uint32_t intid , uint64_t offset ,
130
+ uint32_t reg_bits , uint32_t bits_per_field ,
131
+ bool write , uint32_t * val )
86
132
{
87
133
uint32_t cpu = guest_get_vcpuid ();
88
- uint32_t mask = 1 << (intid % 32 );
89
134
enum gicv3_intid_range intid_range = get_intid_range (intid );
90
- void * reg ;
91
-
92
- /* We care about 'cpu' only for SGIs or PPIs */
93
- if (intid_range == SGI_RANGE || intid_range == PPI_RANGE ) {
94
- GUEST_ASSERT (cpu < gicv3_data .nr_cpus );
95
-
96
- reg = sgi_base_from_redist (gicv3_data .redist_base [cpu ]) +
97
- offset ;
98
- writel (mask , reg );
99
- gicv3_gicr_wait_for_rwp (gicv3_data .redist_base [cpu ]);
100
- } else if (intid_range == SPI_RANGE ) {
101
- reg = gicv3_data .dist_base + offset + (intid / 32 ) * 4 ;
102
- writel (mask , reg );
103
- gicv3_gicd_wait_for_rwp ();
104
- } else {
105
- GUEST_ASSERT (0 );
106
- }
135
+ uint32_t fields_per_reg , index , mask , shift ;
136
+ uint32_t cpu_or_dist ;
137
+
138
+ GUEST_ASSERT (bits_per_field <= reg_bits );
139
+ GUEST_ASSERT (* val < (1U << bits_per_field ));
140
+ /* Some registers like IROUTER are 64 bit long. Those are currently not
141
+ * supported by readl nor writel, so just asserting here until then.
142
+ */
143
+ GUEST_ASSERT (reg_bits == 32 );
144
+
145
+ fields_per_reg = reg_bits / bits_per_field ;
146
+ index = intid % fields_per_reg ;
147
+ shift = index * bits_per_field ;
148
+ mask = ((1U << bits_per_field ) - 1 ) << shift ;
149
+
150
+ /* Set offset to the actual register holding intid's config. */
151
+ offset += (intid / fields_per_reg ) * (reg_bits / 8 );
152
+
153
+ cpu_or_dist = (intid_range == SPI_RANGE ) ? DIST_BIT : cpu ;
154
+
155
+ if (write )
156
+ gicv3_setl_fields (cpu_or_dist , offset , mask , * val << shift );
157
+ * val = gicv3_getl_fields (cpu_or_dist , offset , mask ) >> shift ;
158
+ }
159
+
160
+ static void gicv3_write_reg (uint32_t intid , uint64_t offset ,
161
+ uint32_t reg_bits , uint32_t bits_per_field , uint32_t val )
162
+ {
163
+ gicv3_access_reg (intid , offset , reg_bits ,
164
+ bits_per_field , true, & val );
165
+ }
166
+
167
+ static uint32_t gicv3_read_reg (uint32_t intid , uint64_t offset ,
168
+ uint32_t reg_bits , uint32_t bits_per_field )
169
+ {
170
+ uint32_t val ;
171
+
172
+ gicv3_access_reg (intid , offset , reg_bits ,
173
+ bits_per_field , false, & val );
174
+ return val ;
107
175
}
108
176
109
177
static void gicv3_irq_enable (unsigned int intid )
110
178
{
111
- gicv3_config_irq (intid , GICD_ISENABLER );
179
+ bool is_spi = get_intid_range (intid ) == SPI_RANGE ;
180
+ unsigned int val = 1 ;
181
+ uint32_t cpu = guest_get_vcpuid ();
182
+
183
+ gicv3_write_reg (intid , GICD_ISENABLER , 32 , 1 , val );
184
+ gicv3_wait_for_rwp (is_spi ? DIST_BIT : cpu );
112
185
}
113
186
114
187
static void gicv3_irq_disable (unsigned int intid )
115
188
{
116
- gicv3_config_irq (intid , GICD_ICENABLER );
189
+ bool is_spi = get_intid_range (intid ) == SPI_RANGE ;
190
+ uint32_t val = 1 ;
191
+ uint32_t cpu = guest_get_vcpuid ();
192
+
193
+ gicv3_write_reg (intid , GICD_ICENABLER , 32 , 1 , val );
194
+ gicv3_wait_for_rwp (is_spi ? DIST_BIT : cpu );
117
195
}
118
196
119
197
static void gicv3_enable_redist (void * redist_base )
0 commit comments