diff mbox series

[v9,07/11] hvf: arm: Implement PSCI handling

Message ID 20210912230757.41096-8-agraf@csgraf.de (mailing list archive)
State New, archived
Headers show
Series hvf: Implement Apple Silicon Support | expand

Commit Message

Alexander Graf Sept. 12, 2021, 11:07 p.m. UTC
We need to handle PSCI calls. Most of the TCG code works for us,
but we can simplify it to only handle aa64 mode and we need to
handle SUSPEND differently.

This patch takes the TCG code as template and duplicates it in HVF.

To tell the guest that we support PSCI 0.2 now, update the check in
arm_cpu_initfn() as well.

Signed-off-by: Alexander Graf <agraf@csgraf.de>
Reviewed-by: Sergio Lopez <slp@redhat.com>

---

v6 -> v7:

  - This patch integrates "arm: Set PSCI to 0.2 for HVF"

v7 -> v8:

  - Do not advance for HVC, PC is already updated by hvf
  - Fix checkpatch error

v8 -> v9:

  - Use new hvf_raise_exception() prototype
  - Make cpu_off function void
  - Add comment about return value, use -1 for "not found"
  - Remove cpu_synchronize_state() when halted
---
 target/arm/cpu.c            |   4 +-
 target/arm/hvf/hvf.c        | 127 ++++++++++++++++++++++++++++++++++--
 target/arm/hvf/trace-events |   1 +
 3 files changed, 126 insertions(+), 6 deletions(-)

Comments

Peter Maydell Sept. 13, 2021, 8:54 a.m. UTC | #1
On Mon, 13 Sept 2021 at 00:08, Alexander Graf <agraf@csgraf.de> wrote:
>
> We need to handle PSCI calls. Most of the TCG code works for us,
> but we can simplify it to only handle aa64 mode and we need to
> handle SUSPEND differently.
>
> This patch takes the TCG code as template and duplicates it in HVF.
>
> To tell the guest that we support PSCI 0.2 now, update the check in
> arm_cpu_initfn() as well.
>
> Signed-off-by: Alexander Graf <agraf@csgraf.de>
> Reviewed-by: Sergio Lopez <slp@redhat.com>
>
> ---
>
> v6 -> v7:
>
>   - This patch integrates "arm: Set PSCI to 0.2 for HVF"
>
> v7 -> v8:
>
>   - Do not advance for HVC, PC is already updated by hvf
>   - Fix checkpatch error
>
> v8 -> v9:
>
>   - Use new hvf_raise_exception() prototype
>   - Make cpu_off function void
>   - Add comment about return value, use -1 for "not found"
>   - Remove cpu_synchronize_state() when halted
> ---
>  target/arm/cpu.c            |   4 +-
>  target/arm/hvf/hvf.c        | 127 ++++++++++++++++++++++++++++++++++--
>  target/arm/hvf/trace-events |   1 +
>  3 files changed, 126 insertions(+), 6 deletions(-)

Something in here should be checking whether the insn the guest
used matches the PSCI conduit configured for the VM, ie
what arm_is_psci_call() does after your patch 10.

-- PMM
Alexander Graf Sept. 13, 2021, 11:07 a.m. UTC | #2
On 13.09.21 10:54, Peter Maydell wrote:
> On Mon, 13 Sept 2021 at 00:08, Alexander Graf <agraf@csgraf.de> wrote:
>> We need to handle PSCI calls. Most of the TCG code works for us,
>> but we can simplify it to only handle aa64 mode and we need to
>> handle SUSPEND differently.
>>
>> This patch takes the TCG code as template and duplicates it in HVF.
>>
>> To tell the guest that we support PSCI 0.2 now, update the check in
>> arm_cpu_initfn() as well.
>>
>> Signed-off-by: Alexander Graf <agraf@csgraf.de>
>> Reviewed-by: Sergio Lopez <slp@redhat.com>
>>
>> ---
>>
>> v6 -> v7:
>>
>>   - This patch integrates "arm: Set PSCI to 0.2 for HVF"
>>
>> v7 -> v8:
>>
>>   - Do not advance for HVC, PC is already updated by hvf
>>   - Fix checkpatch error
>>
>> v8 -> v9:
>>
>>   - Use new hvf_raise_exception() prototype
>>   - Make cpu_off function void
>>   - Add comment about return value, use -1 for "not found"
>>   - Remove cpu_synchronize_state() when halted
>> ---
>>  target/arm/cpu.c            |   4 +-
>>  target/arm/hvf/hvf.c        | 127 ++++++++++++++++++++++++++++++++++--
>>  target/arm/hvf/trace-events |   1 +
>>  3 files changed, 126 insertions(+), 6 deletions(-)
> Something in here should be checking whether the insn the guest
> used matches the PSCI conduit configured for the VM, ie
> what arm_is_psci_call() does after your patch 10.


