isa.hh (13545:dd7ca2be0f2b) isa.hh (13581:b6dcd0183747)
1/*
2 * Copyright (c) 2010, 2012-2018 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2009 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Gabe Black
41 */
42
43#ifndef __ARCH_ARM_ISA_HH__
44#define __ARCH_ARM_ISA_HH__
45
46#include "arch/arm/isa_device.hh"
47#include "arch/arm/miscregs.hh"
48#include "arch/arm/registers.hh"
49#include "arch/arm/system.hh"
50#include "arch/arm/tlb.hh"
51#include "arch/arm/types.hh"
52#include "arch/generic/traits.hh"
53#include "debug/Checkpoint.hh"
54#include "enums/VecRegRenameMode.hh"
55#include "sim/sim_object.hh"
56#include "enums/DecoderFlavour.hh"
57
58struct ArmISAParams;
59struct DummyArmISADeviceParams;
60class ThreadContext;
61class Checkpoint;
62class EventManager;
63
64namespace ArmISA
65{
66 class ISA : public SimObject
67 {
68 protected:
69 // Parent system
70 ArmSystem *system;
71
72 // Micro Architecture
73 const Enums::DecoderFlavour _decoderFlavour;
74 const Enums::VecRegRenameMode _vecRegRenameMode;
75
76 /** Dummy device for to handle non-existing ISA devices */
77 DummyISADevice dummyDevice;
78
79 // PMU belonging to this ISA
80 BaseISADevice *pmu;
81
82 // Generic timer interface belonging to this ISA
83 std::unique_ptr<BaseISADevice> timer;
84
85 // GICv3 CPU interface belonging to this ISA
86 std::unique_ptr<BaseISADevice> gicv3CpuInterface;
87
88 // Cached copies of system-level properties
89 bool highestELIs64;
90 bool haveSecurity;
91 bool haveLPAE;
92 bool haveVirtualization;
93 bool haveCrypto;
94 bool haveLargeAsid64;
95 bool haveGICv3CPUInterface;
96 uint8_t physAddrRange;
97
98 /**
99 * If true, accesses to IMPLEMENTATION DEFINED registers are treated
100 * as NOP hence not causing UNDEFINED INSTRUCTION.
101 */
102 bool impdefAsNop;
103
104 /** MiscReg metadata **/
105 struct MiscRegLUTEntry {
106 uint32_t lower; // Lower half mapped to this register
107 uint32_t upper; // Upper half mapped to this register
108 uint64_t _reset; // value taken on reset (i.e. initialization)
109 uint64_t _res0; // reserved
110 uint64_t _res1; // reserved
111 uint64_t _raz; // read as zero (fixed at 0)
112 uint64_t _rao; // read as one (fixed at 1)
113 public:
114 MiscRegLUTEntry() :
115 lower(0), upper(0),
116 _reset(0), _res0(0), _res1(0), _raz(0), _rao(0) {}
117 uint64_t reset() const { return _reset; }
118 uint64_t res0() const { return _res0; }
119 uint64_t res1() const { return _res1; }
120 uint64_t raz() const { return _raz; }
121 uint64_t rao() const { return _rao; }
122 // raz/rao implies writes ignored
123 uint64_t wi() const { return _raz | _rao; }
124 };
125
126 /** Metadata table accessible via the value of the register */
127 static std::vector<struct MiscRegLUTEntry> lookUpMiscReg;
128
129 class MiscRegLUTEntryInitializer {
130 struct MiscRegLUTEntry &entry;
131 std::bitset<NUM_MISCREG_INFOS> &info;
132 typedef const MiscRegLUTEntryInitializer& chain;
133 public:
134 chain mapsTo(uint32_t l, uint32_t u = 0) const {
135 entry.lower = l;
136 entry.upper = u;
137 return *this;
138 }
139 chain res0(uint64_t mask) const {
140 entry._res0 = mask;
141 return *this;
142 }
143 chain res1(uint64_t mask) const {
144 entry._res1 = mask;
145 return *this;
146 }
147 chain raz(uint64_t mask) const {
148 entry._raz = mask;
149 return *this;
150 }
151 chain rao(uint64_t mask) const {
152 entry._rao = mask;
153 return *this;
154 }
155 chain implemented(bool v = true) const {
156 info[MISCREG_IMPLEMENTED] = v;
157 return *this;
158 }
159 chain unimplemented() const {
160 return implemented(false);
161 }
162 chain unverifiable(bool v = true) const {
163 info[MISCREG_UNVERIFIABLE] = v;
164 return *this;
165 }
166 chain warnNotFail(bool v = true) const {
167 info[MISCREG_WARN_NOT_FAIL] = v;
168 return *this;
169 }
170 chain mutex(bool v = true) const {
171 info[MISCREG_MUTEX] = v;
172 return *this;
173 }
174 chain banked(bool v = true) const {
175 info[MISCREG_BANKED] = v;
176 return *this;
177 }
178 chain bankedChild(bool v = true) const {
179 info[MISCREG_BANKED_CHILD] = v;
180 return *this;
181 }
182 chain userNonSecureRead(bool v = true) const {
183 info[MISCREG_USR_NS_RD] = v;
184 return *this;
185 }
186 chain userNonSecureWrite(bool v = true) const {
187 info[MISCREG_USR_NS_WR] = v;
188 return *this;
189 }
190 chain userSecureRead(bool v = true) const {
191 info[MISCREG_USR_S_RD] = v;
192 return *this;
193 }
194 chain userSecureWrite(bool v = true) const {
195 info[MISCREG_USR_S_WR] = v;
196 return *this;
197 }
198 chain user(bool v = true) const {
199 userNonSecureRead(v);
200 userNonSecureWrite(v);
201 userSecureRead(v);
202 userSecureWrite(v);
203 return *this;
204 }
205 chain privNonSecureRead(bool v = true) const {
206 info[MISCREG_PRI_NS_RD] = v;
207 return *this;
208 }
209 chain privNonSecureWrite(bool v = true) const {
210 info[MISCREG_PRI_NS_WR] = v;
211 return *this;
212 }
213 chain privNonSecure(bool v = true) const {
214 privNonSecureRead(v);
215 privNonSecureWrite(v);
216 return *this;
217 }
218 chain privSecureRead(bool v = true) const {
219 info[MISCREG_PRI_S_RD] = v;
220 return *this;
221 }
222 chain privSecureWrite(bool v = true) const {
223 info[MISCREG_PRI_S_WR] = v;
224 return *this;
225 }
226 chain privSecure(bool v = true) const {
227 privSecureRead(v);
228 privSecureWrite(v);
229 return *this;
230 }
231 chain priv(bool v = true) const {
232 privSecure(v);
233 privNonSecure(v);
234 return *this;
235 }
236 chain privRead(bool v = true) const {
237 privSecureRead(v);
238 privNonSecureRead(v);
239 return *this;
240 }
241 chain hypRead(bool v = true) const {
242 info[MISCREG_HYP_RD] = v;
243 return *this;
244 }
245 chain hypWrite(bool v = true) const {
246 info[MISCREG_HYP_WR] = v;
247 return *this;
248 }
249 chain hyp(bool v = true) const {
250 hypRead(v);
251 hypWrite(v);
252 return *this;
253 }
254 chain monSecureRead(bool v = true) const {
255 info[MISCREG_MON_NS0_RD] = v;
256 return *this;
257 }
258 chain monSecureWrite(bool v = true) const {
259 info[MISCREG_MON_NS0_WR] = v;
260 return *this;
261 }
262 chain monNonSecureRead(bool v = true) const {
263 info[MISCREG_MON_NS1_RD] = v;
264 return *this;
265 }
266 chain monNonSecureWrite(bool v = true) const {
267 info[MISCREG_MON_NS1_WR] = v;
268 return *this;
269 }
270 chain mon(bool v = true) const {
271 monSecureRead(v);
272 monSecureWrite(v);
273 monNonSecureRead(v);
274 monNonSecureWrite(v);
275 return *this;
276 }
277 chain monSecure(bool v = true) const {
278 monSecureRead(v);
279 monSecureWrite(v);
280 return *this;
281 }
282 chain monNonSecure(bool v = true) const {
283 monNonSecureRead(v);
284 monNonSecureWrite(v);
285 return *this;
286 }
287 chain allPrivileges(bool v = true) const {
288 userNonSecureRead(v);
289 userNonSecureWrite(v);
290 userSecureRead(v);
291 userSecureWrite(v);
292 privNonSecureRead(v);
293 privNonSecureWrite(v);
294 privSecureRead(v);
295 privSecureWrite(v);
296 hypRead(v);
297 hypWrite(v);
298 monSecureRead(v);
299 monSecureWrite(v);
300 monNonSecureRead(v);
301 monNonSecureWrite(v);
302 return *this;
303 }
304 chain nonSecure(bool v = true) const {
305 userNonSecureRead(v);
306 userNonSecureWrite(v);
307 privNonSecureRead(v);
308 privNonSecureWrite(v);
309 hypRead(v);
310 hypWrite(v);
311 monNonSecureRead(v);
312 monNonSecureWrite(v);
313 return *this;
314 }
315 chain secure(bool v = true) const {
316 userSecureRead(v);
317 userSecureWrite(v);
318 privSecureRead(v);
319 privSecureWrite(v);
320 monSecureRead(v);
321 monSecureWrite(v);
322 return *this;
323 }
324 chain reads(bool v) const {
325 userNonSecureRead(v);
326 userSecureRead(v);
327 privNonSecureRead(v);
328 privSecureRead(v);
329 hypRead(v);
330 monSecureRead(v);
331 monNonSecureRead(v);
332 return *this;
333 }
334 chain writes(bool v) const {
335 userNonSecureWrite(v);
336 userSecureWrite(v);
337 privNonSecureWrite(v);
338 privSecureWrite(v);
339 hypWrite(v);
340 monSecureWrite(v);
341 monNonSecureWrite(v);
342 return *this;
343 }
344 chain exceptUserMode() const {
345 user(0);
346 return *this;
347 }
348 MiscRegLUTEntryInitializer(struct MiscRegLUTEntry &e,
349 std::bitset<NUM_MISCREG_INFOS> &i)
350 : entry(e),
351 info(i)
352 {
353 // force unimplemented registers to be thusly declared
354 implemented(1);
355 }
356 };
357
358 const MiscRegLUTEntryInitializer InitReg(uint32_t reg) {
359 return MiscRegLUTEntryInitializer(lookUpMiscReg[reg],
360 miscRegInfo[reg]);
361 }
362
363 void initializeMiscRegMetadata();
364
1/*
2 * Copyright (c) 2010, 2012-2018 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2009 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Gabe Black
41 */
42
43#ifndef __ARCH_ARM_ISA_HH__
44#define __ARCH_ARM_ISA_HH__
45
46#include "arch/arm/isa_device.hh"
47#include "arch/arm/miscregs.hh"
48#include "arch/arm/registers.hh"
49#include "arch/arm/system.hh"
50#include "arch/arm/tlb.hh"
51#include "arch/arm/types.hh"
52#include "arch/generic/traits.hh"
53#include "debug/Checkpoint.hh"
54#include "enums/VecRegRenameMode.hh"
55#include "sim/sim_object.hh"
56#include "enums/DecoderFlavour.hh"
57
58struct ArmISAParams;
59struct DummyArmISADeviceParams;
60class ThreadContext;
61class Checkpoint;
62class EventManager;
63
64namespace ArmISA
65{
66 class ISA : public SimObject
67 {
68 protected:
69 // Parent system
70 ArmSystem *system;
71
72 // Micro Architecture
73 const Enums::DecoderFlavour _decoderFlavour;
74 const Enums::VecRegRenameMode _vecRegRenameMode;
75
76 /** Dummy device for to handle non-existing ISA devices */
77 DummyISADevice dummyDevice;
78
79 // PMU belonging to this ISA
80 BaseISADevice *pmu;
81
82 // Generic timer interface belonging to this ISA
83 std::unique_ptr<BaseISADevice> timer;
84
85 // GICv3 CPU interface belonging to this ISA
86 std::unique_ptr<BaseISADevice> gicv3CpuInterface;
87
88 // Cached copies of system-level properties
89 bool highestELIs64;
90 bool haveSecurity;
91 bool haveLPAE;
92 bool haveVirtualization;
93 bool haveCrypto;
94 bool haveLargeAsid64;
95 bool haveGICv3CPUInterface;
96 uint8_t physAddrRange;
97
98 /**
99 * If true, accesses to IMPLEMENTATION DEFINED registers are treated
100 * as NOP hence not causing UNDEFINED INSTRUCTION.
101 */
102 bool impdefAsNop;
103
104 /** MiscReg metadata **/
105 struct MiscRegLUTEntry {
106 uint32_t lower; // Lower half mapped to this register
107 uint32_t upper; // Upper half mapped to this register
108 uint64_t _reset; // value taken on reset (i.e. initialization)
109 uint64_t _res0; // reserved
110 uint64_t _res1; // reserved
111 uint64_t _raz; // read as zero (fixed at 0)
112 uint64_t _rao; // read as one (fixed at 1)
113 public:
114 MiscRegLUTEntry() :
115 lower(0), upper(0),
116 _reset(0), _res0(0), _res1(0), _raz(0), _rao(0) {}
117 uint64_t reset() const { return _reset; }
118 uint64_t res0() const { return _res0; }
119 uint64_t res1() const { return _res1; }
120 uint64_t raz() const { return _raz; }
121 uint64_t rao() const { return _rao; }
122 // raz/rao implies writes ignored
123 uint64_t wi() const { return _raz | _rao; }
124 };
125
126 /** Metadata table accessible via the value of the register */
127 static std::vector<struct MiscRegLUTEntry> lookUpMiscReg;
128
129 class MiscRegLUTEntryInitializer {
130 struct MiscRegLUTEntry &entry;
131 std::bitset<NUM_MISCREG_INFOS> &info;
132 typedef const MiscRegLUTEntryInitializer& chain;
133 public:
134 chain mapsTo(uint32_t l, uint32_t u = 0) const {
135 entry.lower = l;
136 entry.upper = u;
137 return *this;
138 }
139 chain res0(uint64_t mask) const {
140 entry._res0 = mask;
141 return *this;
142 }
143 chain res1(uint64_t mask) const {
144 entry._res1 = mask;
145 return *this;
146 }
147 chain raz(uint64_t mask) const {
148 entry._raz = mask;
149 return *this;
150 }
151 chain rao(uint64_t mask) const {
152 entry._rao = mask;
153 return *this;
154 }
155 chain implemented(bool v = true) const {
156 info[MISCREG_IMPLEMENTED] = v;
157 return *this;
158 }
159 chain unimplemented() const {
160 return implemented(false);
161 }
162 chain unverifiable(bool v = true) const {
163 info[MISCREG_UNVERIFIABLE] = v;
164 return *this;
165 }
166 chain warnNotFail(bool v = true) const {
167 info[MISCREG_WARN_NOT_FAIL] = v;
168 return *this;
169 }
170 chain mutex(bool v = true) const {
171 info[MISCREG_MUTEX] = v;
172 return *this;
173 }
174 chain banked(bool v = true) const {
175 info[MISCREG_BANKED] = v;
176 return *this;
177 }
178 chain bankedChild(bool v = true) const {
179 info[MISCREG_BANKED_CHILD] = v;
180 return *this;
181 }
182 chain userNonSecureRead(bool v = true) const {
183 info[MISCREG_USR_NS_RD] = v;
184 return *this;
185 }
186 chain userNonSecureWrite(bool v = true) const {
187 info[MISCREG_USR_NS_WR] = v;
188 return *this;
189 }
190 chain userSecureRead(bool v = true) const {
191 info[MISCREG_USR_S_RD] = v;
192 return *this;
193 }
194 chain userSecureWrite(bool v = true) const {
195 info[MISCREG_USR_S_WR] = v;
196 return *this;
197 }
198 chain user(bool v = true) const {
199 userNonSecureRead(v);
200 userNonSecureWrite(v);
201 userSecureRead(v);
202 userSecureWrite(v);
203 return *this;
204 }
205 chain privNonSecureRead(bool v = true) const {
206 info[MISCREG_PRI_NS_RD] = v;
207 return *this;
208 }
209 chain privNonSecureWrite(bool v = true) const {
210 info[MISCREG_PRI_NS_WR] = v;
211 return *this;
212 }
213 chain privNonSecure(bool v = true) const {
214 privNonSecureRead(v);
215 privNonSecureWrite(v);
216 return *this;
217 }
218 chain privSecureRead(bool v = true) const {
219 info[MISCREG_PRI_S_RD] = v;
220 return *this;
221 }
222 chain privSecureWrite(bool v = true) const {
223 info[MISCREG_PRI_S_WR] = v;
224 return *this;
225 }
226 chain privSecure(bool v = true) const {
227 privSecureRead(v);
228 privSecureWrite(v);
229 return *this;
230 }
231 chain priv(bool v = true) const {
232 privSecure(v);
233 privNonSecure(v);
234 return *this;
235 }
236 chain privRead(bool v = true) const {
237 privSecureRead(v);
238 privNonSecureRead(v);
239 return *this;
240 }
241 chain hypRead(bool v = true) const {
242 info[MISCREG_HYP_RD] = v;
243 return *this;
244 }
245 chain hypWrite(bool v = true) const {
246 info[MISCREG_HYP_WR] = v;
247 return *this;
248 }
249 chain hyp(bool v = true) const {
250 hypRead(v);
251 hypWrite(v);
252 return *this;
253 }
254 chain monSecureRead(bool v = true) const {
255 info[MISCREG_MON_NS0_RD] = v;
256 return *this;
257 }
258 chain monSecureWrite(bool v = true) const {
259 info[MISCREG_MON_NS0_WR] = v;
260 return *this;
261 }
262 chain monNonSecureRead(bool v = true) const {
263 info[MISCREG_MON_NS1_RD] = v;
264 return *this;
265 }
266 chain monNonSecureWrite(bool v = true) const {
267 info[MISCREG_MON_NS1_WR] = v;
268 return *this;
269 }
270 chain mon(bool v = true) const {
271 monSecureRead(v);
272 monSecureWrite(v);
273 monNonSecureRead(v);
274 monNonSecureWrite(v);
275 return *this;
276 }
277 chain monSecure(bool v = true) const {
278 monSecureRead(v);
279 monSecureWrite(v);
280 return *this;
281 }
282 chain monNonSecure(bool v = true) const {
283 monNonSecureRead(v);
284 monNonSecureWrite(v);
285 return *this;
286 }
287 chain allPrivileges(bool v = true) const {
288 userNonSecureRead(v);
289 userNonSecureWrite(v);
290 userSecureRead(v);
291 userSecureWrite(v);
292 privNonSecureRead(v);
293 privNonSecureWrite(v);
294 privSecureRead(v);
295 privSecureWrite(v);
296 hypRead(v);
297 hypWrite(v);
298 monSecureRead(v);
299 monSecureWrite(v);
300 monNonSecureRead(v);
301 monNonSecureWrite(v);
302 return *this;
303 }
304 chain nonSecure(bool v = true) const {
305 userNonSecureRead(v);
306 userNonSecureWrite(v);
307 privNonSecureRead(v);
308 privNonSecureWrite(v);
309 hypRead(v);
310 hypWrite(v);
311 monNonSecureRead(v);
312 monNonSecureWrite(v);
313 return *this;
314 }
315 chain secure(bool v = true) const {
316 userSecureRead(v);
317 userSecureWrite(v);
318 privSecureRead(v);
319 privSecureWrite(v);
320 monSecureRead(v);
321 monSecureWrite(v);
322 return *this;
323 }
324 chain reads(bool v) const {
325 userNonSecureRead(v);
326 userSecureRead(v);
327 privNonSecureRead(v);
328 privSecureRead(v);
329 hypRead(v);
330 monSecureRead(v);
331 monNonSecureRead(v);
332 return *this;
333 }
334 chain writes(bool v) const {
335 userNonSecureWrite(v);
336 userSecureWrite(v);
337 privNonSecureWrite(v);
338 privSecureWrite(v);
339 hypWrite(v);
340 monSecureWrite(v);
341 monNonSecureWrite(v);
342 return *this;
343 }
344 chain exceptUserMode() const {
345 user(0);
346 return *this;
347 }
348 MiscRegLUTEntryInitializer(struct MiscRegLUTEntry &e,
349 std::bitset<NUM_MISCREG_INFOS> &i)
350 : entry(e),
351 info(i)
352 {
353 // force unimplemented registers to be thusly declared
354 implemented(1);
355 }
356 };
357
358 const MiscRegLUTEntryInitializer InitReg(uint32_t reg) {
359 return MiscRegLUTEntryInitializer(lookUpMiscReg[reg],
360 miscRegInfo[reg]);
361 }
362
363 void initializeMiscRegMetadata();
364
365 MiscReg miscRegs[NumMiscRegs];
365 RegVal miscRegs[NumMiscRegs];
366 const IntRegIndex *intRegMap;
367
368 void
369 updateRegMap(CPSR cpsr)
370 {
371 if (cpsr.width == 0) {
372 intRegMap = IntReg64Map;
373 } else {
374 switch (cpsr.mode) {
375 case MODE_USER:
376 case MODE_SYSTEM:
377 intRegMap = IntRegUsrMap;
378 break;
379 case MODE_FIQ:
380 intRegMap = IntRegFiqMap;
381 break;
382 case MODE_IRQ:
383 intRegMap = IntRegIrqMap;
384 break;
385 case MODE_SVC:
386 intRegMap = IntRegSvcMap;
387 break;
388 case MODE_MON:
389 intRegMap = IntRegMonMap;
390 break;
391 case MODE_ABORT:
392 intRegMap = IntRegAbtMap;
393 break;
394 case MODE_HYP:
395 intRegMap = IntRegHypMap;
396 break;
397 case MODE_UNDEFINED:
398 intRegMap = IntRegUndMap;
399 break;
400 default:
401 panic("Unrecognized mode setting in CPSR.\n");
402 }
403 }
404 }
405
406 BaseISADevice &getGenericTimer(ThreadContext *tc);
407 BaseISADevice &getGICv3CPUInterface(ThreadContext *tc);
408
409
410 private:
411 inline void assert32(ThreadContext *tc) {
412 CPSR cpsr M5_VAR_USED = readMiscReg(MISCREG_CPSR, tc);
413 assert(cpsr.width);
414 }
415
416 inline void assert64(ThreadContext *tc) {
417 CPSR cpsr M5_VAR_USED = readMiscReg(MISCREG_CPSR, tc);
418 assert(!cpsr.width);
419 }
420
421 public:
422 void clear();
423
424 protected:
425 void clear32(const ArmISAParams *p, const SCTLR &sctlr_rst);
426 void clear64(const ArmISAParams *p);
427 void initID32(const ArmISAParams *p);
428 void initID64(const ArmISAParams *p);
429
430 public:
366 const IntRegIndex *intRegMap;
367
368 void
369 updateRegMap(CPSR cpsr)
370 {
371 if (cpsr.width == 0) {
372 intRegMap = IntReg64Map;
373 } else {
374 switch (cpsr.mode) {
375 case MODE_USER:
376 case MODE_SYSTEM:
377 intRegMap = IntRegUsrMap;
378 break;
379 case MODE_FIQ:
380 intRegMap = IntRegFiqMap;
381 break;
382 case MODE_IRQ:
383 intRegMap = IntRegIrqMap;
384 break;
385 case MODE_SVC:
386 intRegMap = IntRegSvcMap;
387 break;
388 case MODE_MON:
389 intRegMap = IntRegMonMap;
390 break;
391 case MODE_ABORT:
392 intRegMap = IntRegAbtMap;
393 break;
394 case MODE_HYP:
395 intRegMap = IntRegHypMap;
396 break;
397 case MODE_UNDEFINED:
398 intRegMap = IntRegUndMap;
399 break;
400 default:
401 panic("Unrecognized mode setting in CPSR.\n");
402 }
403 }
404 }
405
406 BaseISADevice &getGenericTimer(ThreadContext *tc);
407 BaseISADevice &getGICv3CPUInterface(ThreadContext *tc);
408
409
410 private:
411 inline void assert32(ThreadContext *tc) {
412 CPSR cpsr M5_VAR_USED = readMiscReg(MISCREG_CPSR, tc);
413 assert(cpsr.width);
414 }
415
416 inline void assert64(ThreadContext *tc) {
417 CPSR cpsr M5_VAR_USED = readMiscReg(MISCREG_CPSR, tc);
418 assert(!cpsr.width);
419 }
420
421 public:
422 void clear();
423
424 protected:
425 void clear32(const ArmISAParams *p, const SCTLR &sctlr_rst);
426 void clear64(const ArmISAParams *p);
427 void initID32(const ArmISAParams *p);
428 void initID64(const ArmISAParams *p);
429
430 public:
431 MiscReg readMiscRegNoEffect(int misc_reg) const;
432 MiscReg readMiscReg(int misc_reg, ThreadContext *tc);
433 void setMiscRegNoEffect(int misc_reg, const MiscReg &val);
434 void setMiscReg(int misc_reg, const MiscReg &val, ThreadContext *tc);
431 RegVal readMiscRegNoEffect(int misc_reg) const;
432 RegVal readMiscReg(int misc_reg, ThreadContext *tc);
433 void setMiscRegNoEffect(int misc_reg, const RegVal &val);
434 void setMiscReg(int misc_reg, const RegVal &val, ThreadContext *tc);
435
436 RegId
437 flattenRegId(const RegId& regId) const
438 {
439 switch (regId.classValue()) {
440 case IntRegClass:
441 return RegId(IntRegClass, flattenIntIndex(regId.index()));
442 case FloatRegClass:
443 return RegId(FloatRegClass, flattenFloatIndex(regId.index()));
444 case VecRegClass:
445 return RegId(VecRegClass, flattenVecIndex(regId.index()));
446 case VecElemClass:
447 return RegId(VecElemClass, flattenVecElemIndex(regId.index()),
448 regId.elemIndex());
449 case CCRegClass:
450 return RegId(CCRegClass, flattenCCIndex(regId.index()));
451 case MiscRegClass:
452 return RegId(MiscRegClass, flattenMiscIndex(regId.index()));
453 }
454 return RegId();
455 }
456
457 int
458 flattenIntIndex(int reg) const
459 {
460 assert(reg >= 0);
461 if (reg < NUM_ARCH_INTREGS) {
462 return intRegMap[reg];
463 } else if (reg < NUM_INTREGS) {
464 return reg;
465 } else if (reg == INTREG_SPX) {
466 CPSR cpsr = miscRegs[MISCREG_CPSR];
467 ExceptionLevel el = opModeToEL(
468 (OperatingMode) (uint8_t) cpsr.mode);
469 if (!cpsr.sp && el != EL0)
470 return INTREG_SP0;
471 switch (el) {
472 case EL3:
473 return INTREG_SP3;
474 case EL2:
475 return INTREG_SP2;
476 case EL1:
477 return INTREG_SP1;
478 case EL0:
479 return INTREG_SP0;
480 default:
481 panic("Invalid exception level");
482 return 0; // Never happens.
483 }
484 } else {
485 return flattenIntRegModeIndex(reg);
486 }
487 }
488
489 int
490 flattenFloatIndex(int reg) const
491 {
492 assert(reg >= 0);
493 return reg;
494 }
495
496 int
497 flattenVecIndex(int reg) const
498 {
499 assert(reg >= 0);
500 return reg;
501 }
502
503 int
504 flattenVecElemIndex(int reg) const
505 {
506 assert(reg >= 0);
507 return reg;
508 }
509
510 int
511 flattenCCIndex(int reg) const
512 {
513 assert(reg >= 0);
514 return reg;
515 }
516
517 int
518 flattenMiscIndex(int reg) const
519 {
520 assert(reg >= 0);
521 int flat_idx = reg;
522
523 if (reg == MISCREG_SPSR) {
524 CPSR cpsr = miscRegs[MISCREG_CPSR];
525 switch (cpsr.mode) {
526 case MODE_EL0T:
527 warn("User mode does not have SPSR\n");
528 flat_idx = MISCREG_SPSR;
529 break;
530 case MODE_EL1T:
531 case MODE_EL1H:
532 flat_idx = MISCREG_SPSR_EL1;
533 break;
534 case MODE_EL2T:
535 case MODE_EL2H:
536 flat_idx = MISCREG_SPSR_EL2;
537 break;
538 case MODE_EL3T:
539 case MODE_EL3H:
540 flat_idx = MISCREG_SPSR_EL3;
541 break;
542 case MODE_USER:
543 warn("User mode does not have SPSR\n");
544 flat_idx = MISCREG_SPSR;
545 break;
546 case MODE_FIQ:
547 flat_idx = MISCREG_SPSR_FIQ;
548 break;
549 case MODE_IRQ:
550 flat_idx = MISCREG_SPSR_IRQ;
551 break;
552 case MODE_SVC:
553 flat_idx = MISCREG_SPSR_SVC;
554 break;
555 case MODE_MON:
556 flat_idx = MISCREG_SPSR_MON;
557 break;
558 case MODE_ABORT:
559 flat_idx = MISCREG_SPSR_ABT;
560 break;
561 case MODE_HYP:
562 flat_idx = MISCREG_SPSR_HYP;
563 break;
564 case MODE_UNDEFINED:
565 flat_idx = MISCREG_SPSR_UND;
566 break;
567 default:
568 warn("Trying to access SPSR in an invalid mode: %d\n",
569 cpsr.mode);
570 flat_idx = MISCREG_SPSR;
571 break;
572 }
573 } else if (miscRegInfo[reg][MISCREG_MUTEX]) {
574 // Mutually exclusive CP15 register
575 switch (reg) {
576 case MISCREG_PRRR_MAIR0:
577 case MISCREG_PRRR_MAIR0_NS:
578 case MISCREG_PRRR_MAIR0_S:
579 {
580 TTBCR ttbcr = readMiscRegNoEffect(MISCREG_TTBCR);
581 // If the muxed reg has been flattened, work out the
582 // offset and apply it to the unmuxed reg
583 int idxOffset = reg - MISCREG_PRRR_MAIR0;
584 if (ttbcr.eae)
585 flat_idx = flattenMiscIndex(MISCREG_MAIR0 +
586 idxOffset);
587 else
588 flat_idx = flattenMiscIndex(MISCREG_PRRR +
589 idxOffset);
590 }
591 break;
592 case MISCREG_NMRR_MAIR1:
593 case MISCREG_NMRR_MAIR1_NS:
594 case MISCREG_NMRR_MAIR1_S:
595 {
596 TTBCR ttbcr = readMiscRegNoEffect(MISCREG_TTBCR);
597 // If the muxed reg has been flattened, work out the
598 // offset and apply it to the unmuxed reg
599 int idxOffset = reg - MISCREG_NMRR_MAIR1;
600 if (ttbcr.eae)
601 flat_idx = flattenMiscIndex(MISCREG_MAIR1 +
602 idxOffset);
603 else
604 flat_idx = flattenMiscIndex(MISCREG_NMRR +
605 idxOffset);
606 }
607 break;
608 case MISCREG_PMXEVTYPER_PMCCFILTR:
609 {
610 PMSELR pmselr = miscRegs[MISCREG_PMSELR];
611 if (pmselr.sel == 31)
612 flat_idx = flattenMiscIndex(MISCREG_PMCCFILTR);
613 else
614 flat_idx = flattenMiscIndex(MISCREG_PMXEVTYPER);
615 }
616 break;
617 default:
618 panic("Unrecognized misc. register.\n");
619 break;
620 }
621 } else {
622 if (miscRegInfo[reg][MISCREG_BANKED]) {
623 bool secureReg = haveSecurity && !highestELIs64 &&
624 inSecureState(miscRegs[MISCREG_SCR],
625 miscRegs[MISCREG_CPSR]);
626 flat_idx += secureReg ? 2 : 1;
627 }
628 }
629 return flat_idx;
630 }
631
632 std::pair<int,int> getMiscIndices(int misc_reg) const
633 {
634 // Note: indexes of AArch64 registers are left unchanged
635 int flat_idx = flattenMiscIndex(misc_reg);
636
637 if (lookUpMiscReg[flat_idx].lower == 0) {
638 return std::make_pair(flat_idx, 0);
639 }
640
641 // do additional S/NS flattenings if mapped to NS while in S
642 bool S = haveSecurity && !highestELIs64 &&
643 inSecureState(miscRegs[MISCREG_SCR],
644 miscRegs[MISCREG_CPSR]);
645 int lower = lookUpMiscReg[flat_idx].lower;
646 int upper = lookUpMiscReg[flat_idx].upper;
647 // upper == 0, which is CPSR, is not MISCREG_BANKED_CHILD (no-op)
648 lower += S && miscRegInfo[lower][MISCREG_BANKED_CHILD];
649 upper += S && miscRegInfo[upper][MISCREG_BANKED_CHILD];
650 return std::make_pair(lower, upper);
651 }
652
653 void serialize(CheckpointOut &cp) const
654 {
655 DPRINTF(Checkpoint, "Serializing Arm Misc Registers\n");
656 SERIALIZE_ARRAY(miscRegs, NUM_PHYS_MISCREGS);
657
658 SERIALIZE_SCALAR(highestELIs64);
659 SERIALIZE_SCALAR(haveSecurity);
660 SERIALIZE_SCALAR(haveLPAE);
661 SERIALIZE_SCALAR(haveVirtualization);
662 SERIALIZE_SCALAR(haveLargeAsid64);
663 SERIALIZE_SCALAR(physAddrRange);
664 }
665 void unserialize(CheckpointIn &cp)
666 {
667 DPRINTF(Checkpoint, "Unserializing Arm Misc Registers\n");
668 UNSERIALIZE_ARRAY(miscRegs, NUM_PHYS_MISCREGS);
669 CPSR tmp_cpsr = miscRegs[MISCREG_CPSR];
670 updateRegMap(tmp_cpsr);
671
672 UNSERIALIZE_SCALAR(highestELIs64);
673 UNSERIALIZE_SCALAR(haveSecurity);
674 UNSERIALIZE_SCALAR(haveLPAE);
675 UNSERIALIZE_SCALAR(haveVirtualization);
676 UNSERIALIZE_SCALAR(haveLargeAsid64);
677 UNSERIALIZE_SCALAR(physAddrRange);
678 }
679
680 void startup(ThreadContext *tc);
681
682 Enums::DecoderFlavour decoderFlavour() const { return _decoderFlavour; }
683
684 Enums::VecRegRenameMode
685 vecRegRenameMode() const
686 {
687 return _vecRegRenameMode;
688 }
689
690 /// Explicitly import the otherwise hidden startup
691 using SimObject::startup;
692
693 typedef ArmISAParams Params;
694
695 const Params *params() const;
696
697 ISA(Params *p);
698 };
699}
700
701template<>
702struct initRenameMode<ArmISA::ISA>
703{
704 static Enums::VecRegRenameMode mode(const ArmISA::ISA* isa)
705 {
706 return isa->vecRegRenameMode();
707 }
708 static bool equals(const ArmISA::ISA* isa1, const ArmISA::ISA* isa2)
709 {
710 return mode(isa1) == mode(isa2);
711 }
712};
713
714#endif
435
436 RegId
437 flattenRegId(const RegId& regId) const
438 {
439 switch (regId.classValue()) {
440 case IntRegClass:
441 return RegId(IntRegClass, flattenIntIndex(regId.index()));
442 case FloatRegClass:
443 return RegId(FloatRegClass, flattenFloatIndex(regId.index()));
444 case VecRegClass:
445 return RegId(VecRegClass, flattenVecIndex(regId.index()));
446 case VecElemClass:
447 return RegId(VecElemClass, flattenVecElemIndex(regId.index()),
448 regId.elemIndex());
449 case CCRegClass:
450 return RegId(CCRegClass, flattenCCIndex(regId.index()));
451 case MiscRegClass:
452 return RegId(MiscRegClass, flattenMiscIndex(regId.index()));
453 }
454 return RegId();
455 }
456
457 int
458 flattenIntIndex(int reg) const
459 {
460 assert(reg >= 0);
461 if (reg < NUM_ARCH_INTREGS) {
462 return intRegMap[reg];
463 } else if (reg < NUM_INTREGS) {
464 return reg;
465 } else if (reg == INTREG_SPX) {
466 CPSR cpsr = miscRegs[MISCREG_CPSR];
467 ExceptionLevel el = opModeToEL(
468 (OperatingMode) (uint8_t) cpsr.mode);
469 if (!cpsr.sp && el != EL0)
470 return INTREG_SP0;
471 switch (el) {
472 case EL3:
473 return INTREG_SP3;
474 case EL2:
475 return INTREG_SP2;
476 case EL1:
477 return INTREG_SP1;
478 case EL0:
479 return INTREG_SP0;
480 default:
481 panic("Invalid exception level");
482 return 0; // Never happens.
483 }
484 } else {
485 return flattenIntRegModeIndex(reg);
486 }
487 }
488
489 int
490 flattenFloatIndex(int reg) const
491 {
492 assert(reg >= 0);
493 return reg;
494 }
495
496 int
497 flattenVecIndex(int reg) const
498 {
499 assert(reg >= 0);
500 return reg;
501 }
502
503 int
504 flattenVecElemIndex(int reg) const
505 {
506 assert(reg >= 0);
507 return reg;
508 }
509
510 int
511 flattenCCIndex(int reg) const
512 {
513 assert(reg >= 0);
514 return reg;
515 }
516
517 int
518 flattenMiscIndex(int reg) const
519 {
520 assert(reg >= 0);
521 int flat_idx = reg;
522
523 if (reg == MISCREG_SPSR) {
524 CPSR cpsr = miscRegs[MISCREG_CPSR];
525 switch (cpsr.mode) {
526 case MODE_EL0T:
527 warn("User mode does not have SPSR\n");
528 flat_idx = MISCREG_SPSR;
529 break;
530 case MODE_EL1T:
531 case MODE_EL1H:
532 flat_idx = MISCREG_SPSR_EL1;
533 break;
534 case MODE_EL2T:
535 case MODE_EL2H:
536 flat_idx = MISCREG_SPSR_EL2;
537 break;
538 case MODE_EL3T:
539 case MODE_EL3H:
540 flat_idx = MISCREG_SPSR_EL3;
541 break;
542 case MODE_USER:
543 warn("User mode does not have SPSR\n");
544 flat_idx = MISCREG_SPSR;
545 break;
546 case MODE_FIQ:
547 flat_idx = MISCREG_SPSR_FIQ;
548 break;
549 case MODE_IRQ:
550 flat_idx = MISCREG_SPSR_IRQ;
551 break;
552 case MODE_SVC:
553 flat_idx = MISCREG_SPSR_SVC;
554 break;
555 case MODE_MON:
556 flat_idx = MISCREG_SPSR_MON;
557 break;
558 case MODE_ABORT:
559 flat_idx = MISCREG_SPSR_ABT;
560 break;
561 case MODE_HYP:
562 flat_idx = MISCREG_SPSR_HYP;
563 break;
564 case MODE_UNDEFINED:
565 flat_idx = MISCREG_SPSR_UND;
566 break;
567 default:
568 warn("Trying to access SPSR in an invalid mode: %d\n",
569 cpsr.mode);
570 flat_idx = MISCREG_SPSR;
571 break;
572 }
573 } else if (miscRegInfo[reg][MISCREG_MUTEX]) {
574 // Mutually exclusive CP15 register
575 switch (reg) {
576 case MISCREG_PRRR_MAIR0:
577 case MISCREG_PRRR_MAIR0_NS:
578 case MISCREG_PRRR_MAIR0_S:
579 {
580 TTBCR ttbcr = readMiscRegNoEffect(MISCREG_TTBCR);
581 // If the muxed reg has been flattened, work out the
582 // offset and apply it to the unmuxed reg
583 int idxOffset = reg - MISCREG_PRRR_MAIR0;
584 if (ttbcr.eae)
585 flat_idx = flattenMiscIndex(MISCREG_MAIR0 +
586 idxOffset);
587 else
588 flat_idx = flattenMiscIndex(MISCREG_PRRR +
589 idxOffset);
590 }
591 break;
592 case MISCREG_NMRR_MAIR1:
593 case MISCREG_NMRR_MAIR1_NS:
594 case MISCREG_NMRR_MAIR1_S:
595 {
596 TTBCR ttbcr = readMiscRegNoEffect(MISCREG_TTBCR);
597 // If the muxed reg has been flattened, work out the
598 // offset and apply it to the unmuxed reg
599 int idxOffset = reg - MISCREG_NMRR_MAIR1;
600 if (ttbcr.eae)
601 flat_idx = flattenMiscIndex(MISCREG_MAIR1 +
602 idxOffset);
603 else
604 flat_idx = flattenMiscIndex(MISCREG_NMRR +
605 idxOffset);
606 }
607 break;
608 case MISCREG_PMXEVTYPER_PMCCFILTR:
609 {
610 PMSELR pmselr = miscRegs[MISCREG_PMSELR];
611 if (pmselr.sel == 31)
612 flat_idx = flattenMiscIndex(MISCREG_PMCCFILTR);
613 else
614 flat_idx = flattenMiscIndex(MISCREG_PMXEVTYPER);
615 }
616 break;
617 default:
618 panic("Unrecognized misc. register.\n");
619 break;
620 }
621 } else {
622 if (miscRegInfo[reg][MISCREG_BANKED]) {
623 bool secureReg = haveSecurity && !highestELIs64 &&
624 inSecureState(miscRegs[MISCREG_SCR],
625 miscRegs[MISCREG_CPSR]);
626 flat_idx += secureReg ? 2 : 1;
627 }
628 }
629 return flat_idx;
630 }
631
632 std::pair<int,int> getMiscIndices(int misc_reg) const
633 {
634 // Note: indexes of AArch64 registers are left unchanged
635 int flat_idx = flattenMiscIndex(misc_reg);
636
637 if (lookUpMiscReg[flat_idx].lower == 0) {
638 return std::make_pair(flat_idx, 0);
639 }
640
641 // do additional S/NS flattenings if mapped to NS while in S
642 bool S = haveSecurity && !highestELIs64 &&
643 inSecureState(miscRegs[MISCREG_SCR],
644 miscRegs[MISCREG_CPSR]);
645 int lower = lookUpMiscReg[flat_idx].lower;
646 int upper = lookUpMiscReg[flat_idx].upper;
647 // upper == 0, which is CPSR, is not MISCREG_BANKED_CHILD (no-op)
648 lower += S && miscRegInfo[lower][MISCREG_BANKED_CHILD];
649 upper += S && miscRegInfo[upper][MISCREG_BANKED_CHILD];
650 return std::make_pair(lower, upper);
651 }
652
653 void serialize(CheckpointOut &cp) const
654 {
655 DPRINTF(Checkpoint, "Serializing Arm Misc Registers\n");
656 SERIALIZE_ARRAY(miscRegs, NUM_PHYS_MISCREGS);
657
658 SERIALIZE_SCALAR(highestELIs64);
659 SERIALIZE_SCALAR(haveSecurity);
660 SERIALIZE_SCALAR(haveLPAE);
661 SERIALIZE_SCALAR(haveVirtualization);
662 SERIALIZE_SCALAR(haveLargeAsid64);
663 SERIALIZE_SCALAR(physAddrRange);
664 }
665 void unserialize(CheckpointIn &cp)
666 {
667 DPRINTF(Checkpoint, "Unserializing Arm Misc Registers\n");
668 UNSERIALIZE_ARRAY(miscRegs, NUM_PHYS_MISCREGS);
669 CPSR tmp_cpsr = miscRegs[MISCREG_CPSR];
670 updateRegMap(tmp_cpsr);
671
672 UNSERIALIZE_SCALAR(highestELIs64);
673 UNSERIALIZE_SCALAR(haveSecurity);
674 UNSERIALIZE_SCALAR(haveLPAE);
675 UNSERIALIZE_SCALAR(haveVirtualization);
676 UNSERIALIZE_SCALAR(haveLargeAsid64);
677 UNSERIALIZE_SCALAR(physAddrRange);
678 }
679
680 void startup(ThreadContext *tc);
681
682 Enums::DecoderFlavour decoderFlavour() const { return _decoderFlavour; }
683
684 Enums::VecRegRenameMode
685 vecRegRenameMode() const
686 {
687 return _vecRegRenameMode;
688 }
689
690 /// Explicitly import the otherwise hidden startup
691 using SimObject::startup;
692
693 typedef ArmISAParams Params;
694
695 const Params *params() const;
696
697 ISA(Params *p);
698 };
699}
700
701template<>
702struct initRenameMode<ArmISA::ISA>
703{
704 static Enums::VecRegRenameMode mode(const ArmISA::ISA* isa)
705 {
706 return isa->vecRegRenameMode();
707 }
708 static bool equals(const ArmISA::ISA* isa1, const ArmISA::ISA* isa2)
709 {
710 return mode(isa1) == mode(isa2);
711 }
712};
713
714#endif