2 * Copyright (c) 2020 ARM Limited
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.
14 * Copyright 2019 Google, Inc.
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions are
18 * met: redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer;
20 * redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution;
23 * neither the name of the copyright holders nor the names of its
24 * contributors may be used to endorse or promote products derived from
25 * this software without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 #include "arch/arm/fastmodel/iris/thread_context.hh"
44 #include "arch/arm/system.hh"
45 #include "arch/arm/utility.hh"
46 #include "iris/detail/IrisCppAdapter.h"
47 #include "iris/detail/IrisObjects.h"
48 #include "mem/se_translating_port_proxy.hh"
49 #include "mem/translating_port_proxy.hh"
50 #include "sim/pseudo_inst.hh"
56 ThreadContext::initFromIrisInstance(const ResourceMap
&resources
)
59 call().perInstanceExecution_getState(_instId
, enabled
);
60 _status
= enabled
? Active
: Suspended
;
64 call().memory_getMemorySpaces(_instId
, memorySpaces
);
65 call().memory_getUsefulAddressTranslations(_instId
, translations
);
67 typedef ThreadContext Self
;
68 iris::EventSourceInfo evSrcInfo
;
70 client
.registerEventCallback
<Self
, &Self::breakpointHit
>(
71 this, "ec_IRIS_BREAKPOINT_HIT",
72 "Handle hitting a breakpoint", "Iris::ThreadContext");
73 call().event_getEventSource(_instId
, evSrcInfo
, "IRIS_BREAKPOINT_HIT");
74 call().eventStream_create(_instId
, breakpointEventStreamId
,
75 evSrcInfo
.evSrcId
, client
.getInstId());
77 for (auto it
= bps
.begin(); it
!= bps
.end(); it
++)
80 client
.registerEventCallback
<Self
, &Self::semihostingEvent
>(
81 this, "ec_IRIS_SEMIHOSTING_CALL_EXTENSION",
82 "Handle a semihosting call", "Iris::ThreadContext");
83 call().event_getEventSource(_instId
, evSrcInfo
,
84 "IRIS_SEMIHOSTING_CALL_EXTENSION");
85 call().eventStream_create(_instId
, semihostingEventStreamId
,
86 evSrcInfo
.evSrcId
, client
.getInstId(),
87 // Set all arguments to their defaults, except syncEc which is
89 nullptr, "", false, 0, nullptr, false, false, true);
93 ThreadContext::extractResourceId(
94 const ResourceMap
&resources
, const std::string
&name
)
96 return resources
.at(name
).rscId
;
100 ThreadContext::extractResourceMap(
101 ResourceIds
&ids
, const ResourceMap
&resources
,
102 const IdxNameMap
&idx_names
)
104 for (const auto &idx_name
: idx_names
) {
105 int idx
= idx_name
.first
;
106 const std::string
&name
= idx_name
.second
;
108 if (idx
>= ids
.size())
109 ids
.resize(idx
+ 1, iris::IRIS_UINT64_MAX
);
111 ids
[idx
] = extractResourceId(resources
, name
);
116 ThreadContext::maintainStepping()
121 if (comInstEventQueue
.empty()) {
122 // Set to 0 to deactivate stepping.
123 call().step_setup(_instId
, 0, "instruction");
127 Tick next
= comInstEventQueue
.nextTick();
129 now
= getCurrentInstCount();
132 comInstEventQueue
.serviceEvents(now
);
133 // Start over now that comInstEventQueue has likely changed.
137 // Set to the number of instructions still to step through.
138 Tick remaining
= next
- now
;
139 call().step_setup(_instId
, remaining
, "instruction");
144 ThreadContext::BpInfoIt
145 ThreadContext::getOrAllocBp(Addr pc
)
147 auto pc_it
= bps
.find(pc
);
149 if (pc_it
!= bps
.end())
152 auto res
= bps
.emplace(std::make_pair(pc
, new BpInfo(pc
)));
153 panic_if(!res
.second
, "Inserting breakpoint failed.");
158 ThreadContext::installBp(BpInfoIt it
)
160 Addr pc
= it
->second
->pc
;
161 const auto &space_ids
= getBpSpaceIds();
162 for (auto sid
: space_ids
) {
164 call().breakpoint_set_code(_instId
, id
, pc
, sid
, 0, true);
165 it
->second
->ids
.push_back(id
);
170 ThreadContext::uninstallBp(BpInfoIt it
)
172 for (auto id
: it
->second
->ids
)
173 call().breakpoint_delete(_instId
, id
);
174 it
->second
->clearIds();
178 ThreadContext::delBp(BpInfoIt it
)
180 panic_if(!it
->second
->empty(),
181 "BP info still had events associated with it.");
183 if (it
->second
->validIds())
190 ThreadContext::instanceRegistryChanged(
191 uint64_t esId
, const iris::IrisValueMap
&fields
, uint64_t time
,
192 uint64_t sInstId
, bool syncEc
, std::string
&error_message_out
)
194 const std::string
&event
= fields
.at("EVENT").getString();
195 const iris::InstanceId id
= fields
.at("INST_ID").getU64();
196 const std::string
&name
= fields
.at("INST_NAME").getString();
198 if (name
!= "component." + _irisPath
)
201 if (event
== "added")
203 else if (event
== "removed")
204 _instId
= iris::IRIS_UINT64_MAX
;
206 panic("Unrecognized event type %s", event
);
212 ThreadContext::phaseInitLeave(
213 uint64_t esId
, const iris::IrisValueMap
&fields
, uint64_t time
,
214 uint64_t sInstId
, bool syncEc
, std::string
&error_message_out
)
216 std::vector
<iris::ResourceInfo
> resources
;
217 call().resource_getList(_instId
, resources
);
219 std::map
<iris::ResourceId
, const iris::ResourceInfo
*>
221 for (const auto &resource
: resources
) {
222 idToResource
[resource
.rscId
] = &resource
;
224 ResourceMap resourceMap
;
225 for (const auto &resource
: resources
) {
226 std::string name
= resource
.name
;
227 iris::ResourceId parentId
= resource
.parentRscId
;
228 while (parentId
!= iris::IRIS_UINT64_MAX
) {
229 const auto *parent
= idToResource
[parentId
];
230 name
= parent
->name
+ "." + name
;
231 parentId
= parent
->parentRscId
;
233 resourceMap
[name
] = resource
;
236 initFromIrisInstance(resourceMap
);
242 ThreadContext::simulationTimeEvent(
243 uint64_t esId
, const iris::IrisValueMap
&fields
, uint64_t time
,
244 uint64_t sInstId
, bool syncEc
, std::string
&error_message_out
)
246 if (fields
.at("RUNNING").getAsBool()) {
247 // If this is just simulation time starting up, don't do anything.
251 // If simulation time has stopped for any reason, IRIS helpfully clears
252 // all stepping counters and we need to set them back. We might also need
253 // to service events based on the current number of executed instructions.
256 // Restart simulation time to make sure things progress once we give
258 call().simulationTime_run(iris::IrisInstIdSimulationEngine
);
264 ThreadContext::breakpointHit(
265 uint64_t esId
, const iris::IrisValueMap
&fields
, uint64_t time
,
266 uint64_t sInstId
, bool syncEc
, std::string
&error_message_out
)
268 Addr pc
= fields
.at("PC").getU64();
270 auto it
= getOrAllocBp(pc
);
272 std::shared_ptr
<BpInfo::EventList
> events
= it
->second
->events
;
273 auto e_it
= events
->begin();
274 while (e_it
!= events
->end()) {
276 // Advance e_it here since e might remove itself from the list.
285 ThreadContext::semihostingEvent(
286 uint64_t esId
, const iris::IrisValueMap
&fields
, uint64_t time
,
287 uint64_t sInstId
, bool syncEc
, std::string
&error_message_out
)
289 if (ArmSystem::callSemihosting(this, true)) {
290 // Stop execution in case an exit of the sim loop was scheduled. We
291 // don't want to keep executing instructions in the mean time.
292 call().perInstanceExecution_setState(_instId
, false);
294 // Schedule an event to resume execution right after any exit has
295 // had a chance to happen.
296 if (!enableAfterPseudoEvent
->scheduled())
297 getCpuPtr()->schedule(enableAfterPseudoEvent
, curTick());
299 call().semihosting_return(_instId
, readIntReg(0));
301 call().semihosting_notImplemented(_instId
);
306 ThreadContext::ThreadContext(
307 BaseCPU
*cpu
, int id
, System
*system
, ::BaseMMU
*mmu
,
308 BaseISA
*isa
, iris::IrisConnectionInterface
*iris_if
,
309 const std::string
&iris_path
) :
310 _cpu(cpu
), _threadId(id
), _system(system
), _mmu(mmu
), _isa(isa
),
311 _irisPath(iris_path
), vecRegs(ArmISA::NumVecRegs
),
312 vecPredRegs(ArmISA::NumVecPredRegs
),
313 comInstEventQueue("instruction-based event queue"),
314 client(iris_if
, "client." + iris_path
)
316 iris::InstanceInfo info
;
317 auto ret_code
= noThrow().instanceRegistry_getInstanceInfoByName(
318 info
, "component." + iris_path
);
319 if (ret_code
== iris::E_ok
) {
320 // The iris instance registry already new about this path.
321 _instId
= info
.instId
;
323 // This path doesn't (yet) exist. Set the ID to something invalid.
324 _instId
= iris::IRIS_UINT64_MAX
;
327 typedef ThreadContext Self
;
328 iris::EventSourceInfo evSrcInfo
;
330 client
.registerEventCallback
<Self
, &Self::instanceRegistryChanged
>(
331 this, "ec_IRIS_INSTANCE_REGISTRY_CHANGED",
332 "Install the iris instance ID", "Iris::ThreadContext");
333 call().event_getEventSource(iris::IrisInstIdGlobalInstance
, evSrcInfo
,
334 "IRIS_INSTANCE_REGISTRY_CHANGED");
335 regEventStreamId
= iris::IRIS_UINT64_MAX
;
336 static const std::vector
<std::string
> fields
=
337 { "EVENT", "INST_ID", "INST_NAME" };
338 call().eventStream_create(iris::IrisInstIdGlobalInstance
, regEventStreamId
,
339 evSrcInfo
.evSrcId
, client
.getInstId(), &fields
);
341 client
.registerEventCallback
<Self
, &Self::phaseInitLeave
>(
342 this, "ec_IRIS_SIM_PHASE_INIT_LEAVE",
343 "Initialize register contexts", "Iris::ThreadContext");
344 call().event_getEventSource(iris::IrisInstIdSimulationEngine
, evSrcInfo
,
345 "IRIS_SIM_PHASE_INIT_LEAVE");
346 initEventStreamId
= iris::IRIS_UINT64_MAX
;
347 call().eventStream_create(
348 iris::IrisInstIdSimulationEngine
, initEventStreamId
,
349 evSrcInfo
.evSrcId
, client
.getInstId());
351 client
.registerEventCallback
<Self
, &Self::simulationTimeEvent
>(
352 this, "ec_IRIS_SIMULATION_TIME_EVENT",
353 "Handle simulation time stopping for breakpoints or stepping",
354 "Iris::ThreadContext");
355 call().event_getEventSource(iris::IrisInstIdSimulationEngine
, evSrcInfo
,
356 "IRIS_SIMULATION_TIME_EVENT");
357 timeEventStreamId
= iris::IRIS_UINT64_MAX
;
358 call().eventStream_create(
359 iris::IrisInstIdSimulationEngine
, timeEventStreamId
,
360 evSrcInfo
.evSrcId
, client
.getInstId());
362 breakpointEventStreamId
= iris::IRIS_UINT64_MAX
;
363 semihostingEventStreamId
= iris::IRIS_UINT64_MAX
;
365 auto enable_lambda
= [this]{
366 call().perInstanceExecution_setState(_instId
, true);
368 enableAfterPseudoEvent
= new EventFunctionWrapper(
369 enable_lambda
, "resume after pseudo inst",
370 false, Event::Sim_Exit_Pri
+ 1);
373 ThreadContext::~ThreadContext()
375 call().eventStream_destroy(
376 iris::IrisInstIdSimulationEngine
, initEventStreamId
);
377 initEventStreamId
= iris::IRIS_UINT64_MAX
;
378 client
.unregisterEventCallback("ec_IRIS_SIM_PHASE_INIT_LEAVE");
380 call().eventStream_destroy(
381 iris::IrisInstIdGlobalInstance
, regEventStreamId
);
382 regEventStreamId
= iris::IRIS_UINT64_MAX
;
383 client
.unregisterEventCallback("ec_IRIS_INSTANCE_REGISTRY_CHANGED");
385 call().eventStream_destroy(
386 iris::IrisInstIdGlobalInstance
, timeEventStreamId
);
387 timeEventStreamId
= iris::IRIS_UINT64_MAX
;
388 client
.unregisterEventCallback("ec_IRIS_SIMULATION_TIME_EVENT");
390 if (enableAfterPseudoEvent
->scheduled())
391 getCpuPtr()->deschedule(enableAfterPseudoEvent
);
392 delete enableAfterPseudoEvent
;
396 ThreadContext::schedule(PCEvent
*e
)
398 auto it
= getOrAllocBp(e
->pc());
399 it
->second
->events
->push_back(e
);
401 if (_instId
!= iris::IRIS_UINT64_MAX
&& !it
->second
->validIds())
408 ThreadContext::remove(PCEvent
*e
)
410 auto it
= getOrAllocBp(e
->pc());
411 it
->second
->events
->remove(e
);
413 if (it
->second
->empty())
420 ThreadContext::translateAddress(Addr
&paddr
, iris::MemorySpaceId p_space
,
421 Addr vaddr
, iris::MemorySpaceId v_space
)
423 iris::MemoryAddressTranslationResult result
;
424 auto ret
= noThrow().memory_translateAddress(
425 _instId
, result
, v_space
, vaddr
, p_space
);
427 if (ret
!= iris::E_ok
) {
428 // Check if there was a legal translation between these two spaces.
429 // If so, something else went wrong.
430 for (auto &trans
: translations
)
431 if (trans
.inSpaceId
== v_space
&& trans
.outSpaceId
== p_space
)
434 panic("No legal translation IRIS address translation found.");
437 if (result
.address
.empty())
440 if (result
.address
.size() > 1) {
441 warn("Multiple mappings for address %#x.", vaddr
);
445 paddr
= result
.address
[0];
450 ThreadContext::scheduleInstCountEvent(Event
*event
, Tick count
)
452 Tick now
= getCurrentInstCount();
453 comInstEventQueue
.schedule(event
, count
);
455 call().simulationTime_stop(iris::IrisInstIdSimulationEngine
);
461 ThreadContext::descheduleInstCountEvent(Event
*event
)
463 comInstEventQueue
.deschedule(event
);
468 ThreadContext::getCurrentInstCount()
471 auto ret
= call().step_getStepCounterValue(_instId
, count
, "instruction");
472 panic_if(ret
!= iris::E_ok
, "Failed to get instruction count.");
477 ThreadContext::initMemProxies(::ThreadContext
*tc
)
480 assert(!physProxy
&& !virtProxy
);
481 physProxy
.reset(new PortProxy(_cpu
->getSendFunctional(),
482 _cpu
->cacheLineSize()));
483 virtProxy
.reset(new TranslatingPortProxy(tc
));
486 virtProxy
.reset(new SETranslatingPortProxy(this,
487 SETranslatingPortProxy::NextPage
));
491 ThreadContext::Status
492 ThreadContext::status() const
498 ThreadContext::setStatus(Status new_status
)
500 if (enableAfterPseudoEvent
->scheduled())
501 getCpuPtr()->deschedule(enableAfterPseudoEvent
);
502 if (new_status
== Active
) {
503 if (_status
!= Active
)
504 call().perInstanceExecution_setState(_instId
, true);
506 if (_status
== Active
)
507 call().perInstanceExecution_setState(_instId
, false);
509 _status
= new_status
;
513 ThreadContext::pcState() const
515 ArmISA::CPSR cpsr
= readMiscRegNoEffect(ArmISA::MISCREG_CPSR
);
519 pc
.nextThumb(pc
.thumb());
521 pc
.nextJazelle(cpsr
.j
);
522 pc
.aarch64(!cpsr
.width
);
523 pc
.nextAArch64(!cpsr
.width
);
524 pc
.illegalExec(false);
525 pc
.itstate(ArmISA::itState(cpsr
));
528 iris::ResourceReadResult result
;
529 call().resource_read(_instId
, result
, pcRscId
);
530 Addr addr
= result
.data
.at(0);
531 if (cpsr
.width
&& cpsr
.t
)
538 ThreadContext::pcState(const ArmISA::PCState
&val
)
542 ArmISA::CPSR cpsr
= readMiscRegNoEffect(ArmISA::MISCREG_CPSR
);
543 if (cpsr
.width
&& cpsr
.t
)
546 iris::ResourceWriteResult result
;
547 call().resource_write(_instId
, result
, pcRscId
, pc
);
551 ThreadContext::instAddr() const
553 return pcState().instAddr();
557 ThreadContext::nextInstAddr() const
559 return pcState().nextInstAddr();
563 ThreadContext::readMiscRegNoEffect(RegIndex misc_reg
) const
565 iris::ResourceReadResult result
;
566 call().resource_read(_instId
, result
, miscRegIds
.at(misc_reg
));
567 return result
.data
.at(0);
571 ThreadContext::setMiscRegNoEffect(RegIndex misc_reg
, const RegVal val
)
573 iris::ResourceWriteResult result
;
574 call().resource_write(_instId
, result
, miscRegIds
.at(misc_reg
), val
);
578 ThreadContext::readIntReg(RegIndex reg_idx
) const
580 ArmISA::CPSR cpsr
= readMiscRegNoEffect(ArmISA::MISCREG_CPSR
);
582 iris::ResourceReadResult result
;
584 call().resource_read(_instId
, result
, intReg32Ids
.at(reg_idx
));
586 call().resource_read(_instId
, result
, intReg64Ids
.at(reg_idx
));
587 return result
.data
.at(0);
591 ThreadContext::setIntReg(RegIndex reg_idx
, RegVal val
)
593 ArmISA::CPSR cpsr
= readMiscRegNoEffect(ArmISA::MISCREG_CPSR
);
595 iris::ResourceWriteResult result
;
597 call().resource_write(_instId
, result
, intReg32Ids
.at(reg_idx
), val
);
599 call().resource_write(_instId
, result
, intReg64Ids
.at(reg_idx
), val
);
603 * The 64 bit version of registers gives us a pre-flattened view of the reg
604 * file, no matter what mode we're in or if we're currently 32 or 64 bit.
607 ThreadContext::readIntRegFlat(RegIndex idx
) const
609 if (idx
>= flattenedIntIds
.size())
611 iris::ResourceId res_id
= flattenedIntIds
.at(idx
);
612 if (res_id
== iris::IRIS_UINT64_MAX
)
614 iris::ResourceReadResult result
;
615 call().resource_read(_instId
, result
, res_id
);
616 return result
.data
.at(0);
620 ThreadContext::setIntRegFlat(RegIndex idx
, uint64_t val
)
622 iris::ResourceId res_id
=
623 (idx
>= flattenedIntIds
.size()) ? iris::IRIS_UINT64_MAX
:
624 flattenedIntIds
.at(idx
);
625 panic_if(res_id
== iris::IRIS_UINT64_MAX
,
626 "Int reg %d is not supported by fast model.", idx
);
627 iris::ResourceWriteResult result
;
628 call().resource_write(_instId
, result
, flattenedIntIds
.at(idx
), val
);
632 ThreadContext::readCCRegFlat(RegIndex idx
) const
634 if (idx
>= ccRegIds
.size())
636 iris::ResourceReadResult result
;
637 call().resource_read(_instId
, result
, ccRegIds
.at(idx
));
638 return result
.data
.at(0);
642 ThreadContext::setCCRegFlat(RegIndex idx
, RegVal val
)
644 panic_if(idx
>= ccRegIds
.size(),
645 "CC reg %d is not supported by fast model.", idx
);
646 iris::ResourceWriteResult result
;
647 call().resource_write(_instId
, result
, ccRegIds
.at(idx
), val
);
650 const ArmISA::VecRegContainer
&
651 ThreadContext::readVecReg(const RegId
®_id
) const
653 const RegIndex idx
= reg_id
.index();
654 ArmISA::VecRegContainer
®
= vecRegs
.at(idx
);
657 // Ignore accesses to registers which aren't architected. gem5 defines a
658 // few extra registers which it uses internally in the implementation of
659 // some instructions.
660 if (idx
>= vecRegIds
.size())
663 iris::ResourceReadResult result
;
664 call().resource_read(_instId
, result
, vecRegIds
.at(idx
));
665 size_t data_size
= result
.data
.size() * (sizeof(*result
.data
.data()));
666 size_t size
= std::min(data_size
, reg
.size());
667 memcpy(reg
.raw_ptr
<void>(), (void *)result
.data
.data(), size
);
672 const ArmISA::VecRegContainer
&
673 ThreadContext::readVecRegFlat(RegIndex idx
) const
675 return readVecReg(RegId(VecRegClass
, idx
));
678 const ArmISA::VecPredRegContainer
&
679 ThreadContext::readVecPredReg(const RegId
®_id
) const
681 RegIndex idx
= reg_id
.index();
683 ArmISA::VecPredRegContainer
®
= vecPredRegs
.at(idx
);
686 if (idx
>= vecPredRegIds
.size())
689 iris::ResourceReadResult result
;
690 call().resource_read(_instId
, result
, vecPredRegIds
.at(idx
));
693 size_t num_bits
= reg
.NUM_BITS
;
694 uint8_t *bytes
= (uint8_t *)result
.data
.data();
695 while (num_bits
> 8) {
696 reg
.set_bits(offset
, 8, *bytes
);
702 reg
.set_bits(offset
, num_bits
, *bytes
);
707 const ArmISA::VecPredRegContainer
&
708 ThreadContext::readVecPredRegFlat(RegIndex idx
) const
710 return readVecPredReg(RegId(VecPredRegClass
, idx
));