It's yet another case where I believe we are both reading the spec
differently :)

  https://documentation-service.arm.com/static/6013e5faeee5236980d08619

Section 2.5.3 speaks about the conduits. It says

    Service calls are expected to be invoked through SMC instructions,
except
    for Standard Hypervisor Calls and Vendor Specific Hypervisor Calls. On
    some platforms, however, SMC instructions are not available, and the
    services can be accessed through HVC instructions. The method that
    is used to invoke the service is referred to as the conduit.

To me, that reads like "Use SMC whenever you can. If your hardware does
not give you a way to handle SMC calls, falling back to HVC is ok. In
that case, indicate that mandate to the OS".

In hvf, we can very easily trap for SMC calls and handle them. Why are
we making OSs implement HVC call paths when SMC would work just as well
for everyone?

To keep your train of thought though, what would you do if we encounter
a conduit that is different from the chosen one? Today, I am aware of 2
different implementations: TCG injects #UD [1] while KVM sets x0 to -1 [2].

IMHO the best way to resolve all of this mess is to consolidate to SMC
as default PSCI handler and for now treat HVC as if it was an SMC call
as well for virtual environments. Once we get nested virtualization, we
will need to move to SMC as default anyway.


Alex

[1]
https://git.qemu.org/?p=qemu.git;a=blob;f=target/arm/op_helper.c;hb=HEAD#l813
[2]
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/kvm/handle_exit.c#n52
Peter Maydell Sept. 13, 2021, 11:44 a.m. UTC | #3
On Mon, 13 Sept 2021 at 12:07, Alexander Graf <agraf@csgraf.de> wrote:
>
>
> On 13.09.21 10:54, Peter Maydell wrote:
> > Something in here should be checking whether the insn the guest
> > used matches the PSCI conduit configured for the VM, ie
> > what arm_is_psci_call() does after your patch 10.
>
>
> It's yet another case where I believe we are both reading the spec
> differently :)
>
>   https://documentation-service.arm.com/static/6013e5faeee5236980d08619
>
> Section 2.5.3 speaks about the conduits. It says
>
>     Service calls are expected to be invoked through SMC instructions,
> except
>     for Standard Hypervisor Calls and Vendor Specific Hypervisor Calls. On
>     some platforms, however, SMC instructions are not available, and the
>     services can be accessed through HVC instructions. The method that
>     is used to invoke the service is referred to as the conduit.
>
> To me, that reads like "Use SMC whenever you can. If your hardware does
> not give you a way to handle SMC calls, falling back to HVC is ok. In
> that case, indicate that mandate to the OS".

QEMU here is being the platform, so we define what the conduit is
(or if one even exists). For the virt board this is "if the
guest has EL3 firmware, then the guest firmware is providing PSCI,
and QEMU should not; otherwise if the guest has EL2 then QEMU's
emulated firmware should be at EL3 using SMC, otherwise use HVC".

(So in practice for hvf at the moment this will mean the conduit
is always HVC, since hvf doesn't allow EL3 or EL2 in the guest.)

> In hvf, we can very easily trap for SMC calls and handle them. Why are
> we making OSs implement HVC call paths when SMC would work just as well
> for everyone?

OSes have to handle both anyway, because on real hardware if
there is no EL3 then it is IMPDEF whether SMC is trappable
to the hypervisor or whether it just UNDEFs to EL1.

> To keep your train of thought though, what would you do if we encounter
> a conduit that is different from the chosen one? Today, I am aware of 2
> different implementations: TCG injects #UD [1] while KVM sets x0 to -1 [2].

If the SMC or HVC insn isn't being used for PSCI then it should
have its standard architectural behaviour.

-- PMM
Alexander Graf Sept. 13, 2021, 12:02 p.m. UTC | #4
On 13.09.21 13:44, Peter Maydell wrote:
> On Mon, 13 Sept 2021 at 12:07, Alexander Graf <agraf@csgraf.de> wrote:
>>
>> On 13.09.21 10:54, Peter Maydell wrote:
>>> Something in here should be checking whether the insn the guest
>>> used matches the PSCI conduit configured for the VM, ie
>>> what arm_is_psci_call() does after your patch 10.
>>
>> It's yet another case where I believe we are both reading the spec
>> differently :)
>>
>>   https://documentation-service.arm.com/static/6013e5faeee5236980d08619
>>
>> Section 2.5.3 speaks about the conduits. It says
>>
>>     Service calls are expected to be invoked through SMC instructions,
>> except
>>     for Standard Hypervisor Calls and Vendor Specific Hypervisor Calls. On
>>     some platforms, however, SMC instructions are not available, and the
>>     services can be accessed through HVC instructions. The method that
>>     is used to invoke the service is referred to as the conduit.
>>
>> To me, that reads like "Use SMC whenever you can. If your hardware does
>> not give you a way to handle SMC calls, falling back to HVC is ok. In
>> that case, indicate that mandate to the OS".
> QEMU here is being the platform, so we define what the conduit is
> (or if one even exists). For the virt board this is "if the
> guest has EL3 firmware, then the guest firmware is providing PSCI,
> and QEMU should not; otherwise if the guest has EL2 then QEMU's
> emulated firmware should be at EL3 using SMC, otherwise use HVC".
>
> (So in practice for hvf at the moment this will mean the conduit
> is always HVC, since hvf doesn't allow EL3 or EL2 in the guest.)
>
>> In hvf, we can very easily trap for SMC calls and handle them. Why are
>> we making OSs implement HVC call paths when SMC would work just as well
>> for everyone?
> OSes have to handle both anyway, because on real hardware if
> there is no EL3 then it is IMPDEF whether SMC is trappable
> to the hypervisor or whether it just UNDEFs to EL1.
>
>> To keep your train of thought though, what would you do if we encounter
>> a conduit that is different from the chosen one? Today, I am aware of 2
>> different implementations: TCG injects #UD [1] while KVM sets x0 to -1 [2].
> If the SMC or HVC insn isn't being used for PSCI then it should
> have its standard architectural behaviour.


Why? Also, why does KVM behave differently? And why does Windows rely on
SMC availability on boot?

If you really insist that you don't care about users running Windows
with TCG and EL2=0, so be it. At least you can enable EL2 and it works
then. But I can't on hvf. It's one of the most useful use cases for hvf
on QEMU and I won't break it just because you insist that "SMC behavior
is IMPDEF, so it must be UNDEF". If it's IMPDEF, it may as well be "set
x0 to -1 and add 4 to pc".

And yes, this is a hill I will die on :)


Alex
Peter Maydell Sept. 13, 2021, 12:30 p.m. UTC | #5
On Mon, 13 Sept 2021 at 13:02, Alexander Graf <agraf@csgraf.de> wrote:
>
>
> On 13.09.21 13:44, Peter Maydell wrote:
> > On Mon, 13 Sept 2021 at 12:07, Alexander Graf <agraf@csgraf.de> wrote:
> >> To keep your train of thought though, what would you do if we encounter
> >> a conduit that is different from the chosen one? Today, I am aware of 2
> >> different implementations: TCG injects #UD [1] while KVM sets x0 to -1 [2].
> > If the SMC or HVC insn isn't being used for PSCI then it should
> > have its standard architectural behaviour.
>
> Why?

QEMU's assumption here is that there are basically two scenarios
for these instructions:
 (1) we're providing an emulation of firmware that uses this
     instruction (and only this insn, not the other one) to
     provide PSCI services
 (2) we're not emulating any firmware at all, we're running it
     in the guest, and that guest firmware is providing PSCI

In case (1) we provide a PSCI ABI on the end of the insn.
In case (2) we provide the architectural behaviour for the insn
so that the guest firmware can use it.

We don't currently have
 (3) we're providing an emulation of firmware that does something
     other than providing PSCI services on this instruction

which is what I think you're asking for. (Alternatively, you might
be after "provide PSCI via SMC, not HVC", ie use a different conduit.
If hvf documents that SMC is guaranteed to trap that would be
possible, I guess.)

> Also, why does KVM behave differently?

Looks like Marc made KVM set x0 to -1 for SMC calls in kernel commit
c0938c72f8070aa; conveniently he's on the cc list here so we can
ask him :-)

> And why does Windows rely on
> SMC availability on boot?

Ask Microsoft, but probably either they don't realize that
SMC might not exist and be trappable, or they only have a limited
set of hosts they care about. CPUs with no EL3 are not that common.

> If you really insist that you don't care about users running Windows
> with TCG and EL2=0, so be it. At least you can enable EL2 and it works
> then. But I can't on hvf. It's one of the most useful use cases for hvf
> on QEMU and I won't break it just because you insist that "SMC behavior
> is IMPDEF, so it must be UNDEF". If it's IMPDEF, it may as well be "set
> x0 to -1 and add 4 to pc".

I am not putting in random hacks for the benefit of specific guest OSes.
If there's a good reason why QEMU's behaviour is wrong then we can change
it, but "I want Windows to boot" doesn't count.

thanks
-- PMM
Alexander Graf Sept. 13, 2021, 9:29 p.m. UTC | #6
On 13.09.21 14:30, Peter Maydell wrote:
> On Mon, 13 Sept 2021 at 13:02, Alexander Graf <agraf@csgraf.de> wrote:
>>
>> On 13.09.21 13:44, Peter Maydell wrote:
>>> On Mon, 13 Sept 2021 at 12:07, Alexander Graf <agraf@csgraf.de> wrote:
>>>> To keep your train of thought though, what would you do if we encounter
>>>> a conduit that is different from the chosen one? Today, I am aware of 2
>>>> different implementations: TCG injects #UD [1] while KVM sets x0 to -1 [2].
>>> If the SMC or HVC insn isn't being used for PSCI then it should
>>> have its standard architectural behaviour.
>> Why?
> QEMU's assumption here is that there are basically two scenarios
> for these instructions:
>  (1) we're providing an emulation of firmware that uses this
>      instruction (and only this insn, not the other one) to
>      provide PSCI services
>  (2) we're not emulating any firmware at all, we're running it
>      in the guest, and that guest firmware is providing PSCI
>
> In case (1) we provide a PSCI ABI on the end of the insn.
> In case (2) we provide the architectural behaviour for the insn
> so that the guest firmware can use it.
>
> We don't currently have
>  (3) we're providing an emulation of firmware that does something
>      other than providing PSCI services on this instruction
>
> which is what I think you're asking for. (Alternatively, you might
> be after "provide PSCI via SMC, not HVC", ie use a different conduit.
> If hvf documents that SMC is guaranteed to trap that would be
> possible, I guess.)


Hvf doesn't document anything. The only documentation it has are its C
headers.

However, M1 does not implement EL3, but traps SMC calls. It's the only
chip Apple has out for hvf on ARM today. I would be very surprised if
they started to regress on that functionality.

So, would you be open to changing the default conduit to SMC for
hvf_enabled()? Is that really a better experience than just modeling
behavior after KVM?


>
>> Also, why does KVM behave differently?
> Looks like Marc made KVM set x0 to -1 for SMC calls in kernel commit
> c0938c72f8070aa; conveniently he's on the cc list here so we can
> ask him :-)
>
>> And why does Windows rely on
>> SMC availability on boot?
> Ask Microsoft, but probably either they don't realize that
> SMC might not exist and be trappable, or they only have a limited
> set of hosts they care about. CPUs with no EL3 are not that common.


I'm pretty sure it's the latter :).


>
>> If you really insist that you don't care about users running Windows
>> with TCG and EL2=0, so be it. At least you can enable EL2 and it works
>> then. But I can't on hvf. It's one of the most useful use cases for hvf
>> on QEMU and I won't break it just because you insist that "SMC behavior
>> is IMPDEF, so it must be UNDEF". If it's IMPDEF, it may as well be "set
>> x0 to -1 and add 4 to pc".
> I am not putting in random hacks for the benefit of specific guest OSes.
> If there's a good reason why QEMU's behaviour is wrong then we can change
> it, but "I want Windows to boot" doesn't count.


Ok, so today we have 2 implementations for SMC traps in an EL0/1 only VM:

  * TCG injects #UD
  * KVM sets x0 = -1 and pc += 4.

With v10 of the HVF patch set, I'm following what KVM is doing. Can we
leave it at that for now and sort out with Marc (and maybe ARM spec
writers) what we want to do consistently across all implementations as a
follow-up?


Thanks,

Alex
Marc Zyngier Sept. 15, 2021, 9:46 a.m. UTC | #7
On Mon, 13 Sep 2021 13:30:57 +0100,
Peter Maydell <peter.maydell@linaro.org> wrote:
> 
> On Mon, 13 Sept 2021 at 13:02, Alexander Graf <agraf@csgraf.de> wrote:
> >
> >
> > On 13.09.21 13:44, Peter Maydell wrote:
> > > On Mon, 13 Sept 2021 at 12:07, Alexander Graf <agraf@csgraf.de> wrote:
> > >> To keep your train of thought though, what would you do if we encounter
> > >> a conduit that is different from the chosen one? Today, I am aware of 2
> > >> different implementations: TCG injects #UD [1] while KVM sets x0 to -1 [2].
> > > If the SMC or HVC insn isn't being used for PSCI then it should
> > > have its standard architectural behaviour.
> >
> > Why?
> 
> QEMU's assumption here is that there are basically two scenarios
> for these instructions:
>  (1) we're providing an emulation of firmware that uses this
>      instruction (and only this insn, not the other one) to
>      provide PSCI services
>  (2) we're not emulating any firmware at all, we're running it
>      in the guest, and that guest firmware is providing PSCI
> 
> In case (1) we provide a PSCI ABI on the end of the insn.
> In case (2) we provide the architectural behaviour for the insn
> so that the guest firmware can use it.
> 
> We don't currently have
>  (3) we're providing an emulation of firmware that does something
>      other than providing PSCI services on this instruction
> 
> which is what I think you're asking for. (Alternatively, you might
> be after "provide PSCI via SMC, not HVC", ie use a different conduit.
> If hvf documents that SMC is guaranteed to trap that would be
> possible, I guess.)
> 
> > Also, why does KVM behave differently?
> 
> Looks like Marc made KVM set x0 to -1 for SMC calls in kernel commit
> c0938c72f8070aa; conveniently he's on the cc list here so we can
> ask him :-)

If we got a SMC trap into KVM, that's because the HW knows about it,
so injecting an UNDEF is rather counter productive (we don't hide the
fact that EL3 actually exists).

However, we don't implement anything on the back of this instruction,
so we just return NOT_IMPLEMENTED (-1). With NV, we actually use it as
a guest hypervisor can use it for PSCI and SMC is guaranteed to trap
even if EL3 doesn't exist in the HW.

For the brain-damaged case where there is no EL3, SMC traps and the
hypervisor doesn't actually advertises EL3, that's likely a guest
bug. Tough luck.

Side note: Not sure where HVF does, but on the M1 running Linux, SMC
appears to trap to EL2 with EC=0x3f, which is a reserved exception
class. This of course results in an UNDEF being injected because as
far as KVM is concerned, this should never happen.

	M.
Alexander Graf Sept. 15, 2021, 10:58 a.m. UTC | #8
On 15.09.21 11:46, Marc Zyngier wrote:
> On Mon, 13 Sep 2021 13:30:57 +0100,
> Peter Maydell <peter.maydell@linaro.org> wrote:
>> On Mon, 13 Sept 2021 at 13:02, Alexander Graf <agraf@csgraf.de> wrote:
>>>
>>> On 13.09.21 13:44, Peter Maydell wrote:
>>>> On Mon, 13 Sept 2021 at 12:07, Alexander Graf <agraf@csgraf.de> wrote:
>>>>> To keep your train of thought though, what would you do if we encounter
>>>>> a conduit that is different from the chosen one? Today, I am aware of 2
>>>>> different implementations: TCG injects #UD [1] while KVM sets x0 to -1 [2].
>>>> If the SMC or HVC insn isn't being used for PSCI then it should
>>>> have its standard architectural behaviour.
>>> Why?
>> QEMU's assumption here is that there are basically two scenarios
>> for these instructions:
>>  (1) we're providing an emulation of firmware that uses this
>>      instruction (and only this insn, not the other one) to
>>      provide PSCI services
>>  (2) we're not emulating any firmware at all, we're running it
>>      in the guest, and that guest firmware is providing PSCI
>>
>> In case (1) we provide a PSCI ABI on the end of the insn.
>> In case (2) we provide the architectural behaviour for the insn
>> so that the guest firmware can use it.
>>
>> We don't currently have
>>  (3) we're providing an emulation of firmware that does something
>>      other than providing PSCI services on this instruction
>>
>> which is what I think you're asking for. (Alternatively, you might
>> be after "provide PSCI via SMC, not HVC", ie use a different conduit.
>> If hvf documents that SMC is guaranteed to trap that would be
>> possible, I guess.)
>>
>>> Also, why does KVM behave differently?
>> Looks like Marc made KVM set x0 to -1 for SMC calls in kernel commit
>> c0938c72f8070aa; conveniently he's on the cc list here so we can
>> ask him :-)
> If we got a SMC trap into KVM, that's because the HW knows about it,
> so injecting an UNDEF is rather counter productive (we don't hide the
> fact that EL3 actually exists).


This is the part where you and Peter disagree :). What would you suggest
to do to create consistency between KVM and TCG based EL0/1 only VMs?


> However, we don't implement anything on the back of this instruction,
> so we just return NOT_IMPLEMENTED (-1). With NV, we actually use it as
> a guest hypervisor can use it for PSCI and SMC is guaranteed to trap
> even if EL3 doesn't exist in the HW.
>
> For the brain-damaged case where there is no EL3, SMC traps and the
> hypervisor doesn't actually advertises EL3, that's likely a guest
> bug. Tough luck.
>
> Side note: Not sure where HVF does, but on the M1 running Linux, SMC
> appears to trap to EL2 with EC=0x3f, which is a reserved exception
> class. This of course results in an UNDEF being injected because as
> far as KVM is concerned, this should never happen.


Could that be yet another magical implementation specific MSR bit that
needs to be set? Hvf returns 0x17 (EC_AA64_SMC) for SMC calls.


Alex
Marc Zyngier Sept. 15, 2021, 3:07 p.m. UTC | #9
On Wed, 15 Sep 2021 11:58:29 +0100,
Alexander Graf <agraf@csgraf.de> wrote:
> 
> 
> On 15.09.21 11:46, Marc Zyngier wrote:
> > On Mon, 13 Sep 2021 13:30:57 +0100,
> > Peter Maydell <peter.maydell@linaro.org> wrote:
> >> On Mon, 13 Sept 2021 at 13:02, Alexander Graf <agraf@csgraf.de> wrote:
> >>>
> >>> On 13.09.21 13:44, Peter Maydell wrote:
> >>>> On Mon, 13 Sept 2021 at 12:07, Alexander Graf <agraf@csgraf.de> wrote:
> >>>>> To keep your train of thought though, what would you do if we encounter
> >>>>> a conduit that is different from the chosen one? Today, I am aware of 2
> >>>>> different implementations: TCG injects #UD [1] while KVM sets x0 to -1 [2].
> >>>> If the SMC or HVC insn isn't being used for PSCI then it should
> >>>> have its standard architectural behaviour.
> >>> Why?
> >> QEMU's assumption here is that there are basically two scenarios
> >> for these instructions:
> >>  (1) we're providing an emulation of firmware that uses this
> >>      instruction (and only this insn, not the other one) to
> >>      provide PSCI services
> >>  (2) we're not emulating any firmware at all, we're running it
> >>      in the guest, and that guest firmware is providing PSCI
> >>
> >> In case (1) we provide a PSCI ABI on the end of the insn.
> >> In case (2) we provide the architectural behaviour for the insn
> >> so that the guest firmware can use it.
> >>
> >> We don't currently have
> >>  (3) we're providing an emulation of firmware that does something
> >>      other than providing PSCI services on this instruction
> >>
> >> which is what I think you're asking for. (Alternatively, you might
> >> be after "provide PSCI via SMC, not HVC", ie use a different conduit.
> >> If hvf documents that SMC is guaranteed to trap that would be
> >> possible, I guess.)
> >>
> >>> Also, why does KVM behave differently?
> >> Looks like Marc made KVM set x0 to -1 for SMC calls in kernel commit
> >> c0938c72f8070aa; conveniently he's on the cc list here so we can
> >> ask him :-)
> > If we got a SMC trap into KVM, that's because the HW knows about it,
> > so injecting an UNDEF is rather counter productive (we don't hide the
> > fact that EL3 actually exists).
> 
> 
> This is the part where you and Peter disagree :). What would you suggest
> to do to create consistency between KVM and TCG based EL0/1 only VMs?

I don't think we disagree. We simply have different implementation
choices. The KVM "firmware" can only be used with HVC, and not
SMC. SMC is reserved for cases where the guest talks to the actual
EL3, or an emulation of it in the case of NV.

As for consistency between TGC and KVM, I have no plan for that
whatsoever. Both implementations are valid, and they don't have to be
identical. Even more, diversity is important, as it weeds out silly
assumptions that are baked in non-portable SW.

Windows doesn't boot? I won't loose any sleep over it.

> 
> > However, we don't implement anything on the back of this instruction,
> > so we just return NOT_IMPLEMENTED (-1). With NV, we actually use it as
> > a guest hypervisor can use it for PSCI and SMC is guaranteed to trap
> > even if EL3 doesn't exist in the HW.
> >
> > For the brain-damaged case where there is no EL3, SMC traps and the
> > hypervisor doesn't actually advertises EL3, that's likely a guest
> > bug. Tough luck.
> >
> > Side note: Not sure where HVF does, but on the M1 running Linux, SMC
> > appears to trap to EL2 with EC=0x3f, which is a reserved exception
> > class. This of course results in an UNDEF being injected because as
> > far as KVM is concerned, this should never happen.
>
> Could that be yet another magical implementation specific MSR bit that
> needs to be set? Hvf returns 0x17 (EC_AA64_SMC) for SMC calls.

That's possible, but that's not something KVM will do. Also, from what
I understand of HVF, this value is what you get in userspace, and it
says nothing of what the kernel side does. It could well be
translating the invalid EC into something else, after having read the
instruction from the guest for all I know.

It is pretty obvious that this HW is not a valid implementation of the
architecture and if it decides to screw itself up, I'm happy to
oblige.

	M.
diff mbox series

Patch

diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 551b15243d..c111b2ee32 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -1093,8 +1093,8 @@  static void arm_cpu_initfn(Object *obj)
     cpu->psci_version = 1; /* By default assume PSCI v0.1 */
     cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE;
 
-    if (tcg_enabled()) {
-        cpu->psci_version = 2; /* TCG implements PSCI 0.2 */
+    if (tcg_enabled() || hvf_enabled()) {
+        cpu->psci_version = 2; /* TCG and HVF implement PSCI 0.2 */
     }
 }
 
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index 04da0dd4db..20d795366a 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -25,6 +25,7 @@ 
 #include "hw/irq.h"
 #include "qemu/main-loop.h"
 #include "sysemu/cpus.h"
+#include "arm-powerctl.h"
 #include "target/arm/cpu.h"
 #include "target/arm/internals.h"
 #include "trace/trace-target_arm_hvf.h"
@@ -48,6 +49,8 @@ 
 #define TMR_CTL_IMASK   (1 << 1)
 #define TMR_CTL_ISTATUS (1 << 2)
 
+static void hvf_wfi(CPUState *cpu);
+
 typedef struct HVFVTimer {
     /* Vtimer value during migration and paused state */
     uint64_t vtimer_val;
@@ -584,6 +587,116 @@  static void hvf_raise_exception(CPUState *cpu, uint32_t excp,
     arm_cpu_do_interrupt(cpu);
 }
 
+static void hvf_psci_cpu_off(ARMCPU *arm_cpu)
+{
+    int32_t ret = arm_set_cpu_off(arm_cpu->mp_affinity);
+    assert(ret == QEMU_ARM_POWERCTL_RET_SUCCESS);
+}
+
+/*
+ * Handle a PSCI call.
+ *
+ * Returns 0 on success
+ *         -1 when the PSCI call is unknown,
+ */
+static int hvf_handle_psci_call(CPUState *cpu)
+{
+    ARMCPU *arm_cpu = ARM_CPU(cpu);
+    CPUARMState *env = &arm_cpu->env;
+    uint64_t param[4] = {
+        env->xregs[0],
+        env->xregs[1],
+        env->xregs[2],
+        env->xregs[3]
+    };
+    uint64_t context_id, mpidr;
+    bool target_aarch64 = true;
+    CPUState *target_cpu_state;
+    ARMCPU *target_cpu;
+    target_ulong entry;
+    int target_el = 1;
+    int32_t ret = 0;
+
+    trace_hvf_psci_call(param[0], param[1], param[2], param[3],
+                        arm_cpu->mp_affinity);
+
+    switch (param[0]) {
+    case QEMU_PSCI_0_2_FN_PSCI_VERSION:
+        ret = QEMU_PSCI_0_2_RET_VERSION_0_2;
+        break;
+    case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
+        ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */
+        break;
+    case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
+    case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
+        mpidr = param[1];
+
+        switch (param[2]) {
+        case 0:
+            target_cpu_state = arm_get_cpu_by_id(mpidr);
+            if (!target_cpu_state) {
+                ret = QEMU_PSCI_RET_INVALID_PARAMS;
+                break;
+            }
+            target_cpu = ARM_CPU(target_cpu_state);
+
+            ret = target_cpu->power_state;
+            break;
+        default:
+            /* Everything above affinity level 0 is always on. */
+            ret = 0;
+        }
+        break;
+    case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
+        qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+        /* QEMU reset and shutdown are async requests, but PSCI
+         * mandates that we never return from the reset/shutdown
+         * call, so power the CPU off now so it doesn't execute
+         * anything further.
+         */
+        hvf_psci_cpu_off(arm_cpu);
+        break;
+    case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
+        qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
+        hvf_psci_cpu_off(arm_cpu);
+        break;
+    case QEMU_PSCI_0_1_FN_CPU_ON:
+    case QEMU_PSCI_0_2_FN_CPU_ON:
+    case QEMU_PSCI_0_2_FN64_CPU_ON:
+        mpidr = param[1];
+        entry = param[2];
+        context_id = param[3];
+        ret = arm_set_cpu_on(mpidr, entry, context_id,
+                             target_el, target_aarch64);
+        break;
+    case QEMU_PSCI_0_1_FN_CPU_OFF:
+    case QEMU_PSCI_0_2_FN_CPU_OFF:
+        hvf_psci_cpu_off(arm_cpu);
+        break;
+    case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
+    case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
+    case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
+        /* Affinity levels are not supported in QEMU */
+        if (param[1] & 0xfffe0000) {
+            ret = QEMU_PSCI_RET_INVALID_PARAMS;
+            break;
+        }
+        /* Powerdown is not supported, we always go into WFI */
+        env->xregs[0] = 0;
+        hvf_wfi(cpu);
+        break;
+    case QEMU_PSCI_0_1_FN_MIGRATE:
+    case QEMU_PSCI_0_2_FN_MIGRATE:
+        ret = QEMU_PSCI_RET_NOT_SUPPORTED;
+        break;
+    default:
+        return -1;
+    }
+
+    env->xregs[0] = ret;
+    return 0;
+}
+
 static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt)
 {
     ARMCPU *arm_cpu = ARM_CPU(cpu);
@@ -883,13 +996,19 @@  int hvf_vcpu_exec(CPUState *cpu)
         break;
     case EC_AA64_HVC:
         cpu_synchronize_state(cpu);
-        trace_hvf_unknown_hvf(env->xregs[0]);
-        hvf_raise_exception(env, EXCP_UDEF, syn_uncategorized());
+        if (hvf_handle_psci_call(cpu)) {
+            trace_hvf_unknown_hvf(env->xregs[0]);
+            hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized());
+        }
         break;
     case EC_AA64_SMC:
         cpu_synchronize_state(cpu);
-        trace_hvf_unknown_smc(env->xregs[0]);
-        hvf_raise_exception(env, EXCP_UDEF, syn_uncategorized());
+        if (!hvf_handle_psci_call(cpu)) {
+            advance_pc = true;
+        } else {
+            trace_hvf_unknown_smc(env->xregs[0]);
+            hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized());
+        }
         break;
     default:
         cpu_synchronize_state(cpu);
diff --git a/target/arm/hvf/trace-events b/target/arm/hvf/trace-events
index e972bdd9ce..cf4fb68f79 100644
--- a/target/arm/hvf/trace-events
+++ b/target/arm/hvf/trace-events
@@ -8,3 +8,4 @@  hvf_sysreg_write(uint32_t reg, uint32_t op0, uint32_t op1, uint32_t crn, uint32_
 hvf_unknown_hvf(uint64_t x0) "unknown HVC! 0x%016"PRIx64
 hvf_unknown_smc(uint64_t x0) "unknown SMC! 0x%016"PRIx64
 hvf_exit(uint64_t syndrome, uint32_t ec, uint64_t pc) "exit: 0x%"PRIx64" [ec=0x%x pc=0x%"PRIx64"]"
+hvf_psci_call(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t cpuid) "PSCI Call x0=0x%016"PRIx64" x1=0x%016"PRIx64" x2=0x%016"PRIx64" x3=0x%016"PRIx64" cpu=0x%x"