linux-coco.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
* Re: [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
@ 2024-04-10 16:17 ` Itaru Kitayama
  2024-04-15  8:59   ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 01/33] arm: Add necessary header files in asm/pgtable.h Suzuki K Poulose
                   ` (33 subsequent siblings)
  34 siblings, 1 reply; 48+ messages in thread
From: Itaru Kitayama @ 2024-04-10 16:17 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger

Hi Suzuki,

On Fri, Apr 12, 2024 at 11:33:35AM +0100, Suzuki K Poulose wrote:
> This series adds support for running the kvm-unit-tests in the Arm CCA reference
> software architecture.
> 
> 
> The changes involve enlightening the boot/setup code with the Realm Service Interface
> (RSI). The series also includes new test cases that exercise the RSI calls.
> 
> Currently we only support "kvmtool" as the VMM for running Realms. There was
> an attempt to add support for running the test scripts using with kvmtool here [1],
> which hasn't progressed. It would be good to have that resolved, so that we can
> run all the tests without manually specifying the commandlines for each run.
> 
> For the purposes of running the Realm specific tests, we have added a "temporary"
> script "run-realm-tests" until the kvmtool support is added. We do not expect
> this to be merged.
> 
> 
> Base Realm Support
> -------------------
> 
> Realm IPA Space
> ---------------
> When running on in Realm world, the (Guest) Physical Address - aka Intermediate
> Physical Address (IPA) in Arm terminology - space of the VM is split into two halves,
> protected (lower half) and un-protected (upper half). A protected IPA will
> always map pages in the "realm world" and  the contents are not accessible to
> the host. An unprotected IPA on the other hand can be mapped to page in the
> "normal world" and thus shared with the host. All host emulated MMIO ranges must
> be in unprotected IPA space.
> 
> Realm can query the Realm Management Monitor for the configuration via RSI call
> (RSI_REALM_CONFIG) and identify the "boundary" of the "IPA" split.
> 
> As far as the hyp/VMM is concerned, there is only one "IPA space" (the lower
> half) of memory map. The "upper half" is "unprotected alias" of the memory map.
> 
> In the guest, this is achieved by "treating the MSB (1 << (IPA_WIDTH - 1))" as
> a protection attribute (we call it - PTE_NS_SHARED), where the Realm applies this
> to any address, it thinks is acccessed/managed by host (e.g., MMIO, shared pages).
> Given that this is runtime variable (but fixed for a given Realm), uses a
> variable to track the value.
> 
> All I/O regions are marked as "shared". Care is taken to ensure I/O access (uart)
> with MMU off uses the "Unprotected Physical address".
> 
> 
> Realm IPA State
> ---------------
> Additionally, each page (4K) in the protected IPA space has a state associated
> (Realm IPA State - RIPAS) with it. It is either of :
>    RIPAS_EMPTY
>    RIPAS_RAM
> 
> Any IPA backed by RAM, must be marked as RIPAS_RAM before an access is made to
> it. The hypervisor/VMM does this for the initial image loaded into the Realm
> memory before the Realm starts execution. Given the kvm-unit-test flat files do
> not contain a metadata header (e.g., like the arm64 Linux kernel Image),
> indicating the "actual image size in memory", the VMM cannot transition the
> area towards the end of the image (e.g., bss, stack) which are accessed very
> early during boot. Thus the early boot assembly code will mark the area upto
> the stack as RAM.
> 
> Once we land in the C code, we mark target relocation area for FDT and
> initrd as RIPAS_RAM. At this point, we can scan the FDT and mark all RAM memory
> blocks as RIPAS_RAM.
> 
> TODO: It would be good to add an image header to the flat files indicating the
> size, which can take the burden off doing the early assembly boot code RSI calls.
> 
> Shared Memory support
> ---------------------
> Given the "default" memory of a VM is not accessible to host, we add new page
> alloc/free routines for "memory shared" with the host. e.g., GICv3-ITS must use
> shared pages for ITS emulation.
> 
> RSI Test suites
> --------------
> There are new testcases added to exercise the RSI interfaces and the RMM flows.
> 
> Attestation and measurement services related RSI tests require parsing tokens
> and claims returned by the RMM. This is achieved with the help of QCBOR library
> [2], which is added as a submodule to the project. We have also added a wrapper
> library - libtokenverifier - around the QCBOR to parse the tokens according to
> the RMM specifications.
> 
> Running Arm CCA Stack
> -------------------
> 
> See more details on Arm CCA and how to build/run the entire stack here[0]
> The easiest way to run the Arm CCA stack is using shrinkwrap and the details
> are available in [0].
> 
> 
> The patches are also available here :
> 
>  https://gitlab.arm.com/linux-arm/kvm-unit-tests-cca cca/v1
> 
> 
> Changes since rfc:
>   [ https://lkml.kernel.org/r/20230127114108.10025-1-joey.gouly@arm.com ]
>   - Add support for RMM-v1.0-EAC5, changes to RSI ABIs
>   - Some hardening checks (FDT overlapping the BSS sections)
>   - Selftest for memory stress
>   - Enable PMU/SVE tests for Realms
> 
>  [0] https://lkml.kernel.org/r/20240412084056.1733704-1-steven.price@arm.com
>  [1] https://lkml.kernel.org/r/20210702163122.96110-1-alexandru.elisei@arm.com
>  [2] https://github.com/laurencelundblade/QCBOR
> 
> Alexandru Elisei (3):
>   arm64: Expand SMCCC arguments and return values
>   arm: selftest: realm: skip pabt test when running in a realm
>   NOT-FOR-MERGING: add run-realm-tests
> 
> Djordje Kovacevic (1):
>   arm: realm: Add tests for in realm SEA
> 
> Gareth Stockwell (1):
>   arm: realm: add hvc and RSI_HOST_CALL tests
> 
> Jean-Philippe Brucker (1):
>   arm: Move io_init after vm initialization
> 
> Joey Gouly (10):
>   arm: Make physical address mask dynamic
>   arm64: Introduce NS_SHARED PTE attribute
>   arm: realm: Add RSI interface header
>   arm: realm: Make uart available before MMU is enabled
>   arm: realm: Add RSI version test
>   arm64: add ESR_ELx EC.SVE
>   arm64: enable SVE at startup
>   arm64: selftest: add realm SVE VL test
>   lib/alloc_page: Add shared page allocation support
>   arm: Add memtest support
> 
> Mate Toth-Pal (2):
>   arm: Add a library to verify tokens using the QCBOR library
>   arm: realm: Add Realm attestation tests
> 
> Subhasish Ghosh (1):
>   arm: realm: Add test for FPU/SIMD context save/restore
> 
> Suzuki K Poulose (14):
>   arm: Add necessary header files in asm/pgtable.h
>   arm: Detect FDT overlap with uninitialised data
>   arm: realm: Realm initialisation
>   arm: realm: Add support for changing the state of memory
>   arm: realm: Set RIPAS state for RAM
>   arm: realm: Early memory setup
>   arm: gic-v3-its: Use shared pages wherever needed
>   arm: realm: Enable memory encryption
>   qcbor: Add QCBOR as a submodule
>   arm: Add build steps for QCBOR library
>   arm: realm: add RSI interface for attestation measurements
>   arm: realm: Add helpers to decode RSI return codes
>   arm: realm: Add Realm attestation tests
>   arm: realm: Add a test for shared memory
> 
>  .gitmodules                         |    3 +
>  arm/Makefile.arm64                  |   25 +-
>  arm/cstart.S                        |   49 +-
>  arm/cstart64.S                      |  154 +++-
>  arm/fpu.c                           |  424 +++++++++
>  arm/realm-attest.c                  | 1251 +++++++++++++++++++++++++++
>  arm/realm-ns-memory.c               |   86 ++
>  arm/realm-rsi.c                     |  159 ++++
>  arm/realm-sea.c                     |  143 +++
>  arm/run-realm-tests                 |  112 +++
>  arm/selftest.c                      |  138 ++-
>  arm/unittests.cfg                   |   96 +-
>  lib/alloc_page.c                    |   20 +-
>  lib/alloc_page.h                    |   24 +
>  lib/arm/asm/arm-smccc.h             |   44 +
>  lib/arm/asm/io.h                    |    6 +
>  lib/arm/asm/pgtable.h               |    9 +
>  lib/arm/asm/psci.h                  |   13 +-
>  lib/arm/asm/rsi.h                   |   21 +
>  lib/arm/asm/sve-vl-test.h           |    9 +
>  lib/arm/gic-v3.c                    |    6 +-
>  lib/arm/io.c                        |   24 +-
>  lib/arm/mmu.c                       |   80 +-
>  lib/arm/psci.c                      |   19 +-
>  lib/arm/setup.c                     |   26 +-
>  lib/arm64/asm/arm-smccc.h           |    6 +
>  lib/arm64/asm/esr.h                 |    1 +
>  lib/arm64/asm/io.h                  |    6 +
>  lib/arm64/asm/pgtable-hwdef.h       |    6 -
>  lib/arm64/asm/pgtable.h             |   20 +
>  lib/arm64/asm/processor.h           |   34 +
>  lib/arm64/asm/rsi.h                 |   89 ++
>  lib/arm64/asm/smc-rsi.h             |  173 ++++
>  lib/arm64/asm/sve-vl-test.h         |   28 +
>  lib/arm64/asm/sysreg.h              |    7 +
>  lib/arm64/gic-v3-its.c              |    6 +-
>  lib/arm64/processor.c               |    1 +
>  lib/arm64/rsi.c                     |  188 ++++
>  lib/asm-generic/io.h                |   12 +
>  lib/libcflat.h                      |    1 +
>  lib/qcbor                           |    1 +
>  lib/token_verifier/attest_defines.h |   50 ++
>  lib/token_verifier/token_dumper.c   |  157 ++++
>  lib/token_verifier/token_dumper.h   |   15 +
>  lib/token_verifier/token_verifier.c |  591 +++++++++++++
>  lib/token_verifier/token_verifier.h |   77 ++
>  46 files changed, 4355 insertions(+), 55 deletions(-)
>  create mode 100644 .gitmodules
>  create mode 100644 arm/fpu.c
>  create mode 100644 arm/realm-attest.c
>  create mode 100644 arm/realm-ns-memory.c
>  create mode 100644 arm/realm-rsi.c
>  create mode 100644 arm/realm-sea.c
>  create mode 100755 arm/run-realm-tests
>  create mode 100644 lib/arm/asm/arm-smccc.h
>  create mode 100644 lib/arm/asm/rsi.h
>  create mode 100644 lib/arm/asm/sve-vl-test.h
>  create mode 100644 lib/arm64/asm/arm-smccc.h
>  create mode 100644 lib/arm64/asm/rsi.h
>  create mode 100644 lib/arm64/asm/smc-rsi.h
>  create mode 100644 lib/arm64/asm/sve-vl-test.h
>  create mode 100644 lib/arm64/rsi.c
>  create mode 160000 lib/qcbor
>  create mode 100644 lib/token_verifier/attest_defines.h
>  create mode 100644 lib/token_verifier/token_dumper.c
>  create mode 100644 lib/token_verifier/token_dumper.h
>  create mode 100644 lib/token_verifier/token_verifier.c
>  create mode 100644 lib/token_verifier/token_verifier.h

Thanks for the update! I'll go through the series one by one in the
coming weeks. Just curious one thing - do you guys wish to add Realm tests to the kvm-unit-test package, but not to kselftests?

Thanks,
Itaru.

> 
> -- 
> 2.34.1
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture
@ 2024-04-12 10:33 Suzuki K Poulose
  2024-04-10 16:17 ` Itaru Kitayama
                   ` (34 more replies)
  0 siblings, 35 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

This series adds support for running the kvm-unit-tests in the Arm CCA reference
software architecture.


The changes involve enlightening the boot/setup code with the Realm Service Interface
(RSI). The series also includes new test cases that exercise the RSI calls.

Currently we only support "kvmtool" as the VMM for running Realms. There was
an attempt to add support for running the test scripts using with kvmtool here [1],
which hasn't progressed. It would be good to have that resolved, so that we can
run all the tests without manually specifying the commandlines for each run.

For the purposes of running the Realm specific tests, we have added a "temporary"
script "run-realm-tests" until the kvmtool support is added. We do not expect
this to be merged.


Base Realm Support
-------------------

Realm IPA Space
---------------
When running on in Realm world, the (Guest) Physical Address - aka Intermediate
Physical Address (IPA) in Arm terminology - space of the VM is split into two halves,
protected (lower half) and un-protected (upper half). A protected IPA will
always map pages in the "realm world" and  the contents are not accessible to
the host. An unprotected IPA on the other hand can be mapped to page in the
"normal world" and thus shared with the host. All host emulated MMIO ranges must
be in unprotected IPA space.

Realm can query the Realm Management Monitor for the configuration via RSI call
(RSI_REALM_CONFIG) and identify the "boundary" of the "IPA" split.

As far as the hyp/VMM is concerned, there is only one "IPA space" (the lower
half) of memory map. The "upper half" is "unprotected alias" of the memory map.

In the guest, this is achieved by "treating the MSB (1 << (IPA_WIDTH - 1))" as
a protection attribute (we call it - PTE_NS_SHARED), where the Realm applies this
to any address, it thinks is acccessed/managed by host (e.g., MMIO, shared pages).
Given that this is runtime variable (but fixed for a given Realm), uses a
variable to track the value.

All I/O regions are marked as "shared". Care is taken to ensure I/O access (uart)
with MMU off uses the "Unprotected Physical address".


Realm IPA State
---------------
Additionally, each page (4K) in the protected IPA space has a state associated
(Realm IPA State - RIPAS) with it. It is either of :
   RIPAS_EMPTY
   RIPAS_RAM

Any IPA backed by RAM, must be marked as RIPAS_RAM before an access is made to
it. The hypervisor/VMM does this for the initial image loaded into the Realm
memory before the Realm starts execution. Given the kvm-unit-test flat files do
not contain a metadata header (e.g., like the arm64 Linux kernel Image),
indicating the "actual image size in memory", the VMM cannot transition the
area towards the end of the image (e.g., bss, stack) which are accessed very
early during boot. Thus the early boot assembly code will mark the area upto
the stack as RAM.

Once we land in the C code, we mark target relocation area for FDT and
initrd as RIPAS_RAM. At this point, we can scan the FDT and mark all RAM memory
blocks as RIPAS_RAM.

TODO: It would be good to add an image header to the flat files indicating the
size, which can take the burden off doing the early assembly boot code RSI calls.

Shared Memory support
---------------------
Given the "default" memory of a VM is not accessible to host, we add new page
alloc/free routines for "memory shared" with the host. e.g., GICv3-ITS must use
shared pages for ITS emulation.

RSI Test suites
--------------
There are new testcases added to exercise the RSI interfaces and the RMM flows.

Attestation and measurement services related RSI tests require parsing tokens
and claims returned by the RMM. This is achieved with the help of QCBOR library
[2], which is added as a submodule to the project. We have also added a wrapper
library - libtokenverifier - around the QCBOR to parse the tokens according to
the RMM specifications.

Running Arm CCA Stack
-------------------

See more details on Arm CCA and how to build/run the entire stack here[0]
The easiest way to run the Arm CCA stack is using shrinkwrap and the details
are available in [0].


The patches are also available here :

 https://gitlab.arm.com/linux-arm/kvm-unit-tests-cca cca/v1


Changes since rfc:
  [ https://lkml.kernel.org/r/20230127114108.10025-1-joey.gouly@arm.com ]
  - Add support for RMM-v1.0-EAC5, changes to RSI ABIs
  - Some hardening checks (FDT overlapping the BSS sections)
  - Selftest for memory stress
  - Enable PMU/SVE tests for Realms

 [0] https://lkml.kernel.org/r/20240412084056.1733704-1-steven.price@arm.com
 [1] https://lkml.kernel.org/r/20210702163122.96110-1-alexandru.elisei@arm.com
 [2] https://github.com/laurencelundblade/QCBOR

Alexandru Elisei (3):
  arm64: Expand SMCCC arguments and return values
  arm: selftest: realm: skip pabt test when running in a realm
  NOT-FOR-MERGING: add run-realm-tests

Djordje Kovacevic (1):
  arm: realm: Add tests for in realm SEA

Gareth Stockwell (1):
  arm: realm: add hvc and RSI_HOST_CALL tests

Jean-Philippe Brucker (1):
  arm: Move io_init after vm initialization

Joey Gouly (10):
  arm: Make physical address mask dynamic
  arm64: Introduce NS_SHARED PTE attribute
  arm: realm: Add RSI interface header
  arm: realm: Make uart available before MMU is enabled
  arm: realm: Add RSI version test
  arm64: add ESR_ELx EC.SVE
  arm64: enable SVE at startup
  arm64: selftest: add realm SVE VL test
  lib/alloc_page: Add shared page allocation support
  arm: Add memtest support

Mate Toth-Pal (2):
  arm: Add a library to verify tokens using the QCBOR library
  arm: realm: Add Realm attestation tests

Subhasish Ghosh (1):
  arm: realm: Add test for FPU/SIMD context save/restore

Suzuki K Poulose (14):
  arm: Add necessary header files in asm/pgtable.h
  arm: Detect FDT overlap with uninitialised data
  arm: realm: Realm initialisation
  arm: realm: Add support for changing the state of memory
  arm: realm: Set RIPAS state for RAM
  arm: realm: Early memory setup
  arm: gic-v3-its: Use shared pages wherever needed
  arm: realm: Enable memory encryption
  qcbor: Add QCBOR as a submodule
  arm: Add build steps for QCBOR library
  arm: realm: add RSI interface for attestation measurements
  arm: realm: Add helpers to decode RSI return codes
  arm: realm: Add Realm attestation tests
  arm: realm: Add a test for shared memory

 .gitmodules                         |    3 +
 arm/Makefile.arm64                  |   25 +-
 arm/cstart.S                        |   49 +-
 arm/cstart64.S                      |  154 +++-
 arm/fpu.c                           |  424 +++++++++
 arm/realm-attest.c                  | 1251 +++++++++++++++++++++++++++
 arm/realm-ns-memory.c               |   86 ++
 arm/realm-rsi.c                     |  159 ++++
 arm/realm-sea.c                     |  143 +++
 arm/run-realm-tests                 |  112 +++
 arm/selftest.c                      |  138 ++-
 arm/unittests.cfg                   |   96 +-
 lib/alloc_page.c                    |   20 +-
 lib/alloc_page.h                    |   24 +
 lib/arm/asm/arm-smccc.h             |   44 +
 lib/arm/asm/io.h                    |    6 +
 lib/arm/asm/pgtable.h               |    9 +
 lib/arm/asm/psci.h                  |   13 +-
 lib/arm/asm/rsi.h                   |   21 +
 lib/arm/asm/sve-vl-test.h           |    9 +
 lib/arm/gic-v3.c                    |    6 +-
 lib/arm/io.c                        |   24 +-
 lib/arm/mmu.c                       |   80 +-
 lib/arm/psci.c                      |   19 +-
 lib/arm/setup.c                     |   26 +-
 lib/arm64/asm/arm-smccc.h           |    6 +
 lib/arm64/asm/esr.h                 |    1 +
 lib/arm64/asm/io.h                  |    6 +
 lib/arm64/asm/pgtable-hwdef.h       |    6 -
 lib/arm64/asm/pgtable.h             |   20 +
 lib/arm64/asm/processor.h           |   34 +
 lib/arm64/asm/rsi.h                 |   89 ++
 lib/arm64/asm/smc-rsi.h             |  173 ++++
 lib/arm64/asm/sve-vl-test.h         |   28 +
 lib/arm64/asm/sysreg.h              |    7 +
 lib/arm64/gic-v3-its.c              |    6 +-
 lib/arm64/processor.c               |    1 +
 lib/arm64/rsi.c                     |  188 ++++
 lib/asm-generic/io.h                |   12 +
 lib/libcflat.h                      |    1 +
 lib/qcbor                           |    1 +
 lib/token_verifier/attest_defines.h |   50 ++
 lib/token_verifier/token_dumper.c   |  157 ++++
 lib/token_verifier/token_dumper.h   |   15 +
 lib/token_verifier/token_verifier.c |  591 +++++++++++++
 lib/token_verifier/token_verifier.h |   77 ++
 46 files changed, 4355 insertions(+), 55 deletions(-)
 create mode 100644 .gitmodules
 create mode 100644 arm/fpu.c
 create mode 100644 arm/realm-attest.c
 create mode 100644 arm/realm-ns-memory.c
 create mode 100644 arm/realm-rsi.c
 create mode 100644 arm/realm-sea.c
 create mode 100755 arm/run-realm-tests
 create mode 100644 lib/arm/asm/arm-smccc.h
 create mode 100644 lib/arm/asm/rsi.h
 create mode 100644 lib/arm/asm/sve-vl-test.h
 create mode 100644 lib/arm64/asm/arm-smccc.h
 create mode 100644 lib/arm64/asm/rsi.h
 create mode 100644 lib/arm64/asm/smc-rsi.h
 create mode 100644 lib/arm64/asm/sve-vl-test.h
 create mode 100644 lib/arm64/rsi.c
 create mode 160000 lib/qcbor
 create mode 100644 lib/token_verifier/attest_defines.h
 create mode 100644 lib/token_verifier/token_dumper.c
 create mode 100644 lib/token_verifier/token_dumper.h
 create mode 100644 lib/token_verifier/token_verifier.c
 create mode 100644 lib/token_verifier/token_verifier.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 01/33] arm: Add necessary header files in asm/pgtable.h
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
  2024-04-10 16:17 ` Itaru Kitayama
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 02/33] arm: Detect FDT overlap with uninitialised data Suzuki K Poulose
                   ` (32 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

We use memalign() and other symbols defined elsewhere without explicitly including
them, which could cause build failures. Add the necessary header files.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm/asm/pgtable.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
index d7c73906..aa98d9ad 100644
--- a/lib/arm/asm/pgtable.h
+++ b/lib/arm/asm/pgtable.h
@@ -13,7 +13,9 @@
  *
  * This work is licensed under the terms of the GNU GPL, version 2.
  */
+#include <alloc.h>
 #include <alloc_page.h>
+#include <asm/setup.h>
 
 /*
  * We can convert va <=> pa page table addresses with simple casts
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 02/33] arm: Detect FDT overlap with uninitialised data
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
  2024-04-10 16:17 ` Itaru Kitayama
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 01/33] arm: Add necessary header files in asm/pgtable.h Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 03/33] arm64: Expand SMCCC arguments and return values Suzuki K Poulose
                   ` (31 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

If the FDT was placed in a region overlapping the bss/stack area, it
would have been overwritten at early boot. Assert this never happened
to detect the case.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm/setup.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/lib/arm/setup.c b/lib/arm/setup.c
index 2f649aff..462a1d51 100644
--- a/lib/arm/setup.c
+++ b/lib/arm/setup.c
@@ -35,6 +35,7 @@
 #define NR_MEM_REGIONS		(MAX_DT_MEM_REGIONS + NR_EXTRA_MEM_REGIONS)
 
 extern unsigned long _text, _etext, _data, _edata;
+extern unsigned long stacktop;
 
 char *initrd;
 u32 initrd_size;
@@ -196,6 +197,12 @@ static void freemem_push_fdt(void **freemem, const void *fdt)
 	u32 fdt_size;
 	int ret;
 
+	/*
+	 * Ensure that the FDT was not overlapping with the uninitialised
+	 * data that was overwritten.
+	 */
+	assert((unsigned long)fdt > (unsigned long)&stacktop);
+
 	fdt_size = fdt_totalsize(fdt);
 	ret = fdt_move(fdt, *freemem, fdt_size);
 	assert(ret == 0);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 03/33] arm64: Expand SMCCC arguments and return values
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (2 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 02/33] arm: Detect FDT overlap with uninitialised data Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 04/33] arm: Make physical address mask dynamic Suzuki K Poulose
                   ` (30 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Alexandru Elisei <alexandru.elisei@arm.com>

PSCI uses the SMC Calling Convention (SMCCC) to communicate with the higher
level software. PSCI uses at most 4 arguments and expend only one return
value. However, SMCCC has provisions for more arguments (upto 17 depending
on the SMCCC version) and upto 10 distinct return values.

We are going to be adding tests that make use of it, so add support for the
extended number of arguments and return values.

Also rename the SMCCC functions to generic, non-PSCI names, so they
can be used for Realm services.

Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com>
Co-developed-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/cstart.S              | 49 ++++++++++++++++++++++++++++------
 arm/cstart64.S            | 55 +++++++++++++++++++++++++++++++++------
 arm/selftest.c            |  2 +-
 lib/arm/asm/arm-smccc.h   | 44 +++++++++++++++++++++++++++++++
 lib/arm/asm/psci.h        | 13 +++++----
 lib/arm/psci.c            | 19 +++++++++++---
 lib/arm64/asm/arm-smccc.h |  6 +++++
 7 files changed, 160 insertions(+), 28 deletions(-)
 create mode 100644 lib/arm/asm/arm-smccc.h
 create mode 100644 lib/arm64/asm/arm-smccc.h

diff --git a/arm/cstart.S b/arm/cstart.S
index 3dd71ed9..29961c37 100644
--- a/arm/cstart.S
+++ b/arm/cstart.S
@@ -96,26 +96,59 @@ start:
 .text
 
 /*
- * psci_invoke_hvc / psci_invoke_smc
+ * arm_smccc_hvc / arm_smccc_smc
  *
  * Inputs:
  *   r0 -- function_id
  *   r1 -- arg0
  *   r2 -- arg1
  *   r3 -- arg2
+ *   [sp] - arg3
+ *   [sp + #4] - arg4
+ *   [sp + #8] - arg5
+ *   [sp + #12] - arg6
+ *   [sp + #16] - arg7
+ *   [sp + #20] - arg8
+ *   [sp + #24] - arg9
+ *   [sp + #28] - arg10
+ *   [sp + #32] - result (as a pointer to a struct smccc_result)
  *
  * Outputs:
  *   r0 -- return code
+ *
+ * If result pointer is not NULL:
+ *   result.r0 -- return code
+ *   result.r1 -- r1
+ *   result.r2 -- r2
+ *   result.r3 -- r3
+ *   result.r4 -- r4
+ *   result.r5 -- r5
+ *   result.r6 -- r6
+ *   result.r7 -- r7
+ *   result.r8 -- r8
+ *   result.r9 -- r9
  */
-.globl psci_invoke_hvc
-psci_invoke_hvc:
-	hvc	#0
+.macro do_smccc_call instr
+	mov	r12, sp
+	push	{r4-r11}
+	ldm	r12, {r4-r11}
+	\instr	#0
+	ldr	r10, [sp, #64]
+	cmp	r10, #0
+	beq	1f
+	stm	r10, {r0-r9}
+1:
+	pop	{r4-r11}
 	mov	pc, lr
+.endm
 
-.globl psci_invoke_smc
-psci_invoke_smc:
-	smc	#0
-	mov	pc, lr
+.globl arm_smccc_hvc
+arm_smccc_hvc:
+	do_smccc_call hvc
+
+.globl arm_smccc_smc
+arm_smccc_smc:
+	do_smccc_call smc
 
 enable_vfp:
 	/* Enable full access to CP10 and CP11: */
diff --git a/arm/cstart64.S b/arm/cstart64.S
index bc2be45a..734b2286 100644
--- a/arm/cstart64.S
+++ b/arm/cstart64.S
@@ -116,26 +116,65 @@ start:
 .text
 
 /*
- * psci_invoke_hvc / psci_invoke_smc
+ * arm_smccc_hvc / arm_smccc_smc
  *
  * Inputs:
  *   w0 -- function_id
  *   x1 -- arg0
  *   x2 -- arg1
  *   x3 -- arg2
+ *   x4 -- arg3
+ *   x5 -- arg4
+ *   x6 -- arg5
+ *   x7 -- arg6
+ *   sp -- { arg7, arg8, arg9, arg10, result }
  *
  * Outputs:
  *   x0 -- return code
+ *
+ * If result pointer is not NULL:
+ *   result.r0 -- return code
+ *   result.r1 -- x1
+ *   result.r2 -- x2
+ *   result.r3 -- x3
+ *   result.r4 -- x4
+ *   result.r5 -- x5
+ *   result.r6 -- x6
+ *   result.r7 -- x7
+ *   result.r8 -- x8
+ *   result.r9 -- x9
  */
-.globl psci_invoke_hvc
-psci_invoke_hvc:
-	hvc	#0
+.macro do_smccc_call instr
+	/* Save x8-x11 on stack */
+	stp	x9, x8,	  [sp, #-16]!
+	stp	x11, x10, [sp, #-16]!
+	/* Load arg7 - arg10 from the stack */
+	ldp	x8, x9,   [sp, #32]
+	ldp	x10, x11, [sp, #48]
+	\instr	#0
+	/* Get the result address */
+	ldr	x10, [sp, #64]
+	cmp	x10, xzr
+	b.eq	1f
+	stp	x0, x1, [x10, #0]
+	stp	x2, x3, [x10, #16]
+	stp	x4, x5, [x10, #32]
+	stp	x6, x7, [x10, #48]
+	stp	x8, x9, [x10, #64]
+1:
+	/* Restore x8-x11 from stack */
+	ldp	x11, x10, [sp], #16
+	ldp	x9, x8,   [sp], #16
 	ret
+.endm
 
-.globl psci_invoke_smc
-psci_invoke_smc:
-	smc	#0
-	ret
+.globl arm_smccc_hvc
+arm_smccc_hvc:
+	do_smccc_call hvc
+
+.globl arm_smccc_smc
+arm_smccc_smc:
+	do_smccc_call smc
 
 get_mmu_off:
 	adrp	x0, auxinfo
diff --git a/arm/selftest.c b/arm/selftest.c
index 007d2309..1553ed8e 100644
--- a/arm/selftest.c
+++ b/arm/selftest.c
@@ -406,7 +406,7 @@ static void psci_print(void)
 	int ver = psci_invoke(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
 	report_info("PSCI version: %d.%d", PSCI_VERSION_MAJOR(ver),
 					  PSCI_VERSION_MINOR(ver));
-	report_info("PSCI method: %s", psci_invoke == psci_invoke_hvc ?
+	report_info("PSCI method: %s", psci_invoke_fn == arm_smccc_hvc ?
 				       "hvc" : "smc");
 }
 
diff --git a/lib/arm/asm/arm-smccc.h b/lib/arm/asm/arm-smccc.h
new file mode 100644
index 00000000..5d85b01a
--- /dev/null
+++ b/lib/arm/asm/arm-smccc.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+#ifndef _ASMARM_ARM_SMCCC_H_
+#define _ASMARM_ARM_SMCCC_H_
+
+struct smccc_result {
+	unsigned long r0;
+	unsigned long r1;
+	unsigned long r2;
+	unsigned long r3;
+	unsigned long r4;
+	unsigned long r5;
+	unsigned long r6;
+	unsigned long r7;
+	unsigned long r8;
+	unsigned long r9;
+};
+
+typedef int (*smccc_invoke_fn)(unsigned int function_id, unsigned long arg0,
+			       unsigned long arg1, unsigned long arg2,
+			       unsigned long arg3, unsigned long arg4,
+			       unsigned long arg5, unsigned long arg6,
+			       unsigned long arg7, unsigned long arg8,
+			       unsigned long arg9, unsigned long arg10,
+			       struct smccc_result *result);
+extern int arm_smccc_hvc(unsigned int function_id, unsigned long arg0,
+			 unsigned long arg1, unsigned long arg2,
+			 unsigned long arg3, unsigned long arg4,
+			 unsigned long arg5, unsigned long arg6,
+			 unsigned long arg7, unsigned long arg8,
+			 unsigned long arg9, unsigned long arg10,
+			 struct smccc_result *result);
+extern int arm_smccc_smc(unsigned int function_id, unsigned long arg0,
+			 unsigned long arg1, unsigned long arg2,
+			 unsigned long arg3, unsigned long arg4,
+			 unsigned long arg5, unsigned long arg6,
+			 unsigned long arg7, unsigned long arg8,
+			 unsigned long arg9, unsigned long arg10,
+			 struct smccc_result *result);
+
+#endif /* _ASMARM_ARM_SMCCC_H_ */
diff --git a/lib/arm/asm/psci.h b/lib/arm/asm/psci.h
index cf03449b..6a399621 100644
--- a/lib/arm/asm/psci.h
+++ b/lib/arm/asm/psci.h
@@ -3,13 +3,12 @@
 #include <libcflat.h>
 #include <linux/psci.h>
 
-typedef int (*psci_invoke_fn)(unsigned int function_id, unsigned long arg0,
-			      unsigned long arg1, unsigned long arg2);
-extern psci_invoke_fn psci_invoke;
-extern int psci_invoke_hvc(unsigned int function_id, unsigned long arg0,
-			   unsigned long arg1, unsigned long arg2);
-extern int psci_invoke_smc(unsigned int function_id, unsigned long arg0,
-			   unsigned long arg1, unsigned long arg2);
+#include <asm/arm-smccc.h>
+
+extern smccc_invoke_fn psci_invoke_fn;
+
+extern int psci_invoke(unsigned int function_id, unsigned long arg0,
+		       unsigned long arg1, unsigned long arg2);
 extern void psci_set_conduit(void);
 extern int psci_cpu_on(unsigned long cpuid, unsigned long entry_point);
 extern void psci_system_reset(void);
diff --git a/lib/arm/psci.c b/lib/arm/psci.c
index bddb0787..25a84a4b 100644
--- a/lib/arm/psci.c
+++ b/lib/arm/psci.c
@@ -13,13 +13,24 @@
 #include <asm/smp.h>
 
 static int psci_invoke_none(unsigned int function_id, unsigned long arg0,
-			    unsigned long arg1, unsigned long arg2)
+			    unsigned long arg1, unsigned long arg2,
+			    unsigned long arg3, unsigned long arg4,
+			    unsigned long arg5, unsigned long arg6,
+			    unsigned long arg7, unsigned long arg8,
+			    unsigned long arg9, unsigned long arg10,
+			    struct smccc_result *result)
 {
 	printf("No PSCI method configured! Can't invoke...\n");
 	return PSCI_RET_NOT_PRESENT;
 }
 
-psci_invoke_fn psci_invoke = psci_invoke_none;
+smccc_invoke_fn psci_invoke_fn = psci_invoke_none;
+
+int psci_invoke(unsigned int function_id, unsigned long arg0,
+		unsigned long arg1, unsigned long arg2)
+{
+	return psci_invoke_fn(function_id, arg0, arg1, arg2, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
+}
 
 int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
 {
@@ -69,9 +80,9 @@ static void psci_set_conduit_fdt(void)
 	assert(method != NULL && len == 4);
 
 	if (strcmp(method->data, "hvc") == 0)
-		psci_invoke = psci_invoke_hvc;
+		psci_invoke_fn = arm_smccc_hvc;
 	else if (strcmp(method->data, "smc") == 0)
-		psci_invoke = psci_invoke_smc;
+		psci_invoke_fn = arm_smccc_smc;
 	else
 		assert_msg(false, "Unknown PSCI conduit: %s", method->data);
 }
diff --git a/lib/arm64/asm/arm-smccc.h b/lib/arm64/asm/arm-smccc.h
new file mode 100644
index 00000000..ab649489
--- /dev/null
+++ b/lib/arm64/asm/arm-smccc.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+#include "../../arm/asm/arm-smccc.h"
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 04/33] arm: Make physical address mask dynamic
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (3 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 03/33] arm64: Expand SMCCC arguments and return values Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 05/33] arm64: Introduce NS_SHARED PTE attribute Suzuki K Poulose
                   ` (29 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Joey Gouly <joey.gouly@arm.com>

We are about to add Realm support where the physical address width may be known
via RSI. Make the Physical Address mask dynamic, so that it can be adjusted
to the limit for the realm. This will be required for making pages shared, as
we introduce the "sharing" attribute as the top bit of the IPA.

Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm/mmu.c                 | 2 ++
 lib/arm/setup.c               | 1 +
 lib/arm64/asm/pgtable-hwdef.h | 6 ------
 lib/arm64/asm/pgtable.h       | 8 ++++++++
 4 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/lib/arm/mmu.c b/lib/arm/mmu.c
index 9dce7da8..5bbd6d76 100644
--- a/lib/arm/mmu.c
+++ b/lib/arm/mmu.c
@@ -23,6 +23,8 @@
 
 pgd_t *mmu_idmap;
 
+unsigned long phys_mask_shift = 48;
+
 /* CPU 0 starts with disabled MMU */
 static cpumask_t mmu_enabled_cpumask;
 
diff --git a/lib/arm/setup.c b/lib/arm/setup.c
index 462a1d51..34381218 100644
--- a/lib/arm/setup.c
+++ b/lib/arm/setup.c
@@ -23,6 +23,7 @@
 #include <asm/thread_info.h>
 #include <asm/setup.h>
 #include <asm/page.h>
+#include <asm/pgtable.h>
 #include <asm/processor.h>
 #include <asm/smp.h>
 #include <asm/timer.h>
diff --git a/lib/arm64/asm/pgtable-hwdef.h b/lib/arm64/asm/pgtable-hwdef.h
index 8c41fe12..ac95550b 100644
--- a/lib/arm64/asm/pgtable-hwdef.h
+++ b/lib/arm64/asm/pgtable-hwdef.h
@@ -115,12 +115,6 @@
 #define PTE_ATTRINDX(t)		(_AT(pteval_t, (t)) << 2)
 #define PTE_ATTRINDX_MASK	(_AT(pteval_t, 7) << 2)
 
-/*
- * Highest possible physical address supported.
- */
-#define PHYS_MASK_SHIFT		(48)
-#define PHYS_MASK		((UL(1) << PHYS_MASK_SHIFT) - 1)
-
 /*
  * TCR flags.
  */
diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
index bfb8a993..257fae76 100644
--- a/lib/arm64/asm/pgtable.h
+++ b/lib/arm64/asm/pgtable.h
@@ -21,6 +21,14 @@
 
 #include <linux/compiler.h>
 
+/*
+ * Highest possible physical address supported.
+ */
+extern unsigned long phys_mask_shift;
+#define PHYS_MASK_SHIFT		(phys_mask_shift)
+#define PHYS_MASK		((UL(1) << PHYS_MASK_SHIFT) - 1)
+
+
 /*
  * We can convert va <=> pa page table addresses with simple casts
  * because we always allocate their pages with alloc_page(), and
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 05/33] arm64: Introduce NS_SHARED PTE attribute
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (4 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 04/33] arm: Make physical address mask dynamic Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 06/33] arm: Move io_init after vm initialization Suzuki K Poulose
                   ` (28 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Joey Gouly <joey.gouly@arm.com>

Introduce a new attribute to indicate the mapping is "Shared" with the
host. This will be used by the Realms to share pages with the Host.
For normal VMs, this is always 0.

For realms, this is dynamic, depending on the IPA width. The top bit of the
IPA is "treated" as the "NS_SHARED" attribute, making the VM access the
unprotected alias of the IPA.

By default, apply the NS_SHARED attribute for all I/O.

Signed-off-by: Joey Gouly <joey.gouly@arm.com>
[ Fix arm32 build failure ]
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm/asm/pgtable.h   | 2 ++
 lib/arm/mmu.c           | 5 ++++-
 lib/arm64/asm/pgtable.h | 7 +++++++
 3 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
index aa98d9ad..350039ff 100644
--- a/lib/arm/asm/pgtable.h
+++ b/lib/arm/asm/pgtable.h
@@ -42,6 +42,8 @@
 	(((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))
 #define pgd_offset(pgtable, addr) ((pgtable) + pgd_index(addr))
 
+#define PTE_NS_SHARED		0
+
 #define pgd_free(pgd) free(pgd)
 static inline pgd_t *pgd_alloc(void)
 {
diff --git a/lib/arm/mmu.c b/lib/arm/mmu.c
index 5bbd6d76..41a8304d 100644
--- a/lib/arm/mmu.c
+++ b/lib/arm/mmu.c
@@ -23,6 +23,8 @@
 
 pgd_t *mmu_idmap;
 
+/* Used by Realms, depends on IPA size */
+unsigned long prot_ns_shared = 0;
 unsigned long phys_mask_shift = 48;
 
 /* CPU 0 starts with disabled MMU */
@@ -243,7 +245,8 @@ void __iomem *__ioremap(phys_addr_t phys_addr, size_t size)
 {
 	phys_addr_t paddr_aligned = phys_addr & PAGE_MASK;
 	phys_addr_t paddr_end = PAGE_ALIGN(phys_addr + size);
-	pgprot_t prot = __pgprot(PTE_UNCACHED | PTE_USER | PTE_UXN | PTE_PXN);
+	pgprot_t prot = __pgprot(PTE_UNCACHED | PTE_USER | PTE_UXN |
+				 PTE_PXN | PTE_NS_SHARED);
 	pgd_t *pgtable;
 
 	assert(sizeof(long) == 8 || !(phys_addr >> 32));
diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
index 257fae76..5b9f40b0 100644
--- a/lib/arm64/asm/pgtable.h
+++ b/lib/arm64/asm/pgtable.h
@@ -21,6 +21,13 @@
 
 #include <linux/compiler.h>
 
+extern unsigned long prot_ns_shared;
+/*
+ * The Non-secure shared bit for Realms is actually part of the output
+ * address, however it is modeled as a PTE attribute.
+*/
+#define PTE_NS_SHARED		(prot_ns_shared)
+
 /*
  * Highest possible physical address supported.
  */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 06/33] arm: Move io_init after vm initialization
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (5 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 05/33] arm64: Introduce NS_SHARED PTE attribute Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 07/33] arm: realm: Add RSI interface header Suzuki K Poulose
                   ` (27 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Jean-Philippe Brucker,
	Suzuki K Poulose

From: Jean-Philippe Brucker <jean-philippe@linaro.org>

To create shared pages, the NS_SHARED bit must be written into the
idmap. Before VM initializations, idmap hasn't necessarily been created.
To write shared pages, access must be done on a IPA with the NS_SHARED
bit. When the stage-1 MMU is enabled, that bit is set in the PTE. But
when the stage-1 MMU is disabled, then the realm must write to the IPA
with NS_SHARED directly.

To avoid changing the whole virtio infrastructure to support pre-MMU in
a realm, move the IO initialization after MMU enablement.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm/setup.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/arm/setup.c b/lib/arm/setup.c
index 34381218..fbb8f523 100644
--- a/lib/arm/setup.c
+++ b/lib/arm/setup.c
@@ -262,9 +262,6 @@ void setup(const void *fdt, phys_addr_t freemem_start)
 	/* cpu_init must be called before thread_info_init */
 	thread_info_init(current_thread_info(), 0);
 
-	/* mem_init must be called before io_init */
-	io_init();
-
 	timer_save_state();
 
 	ret = dt_get_bootargs(&bootargs);
@@ -275,6 +272,9 @@ void setup(const void *fdt, phys_addr_t freemem_start)
 
 	if (!(auxinfo.flags & AUXINFO_MMU_OFF))
 		setup_vm();
+
+	/* mem_init and setup_vm must be called before io_init */
+	io_init();
 }
 
 #ifdef CONFIG_EFI
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 07/33] arm: realm: Add RSI interface header
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (6 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 06/33] arm: Move io_init after vm initialization Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled Suzuki K Poulose
                   ` (26 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Joey Gouly <joey.gouly@arm.com>

Add the defintions for the Realm Service Interface (RSI). RSI calls are a way
for the Realm to communicate with the RMM and request information/services.

Signed-off-by: Joey Gouly <joey.gouly@arm.com>
[ Update RSI headers to match the RMM spec v1.0-eac5 ]
Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm64/asm/smc-rsi.h | 173 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 173 insertions(+)
 create mode 100644 lib/arm64/asm/smc-rsi.h

diff --git a/lib/arm64/asm/smc-rsi.h b/lib/arm64/asm/smc-rsi.h
new file mode 100644
index 00000000..7fafbb69
--- /dev/null
+++ b/lib/arm64/asm/smc-rsi.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+#ifndef __SMC_RSI_H_
+#define __SMC_RSI_H_
+
+/*
+ * This file describes the Realm Services Interface (RSI) Application Binary
+ * Interface (ABI) for SMC calls made from within the Realm to the RMM and
+ * serviced by the RMM.
+ */
+
+#define SMC_RSI_CALL_BASE		0xC4000190
+
+#define RSI_ABI_VERSION_MAJOR		1
+#define RSI_ABI_VERSION_MINOR		0
+
+#define RSI_ABI_VERSION			((RSI_ABI_VERSION_MAJOR << 16) | \
+					 RSI_ABI_VERSION_MINOR)
+
+#define RSI_ABI_VERSION_GET_MAJOR(_version) ((_version) >> 16)
+#define RSI_ABI_VERSION_GET_MINOR(_version) ((_version) & 0xFFFF)
+
+#define RSI_SUCCESS			0
+#define RSI_ERROR_INPUT			1
+#define RSI_ERROR_STATE			2
+#define RSI_INCOMPLETE			3
+#define RSI_ERROR_COUNT			4
+
+#define RSI_HASH_SHA_256		0
+#define RSI_HASH_SHA_512		1
+
+
+#define SMC_RSI_FID(_x)			(SMC_RSI_CALL_BASE + (_x))
+
+/*
+ * Returns whether the requested RSI version is compatible.
+ * arg1: Requested interface version.
+ * ret0: RSI_SUCCESS / RSI_ERROR_INPUT
+ * ret1: Lower implemented interface version
+ * ret2: Higher implemented interface version
+ */
+#define SMC_RSI_ABI_VERSION			SMC_RSI_FID(0)
+
+/*
+ * Returns RSI features.
+ * arg1: Feature register index
+ * ret0: Status
+ * ret1: Feature register value
+ */
+#define SMC_RSI_FEATURES			SMC_RSI_FID(1)
+
+
+/*
+ * Returns a measurement
+ * arg1 == Index (0..4), which measurement (RIM or REM) to read
+ * ret0 == Status / error
+ * ret1 == Measurement value, bytes:  0 -  7
+ * ret2 == Measurement value, bytes:  7 - 15
+ * ret3 == Measurement value, bytes: 16 - 23
+ * ret4 == Measurement value, bytes: 24 - 31
+ * ret5 == Measurement value, bytes: 32 - 39
+ * ret6 == Measurement value, bytes: 40 - 47
+ * ret7 == Measurement value, bytes: 48 - 55
+ * ret8 == Measurement value, bytes: 56 - 63
+ */
+#define SMC_RSI_MEASUREMENT_READ		SMC_RSI_FID(2)
+
+/*
+ * Extend a Realm Exetendable measurement.
+ * arg1  == Index (1..4), which measurement (REM) to extend
+ * arg2  == Size of realm measurement in bytes, max 64 bytes
+ * arg3  == Measurement value, bytes:  0 -  7
+ * arg4  == Measurement value, bytes:  7 - 15
+ * arg5  == Measurement value, bytes: 16 - 23
+ * arg6  == Measurement value, bytes: 24 - 31
+ * arg7  == Measurement value, bytes: 32 - 39
+ * arg8  == Measurement value, bytes: 40 - 47
+ * arg9  == Measurement value, bytes: 48 - 55
+ * arg10 == Measurement value, bytes: 56 - 63
+ * ret0  == Status / error
+ */
+#define SMC_RSI_MEASUREMENT_EXTEND		SMC_RSI_FID(3)
+
+/*
+ * Initialise the operation to retrieve an attestation token
+ * arg1 == Challenge value, bytes:  0 -  7
+ * arg2 == Challenge value, bytes:  7 - 15
+ * arg3 == Challenge value, bytes: 16 - 23
+ * arg4 == Challenge value, bytes: 24 - 31
+ * arg5 == Challenge value, bytes: 32 - 39
+ * arg6 == Challenge value, bytes: 40 - 47
+ * arg7 == Challenge value, bytes: 48 - 55
+ * arg8 == Challenge value, bytes: 56 - 63
+ * ret0 == Status / error
+ * ret1 == Upper bound of the token in bytes
+ */
+#define SMC_RSI_ATTEST_TOKEN_INIT		SMC_RSI_FID(4)
+
+/*
+ * Continue the operation to retrieve an attestation token
+ * arg1 == The IPA of token buffer
+ * arg2 == Offset within the from the beginning of @arg1
+ * arg3 == Space available from @arg2 in the buffer.
+ * ret0 == Status / error
+ * ret1 == Size of completed token in bytes
+ */
+#define SMC_RSI_ATTEST_TOKEN_CONTINUE		SMC_RSI_FID(5)
+
+
+
+#ifndef __ASSEMBLY__
+
+struct rsi_realm_config {
+	union {
+		struct {
+			/* Width of IPA in bits */
+			unsigned long ipa_width;
+			/* Hash algorithm */
+			unsigned long algorithm;
+		};
+		unsigned char __reserved0[0x1000];
+	};
+	/* Offset 0x1000 */
+};
+
+#endif /* __ASSEMBLY__ */
+
+/*
+ * arg0 == struct rsi_realm_config addr
+ */
+#define SMC_RSI_REALM_CONFIG			SMC_RSI_FID(6)
+
+/*
+ * arg0 == IPA address of target region
+ * arg1 == size of target region in bytes
+ * arg2 == RIPAS value
+ * arg3 == RipasChangeFlags
+ * ret0 == Status / error
+ * ret1 == Top of modified IPA range
+ */
+#define RSI_NO_CHANGE_DESTROYED			0
+#define RSI_CHANGE_DESTROYED			1
+
+#define SMC_RSI_IPA_STATE_SET			SMC_RSI_FID(7)
+
+/*
+ * Get the IPA state for the given IPA
+ * arg0 == IPA
+ * ret0 == Status/error
+ * ret1 == RIPAS value.
+ */
+#define SMC_RSI_IPA_STATE_GET			SMC_RSI_FID(8)
+
+#define RSI_HOST_CALL_NR_GPRS			31
+
+#ifndef __ASSEMBLY__
+
+struct rsi_host_call {
+	unsigned int imm;
+	unsigned long gprs[RSI_HOST_CALL_NR_GPRS];
+};
+
+#endif /* __ASSEMBLY__ */
+
+/*
+ * arg0 == struct rsi_host_call addr
+ */
+#define SMC_RSI_HOST_CALL			SMC_RSI_FID(9)
+
+#endif /* __SMC_RSI_H_ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (7 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 07/33] arm: realm: Add RSI interface header Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-22 11:58   ` Alexandru Elisei
  2024-04-22 15:38   ` Alexandru Elisei
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 09/33] arm: realm: Realm initialisation Suzuki K Poulose
                   ` (25 subsequent siblings)
  34 siblings, 2 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Joey Gouly <joey.gouly@arm.com>

A Realm must access any emulated I/O mappings with the PTE_NS_SHARED bit set.
This is modelled as a PTE attribute, but is actually part of the address.

So, when MMU is disabled, the "physical address" must reflect this bit set. We
access the UART early before the MMU is enabled. So, make sure the UART is
accessed always with the bit set.

Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm/asm/pgtable.h   |  5 +++++
 lib/arm/io.c            | 24 +++++++++++++++++++++++-
 lib/arm64/asm/pgtable.h |  5 +++++
 3 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
index 350039ff..7e85e7c6 100644
--- a/lib/arm/asm/pgtable.h
+++ b/lib/arm/asm/pgtable.h
@@ -112,4 +112,9 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
 	return pte_offset(pmd, addr);
 }
 
+static inline unsigned long arm_shared_phys_alias(void *x)
+{
+	return ((unsigned long)(x) | PTE_NS_SHARED);
+}
+
 #endif /* _ASMARM_PGTABLE_H_ */
diff --git a/lib/arm/io.c b/lib/arm/io.c
index 836fa854..127727e4 100644
--- a/lib/arm/io.c
+++ b/lib/arm/io.c
@@ -15,6 +15,8 @@
 #include <asm/psci.h>
 #include <asm/spinlock.h>
 #include <asm/io.h>
+#include <asm/mmu-api.h>
+#include <asm/pgtable.h>
 
 #include "io.h"
 
@@ -30,6 +32,24 @@ static struct spinlock uart_lock;
 static volatile u8 *uart0_base = UART_EARLY_BASE;
 bool is_pl011_uart;
 
+static inline volatile u8 *get_uart_base(void)
+{
+	/*
+	 * The address of the UART base may be different
+	 * based on whether we are running with/without
+	 * MMU enabled.
+	 *
+	 * For realms, we must force to use the shared physical
+	 * alias with MMU disabled, to make sure the I/O can
+	 * be emulated.
+	 * When the MMU is turned ON, the mappings are created
+	 * appropriately.
+	 */
+	if (mmu_enabled())
+		return uart0_base;
+	return (u8 *)arm_shared_phys_alias((void *)uart0_base);
+}
+
 static void uart0_init_fdt(void)
 {
 	/*
@@ -109,9 +129,11 @@ void io_init(void)
 
 void puts(const char *s)
 {
+	volatile u8 *uart_base = get_uart_base();
+
 	spin_lock(&uart_lock);
 	while (*s)
-		writeb(*s++, uart0_base);
+		writeb(*s++, uart_base);
 	spin_unlock(&uart_lock);
 }
 
diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
index 5b9f40b0..871c03e9 100644
--- a/lib/arm64/asm/pgtable.h
+++ b/lib/arm64/asm/pgtable.h
@@ -28,6 +28,11 @@ extern unsigned long prot_ns_shared;
 */
 #define PTE_NS_SHARED		(prot_ns_shared)
 
+static inline unsigned long arm_shared_phys_alias(void *addr)
+{
+	return ((unsigned long)addr | PTE_NS_SHARED);
+}
+
 /*
  * Highest possible physical address supported.
  */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 09/33] arm: realm: Realm initialisation
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (8 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 10/33] arm: realm: Add support for changing the state of memory Suzuki K Poulose
                   ` (24 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

During the boot, run a check for the presence of RMM. If we are Realm,
detect the Realm configuration using RSI and initialise the key parameters.

Also expose a helper to indicate if this is running inside a Realm

Co-developed-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/Makefile.arm64        |  1 +
 lib/arm/asm/rsi.h         | 18 ++++++++++
 lib/arm/setup.c           |  3 ++
 lib/arm64/asm/processor.h |  8 +++++
 lib/arm64/asm/rsi.h       | 37 ++++++++++++++++++++
 lib/arm64/rsi.c           | 73 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 140 insertions(+)
 create mode 100644 lib/arm/asm/rsi.h
 create mode 100644 lib/arm64/asm/rsi.h
 create mode 100644 lib/arm64/rsi.c

diff --git a/arm/Makefile.arm64 b/arm/Makefile.arm64
index 960880f1..bd167db1 100644
--- a/arm/Makefile.arm64
+++ b/arm/Makefile.arm64
@@ -26,6 +26,7 @@ cflatobjs += lib/arm64/stack.o
 cflatobjs += lib/arm64/processor.o
 cflatobjs += lib/arm64/spinlock.o
 cflatobjs += lib/arm64/gic-v3-its.o lib/arm64/gic-v3-its-cmd.o
+cflatobjs += lib/arm64/rsi.o
 
 ifeq ($(CONFIG_EFI),y)
 cflatobjs += lib/acpi.o
diff --git a/lib/arm/asm/rsi.h b/lib/arm/asm/rsi.h
new file mode 100644
index 00000000..5ff8d011
--- /dev/null
+++ b/lib/arm/asm/rsi.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+#ifndef __ASMARM_RSI_H_
+#define __ASMARM_RSI_H_
+
+#include <stdbool.h>
+
+static inline bool is_realm(void)
+{
+	return false;
+}
+
+static inline void arm_rsi_init(void) {}
+
+#endif /* __ASMARM_RSI_H_ */
diff --git a/lib/arm/setup.c b/lib/arm/setup.c
index fbb8f523..ebd6d058 100644
--- a/lib/arm/setup.c
+++ b/lib/arm/setup.c
@@ -25,6 +25,7 @@
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/processor.h>
+#include <asm/rsi.h>
 #include <asm/smp.h>
 #include <asm/timer.h>
 #include <asm/psci.h>
@@ -248,6 +249,8 @@ void setup(const void *fdt, phys_addr_t freemem_start)
 	assert(sizeof(long) == 8 || freemem_start < (3ul << 30));
 	freemem = (void *)(unsigned long)freemem_start;
 
+	arm_rsi_init();
+
 	freemem_push_fdt(&freemem, fdt);
 	freemem_push_dt_initrd(&freemem);
 
diff --git a/lib/arm64/asm/processor.h b/lib/arm64/asm/processor.h
index 1c73ba32..320ebaef 100644
--- a/lib/arm64/asm/processor.h
+++ b/lib/arm64/asm/processor.h
@@ -114,6 +114,14 @@ static inline unsigned long get_id_aa64mmfr0_el1(void)
 #define ID_AA64MMFR0_TGRAN64_SUPPORTED	0x0
 #define ID_AA64MMFR0_TGRAN16_SUPPORTED	0x1
 
+static inline unsigned long get_id_aa64pfr0_el1(void)
+{
+	return read_sysreg(id_aa64pfr0_el1);
+}
+
+#define ID_AA64PFR0_EL1_EL3	(0xf << 12)
+#define ID_AA64PFR0_EL1_EL3_NI	(0x0 << 12)
+
 static inline bool system_supports_granule(size_t granule)
 {
 	u32 shift;
diff --git a/lib/arm64/asm/rsi.h b/lib/arm64/asm/rsi.h
new file mode 100644
index 00000000..37103210
--- /dev/null
+++ b/lib/arm64/asm/rsi.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+#ifndef __ASMARM64_RSI_H_
+#define __ASMARM64_RSI_H_
+
+#include <stdbool.h>
+
+#include <asm/arm-smccc.h>
+#include <asm/io.h>
+#include <asm/smc-rsi.h>
+
+#define RSI_GRANULE_SIZE	SZ_4K
+
+extern bool rsi_present;
+
+void arm_rsi_init(void);
+
+int rsi_invoke(unsigned int function_id, unsigned long arg0,
+	       unsigned long arg1, unsigned long arg2,
+	       unsigned long arg3, unsigned long arg4,
+	       unsigned long arg5, unsigned long arg6,
+	       unsigned long arg7, unsigned long arg8,
+	       unsigned long arg9, unsigned long arg10,
+	       struct smccc_result *result);
+
+int __rsi_get_version(unsigned long ver, struct smccc_result *res);
+int rsi_get_version(unsigned long ver);
+
+static inline bool is_realm(void)
+{
+	return rsi_present;
+}
+
+#endif /* __ASMARM64_RSI_H_ */
diff --git a/lib/arm64/rsi.c b/lib/arm64/rsi.c
new file mode 100644
index 00000000..c4560866
--- /dev/null
+++ b/lib/arm64/rsi.c
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+#include <libcflat.h>
+
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/rsi.h>
+
+bool rsi_present;
+
+int rsi_invoke(unsigned int function_id, unsigned long arg0,
+	       unsigned long arg1, unsigned long arg2,
+	       unsigned long arg3, unsigned long arg4,
+	       unsigned long arg5, unsigned long arg6,
+	       unsigned long arg7, unsigned long arg8,
+	       unsigned long arg9, unsigned long arg10,
+	       struct smccc_result *result)
+{
+	return arm_smccc_smc(function_id, arg0, arg1, arg2, arg3, arg4, arg5,
+			     arg6, arg7, arg8, arg9, arg10, result);
+}
+
+struct rsi_realm_config __attribute__((aligned(RSI_GRANULE_SIZE))) config;
+
+static unsigned long rsi_get_realm_config(struct rsi_realm_config *cfg)
+{
+	struct smccc_result res;
+
+	rsi_invoke(SMC_RSI_REALM_CONFIG, __virt_to_phys((unsigned long)cfg),
+		   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &res);
+
+	return res.r0;
+}
+
+int __rsi_get_version(unsigned long ver, struct smccc_result *res)
+{
+	if ((get_id_aa64pfr0_el1() & ID_AA64PFR0_EL1_EL3) == ID_AA64PFR0_EL1_EL3_NI)
+		return -1;
+
+	return rsi_invoke(SMC_RSI_ABI_VERSION, ver, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		          0, res);
+}
+
+int rsi_get_version(unsigned long ver)
+{
+	struct smccc_result res = {};
+	int ret;
+
+
+	ret = __rsi_get_version(ver, &res);
+	if (ret == -1)
+		return ret;
+
+	return res.r0;
+}
+
+void arm_rsi_init(void)
+{
+	if (rsi_get_version(RSI_ABI_VERSION) != RSI_SUCCESS)
+		return;
+
+	if (rsi_get_realm_config(&config))
+		return;
+
+	rsi_present = true;
+
+	phys_mask_shift = (config.ipa_width - 1);
+	/* Set the upper bit of the IPA as the NS_SHARED pte attribute */
+	prot_ns_shared = (1UL << phys_mask_shift);
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 10/33] arm: realm: Add support for changing the state of memory
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (9 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 09/33] arm: realm: Realm initialisation Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 11/33] arm: realm: Set RIPAS state for RAM Suzuki K Poulose
                   ` (23 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

For a Realm, the guest physical address (in reality the IPA/GPA of the VM)
has an associated state (Realm IPA State, RIPAS) which is one of :
   RIPAS_RAM
   RIPAS_EMPTY
   RIPAS_DESTROYED

The state of the physical address decides certain behaviors. e.g., any access
to a RIPAS_EMPTY PA will generate a Synchronous External Abort back to the Realm,
from the RMM.

All "PA" that represents RAM for the Realm, must be set to RIPAS_RAM before
an access is made. When the initial image (e.g., test, DTB) of a Realm is
loaded, the hypervisor/VMM can transition the state of the loaded "area" to
RIPAS_RAM. The rest of the "RAM" must be transitioned by the test payload
before any access is made.

Similarly, a Realm could set an "IPA" to RIPAS_EMPTY, when it is about to use
the "unprotected" alias of the IPA. This is a hint for the host to reclaim the
page from the protected "IPA.

RIPAS_DESTROYED indicates that the Host has destroyed a data granule at the IPA,
without the consent from the realm and is not reachable by a Realm action.

This patchs adds supporting helpers for setting the IPA state from Realm. These
will be used later for the Realm.

Co-developed-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm/asm/rsi.h   |  3 +++
 lib/arm/mmu.c       |  1 +
 lib/arm64/asm/rsi.h |  9 +++++++
 lib/arm64/rsi.c     | 63 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 76 insertions(+)

diff --git a/lib/arm/asm/rsi.h b/lib/arm/asm/rsi.h
index 5ff8d011..98e75303 100644
--- a/lib/arm/asm/rsi.h
+++ b/lib/arm/asm/rsi.h
@@ -14,5 +14,8 @@ static inline bool is_realm(void)
 }
 
 static inline void arm_rsi_init(void) {}
+static inline void arm_set_memory_protected(unsigned long va, size_t size) {}
+static inline void arm_set_memory_protected_safe(unsigned long va, size_t size) {}
+static inline void arm_set_memory_shared(unsigned long va, size_t size) {}
 
 #endif /* __ASMARM_RSI_H_ */
diff --git a/lib/arm/mmu.c b/lib/arm/mmu.c
index 41a8304d..16ceffcc 100644
--- a/lib/arm/mmu.c
+++ b/lib/arm/mmu.c
@@ -13,6 +13,7 @@
 #include <asm/setup.h>
 #include <asm/page.h>
 #include <asm/io.h>
+#include <asm/rsi.h>
 
 #include "alloc_page.h"
 #include "vmalloc.h"
diff --git a/lib/arm64/asm/rsi.h b/lib/arm64/asm/rsi.h
index 37103210..0b726684 100644
--- a/lib/arm64/asm/rsi.h
+++ b/lib/arm64/asm/rsi.h
@@ -34,4 +34,13 @@ static inline bool is_realm(void)
 	return rsi_present;
 }
 
+enum ripas_t {
+	RIPAS_EMPTY,
+	RIPAS_RAM,
+};
+
+void arm_set_memory_protected(unsigned long va, size_t size);
+void arm_set_memory_protected_safe(unsigned long va, size_t size);
+void arm_set_memory_shared(unsigned long va, size_t size);
+
 #endif /* __ASMARM64_RSI_H_ */
diff --git a/lib/arm64/rsi.c b/lib/arm64/rsi.c
index c4560866..e58d9660 100644
--- a/lib/arm64/rsi.c
+++ b/lib/arm64/rsi.c
@@ -71,3 +71,66 @@ void arm_rsi_init(void)
 	/* Set the upper bit of the IPA as the NS_SHARED pte attribute */
 	prot_ns_shared = (1UL << phys_mask_shift);
 }
+
+static unsigned rsi_set_addr_range_state(unsigned long start, unsigned long end,
+					 enum ripas_t state, unsigned int flags,
+					 unsigned long *top)
+{
+	struct smccc_result res;
+
+	rsi_invoke(SMC_RSI_IPA_STATE_SET, start, end, state, flags,
+		   0, 0, 0, 0, 0, 0, 0, &res);
+	*top = res.r1;
+	return res.r0;
+}
+
+static void arm_set_memory_state(unsigned long start,
+				 unsigned long size,
+				 unsigned int ripas,
+				 unsigned int flags)
+{
+	int ret;
+	unsigned long end, top;
+	unsigned long old_start = start;
+
+	if (!is_realm())
+		return;
+
+	start = ALIGN_DOWN(start, RSI_GRANULE_SIZE);
+	if (start != old_start)
+		size += old_start - start;
+	end = ALIGN(start + size, RSI_GRANULE_SIZE);
+	while (start != end) {
+		ret = rsi_set_addr_range_state(start, end, ripas, flags, &top);
+		assert(!ret);
+		assert(top <= end);
+		start = top;
+	}
+}
+
+/*
+ * Convert the IPA state of the given range to RIPAS_RAM, ignoring the
+ * fact that the host could have destroyed the contents and we don't
+ * rely on the previous state of the contents.
+ */
+void arm_set_memory_protected(unsigned long start, unsigned long size)
+{
+	arm_set_memory_state(start, size, RIPAS_RAM, RSI_CHANGE_DESTROYED);
+}
+
+/*
+ * Convert the IPA state of the given range to RSI_RAM, ensuring that the
+ * host has not destroyed any of the contents in the IPA range. Useful in
+ * converting a range of addresses where some of the IPA may already be in
+ * RSI_RAM state (e.g., images loaded at boot) and we want to make sure the
+ * host hasn't modified (by destroying them) the contents.
+ */
+void arm_set_memory_protected_safe(unsigned long start, unsigned long size)
+{
+	arm_set_memory_state(start, size, RIPAS_RAM, RSI_NO_CHANGE_DESTROYED);
+}
+
+void arm_set_memory_shared(unsigned long start, unsigned long size)
+{
+	arm_set_memory_state(start, size, RIPAS_EMPTY, RSI_CHANGE_DESTROYED);
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 11/33] arm: realm: Set RIPAS state for RAM
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (10 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 10/33] arm: realm: Add support for changing the state of memory Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 12/33] arm: realm: Early memory setup Suzuki K Poulose
                   ` (22 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

A Realm must ensure that the "RAM" region is set to RIPAS_RAM, before any
access is made. This patch makes sure that all memory blocks are marked as
RIPAS_RAM. Also, before we relocate the "FDT" and "initrd", make sure the
target location is marked too. This happens before we parse the memory blocks.

It is OK to do this operation on a given IPA multiple times. So, we don't
exclude the inital image areas from the "target" list.

Also, this operation doesn't require the host to commit physical memory to back
the IPAs yet. It can be done on demand via fault handling.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm/setup.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/lib/arm/setup.c b/lib/arm/setup.c
index ebd6d058..d726c32a 100644
--- a/lib/arm/setup.c
+++ b/lib/arm/setup.c
@@ -184,6 +184,7 @@ static void mem_init(phys_addr_t freemem_start)
 	while (r && r->end != mem.end)
 		r = memregions_find(r->end);
 	assert(r);
+	arm_set_memory_protected_safe(r->start, r->end - r->start);
 
 	/* Ensure our selected freemem range is somewhere in our full range */
 	assert(freemem_start >= mem.start && freemem->end <= mem.end);
@@ -206,7 +207,14 @@ static void freemem_push_fdt(void **freemem, const void *fdt)
 	assert((unsigned long)fdt > (unsigned long)&stacktop);
 
 	fdt_size = fdt_totalsize(fdt);
+
+	/*
+	 * Before we touch the memory @freemem, make sure it
+	 * is set to protected for Realms.
+	 */
+	arm_set_memory_protected_safe((unsigned long)*freemem, fdt_size);
 	ret = fdt_move(fdt, *freemem, fdt_size);
+
 	assert(ret == 0);
 	ret = dt_init(*freemem);
 	assert(ret == 0);
@@ -222,6 +230,7 @@ static void freemem_push_dt_initrd(void **freemem)
 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
 	if (ret == 0) {
 		initrd = *freemem;
+		arm_set_memory_protected_safe((unsigned long)initrd, initrd_size);
 		memmove(initrd, tmp, initrd_size);
 		*freemem += initrd_size;
 	}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 12/33] arm: realm: Early memory setup
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (11 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 11/33] arm: realm: Set RIPAS state for RAM Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 13/33] arm: realm: Add RSI version test Suzuki K Poulose
                   ` (21 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose,
	Jean-Philippe Brucker

A Realm must mark areas of memory as RIPAS_RAM before an access is made.

The binary image is loaded by the VMM and thus the area is converted.
However, the file image may not cover tail portion of the "memory" image (e.g,
BSS, stack etc.). Convert the area touched by the early boot code to RAM
before the access is made in early assembly code.

Once, we land in the C code, we take care of converting the entire RAM region
to RIPAS_RAM.

Please note that this operation doesn't require the host to commit memory to
the Realm.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Co-developed-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Co-developed-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/cstart64.S | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)

diff --git a/arm/cstart64.S b/arm/cstart64.S
index 734b2286..92631349 100644
--- a/arm/cstart64.S
+++ b/arm/cstart64.S
@@ -14,6 +14,7 @@
 #include <asm/pgtable-hwdef.h>
 #include <asm/thread_info.h>
 #include <asm/sysreg.h>
+#include <asm/smc-rsi.h>
 
 #ifdef CONFIG_EFI
 #include "efi/crt0-efi-aarch64.S"
@@ -65,6 +66,11 @@ start:
 	b	1b
 
 1:
+	/*
+	 * For a Realm, before we touch any memory, we must
+	 * make sure it is in the RsiRipas == RSI_RAM state.
+	 */
+	bl	__early_mem_setup
 	/* zero BSS */
 	adrp	x4, bss
 	add	x4, x4, :lo12:bss
@@ -176,6 +182,94 @@ arm_smccc_hvc:
 arm_smccc_smc:
 	do_smccc_call smc
 
+__early_mem_setup:
+	/* Preserve x0 - x3 */
+	mov	x5, x0
+	mov	x6, x1
+	mov	x7, x2
+	mov	x8, x3
+
+	/*
+	 * Check for EL3, otherwise an SMC instruction
+	 * will cause an UNDEFINED exception.
+	 */
+	mrs	x9, ID_AA64PFR0_EL1
+	lsr	x9, x9, #12
+	and	x9, x9, 0b11
+	cbnz	x9, 1f
+	ret
+
+1:
+	/*
+	 * Are we a realm? Request the RSI ABI version.
+	 * If KVM is catching SMCs, it returns an error in x0 (~0UL)
+	 */
+	movz	x0, :abs_g2_s:SMC_RSI_ABI_VERSION
+	movk	x0, :abs_g1_nc:SMC_RSI_ABI_VERSION
+	movk	x0, :abs_g0_nc:SMC_RSI_ABI_VERSION
+	ldr	x1, =RSI_ABI_VERSION
+	smc	#0
+
+	/*
+	 * RMM if present, returns RSI_SUCCESS if the requested
+	 * version is compatible. Otherwise returns RSI_ERROR_INPUT,
+	 * which is fatal for the Realm.
+	 */
+	cmp	x0, #RSI_ERROR_INPUT
+	beq	halt
+
+	/*
+	 * Anything other than RSI_SUCCESS or RSI_ERROR_INPUT
+	 * indicates, RMM is not present.
+	 */
+	cmp	x0, #RSI_SUCCESS
+	bne	3f
+
+	/*
+	 * For realms, we must mark area from bss
+	 * to the end of stack as memory before it is
+	 * accessed, as they are not populated as part
+	 * of the initial image. As such we can run
+	 * this unconditionally irrespective of whether
+	 * we are a normal VM or Realm.
+	 */
+	/* x1 = bss */
+	adrp	x1, bss
+
+	/* x7 = SMC_RSI_IPA_STATE_SET */
+	movz	x7, :abs_g2_s:SMC_RSI_IPA_STATE_SET
+	movk	x7, :abs_g1_nc:SMC_RSI_IPA_STATE_SET
+	movk	x7, :abs_g0_nc:SMC_RSI_IPA_STATE_SET
+
+	/* x9 = (end of stack) */
+	adrp	x9, (stacktop + PAGE_SIZE)
+2:
+	/* x2 = (end of stack) */
+	mov	x2, x9
+
+	/* x3 = RIPAS_RAM */
+	mov	x3, #1
+	/* x4 = RSI_NO_CHANGE_DESTROYED */
+	mov	x4, #RSI_NO_CHANGE_DESTROYED
+
+	/* x0 = SMC_RSI_IPA_STATE_SET */
+	mov	x0, x7
+	/* Run the RSI request */
+	smc	#0
+
+	/* halt if there is an error */
+	cbnz x0, halt
+
+	/* Check if (next == end of stack) */
+	cmp x1, x9
+	bne 2b
+3:
+	mov	x3, x8
+	mov	x2, x7
+	mov	x1, x6
+	mov	x0, x5
+	ret
+
 get_mmu_off:
 	adrp	x0, auxinfo
 	ldr	x0, [x0, :lo12:auxinfo + 8]
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 13/33] arm: realm: Add RSI version test
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (12 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 12/33] arm: realm: Early memory setup Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 14/33] arm: selftest: realm: skip pabt test when running in a realm Suzuki K Poulose
                   ` (20 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Joey Gouly <joey.gouly@arm.com>

Add basic test for checking the RSI version command.

Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/Makefile.arm64 |  1 +
 arm/realm-rsi.c    | 51 ++++++++++++++++++++++++++++++++++++++++++++++
 arm/unittests.cfg  |  7 +++++++
 3 files changed, 59 insertions(+)
 create mode 100644 arm/realm-rsi.c

diff --git a/arm/Makefile.arm64 b/arm/Makefile.arm64
index bd167db1..90d95e79 100644
--- a/arm/Makefile.arm64
+++ b/arm/Makefile.arm64
@@ -49,6 +49,7 @@ tests = $(TEST_DIR)/timer.$(exe)
 tests += $(TEST_DIR)/micro-bench.$(exe)
 tests += $(TEST_DIR)/cache.$(exe)
 tests += $(TEST_DIR)/debug.$(exe)
+tests += $(TEST_DIR)/realm-rsi.$(exe)
 
 include $(SRCDIR)/$(TEST_DIR)/Makefile.common
 
diff --git a/arm/realm-rsi.c b/arm/realm-rsi.c
new file mode 100644
index 00000000..6c228e42
--- /dev/null
+++ b/arm/realm-rsi.c
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+
+#include <libcflat.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/processor.h>
+#include <asm/psci.h>
+#include <alloc_page.h>
+#include <asm/rsi.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+
+static void rsi_test_version(void)
+{
+	struct smccc_result res;
+	int ret, version;
+
+	report_prefix_push("version");
+
+	ret = __rsi_get_version(RSI_ABI_VERSION, &res);
+	if (ret < 0) {
+		report(false, "SMC_RSI_ABI_VERSION failed (%d)", ret);
+		return;
+	}
+
+	version = res.r1;
+	report(res.r0 == RSI_SUCCESS, "RSI ABI version %u.%u (expected: %u.%u)",
+	       RSI_ABI_VERSION_GET_MAJOR(version),
+	       RSI_ABI_VERSION_GET_MINOR(version),
+	       RSI_ABI_VERSION_GET_MAJOR(RSI_ABI_VERSION),
+	       RSI_ABI_VERSION_GET_MINOR(RSI_ABI_VERSION));
+	report_prefix_pop();
+}
+
+int main(int argc, char **argv)
+{
+	report_prefix_push("rsi");
+
+	if (!is_realm()) {
+		report_skip("Not a realm, skipping tests");
+		goto exit;
+	}
+
+	rsi_test_version();
+exit:
+	return report_summary();
+}
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
index fe601cbb..a46c9ec7 100644
--- a/arm/unittests.cfg
+++ b/arm/unittests.cfg
@@ -281,3 +281,10 @@ file = debug.flat
 arch = arm64
 extra_params = -append 'ss-migration'
 groups = debug migration
+
+# Realm RSI ABI test
+[realm-rsi]
+file = realm-rsi.flat
+groups = nodefault realms
+accel = kvm
+arch = arm64
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 14/33] arm: selftest: realm: skip pabt test when running in a realm
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (13 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 13/33] arm: realm: Add RSI version test Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-22 15:48   ` Alexandru Elisei
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 15/33] arm: realm: add hvc and RSI_HOST_CALL tests Suzuki K Poulose
                   ` (19 subsequent siblings)
  34 siblings, 1 reply; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Alexandru Elisei <alexandru.elisei@arm.com>

The realm manager treats instruction aborts as fatal errors, skip this
test.

Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com>
Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/selftest.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arm/selftest.c b/arm/selftest.c
index 1553ed8e..8caadad3 100644
--- a/arm/selftest.c
+++ b/arm/selftest.c
@@ -19,6 +19,7 @@
 #include <asm/smp.h>
 #include <asm/mmu.h>
 #include <asm/barrier.h>
+#include <asm/rsi.h>
 
 static cpumask_t ready, valid;
 
@@ -393,11 +394,17 @@ static void check_vectors(void *arg __unused)
 					  user_psci_system_off);
 #endif
 	} else {
+		if (is_realm()) {
+			report_skip("pabt test not supported in a realm");
+			goto out;
+		}
+
 		if (!check_pabt_init())
 			report_skip("Couldn't guess an invalid physical address");
 		else
 			report(check_pabt(), "pabt");
 	}
+out:
 	exit(report_summary());
 }
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 15/33] arm: realm: add hvc and RSI_HOST_CALL tests
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (14 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 14/33] arm: selftest: realm: skip pabt test when running in a realm Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 16/33] arm64: add ESR_ELx EC.SVE Suzuki K Poulose
                   ` (18 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Gareth Stockwell, Suzuki K Poulose

From: Gareth Stockwell <gareth.stockwell@arm.com>

Test that a HVC instruction in a Realm is turned into an undefined exception.

Test that RSI_HOST_CALL passes through to the Hypervisor.

Signed-off-by: Gareth Stockwell <gareth.stockwell@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/realm-rsi.c   | 110 +++++++++++++++++++++++++++++++++++++++++++++-
 arm/unittests.cfg |  15 +++++++
 2 files changed, 124 insertions(+), 1 deletion(-)

diff --git a/arm/realm-rsi.c b/arm/realm-rsi.c
index 6c228e42..5b90a343 100644
--- a/arm/realm-rsi.c
+++ b/arm/realm-rsi.c
@@ -14,6 +14,96 @@
 #include <asm/pgtable.h>
 #include <asm/processor.h>
 
+#define FID_SMCCC_VERSION	0x80000000
+#define FID_INVALID		0xc5000041
+
+#define SMCCC_VERSION_1_1	0x10001
+#define SMCCC_SUCCESS		0
+#define SMCCC_NOT_SUPPORTED	-1
+
+static bool unknown_taken;
+
+static void unknown_handler(struct pt_regs *regs, unsigned int esr)
+{
+	report_info("unknown_handler: esr=0x%x", esr);
+	unknown_taken = true;
+}
+
+static void hvc_call(unsigned int fid)
+{
+	struct smccc_result res;
+
+	unknown_taken = false;
+	arm_smccc_hvc(fid, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &res);
+
+	if (unknown_taken) {
+		report(true, "FID=0x%x caused Unknown exception", fid);
+	} else {
+		report(false, "FID=0x%x did not cause Unknown exception", fid);
+		report_info("x0:  0x%lx", res.r0);
+		report_info("x1:  0x%lx", res.r1);
+		report_info("x2:  0x%lx", res.r2);
+		report_info("x3:  0x%lx", res.r3);
+		report_info("x4:  0x%lx", res.r4);
+		report_info("x5:  0x%lx", res.r5);
+		report_info("x6:  0x%lx", res.r6);
+		report_info("x7:  0x%lx", res.r7);
+	}
+}
+
+static void rsi_test_hvc(void)
+{
+	report_prefix_push("hvc");
+
+	/* Test that HVC causes Undefined exception, regardless of FID */
+	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_UNKNOWN, unknown_handler);
+	hvc_call(FID_SMCCC_VERSION);
+	hvc_call(FID_INVALID);
+	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_UNKNOWN, NULL);
+
+	report_prefix_pop();
+}
+
+static void host_call(unsigned int fid, unsigned long expected_x0)
+{
+	struct smccc_result res;
+	struct rsi_host_call __attribute__((aligned(256))) host_call_data = { 0 };
+
+	host_call_data.gprs[0] = fid;
+
+	arm_smccc_smc(SMC_RSI_HOST_CALL, virt_to_phys(&host_call_data),
+		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &res);
+
+	if (res.r0) {
+		report(false, "RSI_HOST_CALL returned 0x%lx", res.r0);
+	} else {
+		if (host_call_data.gprs[0] == expected_x0) {
+			report(true, "FID=0x%x x0=0x%lx",
+				fid, host_call_data.gprs[0]);
+		} else {
+			report(false, "FID=0x%x x0=0x%lx expected=0x%lx",
+				fid, host_call_data.gprs[0], expected_x0);
+			report_info("x1:  0x%lx", host_call_data.gprs[1]);
+			report_info("x2:  0x%lx", host_call_data.gprs[2]);
+			report_info("x3:  0x%lx", host_call_data.gprs[3]);
+			report_info("x4:  0x%lx", host_call_data.gprs[4]);
+			report_info("x5:  0x%lx", host_call_data.gprs[5]);
+			report_info("x6:  0x%lx", host_call_data.gprs[6]);
+		}
+	}
+}
+
+static void rsi_test_host_call(void)
+{
+	report_prefix_push("host_call");
+
+	/* Test that host calls return expected values */
+	host_call(FID_SMCCC_VERSION, SMCCC_VERSION_1_1);
+	host_call(FID_INVALID, SMCCC_NOT_SUPPORTED);
+
+	report_prefix_pop();
+}
+
 static void rsi_test_version(void)
 {
 	struct smccc_result res;
@@ -38,6 +128,8 @@ static void rsi_test_version(void)
 
 int main(int argc, char **argv)
 {
+	int i;
+
 	report_prefix_push("rsi");
 
 	if (!is_realm()) {
@@ -45,7 +137,23 @@ int main(int argc, char **argv)
 		goto exit;
 	}
 
-	rsi_test_version();
+	if (argc < 2) {
+		rsi_test_version();
+		rsi_test_host_call();
+		rsi_test_hvc();
+	} else {
+		for (i = 1; i < argc; i++) {
+			if (strcmp(argv[i], "version") == 0) {
+				rsi_test_version();
+			} else if (strcmp(argv[i], "hvc") == 0) {
+				rsi_test_hvc();
+			} else if (strcmp(argv[i], "host_call") == 0) {
+				rsi_test_host_call();
+			} else {
+				report_abort("Unknown subtest '%s'", argv[1]);
+			}
+		}
+	}
 exit:
 	return report_summary();
 }
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
index a46c9ec7..b5be6668 100644
--- a/arm/unittests.cfg
+++ b/arm/unittests.cfg
@@ -286,5 +286,20 @@ groups = debug migration
 [realm-rsi]
 file = realm-rsi.flat
 groups = nodefault realms
+extra_params = -append 'version'
+accel = kvm
+arch = arm64
+
+[realm-host-call]
+file = realm-rsi.flat
+groups = nodefault realms
+extra_params = -append 'host_call'
+accel = kvm
+arch = arm64
+
+[realm-hvc]
+file = realm-rsi.flat
+groups = nodefault realms
+extra_params = -append 'hvc'
 accel = kvm
 arch = arm64
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 16/33] arm64: add ESR_ELx EC.SVE
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (15 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 15/33] arm: realm: add hvc and RSI_HOST_CALL tests Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 17/33] arm64: enable SVE at startup Suzuki K Poulose
                   ` (17 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Joey Gouly <joey.gouly@arm.com>

Add the SVE exception class, so that SVE exceptions are not printed
as 'unknown' exceptions.

Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm64/asm/esr.h   | 1 +
 lib/arm64/processor.c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/lib/arm64/asm/esr.h b/lib/arm64/asm/esr.h
index 8c351631..335343c5 100644
--- a/lib/arm64/asm/esr.h
+++ b/lib/arm64/asm/esr.h
@@ -26,6 +26,7 @@
 #define ESR_EL1_EC_SVC32	(0x11)
 #define ESR_EL1_EC_SVC64	(0x15)
 #define ESR_EL1_EC_SYS64	(0x18)
+#define ESR_EL1_EC_SVE		(0x19)
 #define ESR_EL1_EC_IABT_EL0	(0x20)
 #define ESR_EL1_EC_IABT_EL1	(0x21)
 #define ESR_EL1_EC_PC_ALIGN	(0x22)
diff --git a/lib/arm64/processor.c b/lib/arm64/processor.c
index 06fd7cfc..eb93fd7c 100644
--- a/lib/arm64/processor.c
+++ b/lib/arm64/processor.c
@@ -43,6 +43,7 @@ static const char *ec_names[EC_MAX] = {
 	[ESR_EL1_EC_SVC32]		= "SVC32",
 	[ESR_EL1_EC_SVC64]		= "SVC64",
 	[ESR_EL1_EC_SYS64]		= "SYS64",
+	[ESR_EL1_EC_SVE]		= "SVE",
 	[ESR_EL1_EC_IABT_EL0]		= "IABT_EL0",
 	[ESR_EL1_EC_IABT_EL1]		= "IABT_EL1",
 	[ESR_EL1_EC_PC_ALIGN]		= "PC_ALIGN",
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 17/33] arm64: enable SVE at startup
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (16 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 16/33] arm64: add ESR_ELx EC.SVE Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 18/33] arm: realm: Add test for FPU/SIMD context save/restore Suzuki K Poulose
                   ` (16 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Joey Gouly <joey.gouly@arm.com>

Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/cstart64.S | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/arm/cstart64.S b/arm/cstart64.S
index 92631349..c081365f 100644
--- a/arm/cstart64.S
+++ b/arm/cstart64.S
@@ -94,8 +94,9 @@ start:
 	adrp    x4, stackptr
 	add     sp, x4, :lo12:stackptr
 
-	/* enable FP/ASIMD */
-	mov	x4, #(3 << 20)
+	/* enable FP/ASIMD and SVE */
+	mov	x4, (3 << 20)
+	orr	x4, x4, (3 << 16)
 	msr	cpacr_el1, x4
 
 	/* set up exception handling */
@@ -278,8 +279,9 @@ get_mmu_off:
 
 .globl secondary_entry
 secondary_entry:
-	/* Enable FP/ASIMD */
+	/* enable FP/ASIMD and SVE */
 	mov	x0, #(3 << 20)
+	orr	x0, x0, #(3 << 16)
 	msr	cpacr_el1, x0
 
 	/* set up exception handling */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 18/33] arm: realm: Add test for FPU/SIMD context save/restore
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (17 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 17/33] arm64: enable SVE at startup Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 19/33] arm64: selftest: add realm SVE VL test Suzuki K Poulose
                   ` (15 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Subhasish Ghosh, Suzuki K Poulose

From: Subhasish Ghosh <subhasish.ghosh@arm.com>

Test that the FPU/SIMD registers are saved and restored correctly when
context switching CPUs.

In order to test fpu/simd functionality, we need to make sure that
kvm-unit-tests doesn't generate code that uses the fpu registers, as that
might interfere with the test results. Thus make sure we compile the tests
with -mgeneral-regs-only.

Signed-off-by: Subhasish Ghosh <subhasish.ghosh@arm.com>
[ Added SVE register tests ]
Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/Makefile.arm64        |   9 +
 arm/cstart64.S            |   1 +
 arm/fpu.c                 | 424 ++++++++++++++++++++++++++++++++++++++
 arm/unittests.cfg         |   8 +
 lib/arm64/asm/processor.h |  26 +++
 lib/arm64/asm/sysreg.h    |   7 +
 6 files changed, 475 insertions(+)
 create mode 100644 arm/fpu.c

diff --git a/arm/Makefile.arm64 b/arm/Makefile.arm64
index 90d95e79..5a9943c8 100644
--- a/arm/Makefile.arm64
+++ b/arm/Makefile.arm64
@@ -10,9 +10,17 @@ arch_LDFLAGS = -pie -n
 arch_LDFLAGS += -z notext
 CFLAGS += -mstrict-align
 
+sve_flag := $(call cc-option, -march=armv8.5-a+sve, "")
+ifneq ($(strip $(sve_flag)),)
+# Don't pass the option to the compiler, we don't
+# want the compiler to generate SVE instructions.
+CFLAGS += -DCC_HAS_SVE
+endif
+
 mno_outline_atomics := $(call cc-option, -mno-outline-atomics, "")
 CFLAGS += $(mno_outline_atomics)
 CFLAGS += -DCONFIG_RELOC
+CFLAGS += -mgeneral-regs-only
 
 define arch_elf_check =
 	$(if $(shell ! $(READELF) -rW $(1) >&/dev/null && echo "nok"),
@@ -49,6 +57,7 @@ tests = $(TEST_DIR)/timer.$(exe)
 tests += $(TEST_DIR)/micro-bench.$(exe)
 tests += $(TEST_DIR)/cache.$(exe)
 tests += $(TEST_DIR)/debug.$(exe)
+tests += $(TEST_DIR)/fpu.$(exe)
 tests += $(TEST_DIR)/realm-rsi.$(exe)
 
 include $(SRCDIR)/$(TEST_DIR)/Makefile.common
diff --git a/arm/cstart64.S b/arm/cstart64.S
index c081365f..53acf796 100644
--- a/arm/cstart64.S
+++ b/arm/cstart64.S
@@ -12,6 +12,7 @@
 #include <asm/ptrace.h>
 #include <asm/page.h>
 #include <asm/pgtable-hwdef.h>
+#include <asm/processor.h>
 #include <asm/thread_info.h>
 #include <asm/sysreg.h>
 #include <asm/smc-rsi.h>
diff --git a/arm/fpu.c b/arm/fpu.c
new file mode 100644
index 00000000..06e5a845
--- /dev/null
+++ b/arm/fpu.c
@@ -0,0 +1,424 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Arm Limited.
+ * All rights reserved.
+ */
+
+#include <libcflat.h>
+#include <asm/smp.h>
+#include <stdlib.h>
+
+#include <asm/rsi.h>
+
+#define CPU0_ID			0
+#define CPU1_ID			(CPU0_ID + 1)
+#define CPUS_MAX		(CPU1_ID + 1)
+#define FPU_QREG_MAX	32
+#define FPU_RESULT_PASS	(-1U)
+
+/*
+ * Write 8 bytes of random data in random. Returns true on success, false on
+ * failure.
+ */
+static inline bool arch_collect_entropy(uint64_t *random)
+{
+	unsigned long ret;
+
+	asm volatile(
+	"	mrs  %[ptr], " xstr(RNDR) "\n"
+	"	cset %[ret], ne\n" /* RNDR sets NZCV to 0b0100 on failure */
+	:
+	  [ret] "=r" (ret),
+	  [ptr] "=r" (*random)
+	:
+	: "cc"
+	);
+
+	return ret == 1;
+}
+
+#define fpu_reg_read(val)				\
+({							\
+	uint64_t *__val = (val);			\
+	asm volatile("stp q0, q1, [%0], #32\n\t"	\
+		     "stp q2, q3, [%0], #32\n\t"	\
+		     "stp q4, q5, [%0], #32\n\t"	\
+		     "stp q6, q7, [%0], #32\n\t"	\
+		     "stp q8, q9, [%0], #32\n\t"	\
+		     "stp q10, q11, [%0], #32\n\t"	\
+		     "stp q12, q13, [%0], #32\n\t"	\
+		     "stp q14, q15, [%0], #32\n\t"	\
+		     "stp q16, q17, [%0], #32\n\t"	\
+		     "stp q18, q19, [%0], #32\n\t"	\
+		     "stp q20, q21, [%0], #32\n\t"	\
+		     "stp q22, q23, [%0], #32\n\t"	\
+		     "stp q24, q25, [%0], #32\n\t"	\
+		     "stp q26, q27, [%0], #32\n\t"	\
+		     "stp q28, q29, [%0], #32\n\t"	\
+		     "stp q30, q31, [%0], #32\n\t"	\
+		     : "=r" (__val)			\
+		     :					\
+		     : "q0", "q1", "q2", "q3",		\
+			"q4", "q5", "q6", "q7",		\
+			"q8", "q9", "q10", "q11",	\
+			"q12", "q13", "q14",		\
+			"q15", "q16", "q17",		\
+			"q18", "q19", "q20",		\
+			"q21", "q22", "q23",		\
+			"q24", "q25", "q26",		\
+			"q27", "q28", "q29",		\
+			"q30", "q31", "memory");	\
+})
+
+#define fpu_reg_write(val)				\
+do {							\
+	uint64_t *__val = (val);			\
+	asm volatile("ldp q0, q1, [%0], #32\n\t"	\
+		     "ldp q2, q3, [%0], #32\n\t"	\
+		     "ldp q4, q5, [%0], #32\n\t"	\
+		     "ldp q6, q7, [%0], #32\n\t"	\
+		     "ldp q8, q9, [%0], #32\n\t"	\
+		     "ldp q10, q11, [%0], #32\n\t"	\
+		     "ldp q12, q13, [%0], #32\n\t"	\
+		     "ldp q14, q15, [%0], #32\n\t"	\
+		     "ldp q16, q17, [%0], #32\n\t"	\
+		     "ldp q18, q19, [%0], #32\n\t"	\
+		     "ldp q20, q21, [%0], #32\n\t"	\
+		     "ldp q22, q23, [%0], #32\n\t"	\
+		     "ldp q24, q25, [%0], #32\n\t"	\
+		     "ldp q26, q27, [%0], #32\n\t"	\
+		     "ldp q28, q29, [%0], #32\n\t"	\
+		     "ldp q30, q31, [%0], #32\n\t"	\
+		     :					\
+		     : "r" (__val)			\
+		     : "q0", "q1", "q2", "q3",		\
+			"q4", "q5", "q6", "q7",		\
+			"q8", "q9", "q10", "q11",	\
+			"q12", "q13", "q14",		\
+			"q15", "q16", "q17",		\
+			"q18", "q19", "q20",		\
+			"q21", "q22", "q23",		\
+			"q24", "q25", "q26",		\
+			"q27", "q28", "q29",		\
+			"q30", "q31", "memory");	\
+} while (0)
+
+#ifdef CC_HAS_SVE
+#define sve_reg_read(val)				\
+({							\
+	uint64_t *__val = (val);			\
+	asm volatile(".arch_extension sve\n"		\
+		     "str z0, [%0, #0, MUL VL]\n"	\
+		     "str z1, [%0, #1, MUL VL]\n"	\
+		     "str z2, [%0, #2, MUL VL]\n"	\
+		     "str z3, [%0, #3, MUL VL]\n"	\
+		     "str z4, [%0, #4, MUL VL]\n"	\
+		     "str z5, [%0, #5, MUL VL]\n"	\
+		     "str z6, [%0, #6, MUL VL]\n"	\
+		     "str z7, [%0, #7, MUL VL]\n"	\
+		     "str z8, [%0, #8, MUL VL]\n"	\
+		     "str z9, [%0, #9, MUL VL]\n"	\
+		     "str z10, [%0, #10, MUL VL]\n"	\
+		     "str z11, [%0, #11, MUL VL]\n"	\
+		     "str z12, [%0, #12, MUL VL]\n"	\
+		     "str z13, [%0, #13, MUL VL]\n"	\
+		     "str z14, [%0, #14, MUL VL]\n"	\
+		     "str z15, [%0, #15, MUL VL]\n"	\
+		     "str z16, [%0, #16, MUL VL]\n"	\
+		     "str z17, [%0, #17, MUL VL]\n"	\
+		     "str z18, [%0, #18, MUL VL]\n"	\
+		     "str z19, [%0, #19, MUL VL]\n"	\
+		     "str z20, [%0, #20, MUL VL]\n"	\
+		     "str z21, [%0, #21, MUL VL]\n"	\
+		     "str z22, [%0, #22, MUL VL]\n"	\
+		     "str z23, [%0, #23, MUL VL]\n"	\
+		     "str z24, [%0, #24, MUL VL]\n"	\
+		     "str z25, [%0, #25, MUL VL]\n"	\
+		     "str z26, [%0, #26, MUL VL]\n"	\
+		     "str z27, [%0, #27, MUL VL]\n"	\
+		     "str z28, [%0, #28, MUL VL]\n"	\
+		     "str z29, [%0, #29, MUL VL]\n"	\
+		     "str z30, [%0, #30, MUL VL]\n"	\
+		     "str z31, [%0, #31, MUL VL]\n"	\
+		     : "=r" (__val)			\
+		     :					\
+		     : "z0", "z1", "z2", "z3",		\
+			"z4", "z5", "z6", "z7",		\
+			"z8", "z9", "z10", "z11",	\
+			"z12", "z13", "z14",		\
+			"z15", "z16", "z17",		\
+			"z18", "z19", "z20",		\
+			"z21", "z22", "z23",		\
+			"z24", "z25", "z26",		\
+			"z27", "z28", "z29",		\
+			"z30", "z31", "memory");	\
+})
+
+#define sve_reg_write(val)				\
+({							\
+	uint64_t *__val = (val);			\
+	asm volatile(".arch_extension sve\n"		\
+		     "ldr z0, [%0, #0, MUL VL]\n"	\
+		     "ldr z1, [%0, #1, MUL VL]\n"	\
+		     "ldr z2, [%0, #2, MUL VL]\n"	\
+		     "ldr z3, [%0, #3, MUL VL]\n"	\
+		     "ldr z4, [%0, #4, MUL VL]\n"	\
+		     "ldr z5, [%0, #5, MUL VL]\n"	\
+		     "ldr z6, [%0, #6, MUL VL]\n"	\
+		     "ldr z7, [%0, #7, MUL VL]\n"	\
+		     "ldr z8, [%0, #8, MUL VL]\n"	\
+		     "ldr z9, [%0, #9, MUL VL]\n"	\
+		     "ldr z10, [%0, #10, MUL VL]\n"	\
+		     "ldr z11, [%0, #11, MUL VL]\n"	\
+		     "ldr z12, [%0, #12, MUL VL]\n"	\
+		     "ldr z13, [%0, #13, MUL VL]\n"	\
+		     "ldr z14, [%0, #14, MUL VL]\n"	\
+		     "ldr z15, [%0, #15, MUL VL]\n"	\
+		     "ldr z16, [%0, #16, MUL VL]\n"	\
+		     "ldr z17, [%0, #17, MUL VL]\n"	\
+		     "ldr z18, [%0, #18, MUL VL]\n"	\
+		     "ldr z19, [%0, #19, MUL VL]\n"	\
+		     "ldr z20, [%0, #20, MUL VL]\n"	\
+		     "ldr z21, [%0, #21, MUL VL]\n"	\
+		     "ldr z22, [%0, #22, MUL VL]\n"	\
+		     "ldr z23, [%0, #23, MUL VL]\n"	\
+		     "ldr z24, [%0, #24, MUL VL]\n"	\
+		     "ldr z25, [%0, #25, MUL VL]\n"	\
+		     "ldr z26, [%0, #26, MUL VL]\n"	\
+		     "ldr z27, [%0, #27, MUL VL]\n"	\
+		     "ldr z28, [%0, #28, MUL VL]\n"	\
+		     "ldr z29, [%0, #29, MUL VL]\n"	\
+		     "ldr z30, [%0, #30, MUL VL]\n"	\
+		     "ldr z31, [%0, #31, MUL VL]\n"	\
+		     :					\
+		     : "r" (__val)			\
+		     : "z0", "z1", "z2", "z3",		\
+			"z4", "z5", "z6", "z7",		\
+			"z8", "z9", "z10", "z11",	\
+			"z12", "z13", "z14",		\
+			"z15", "z16", "z17",		\
+			"z18", "z19", "z20",		\
+			"z21", "z22", "z23",		\
+			"z24", "z25", "z26",		\
+			"z27", "z28", "z29",		\
+			"z30", "z31", "memory");	\
+})
+#else
+#define sve_reg_read(val)	report_abort("SVE: not supported")
+#define sve_reg_write(val)	report_abort("SVE: not supported")
+#endif
+
+static void nr_cpu_check(int nr)
+{
+	if (nr_cpus < nr)
+		report_abort("At least %d cpus required", nr);
+}
+
+/**
+ * @brief check if the FPU/SIMD/SVE register contents are the same as
+ * the input data provided.
+ */
+static uint32_t __fpuregs_testall(uint64_t *indata, int sve)
+{
+	/* 128b aligned array to read data into */
+	uint64_t outdata[FPU_QREG_MAX * 2]
+			 __attribute__((aligned(sizeof(__uint128_t)))) = {
+			[0 ... ((FPU_QREG_MAX * 2) - 1)] = 0 };
+	uint8_t regcnt	= 0;
+	uint32_t result	= 0;
+
+	if (indata == NULL)
+		report_abort("invalid data pointer received");
+
+	/* Read data from FPU/SVE registers */
+	if (sve)
+		sve_reg_read(outdata);
+	else
+		fpu_reg_read(outdata);
+
+	/* Check is the data is the same */
+	for (regcnt = 0; regcnt < (FPU_QREG_MAX * 2); regcnt += 2) {
+		if ((outdata[regcnt] != indata[regcnt]) ||
+			(outdata[regcnt + 1] != indata[regcnt + 1])) {
+			report_info(
+			"%s save/restore failed for reg: %c%u expected: %lx_%lx received: %lx_%lx\n",
+			sve ? "SVE" : "FPU/SIMD",
+			sve ? 'z' : 'q',
+			regcnt / 2,
+			indata[regcnt + 1], indata[regcnt],
+			outdata[regcnt + 1], outdata[regcnt]);
+		} else {
+			/* populate a bitmask indicating which
+			 * registers passed/failed
+			 */
+			result |= (1 << (regcnt / 2));
+		}
+	}
+
+	return result;
+}
+
+/**
+ * @brief writes randomly sampled data into the FPU/SIMD registers.
+ */
+static void __fpuregs_writeall_random(uint64_t **indata, int sve)
+{
+	/* allocate 128b aligned memory */
+	*indata = memalign(sizeof(__uint128_t), sizeof(uint64_t) * FPU_QREG_MAX);
+
+	if (system_supports_rndr()) {
+		/* Populate memory with random data */
+		for (unsigned int i = 0; i < (FPU_QREG_MAX * 2); i++)
+			while (!arch_collect_entropy(&(*indata)[i])) {}
+	} else {
+		/* Populate memory with data from the counter register */
+		for (unsigned int i = 0; i < (FPU_QREG_MAX * 2); i++)
+			(*indata)[i] = get_cntvct();
+	}
+
+	/* Write data into FPU registers */
+	if (sve)
+		sve_reg_write(*indata);
+	else
+		fpu_reg_write(*indata);
+}
+
+static void fpuregs_writeall_run(void *data)
+{
+	uint64_t **indata	= (uint64_t **)data;
+
+	__fpuregs_writeall_random(indata, 0);
+}
+
+static void sveregs_writeall_run(void *data)
+{
+	uint64_t **indata	= (uint64_t **)data;
+
+	__fpuregs_writeall_random(indata, 1);
+}
+
+static void fpuregs_testall_run(void *data)
+{
+	uint64_t *indata	= (uint64_t *)data;
+	uint32_t result		= 0;
+
+	result = __fpuregs_testall(indata, 0);
+	report((result == FPU_RESULT_PASS),
+	       "FPU/SIMD register save/restore mask: 0x%x", result);
+}
+
+static void sveregs_testall_run(void *data)
+{
+	uint64_t *indata	= (uint64_t *)data;
+	uint32_t result		= 0;
+
+	result = __fpuregs_testall(indata, 1);
+	report((result == FPU_RESULT_PASS),
+	       "SVE register save/restore mask: 0x%x", result);
+}
+
+/**
+ * @brief This test uses two CPUs to test FPU/SIMD save/restore
+ * @details CPU1 writes random data into FPU/SIMD registers,
+ * CPU0 corrupts/overwrites the data and finally CPU1 checks
+ * if the data remains unchanged in its context.
+ */
+static void fpuregs_context_switch_cpu1(int sve)
+{
+	int target		= CPU1_ID;
+	uint64_t *indata_remote	= NULL;
+	uint64_t *indata_local	= NULL;
+
+	/* write data from CPU1 */
+	on_cpu(target, sve ? sveregs_writeall_run
+	                   : fpuregs_writeall_run,
+	       &indata_remote);
+
+	/* Overwrite from CPU0 */
+	__fpuregs_writeall_random(&indata_local, sve);
+
+	/* Check data consistency */
+	on_cpu(target, sve ? sveregs_testall_run
+	                   : fpuregs_testall_run,
+	       indata_remote);
+
+	free(indata_remote);
+	free(indata_local);
+}
+
+/**
+ * @brief This test uses two CPUs to test FPU/SIMD save/restore
+ * @details CPU0 writes random data into FPU/SIMD registers,
+ * CPU1 corrupts/overwrites the data and finally CPU0 checks if
+ * the data remains unchanged in its context.
+ */
+static void fpuregs_context_switch_cpu0(int sve)
+{
+	int target		= CPU1_ID;
+	uint64_t *indata_local	= NULL;
+	uint64_t *indata_remote	= NULL;
+	uint32_t result		= 0;
+
+	/* write data from CPU0 */
+	__fpuregs_writeall_random(&indata_local, sve);
+
+	/* Overwrite from CPU1 */
+	on_cpu(target, sve ? sveregs_writeall_run
+	                   : fpuregs_writeall_run,
+	       &indata_remote);
+
+	/* Check data consistency */
+	result = __fpuregs_testall(indata_local, sve);
+	report((result == FPU_RESULT_PASS),
+	       "%s register save/restore mask: 0x%x", sve ? "SVE" : "FPU/SIMD", result);
+
+	free(indata_remote);
+	free(indata_local);
+}
+
+/**
+ * Checks if during context switch, FPU/SIMD registers
+ * are saved/restored.
+ */
+static void fpuregs_context_switch(void)
+{
+	fpuregs_context_switch_cpu0(0);
+	fpuregs_context_switch_cpu1(0);
+}
+
+/**
+ * Checks if during realm context switch, SVE registers
+ * are saved/restored.
+ */
+static void sveregs_context_switch(void)
+{
+	unsigned long zcr = read_sysreg(ZCR_EL1);
+
+	// Set the SVE vector length to 128-bits
+	write_sysreg(zcr & ~ZCR_EL1_LEN, ZCR_EL1);
+
+	fpuregs_context_switch_cpu0(1);
+	fpuregs_context_switch_cpu1(1);
+}
+
+static bool should_run_sve_tests(void)
+{
+#ifdef CC_HAS_SVE
+	if (system_supports_sve())
+		return true;
+#endif
+	return false;
+}
+
+int main(int argc, char **argv)
+{
+	report_prefix_pushf("fpu");
+
+	nr_cpu_check(CPUS_MAX);
+	fpuregs_context_switch();
+
+	if (should_run_sve_tests())
+		sveregs_context_switch();
+
+	return report_summary();
+}
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
index b5be6668..e35e8506 100644
--- a/arm/unittests.cfg
+++ b/arm/unittests.cfg
@@ -303,3 +303,11 @@ groups = nodefault realms
 extra_params = -append 'hvc'
 accel = kvm
 arch = arm64
+
+# FPU/SIMD test
+[fpu-context]
+file = fpu.flat
+smp = 2
+groups = nodefault realms
+accel = kvm
+arch = arm64
diff --git a/lib/arm64/asm/processor.h b/lib/arm64/asm/processor.h
index 320ebaef..cc993c6a 100644
--- a/lib/arm64/asm/processor.h
+++ b/lib/arm64/asm/processor.h
@@ -122,6 +122,8 @@ static inline unsigned long get_id_aa64pfr0_el1(void)
 #define ID_AA64PFR0_EL1_EL3	(0xf << 12)
 #define ID_AA64PFR0_EL1_EL3_NI	(0x0 << 12)
 
+#define ID_AA64PFR0_EL1_SVE_SHIFT	32
+
 static inline bool system_supports_granule(size_t granule)
 {
 	u32 shift;
@@ -145,5 +147,29 @@ static inline bool system_supports_granule(size_t granule)
 	return ((mmfr0 >> shift) & 0xf) == val;
 }
 
+static inline bool system_supports_sve(void)
+{
+	return ((get_id_aa64pfr0_el1() >> ID_AA64PFR0_EL1_SVE_SHIFT) & 0xf) != 0;
+}
+
+static inline int sve_vl(void)
+{
+	int vl;
+
+	asm volatile(".arch_extension sve\n"
+		     "rdvl %0, #8"
+		     : "=r" (vl));
+
+	return vl;
+}
+
+
+static inline bool system_supports_rndr(void)
+{
+	u64 id_aa64isar0_el1 = read_sysreg(ID_AA64ISAR0_EL1);
+
+	return ((id_aa64isar0_el1 >> ID_AA64ISAR0_EL1_RNDR_SHIFT) & 0xf) != 0;
+}
+
 #endif /* !__ASSEMBLY__ */
 #endif /* _ASMARM64_PROCESSOR_H_ */
diff --git a/lib/arm64/asm/sysreg.h b/lib/arm64/asm/sysreg.h
index 6cae8b84..f214a4f0 100644
--- a/lib/arm64/asm/sysreg.h
+++ b/lib/arm64/asm/sysreg.h
@@ -73,6 +73,8 @@ asm(
 );
 #endif /* __ASSEMBLY__ */
 
+#define ID_AA64ISAR0_EL1_RNDR_SHIFT	60
+
 #define ICC_PMR_EL1			sys_reg(3, 0, 4, 6, 0)
 #define ICC_SGI1R_EL1			sys_reg(3, 0, 12, 11, 5)
 #define ICC_IAR1_EL1			sys_reg(3, 0, 12, 12, 0)
@@ -102,4 +104,9 @@ asm(
 			 SCTLR_EL1_TSCXT | SCTLR_EL1_EIS | SCTLR_EL1_SPAN | \
 			 SCTLR_EL1_NTLSMD | SCTLR_EL1_LSMAOE)
 
+#define ZCR_EL1		S3_0_C1_C2_0
+#define ZCR_EL1_LEN	GENMASK(3, 0)
+
+#define RNDR		S3_3_C2_C4_0
+
 #endif /* _ASMARM64_SYSREG_H_ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 19/33] arm64: selftest: add realm SVE VL test
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (18 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 18/33] arm: realm: Add test for FPU/SIMD context save/restore Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 20/33] arm: realm: Add tests for in realm SEA Suzuki K Poulose
                   ` (14 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Joey Gouly <joey.gouly@arm.com>

Check that the requested SVE vector length matches what
the Realm was configured with.

Signed-off-by: Joey Gouly <joey.gouly@arm.com>
[ Fix build failures on arm ]
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/selftest.c              |  6 ++++++
 arm/unittests.cfg           |  2 +-
 lib/arm/asm/sve-vl-test.h   |  9 +++++++++
 lib/arm64/asm/sve-vl-test.h | 28 ++++++++++++++++++++++++++++
 4 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 lib/arm/asm/sve-vl-test.h
 create mode 100644 lib/arm64/asm/sve-vl-test.h

diff --git a/arm/selftest.c b/arm/selftest.c
index 8caadad3..7bc5fb76 100644
--- a/arm/selftest.c
+++ b/arm/selftest.c
@@ -21,6 +21,8 @@
 #include <asm/barrier.h>
 #include <asm/rsi.h>
 
+#include <asm/sve-vl-test.h>
+
 static cpumask_t ready, valid;
 
 static void __user_psci_system_off(void)
@@ -60,6 +62,10 @@ static void check_setup(int argc, char **argv)
 			       "number of CPUs matches expectation");
 			report_info("found %d CPUs", nr_cpus);
 			++nr_tests;
+
+		} else if (strcmp(argv[i], "sve-vl") == 0) {
+			if (check_arm_sve_vl(val))
+				nr_tests++;
 		}
 
 		report_prefix_pop();
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
index e35e8506..3cf6b719 100644
--- a/arm/unittests.cfg
+++ b/arm/unittests.cfg
@@ -33,7 +33,7 @@
 [selftest-setup]
 file = selftest.flat
 smp = 2
-extra_params = -m 256 -append 'setup smp=2 mem=256'
+extra_params = -m 256 -append 'setup smp=2 mem=256 sve-vl'
 groups = selftest
 
 # Test vector setup and exception handling (kernel mode).
diff --git a/lib/arm/asm/sve-vl-test.h b/lib/arm/asm/sve-vl-test.h
new file mode 100644
index 00000000..19eaf669
--- /dev/null
+++ b/lib/arm/asm/sve-vl-test.h
@@ -0,0 +1,9 @@
+#ifndef __ARM_SVE_VL_TEST_H
+#define __ARM_SVE_VL_TEST_H
+
+static bool check_arm_sve_vl(long val)
+{
+	return false;
+}
+
+#endif
diff --git a/lib/arm64/asm/sve-vl-test.h b/lib/arm64/asm/sve-vl-test.h
new file mode 100644
index 00000000..c82ea154
--- /dev/null
+++ b/lib/arm64/asm/sve-vl-test.h
@@ -0,0 +1,28 @@
+#ifndef __ARM_SVE_VL_TEST_H_
+#define __ARM_SVE_VL_TEST_H_
+
+#include <asm/processor.h>
+#include <asm/sysreg.h>
+
+static bool check_arm_sve_vl(long val)
+{
+	unsigned long vl;
+
+	if (!system_supports_sve()) {
+		report_skip("SVE is not supported\n");
+	} else {
+		/* Enable the maxium SVE vector length */
+		write_sysreg(ZCR_EL1_LEN, ZCR_EL1);
+		vl = sve_vl();
+		/* Realms are configured with a SVE VL */
+		if (is_realm()) {
+			report(vl == val,
+				"SVE VL expected (%ld), detected (%ld)",
+				val, vl);
+		} else {
+			report(true, "Detected SVE VL %ld\n", vl);
+		}
+	}
+	return true;
+}
+#endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 20/33] arm: realm: Add tests for in realm SEA
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (19 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 19/33] arm64: selftest: add realm SVE VL test Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 21/33] lib/alloc_page: Add shared page allocation support Suzuki K Poulose
                   ` (13 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Djordje Kovacevic, Suzuki K Poulose

From: Djordje Kovacevic <djordje.kovacevic@arm.com>

The RMM/Host could inject Synchronous External Aborts in to the Realm
for various reasons.

RMM injects the SEA for :
  * Instruction/Data fetch from an IPA that is in RIPAS_EMPTY state
  * Instruction fetch from an Unprotected IPA.

Trigger these conditions from within the Realm and verify that the
SEAs are received.

Signed-off-by: Djordje Kovacevic <djordje.kovacevic@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/Makefile.arm64 |   1 +
 arm/realm-sea.c    | 143 +++++++++++++++++++++++++++++++++++++++++++++
 arm/unittests.cfg  |   6 ++
 3 files changed, 150 insertions(+)
 create mode 100644 arm/realm-sea.c

diff --git a/arm/Makefile.arm64 b/arm/Makefile.arm64
index 5a9943c8..b3e085d3 100644
--- a/arm/Makefile.arm64
+++ b/arm/Makefile.arm64
@@ -59,6 +59,7 @@ tests += $(TEST_DIR)/cache.$(exe)
 tests += $(TEST_DIR)/debug.$(exe)
 tests += $(TEST_DIR)/fpu.$(exe)
 tests += $(TEST_DIR)/realm-rsi.$(exe)
+tests += $(TEST_DIR)/realm-sea.$(exe)
 
 include $(SRCDIR)/$(TEST_DIR)/Makefile.common
 
diff --git a/arm/realm-sea.c b/arm/realm-sea.c
new file mode 100644
index 00000000..5ef3e2a4
--- /dev/null
+++ b/arm/realm-sea.c
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+#include <libcflat.h>
+#include <vmalloc.h>
+#include <asm/ptrace.h>
+#include <asm/thread_info.h>
+#include <asm/mmu.h>
+#include <asm/rsi.h>
+#include <linux/compiler.h>
+#include <alloc_page.h>
+#include <asm/pgtable.h>
+
+typedef void (*empty_fn)(void);
+
+static bool test_passed;
+
+/*
+ * The virtual address of the page that the test has made the access to
+ * in order to cause the I/DAbort with I/DFSC = Synchronous External Abort.
+ */
+static void* target_page_va;
+
+/*
+ * Ensure that the @va is the executable location from EL1:
+ * - SCTLR_EL1.WXN must be off.
+ * - Disable the access from EL0 (controlled by AP[1] in PTE).
+ */
+static void enable_instruction_fetch(void* va)
+{
+	unsigned long sctlr = read_sysreg(sctlr_el1);
+	if (sctlr & SCTLR_EL1_WXN) {
+		sctlr &= ~SCTLR_EL1_WXN;
+		write_sysreg(sctlr, sctlr_el1);
+		isb();
+		flush_tlb_all();
+	}
+
+	mmu_clear_user(current_thread_info()->pgtable, (u64)va);
+}
+
+static void data_abort_handler(struct pt_regs *regs, unsigned int esr)
+{
+	if ((esr & ESR_EL1_FSC_MASK) == ESR_EL1_FSC_EXTABT)
+		test_passed = true;
+
+	report_info("esr = %x", esr);
+	/*
+	 * Advance the PC to complete the test.
+	 */
+	regs->pc += 4;
+}
+
+static void data_access_to_empty(void)
+{
+	test_passed = false;
+	target_page_va = alloc_page();
+	phys_addr_t empty_ipa = virt_to_phys(target_page_va);
+
+	arm_set_memory_shared(empty_ipa, SZ_4K);
+
+	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_DABT_EL1, data_abort_handler);
+	READ_ONCE(((char*)target_page_va)[0x55]);
+	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_DABT_EL1, NULL);
+
+	report(test_passed, " ");
+}
+
+static void instruction_abort_handler(struct pt_regs *regs, unsigned int esr)
+{
+	if (((esr & ESR_EL1_FSC_MASK) == ESR_EL1_FSC_EXTABT) &&
+	     (regs->pc == (u64)target_page_va))
+		test_passed = true;
+
+	report_info("esr = %x", esr);
+	/*
+	 * Simulate the RET instruction to complete the test.
+	 */
+	regs->pc = regs->regs[30];
+}
+
+static void instr_fetch_from_empty(void)
+{
+	phys_addr_t empty_ipa;
+
+	test_passed = false;
+	target_page_va = alloc_page();
+	enable_instruction_fetch(target_page_va);
+
+	empty_ipa = virt_to_phys((void*)target_page_va);
+
+	arm_set_memory_shared(empty_ipa, SZ_4K);
+
+	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_IABT_EL1, instruction_abort_handler);
+	/*
+	 * This should cause the IAbort with IFSC = SEA
+	 */
+	((empty_fn)target_page_va)();
+	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_IABT_EL1, NULL);
+
+	report(test_passed, " ");
+}
+
+static void instr_fetch_from_unprotected(void)
+{
+	test_passed = false;
+	/*
+	 * The test will attempt to execute an instruction from the start of
+	 * the unprotected IPA space.
+	 */
+	target_page_va = vmap(PTE_NS_SHARED, SZ_4K);
+	enable_instruction_fetch(target_page_va);
+
+	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_IABT_EL1, instruction_abort_handler);
+	/*
+	 * This should cause the IAbort with IFSC = SEA
+	 */
+	((empty_fn)target_page_va)();
+	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_IABT_EL1, NULL);
+
+	report(test_passed, " ");
+}
+
+int main(int argc, char **argv)
+{
+	report_prefix_push("in_realm_sea");
+
+	report_prefix_push("data_access_to_empty");
+	data_access_to_empty();
+	report_prefix_pop();
+
+	report_prefix_push("instr_fetch_from_empty");
+	instr_fetch_from_empty();
+	report_prefix_pop();
+
+	report_prefix_push("instr_fetch_from_unprotected");
+	instr_fetch_from_unprotected();
+	report_prefix_pop();
+
+	return report_summary();
+}
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
index 3cf6b719..e2821c26 100644
--- a/arm/unittests.cfg
+++ b/arm/unittests.cfg
@@ -311,3 +311,9 @@ smp = 2
 groups = nodefault realms
 accel = kvm
 arch = arm64
+
+[realm-sea]
+file = realm-sea.flat
+groups = nodefault realms
+accel = kvm
+arch = arm64
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 21/33] lib/alloc_page: Add shared page allocation support
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (20 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 20/33] arm: realm: Add tests for in realm SEA Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 22/33] arm: gic-v3-its: Use shared pages wherever needed Suzuki K Poulose
                   ` (12 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Joey Gouly <joey.gouly@arm.com>

Add support for allocating "pages" that can be shared with the host.
Or in other words, decrypted pages. This is achieved by adding hooks for
setting a memory region as "encrypted" or "decrypted", which can be overridden
by the architecture specific backends.

Also add a new flag - FLAG_SHARED - for allocating shared pages.

The page allocation/free routines get a "_shared_" variant too.
These will be later used for Realm support and tests.

Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/alloc_page.c     | 20 +++++++++++++++++---
 lib/alloc_page.h     | 24 ++++++++++++++++++++++++
 lib/asm-generic/io.h | 12 ++++++++++++
 3 files changed, 53 insertions(+), 3 deletions(-)

diff --git a/lib/alloc_page.c b/lib/alloc_page.c
index 84f01e11..e253cd1d 100644
--- a/lib/alloc_page.c
+++ b/lib/alloc_page.c
@@ -263,7 +263,7 @@ static bool coalesce(struct mem_area *a, u8 order, pfn_t pfn, pfn_t pfn2)
  * - no pages in the memory block were already free
  * - no pages in the memory block are special
  */
-static void _free_pages(void *mem)
+static void _free_pages(void *mem, u32 flags)
 {
 	pfn_t pfn2, pfn = virt_to_pfn(mem);
 	struct mem_area *a = NULL;
@@ -281,6 +281,9 @@ static void _free_pages(void *mem)
 	p = pfn - a->base;
 	order = a->page_states[p] & ORDER_MASK;
 
+	if (flags & FLAG_SHARED)
+		set_memory_encrypted((unsigned long)mem, BIT(order) * PAGE_SIZE);
+
 	/* ensure that the first page is allocated and not special */
 	assert(IS_ALLOCATED(a->page_states[p]));
 	/* ensure that the order has a sane value */
@@ -320,7 +323,14 @@ static void _free_pages(void *mem)
 void free_pages(void *mem)
 {
 	spin_lock(&lock);
-	_free_pages(mem);
+	_free_pages(mem, 0);
+	spin_unlock(&lock);
+}
+
+void free_pages_shared(void *mem)
+{
+	spin_lock(&lock);
+	_free_pages(mem, FLAG_SHARED);
 	spin_unlock(&lock);
 }
 
@@ -353,7 +363,7 @@ static void _unreserve_one_page(pfn_t pfn)
 	i = pfn - a->base;
 	assert(a->page_states[i] == STATUS_SPECIAL);
 	a->page_states[i] = STATUS_ALLOCATED;
-	_free_pages(pfn_to_virt(pfn));
+	_free_pages(pfn_to_virt(pfn), 0);
 }
 
 int reserve_pages(phys_addr_t addr, size_t n)
@@ -401,6 +411,10 @@ static void *page_memalign_order_flags(u8 al, u8 ord, u32 flags)
 		if (area & BIT(i))
 			res = page_memalign_order(areas + i, al, ord, fresh);
 	spin_unlock(&lock);
+
+	if (res && (flags & FLAG_SHARED))
+		set_memory_decrypted((unsigned long)res, BIT(ord) * PAGE_SIZE);
+
 	if (res && !(flags & FLAG_DONTZERO))
 		memset(res, 0, BIT(ord) * PAGE_SIZE);
 	return res;
diff --git a/lib/alloc_page.h b/lib/alloc_page.h
index 060e0418..8c1ea7b5 100644
--- a/lib/alloc_page.h
+++ b/lib/alloc_page.h
@@ -21,6 +21,7 @@
 
 #define FLAG_DONTZERO	0x10000
 #define FLAG_FRESH	0x20000
+#define FLAG_SHARED	0x40000
 
 /* Returns true if the page allocator has been initialized */
 bool page_alloc_initialized(void);
@@ -121,4 +122,27 @@ int reserve_pages(phys_addr_t addr, size_t npages);
  */
 void unreserve_pages(phys_addr_t addr, size_t npages);
 
+/* Shared page operations */
+static inline void *alloc_pages_shared(unsigned int order)
+{
+	return alloc_pages_flags(order, FLAG_SHARED);
+}
+
+static inline void *alloc_page_shared(void)
+{
+	return alloc_pages_shared(0);
+}
+
+void free_pages_shared(void *mem);
+
+static inline void free_page_shared(void *page)
+{
+	free_pages_shared(page);
+}
+
+static inline void free_pages_shared_by_order(void *mem, unsigned int order)
+{
+	free_pages_shared(mem);
+}
+
 #endif
diff --git a/lib/asm-generic/io.h b/lib/asm-generic/io.h
index dc0f46f5..fb65184b 100644
--- a/lib/asm-generic/io.h
+++ b/lib/asm-generic/io.h
@@ -214,4 +214,16 @@ static inline void *phys_to_virt(unsigned long address)
 }
 #endif
 
+#ifndef set_memory_encrypted
+static inline void set_memory_encrypted(unsigned long mem, size_t size)
+{
+}
+#endif
+
+#ifndef set_memory_decrypted
+static inline void set_memory_decrypted(unsigned long mem, size_t size)
+{
+}
+#endif
+
 #endif /* _ASM_GENERIC_IO_H_ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 22/33] arm: gic-v3-its: Use shared pages wherever needed
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (21 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 21/33] lib/alloc_page: Add shared page allocation support Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 23/33] arm: realm: Enable memory encryption Suzuki K Poulose
                   ` (11 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

GICv3-ITS is emulated by the host and thus we should allocate shared pages for
access by the host. Make sure the allocations are shared.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm/gic-v3.c       | 6 ++++--
 lib/arm64/gic-v3-its.c | 6 +++---
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/lib/arm/gic-v3.c b/lib/arm/gic-v3.c
index 2f7870ab..813cd5a6 100644
--- a/lib/arm/gic-v3.c
+++ b/lib/arm/gic-v3.c
@@ -171,7 +171,9 @@ void gicv3_lpi_alloc_tables(void)
 	u64 prop_val;
 	int cpu;
 
-	gicv3_data.lpi_prop = alloc_pages(order);
+	assert(gicv3_redist_base());
+
+	gicv3_data.lpi_prop = alloc_pages_shared(order);
 
 	/* ID bits = 13, ie. up to 14b LPI INTID */
 	prop_val = (u64)(virt_to_phys(gicv3_data.lpi_prop)) | 13;
@@ -186,7 +188,7 @@ void gicv3_lpi_alloc_tables(void)
 
 		writeq(prop_val, ptr + GICR_PROPBASER);
 
-		gicv3_data.lpi_pend[cpu] = alloc_pages(order);
+		gicv3_data.lpi_pend[cpu] = alloc_pages_shared(order);
 		pend_val = (u64)(virt_to_phys(gicv3_data.lpi_pend[cpu]));
 		writeq(pend_val, ptr + GICR_PENDBASER);
 	}
diff --git a/lib/arm64/gic-v3-its.c b/lib/arm64/gic-v3-its.c
index 2c69cfda..07dbeb81 100644
--- a/lib/arm64/gic-v3-its.c
+++ b/lib/arm64/gic-v3-its.c
@@ -54,7 +54,7 @@ static void its_baser_alloc_table(struct its_baser *baser, size_t size)
 	void *reg_addr = gicv3_its_base() + GITS_BASER + baser->index * 8;
 	u64 val = readq(reg_addr);
 
-	baser->table_addr = alloc_pages(order);
+	baser->table_addr = alloc_pages_shared(order);
 
 	val |= virt_to_phys(baser->table_addr) | GITS_BASER_VALID;
 
@@ -70,7 +70,7 @@ static void its_cmd_queue_init(void)
 	unsigned long order = get_order(SZ_64K >> PAGE_SHIFT);
 	u64 cbaser;
 
-	its_data.cmd_base = alloc_pages(order);
+	its_data.cmd_base = alloc_pages_shared(order);
 
 	cbaser = virt_to_phys(its_data.cmd_base) | (SZ_64K / SZ_4K - 1) | GITS_CBASER_VALID;
 
@@ -123,7 +123,7 @@ struct its_device *its_create_device(u32 device_id, int nr_ites)
 	new->nr_ites = nr_ites;
 
 	n = (its_data.typer.ite_size * nr_ites) >> PAGE_SHIFT;
-	new->itt = alloc_pages(get_order(n));
+	new->itt = alloc_pages_shared(get_order(n));
 
 	its_data.nr_devices++;
 	return new;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 23/33] arm: realm: Enable memory encryption
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (22 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 22/33] arm: gic-v3-its: Use shared pages wherever needed Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 24/33] qcbor: Add QCBOR as a submodule Suzuki K Poulose
                   ` (10 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

Enable memory encryption support for Realms.

When a page is "decrypted", we set the RIPAS to EMPTY, hinting to the hypervisor
that it could reclaim the page backing the IPA. Also the pagetable is updated
with the PTE_NS_SHARED attrbiute, which in effect turns the "ipa" to the
unprotected alias.

Similarly for "encryption" we mark the IPA back to RIPAS_RAM and clear the
PTE_NS_SHARED attribute.

The addresses passed into the helpers must be idmap/linear map addresses.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm/asm/io.h   |  6 ++++
 lib/arm/mmu.c      | 72 +++++++++++++++++++++++++++++++++++++++++++---
 lib/arm64/asm/io.h |  6 ++++
 3 files changed, 80 insertions(+), 4 deletions(-)

diff --git a/lib/arm/asm/io.h b/lib/arm/asm/io.h
index e4caa6ff..8529f668 100644
--- a/lib/arm/asm/io.h
+++ b/lib/arm/asm/io.h
@@ -95,6 +95,12 @@ static inline void *phys_to_virt(phys_addr_t x)
 	return (void *)__phys_to_virt(x);
 }
 
+extern void set_memory_decrypted(unsigned long va, size_t size);
+#define set_memory_decrypted		set_memory_decrypted
+
+extern void set_memory_encrypted(unsigned long va, size_t size);
+#define set_memory_encrypted	set_memory_encrypted
+
 #include <asm-generic/io.h>
 
 #endif /* _ASMARM_IO_H_ */
diff --git a/lib/arm/mmu.c b/lib/arm/mmu.c
index 16ceffcc..4d5770dc 100644
--- a/lib/arm/mmu.c
+++ b/lib/arm/mmu.c
@@ -23,6 +23,7 @@
 #include <linux/compiler.h>
 
 pgd_t *mmu_idmap;
+unsigned long idmap_end;
 
 /* Used by Realms, depends on IPA size */
 unsigned long prot_ns_shared = 0;
@@ -31,6 +32,11 @@ unsigned long phys_mask_shift = 48;
 /* CPU 0 starts with disabled MMU */
 static cpumask_t mmu_enabled_cpumask;
 
+static bool is_idmap_address(phys_addr_t pa)
+{
+	return pa < idmap_end;
+}
+
 bool mmu_enabled(void)
 {
 	/*
@@ -93,12 +99,17 @@ static pteval_t *get_pte(pgd_t *pgtable, uintptr_t vaddr)
 	return &pte_val(*pte);
 }
 
-static pteval_t *install_pte(pgd_t *pgtable, uintptr_t vaddr, pteval_t pte)
+static void set_pte(uintptr_t vaddr, pteval_t *p_pte, pteval_t pte)
 {
-	pteval_t *p_pte = get_pte(pgtable, vaddr);
-
 	WRITE_ONCE(*p_pte, pte);
 	flush_tlb_page(vaddr);
+}
+
+static pteval_t *install_pte(pgd_t *pgtable, uintptr_t vaddr, pteval_t pte)
+{
+	pteval_t *p_pte = get_pte(pgtable, vaddr);
+
+	set_pte(vaddr, p_pte, pte);
 	return p_pte;
 }
 
@@ -171,6 +182,39 @@ phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *virt)
 		((phys_addr_t)(unsigned long)virt & ~mask);
 }
 
+/*
+ * __idmap_set_range_prot - Apply permissions to the given idmap range.
+ */
+static void __idmap_set_range_prot(unsigned long virt_offset, size_t size, pgprot_t prot)
+{
+	pteval_t *ptep;
+	pteval_t default_prot = PTE_TYPE_PAGE | PTE_AF | PTE_SHARED;
+
+	while (size > 0) {
+		pteval_t pte = virt_offset | default_prot | pgprot_val(prot);
+
+		if (!is_idmap_address(virt_offset))
+			break;
+		/* Break before make : Clear the PTE entry first */
+		ptep = install_pte(mmu_idmap, (uintptr_t)virt_offset, 0);
+		/* Now apply the changes */
+		set_pte((uintptr_t)virt_offset, ptep, pte);
+
+		size -= PAGE_SIZE;
+		virt_offset += PAGE_SIZE;
+	}
+}
+
+static void idmap_set_range_shared(unsigned long virt_offset, size_t size)
+{
+	return __idmap_set_range_prot(virt_offset, size, __pgprot(PTE_WBWA | PTE_USER | PTE_NS_SHARED));
+}
+
+static void idmap_set_range_protected(unsigned long virt_offset, size_t size)
+{
+	__idmap_set_range_prot(virt_offset, size, __pgprot(PTE_WBWA | PTE_USER));
+}
+
 void mmu_set_range_ptes(pgd_t *pgtable, uintptr_t virt_offset,
 			phys_addr_t phys_start, phys_addr_t phys_end,
 			pgprot_t prot)
@@ -210,11 +254,12 @@ void mmu_set_range_sect(pgd_t *pgtable, uintptr_t virt_offset,
 void *setup_mmu(phys_addr_t phys_end, void *unused)
 {
 	struct mem_region *r;
+	unsigned long end = 0;
 
 	/* 3G-4G region is reserved for vmalloc, cap phys_end at 3G */
 	if (phys_end > (3ul << 30))
 		phys_end = 3ul << 30;
-
+	end = phys_end;
 #ifdef __aarch64__
 	init_alloc_vpage((void*)(4ul << 30));
 
@@ -236,9 +281,12 @@ void *setup_mmu(phys_addr_t phys_end, void *unused)
 			mmu_set_range_ptes(mmu_idmap, r->start, r->start, r->end,
 					   __pgprot(PTE_WBWA | PTE_USER));
 		}
+		if (r->end > end)
+			end = r->end;
 	}
 
 	mmu_enable(mmu_idmap);
+	idmap_end = end;
 	return mmu_idmap;
 }
 
@@ -295,3 +343,19 @@ void mmu_clear_user(pgd_t *pgtable, unsigned long vaddr)
 		flush_tlb_page(vaddr);
 	}
 }
+
+void set_memory_encrypted(unsigned long va, size_t size)
+{
+	if (is_realm()) {
+		arm_set_memory_protected(__virt_to_phys(va), size);
+		idmap_set_range_protected(va, size);
+	}
+}
+
+void set_memory_decrypted(unsigned long va, size_t size)
+{
+	if (is_realm()) {
+		arm_set_memory_shared(__virt_to_phys(va), size);
+		idmap_set_range_shared(va, size);
+	}
+}
diff --git a/lib/arm64/asm/io.h b/lib/arm64/asm/io.h
index be19f471..3f71254d 100644
--- a/lib/arm64/asm/io.h
+++ b/lib/arm64/asm/io.h
@@ -89,6 +89,12 @@ static inline void *phys_to_virt(phys_addr_t x)
 	return (void *)__phys_to_virt(x);
 }
 
+extern void set_memory_decrypted(unsigned long va, size_t size);
+#define set_memory_decrypted		set_memory_decrypted
+
+extern void set_memory_encrypted(unsigned long va, size_t size);
+#define set_memory_encrypted	set_memory_encrypted
+
 #include <asm-generic/io.h>
 
 #endif /* _ASMARM64_IO_H_ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 24/33] qcbor: Add QCBOR as a submodule
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (23 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 23/33] arm: realm: Enable memory encryption Suzuki K Poulose
@ 2024-04-12 10:33 ` Suzuki K Poulose
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 25/33] arm: Add build steps for QCBOR library Suzuki K Poulose
                   ` (9 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:33 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

Adds the library QCBOR as submodule. This will be later used
for arm64 realm attestation token parsing. The repository is
available at:

	https://github.com/laurencelundblade/QCBOR tag v1.0

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 .gitmodules | 3 +++
 lib/qcbor   | 1 +
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 lib/qcbor

diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..29fdbc5d
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "lib/qcbor"]
+	path = lib/qcbor
+	url = https://github.com/laurencelundblade/QCBOR.git
diff --git a/lib/qcbor b/lib/qcbor
new file mode 160000
index 00000000..56b17bf9
--- /dev/null
+++ b/lib/qcbor
@@ -0,0 +1 @@
+Subproject commit 56b17bf9f74096774944bcac0829adcd887d391e
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 25/33] arm: Add build steps for QCBOR library
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (24 preceding siblings ...)
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 24/33] qcbor: Add QCBOR as a submodule Suzuki K Poulose
@ 2024-04-12 10:34 ` Suzuki K Poulose
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 26/33] arm: Add a library to verify tokens using the " Suzuki K Poulose
                   ` (8 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:34 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

The QCBOR library will be used for Realm attestation.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/Makefile.arm64 | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/arm/Makefile.arm64 b/arm/Makefile.arm64
index b3e085d3..de73601d 100644
--- a/arm/Makefile.arm64
+++ b/arm/Makefile.arm64
@@ -9,6 +9,8 @@ ldarch = elf64-littleaarch64
 arch_LDFLAGS = -pie -n
 arch_LDFLAGS += -z notext
 CFLAGS += -mstrict-align
+CFLAGS += -I $(SRCDIR)/lib/qcbor/inc
+CFLAGS += -DQCBOR_DISABLE_FLOAT_HW_USE -DQCBOR_DISABLE_PREFERRED_FLOAT -DUSEFULBUF_DISABLE_ALL_FLOAT
 
 sve_flag := $(call cc-option, -march=armv8.5-a+sve, "")
 ifneq ($(strip $(sve_flag)),)
@@ -35,6 +37,7 @@ cflatobjs += lib/arm64/processor.o
 cflatobjs += lib/arm64/spinlock.o
 cflatobjs += lib/arm64/gic-v3-its.o lib/arm64/gic-v3-its-cmd.o
 cflatobjs += lib/arm64/rsi.o
+cflatobjs += lib/qcbor/src/qcbor_decode.o lib/qcbor/src/UsefulBuf.o
 
 ifeq ($(CONFIG_EFI),y)
 cflatobjs += lib/acpi.o
@@ -64,4 +67,5 @@ tests += $(TEST_DIR)/realm-sea.$(exe)
 include $(SRCDIR)/$(TEST_DIR)/Makefile.common
 
 arch_clean: arm_clean
-	$(RM) lib/arm64/.*.d
+	$(RM) lib/arm64/.*.d		\
+	      lib/qcbor/src/.*.d
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 26/33] arm: Add a library to verify tokens using the QCBOR library
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (25 preceding siblings ...)
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 25/33] arm: Add build steps for QCBOR library Suzuki K Poulose
@ 2024-04-12 10:34 ` Suzuki K Poulose
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 27/33] arm: realm: add RSI interface for attestation measurements Suzuki K Poulose
                   ` (7 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:34 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Mate Toth-Pal, Suzuki K Poulose

From: Mate Toth-Pal <mate.toth-pal@arm.com>

Add a library wrapper around the QCBOR for parsing the Arm CCA attestation
tokens.

Signed-off-by: Mate Toth-Pal <mate.toth-pal@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/Makefile.arm64                  |   7 +-
 lib/token_verifier/attest_defines.h |  50 +++
 lib/token_verifier/token_dumper.c   | 157 ++++++++
 lib/token_verifier/token_dumper.h   |  15 +
 lib/token_verifier/token_verifier.c | 591 ++++++++++++++++++++++++++++
 lib/token_verifier/token_verifier.h |  77 ++++
 6 files changed, 896 insertions(+), 1 deletion(-)
 create mode 100644 lib/token_verifier/attest_defines.h
 create mode 100644 lib/token_verifier/token_dumper.c
 create mode 100644 lib/token_verifier/token_dumper.h
 create mode 100644 lib/token_verifier/token_verifier.c
 create mode 100644 lib/token_verifier/token_verifier.h

diff --git a/arm/Makefile.arm64 b/arm/Makefile.arm64
index de73601d..79952914 100644
--- a/arm/Makefile.arm64
+++ b/arm/Makefile.arm64
@@ -11,6 +11,7 @@ arch_LDFLAGS += -z notext
 CFLAGS += -mstrict-align
 CFLAGS += -I $(SRCDIR)/lib/qcbor/inc
 CFLAGS += -DQCBOR_DISABLE_FLOAT_HW_USE -DQCBOR_DISABLE_PREFERRED_FLOAT -DUSEFULBUF_DISABLE_ALL_FLOAT
+CFLAGS += -I $(SRCDIR)/lib/token_verifier
 
 sve_flag := $(call cc-option, -march=armv8.5-a+sve, "")
 ifneq ($(strip $(sve_flag)),)
@@ -38,6 +39,9 @@ cflatobjs += lib/arm64/spinlock.o
 cflatobjs += lib/arm64/gic-v3-its.o lib/arm64/gic-v3-its-cmd.o
 cflatobjs += lib/arm64/rsi.o
 cflatobjs += lib/qcbor/src/qcbor_decode.o lib/qcbor/src/UsefulBuf.o
+cflatobjs += lib/token_verifier/token_verifier.o
+cflatobjs += lib/token_verifier/token_dumper.o
+
 
 ifeq ($(CONFIG_EFI),y)
 cflatobjs += lib/acpi.o
@@ -68,4 +72,5 @@ include $(SRCDIR)/$(TEST_DIR)/Makefile.common
 
 arch_clean: arm_clean
 	$(RM) lib/arm64/.*.d		\
-	      lib/qcbor/src/.*.d
+	      lib/qcbor/src/.*.d	\
+	      lib/token_verifier/.*.d
diff --git a/lib/token_verifier/attest_defines.h b/lib/token_verifier/attest_defines.h
new file mode 100644
index 00000000..daf51c5f
--- /dev/null
+++ b/lib/token_verifier/attest_defines.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+
+#ifndef __ATTEST_DEFINES_H__
+#define __ATTEST_DEFINES_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TAG_COSE_SIGN1                       (18)
+#define TAG_CCA_TOKEN                       (399)
+
+#define CCA_PLAT_TOKEN                    (44234)    /* 0xACCA */
+#define CCA_REALM_DELEGATED_TOKEN         (44241)
+
+/* CCA Platform Attestation Token */
+#define CCA_PLAT_CHALLENGE                   (10)    /* EAT nonce */
+#define CCA_PLAT_INSTANCE_ID                (256)    /* EAT ueid */
+#define CCA_PLAT_PROFILE                    (265)    /* EAT profile */
+#define CCA_PLAT_SECURITY_LIFECYCLE        (2395)
+#define CCA_PLAT_IMPLEMENTATION_ID         (2396)
+#define CCA_PLAT_SW_COMPONENTS             (2399)
+#define CCA_PLAT_VERIFICATION_SERVICE      (2400)
+#define CCA_PLAT_CONFIGURATION             (2401)
+#define CCA_PLAT_HASH_ALGO_ID              (2402)
+
+/* CCA Realm Delegated Attestation Token */
+#define CCA_REALM_CHALLENGE                  (10)    /* EAT nonce */
+#define CCA_REALM_PERSONALIZATION_VALUE   (44235)
+#define CCA_REALM_HASH_ALGO_ID            (44236)
+#define CCA_REALM_PUB_KEY                 (44237)
+#define CCA_REALM_INITIAL_MEASUREMENT     (44238)
+#define CCA_REALM_EXTENSIBLE_MEASUREMENTS (44239)
+#define CCA_REALM_PUB_KEY_HASH_ALGO_ID    (44240)
+
+/* Software components */
+#define CCA_SW_COMP_MEASUREMENT_VALUE         (2)
+#define CCA_SW_COMP_VERSION                   (4)
+#define CCA_SW_COMP_SIGNER_ID                 (5)
+#define CCA_SW_COMP_HASH_ALGORITHM            (6)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ATTEST_DEFINES_H__ */
diff --git a/lib/token_verifier/token_dumper.c b/lib/token_verifier/token_dumper.c
new file mode 100644
index 00000000..275d1fc5
--- /dev/null
+++ b/lib/token_verifier/token_dumper.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+
+#include "attest_defines.h"
+#include "libcflat.h"
+#include "token_dumper.h"
+
+#define COLUMN_WIDTH "20"
+
+void print_raw_token(const char *token, size_t size)
+{
+	int i;
+	char byte;
+
+	printf("\r\nCopy paste token to www.cbor.me\r\n");
+	for (i = 0; i < size; ++i) {
+		byte = token[i];
+		if (byte == 0)
+			printf("0x%#02x ", byte);
+		else
+			printf("0x%02x ", byte);
+		if (((i + 1) % 8) == 0)
+			printf("\r\n");
+	}
+	printf("\r\n");
+}
+
+static void print_indent(int indent_level)
+{
+	int i;
+
+	for (i = 0; i < indent_level; ++i) {
+		printf("  ");
+	}
+}
+
+static void print_byte_string(const char *name, int index,
+			      struct q_useful_buf_c buf)
+{
+	int i;
+
+	printf("%-"COLUMN_WIDTH"s (#%d) = [", name, index);
+	for (i = 0; i < buf.len; ++i) {
+		printf("%02x", ((uint8_t *)buf.ptr)[i]);
+	}
+	printf("]\r\n");
+}
+
+static void print_text(const char *name, int index, struct q_useful_buf_c buf)
+{
+	int i;
+
+	printf("%-"COLUMN_WIDTH"s (#%d) = \"", name, index);
+	for (i = 0; i < buf.len; ++i) {
+		printf("%c", ((uint8_t *)buf.ptr)[i]);
+	}
+	printf("\"\r\n");
+}
+
+static void print_claim(struct claim_t *claim, int indent_level)
+{
+	print_indent(indent_level);
+	if (claim->present) {
+		switch (claim->type) {
+		case CLAIM_INT64:
+			printf("%-"COLUMN_WIDTH"s (#%" PRId64 ") = %" PRId64
+				"\r\n", claim->title,
+			claim->key, claim->int_data);
+			break;
+		case CLAIM_BOOL:
+			printf("%-"COLUMN_WIDTH"s (#%" PRId64 ") = %s\r\n",
+			claim->title, claim->key,
+			claim->bool_data?"true":"false");
+			break;
+		case CLAIM_BSTR:
+			print_byte_string(claim->title, claim->key,
+				claim->buffer_data);
+			break;
+		case CLAIM_TEXT:
+			print_text(claim->title, claim->key,
+				claim->buffer_data);
+			break;
+		default:
+			printf("* Internal error at  %s:%d.\r\n", __FILE__,
+				(int)__LINE__);
+			break;
+		}
+	} else {
+		printf("* Missing%s claim with key: %" PRId64 " (%s)\r\n",
+			claim->mandatory?" mandatory":"",
+			claim->key, claim->title);
+	}
+}
+
+static void print_cose_sign1_wrapper(const char *token_type,
+				     struct claim_t *cose_sign1_wrapper)
+{
+	printf("\r\n== %s Token cose header:\r\n", token_type);
+	print_claim(cose_sign1_wrapper + 0, 0);
+	/* Don't print wrapped token bytestring */
+	print_claim(cose_sign1_wrapper + 2, 0);
+	printf("== End of %s Token cose header\r\n\r\n", token_type);
+}
+
+void print_token(struct attestation_claims *claims)
+{
+	int i;
+
+	print_cose_sign1_wrapper("Realm", claims->realm_cose_sign1_wrapper);
+
+	printf("\r\n== Realm Token:\r\n");
+	/* print the claims except the last one. That is printed in detail
+	 * below.
+	 */
+	for (i = 0; i < CLAIM_COUNT_REALM_TOKEN; ++i) {
+		struct claim_t *claim = claims->realm_token_claims + i;
+
+		print_claim(claim, 0);
+	}
+
+	printf("%-"COLUMN_WIDTH"s (#%d)\r\n", "Realm measurements",
+		CCA_REALM_EXTENSIBLE_MEASUREMENTS);
+	for (i = 0; i < CLAIM_COUNT_REALM_EXTENSIBLE_MEASUREMENTS; ++i) {
+		struct claim_t *claim = claims->realm_measurement_claims + i;
+
+		print_claim(claim, 1);
+	}
+	printf("== End of Realm Token.\r\n");
+
+	print_cose_sign1_wrapper("Platform", claims->plat_cose_sign1_wrapper);
+
+	printf("\r\n== Platform Token:\r\n");
+	for (i = 0; i < CLAIM_COUNT_PLATFORM_TOKEN; ++i) {
+		struct claim_t *claim = claims->plat_token_claims + i;
+
+		print_claim(claim, 0);
+	}
+	printf("== End of Platform Token\r\n\r\n");
+
+	printf("\r\n== Platform Token SW components:\r\n");
+
+	for (i = 0; i < MAX_SW_COMPONENT_COUNT; ++i) {
+		struct sw_component_t *component =
+			claims->sw_component_claims + i;
+
+		if (component->present) {
+			printf("  SW component #%d:\r\n", i);
+			for (int j = 0; j < CLAIM_COUNT_SW_COMPONENT; ++j) {
+				print_claim(component->claims + j, 2);
+			}
+		}
+	}
+	printf("== End of Platform Token SW components\r\n\r\n");
+}
diff --git a/lib/token_verifier/token_dumper.h b/lib/token_verifier/token_dumper.h
new file mode 100644
index 00000000..96cc0744
--- /dev/null
+++ b/lib/token_verifier/token_dumper.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+
+#ifndef __TOKEN_DUMPER_H__
+#define __TOKEN_DUMPER_H__
+
+#include "token_verifier.h"
+
+void print_raw_token(const char *token, size_t size);
+void print_token(struct attestation_claims *claims);
+
+#endif /* __TOKEN_DUMPER_H__ */
diff --git a/lib/token_verifier/token_verifier.c b/lib/token_verifier/token_verifier.c
new file mode 100644
index 00000000..ba2a89f6
--- /dev/null
+++ b/lib/token_verifier/token_verifier.c
@@ -0,0 +1,591 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+
+#include <libcflat.h>
+#include <inttypes.h>
+#include <qcbor/qcbor_decode.h>
+#include <qcbor/qcbor_spiffy_decode.h>
+#include "attest_defines.h"
+#include "token_verifier.h"
+#include "token_dumper.h"
+
+#define SHA256_SIZE 32
+#define SHA512_SIZE 64
+
+#define RETURN_ON_DECODE_ERROR(p_context) \
+	do { \
+		QCBORError ret; \
+		ret = QCBORDecode_GetError(p_context); \
+		if (ret != QCBOR_SUCCESS) { \
+			printf("QCBOR decode failed with error at %s:%d." \
+				" err = %d\r\n", \
+				__FILE__, (int)__LINE__, (int)ret); \
+			return TOKEN_VERIFICATION_ERR_QCBOR(ret); \
+		} \
+	} while (0)
+
+static void init_claim(struct claim_t *claim,
+		       bool mandatory, enum claim_data_type type,
+		       int64_t key, const char *title, bool present)
+{
+	claim->mandatory = mandatory;
+	claim->type = type;
+	claim->key = key;
+	claim->title = title;
+	claim->present = present;
+}
+
+static int init_cose_wrapper_claim(struct claim_t *cose_sign1_wrapper)
+{
+	struct claim_t *c;
+
+	/* The cose wrapper looks like the following:
+	 *  - Protected header (bytestring).
+	 *  - Unprotected header: might contain 0 items. This is a map. Due to
+	 *    the way this thing is implemented, it is not in the below list,
+	 *    but is handled in the verify_token_cose_sign1_wrapping
+	 *    function.
+	 *  - Payload: Platform token (bytestring). The content is passed for
+	 *    verify_platform_token.
+	 *  - Signature.
+	 */
+	c = cose_sign1_wrapper;
+	/* This structure is in an array, so the key is not used */
+	init_claim(c++, true, CLAIM_BSTR, 0, "Protected header",  false);
+	init_claim(c++, true, CLAIM_BSTR, 0, "Platform token payload", false);
+	init_claim(c++, true, CLAIM_BSTR, 0, "Signature",  false);
+	if (c > cose_sign1_wrapper + CLAIM_COUNT_COSE_SIGN1_WRAPPER) {
+		return TOKEN_VERIFICATION_ERR_INIT_ERROR;
+	}
+	return 0;
+}
+
+static int init_claims(struct attestation_claims *attest_claims)
+{
+	int i;
+	int ret;
+	struct claim_t *c;
+	/* TODO: All the buffer overwrite checks are happening too late.
+	 * Either remove, or find a better way.
+	 */
+	c = attest_claims->realm_token_claims;
+	init_claim(c++, true, CLAIM_BSTR, CCA_REALM_CHALLENGE,             "Realm challenge",                false);
+	init_claim(c++, true, CLAIM_BSTR, CCA_REALM_PERSONALIZATION_VALUE, "Realm personalization value",    false);
+	init_claim(c++, true, CLAIM_TEXT, CCA_REALM_HASH_ALGO_ID,          "Realm hash algo id",             false);
+	init_claim(c++, true, CLAIM_TEXT, CCA_REALM_PUB_KEY_HASH_ALGO_ID,  "Realm public key hash algo id",  false);
+	init_claim(c++, true, CLAIM_BSTR, CCA_REALM_PUB_KEY,               "Realm signing public key",       false);
+	init_claim(c++, true, CLAIM_BSTR, CCA_REALM_INITIAL_MEASUREMENT,   "Realm initial measurement",      false);
+	/* Realm extensible measurements are not present here as they are
+	 * encoded as a CBOR array, and it is handled specially in
+	 * verify_realm_token().
+	 */
+	if (c > attest_claims->realm_token_claims + CLAIM_COUNT_REALM_TOKEN) {
+		return TOKEN_VERIFICATION_ERR_INIT_ERROR;
+	}
+
+	ret = init_cose_wrapper_claim(attest_claims->realm_cose_sign1_wrapper);
+	if (ret != 0) {
+		return ret;
+	}
+	ret = init_cose_wrapper_claim(attest_claims->plat_cose_sign1_wrapper);
+	if (ret != 0) {
+		return ret;
+	}
+
+	c = attest_claims->plat_token_claims;
+	init_claim(c++, true,  CLAIM_BSTR,  CCA_PLAT_CHALLENGE,            "Challenge",            false);
+	init_claim(c++, false, CLAIM_TEXT,  CCA_PLAT_VERIFICATION_SERVICE, "Verification service", false);
+	init_claim(c++, true,  CLAIM_TEXT,  CCA_PLAT_PROFILE,              "Profile",              false);
+	init_claim(c++, true,  CLAIM_BSTR,  CCA_PLAT_INSTANCE_ID,          "Instance ID",          false);
+	init_claim(c++, true,  CLAIM_BSTR,  CCA_PLAT_IMPLEMENTATION_ID,    "Implementation ID",    false);
+	init_claim(c++, true,  CLAIM_INT64, CCA_PLAT_SECURITY_LIFECYCLE,   "Lifecycle",            false);
+	init_claim(c++, true,  CLAIM_BSTR,  CCA_PLAT_CONFIGURATION,        "Configuration",        false);
+	init_claim(c++, true,  CLAIM_TEXT,  CCA_PLAT_HASH_ALGO_ID,         "Platform hash algo",   false);
+	if (c > attest_claims->plat_token_claims +
+		CLAIM_COUNT_PLATFORM_TOKEN) {
+		return TOKEN_VERIFICATION_ERR_INIT_ERROR;
+	}
+
+	for (i = 0; i < CLAIM_COUNT_REALM_EXTENSIBLE_MEASUREMENTS; ++i) {
+		c = attest_claims->realm_measurement_claims + i;
+		init_claim(c, true, CLAIM_BSTR, i,
+			"Realm extensible measurements", false);
+	}
+
+	for (i = 0; i < MAX_SW_COMPONENT_COUNT; ++i) {
+		struct sw_component_t *component =
+			attest_claims->sw_component_claims + i;
+
+		component->present = false;
+		c = component->claims;
+		init_claim(c++, false, CLAIM_TEXT, CCA_SW_COMP_HASH_ALGORITHM,    "Hash algo.",  false);
+		init_claim(c++, true,  CLAIM_BSTR, CCA_SW_COMP_MEASUREMENT_VALUE, "Meas. val.", false);
+		init_claim(c++, false, CLAIM_TEXT, CCA_SW_COMP_VERSION,           "Version",    false);
+		init_claim(c++, true,  CLAIM_BSTR, CCA_SW_COMP_SIGNER_ID,         "Signer ID",  false);
+		if (c > component->claims + CLAIM_COUNT_SW_COMPONENT) {
+			return TOKEN_VERIFICATION_ERR_INIT_ERROR;
+		}
+	}
+	return TOKEN_VERIFICATION_ERR_SUCCESS;
+}
+
+static int handle_claim_decode_error(const struct claim_t *claim,
+				     QCBORError err)
+{
+	if (err == QCBOR_ERR_LABEL_NOT_FOUND) {
+		if (claim->mandatory) {
+			printf("Mandatory claim with key %" PRId64 " (%s) is "
+				"missing from token.\r\n", claim->key,
+				claim->title);
+			return TOKEN_VERIFICATION_ERR_MISSING_MANDATORY_CLAIM;
+		}
+	} else {
+		printf("Decode failed with error at %s:%d. err = %d key = %"
+			PRId64 " (%s).\r\n",  __FILE__, (int)__LINE__, err,
+			claim->key, claim->title);
+		return TOKEN_VERIFICATION_ERR_QCBOR(err);
+	}
+	return TOKEN_VERIFICATION_ERR_SUCCESS;
+}
+
+/* Consume claims from a map.
+ *
+ * This function iterates on the array 'claims', and looks up items with the
+ * specified keys. If a claim flagged as mandatory is not found, an error is
+ * returned. The function doesn't checks for extra items. So if the map contains
+ * items with keys that are not in the claims array, no error is reported.
+ *
+ * The map needs to be 'entered' before calling this function, and be 'exited'
+ * after it returns.
+ */
+static int get_claims_from_map(QCBORDecodeContext *p_context,
+			       struct claim_t *claims,
+			       size_t num_of_claims)
+{
+	QCBORError err;
+	int token_verification_error;
+	int i;
+
+	for (i = 0; i < num_of_claims; ++i) {
+		struct claim_t *claim = claims + i;
+
+		switch (claim->type) {
+		case CLAIM_INT64:
+			QCBORDecode_GetInt64InMapN(p_context, claim->key,
+				&(claim->int_data));
+			break;
+		case CLAIM_BOOL:
+			QCBORDecode_GetBoolInMapN(p_context, claim->key,
+				&(claim->bool_data));
+			break;
+		case CLAIM_BSTR:
+			QCBORDecode_GetByteStringInMapN(p_context, claim->key,
+				&(claim->buffer_data));
+			break;
+		case CLAIM_TEXT:
+			QCBORDecode_GetTextStringInMapN(p_context, claim->key,
+				&(claim->buffer_data));
+			break;
+		default:
+			printf("Internal error at  %s:%d.\r\n",
+				__FILE__, (int)__LINE__);
+			return TOKEN_VERIFICATION_ERR_INTERNAL_ERROR;
+		}
+		err = QCBORDecode_GetAndResetError(p_context);
+		if (err == QCBOR_SUCCESS) {
+			claim->present = true;
+		} else {
+			token_verification_error =
+				handle_claim_decode_error(claim, err);
+			if (token_verification_error !=
+				TOKEN_VERIFICATION_ERR_SUCCESS) {
+				return token_verification_error;
+			}
+		}
+	}
+	return TOKEN_VERIFICATION_ERR_SUCCESS;
+}
+
+/* Consume a single claim from an array and from the top level.
+ *
+ * The claim's 'key' and 'mandatory' attribute is not used in this function.
+ * The claim is considered mandatory.
+ */
+static int get_claim(QCBORDecodeContext *p_context, struct claim_t *claim)
+{
+	QCBORError err;
+
+	switch (claim->type) {
+	case CLAIM_INT64:
+		QCBORDecode_GetInt64(p_context, &(claim->int_data));
+		break;
+	case CLAIM_BOOL:
+		QCBORDecode_GetBool(p_context, &(claim->bool_data));
+		break;
+	case CLAIM_BSTR:
+		QCBORDecode_GetByteString(p_context, &(claim->buffer_data));
+		break;
+	case CLAIM_TEXT:
+		QCBORDecode_GetTextString(p_context, &(claim->buffer_data));
+		break;
+	default:
+		printf("Internal error at  %s:%d.\r\n",
+			__FILE__, (int)__LINE__);
+		break;
+	}
+	err = QCBORDecode_GetAndResetError(p_context);
+	if (err == QCBOR_SUCCESS) {
+		claim->present = true;
+		return TOKEN_VERIFICATION_ERR_SUCCESS;
+	}
+	printf("Decode failed with error at %s:%d. err = %d claim: \"%s\".\r\n",
+		__FILE__, (int)__LINE__, err, claim->title);
+	return TOKEN_VERIFICATION_ERR_QCBOR(err);
+}
+
+/* Consume claims from an array and from the top level.
+ *
+ * This function iterates on the array 'claims', and gets an item for each
+ * element. If the array or the cbor runs out of elements before reaching the
+ * end of the 'claims' array, then error is returned.
+ *
+ * The claim's 'key' and 'mandatory' attribute is not used in this function.
+ * All the elements considered mandatory.
+ */
+static int get_claims(QCBORDecodeContext *p_context, struct claim_t *claims,
+		      size_t num_of_claims)
+{
+	QCBORError err;
+	int i;
+
+	for (i = 0; i < num_of_claims; ++i) {
+		struct claim_t *claim = claims + i;
+
+		err = get_claim(p_context, claim);
+		if (err != TOKEN_VERIFICATION_ERR_SUCCESS) {
+			return err;
+		}
+	}
+	return TOKEN_VERIFICATION_ERR_SUCCESS;
+}
+
+static int verify_platform_token(struct q_useful_buf_c buf,
+				 struct attestation_claims *attest_claims)
+{
+	QCBORDecodeContext context;
+	int err;
+	int label, index;
+
+	QCBORDecode_Init(&context, buf, QCBOR_DECODE_MODE_NORMAL);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	QCBORDecode_EnterMap(&context, NULL);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	err = get_claims_from_map(&context,
+		attest_claims->plat_token_claims,
+		CLAIM_COUNT_PLATFORM_TOKEN);
+	if (err != TOKEN_VERIFICATION_ERR_SUCCESS) {
+		return err;
+	}
+
+	label = CCA_PLAT_SW_COMPONENTS;
+	QCBORDecode_EnterArrayFromMapN(&context, label);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	index = 0;
+	while (1) {
+		QCBORDecode_EnterMap(&context, NULL);
+		if (QCBORDecode_GetError(&context) == QCBOR_ERR_NO_MORE_ITEMS) {
+			/* This is OK. We just reached the end of the array.
+			 * Break from the loop.
+			 */
+			break;
+		}
+
+		if (index >= MAX_SW_COMPONENT_COUNT) {
+			printf("Not enough slots in sw_component_claims.\r\n");
+			printf("Increase MAX_SW_COMPONENT_COUNT in %s.\r\n",
+				__FILE__);
+			return TOKEN_VERIFICATION_ERR_INTERNAL_ERROR;
+		}
+
+		err = get_claims_from_map(&context,
+			attest_claims->sw_component_claims[index].claims,
+			CLAIM_COUNT_SW_COMPONENT);
+		if (err != TOKEN_VERIFICATION_ERR_SUCCESS) {
+			return err;
+		}
+		attest_claims->sw_component_claims[index].present = true;
+
+		QCBORDecode_ExitMap(&context);
+		RETURN_ON_DECODE_ERROR(&context);
+
+		++index;
+	}
+	/* We only get here if the decode error code was a
+	 * QCBOR_ERR_NO_MORE_ITEMS which is expected when the end of an array is
+	 * reached. In this case the processing must be continued, so clear the
+	 * error.
+	 */
+	QCBORDecode_GetAndResetError(&context);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	QCBORDecode_ExitArray(&context);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	QCBORDecode_ExitMap(&context);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	QCBORDecode_Finish(&context);
+
+	return TOKEN_VERIFICATION_ERR_SUCCESS;
+}
+
+static bool verify_length_of_measurement(size_t len)
+{
+	size_t allowed_lengths[] = {SHA256_SIZE, SHA512_SIZE};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(allowed_lengths); ++i) {
+		if (len == allowed_lengths[i])
+			return true;
+	}
+
+	return false;
+}
+
+static int verify_realm_token(struct q_useful_buf_c buf,
+			     struct attestation_claims *attest_claims)
+{
+	QCBORDecodeContext context;
+	int err;
+	int i;
+
+	QCBORDecode_Init(&context, buf, QCBOR_DECODE_MODE_NORMAL);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	QCBORDecode_EnterMap(&context, NULL);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	err = get_claims_from_map(&context, attest_claims->realm_token_claims,
+		CLAIM_COUNT_REALM_TOKEN);
+	if (err != TOKEN_VERIFICATION_ERR_SUCCESS) {
+		return err;
+	}
+
+	/* Now get the realm extensible measurements */
+	QCBORDecode_EnterArrayFromMapN(&context,
+					CCA_REALM_EXTENSIBLE_MEASUREMENTS);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	err = get_claims(&context,
+		attest_claims->realm_measurement_claims,
+		CLAIM_COUNT_REALM_EXTENSIBLE_MEASUREMENTS);
+	if (err != TOKEN_VERIFICATION_ERR_SUCCESS) {
+		return err;
+	}
+
+	for (i = 0; i < CLAIM_COUNT_REALM_EXTENSIBLE_MEASUREMENTS; ++i) {
+		struct claim_t *claims =
+			attest_claims->realm_measurement_claims;
+		struct q_useful_buf_c buf = claims[i].buffer_data;
+
+		if (!verify_length_of_measurement(buf.len)) {
+			return TOKEN_VERIFICATION_ERR_INVALID_CLAIM_LEN;
+		}
+	}
+
+	QCBORDecode_ExitArray(&context);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	QCBORDecode_ExitMap(&context);
+	QCBORDecode_Finish(&context);
+
+	return TOKEN_VERIFICATION_ERR_SUCCESS;
+}
+
+/* Returns a pointer to the wrapped token in: 'token_payload'.
+ * Returns the claims in the wrapper in cose_sign1_wrapper.
+ */
+static int verify_token_cose_sign1_wrapping(
+				  struct q_useful_buf_c token,
+				  struct q_useful_buf_c *token_payload,
+				  struct claim_t *cose_sign1_wrapper)
+{
+	QCBORDecodeContext context;
+	QCBORItem item;
+	int err;
+
+	QCBORDecode_Init(&context, token, QCBOR_DECODE_MODE_NORMAL);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	/* Check COSE tag. */
+	QCBORDecode_PeekNext(&context, &item);
+	if (!QCBORDecode_IsTagged(&context, &item,
+		TAG_COSE_SIGN1)) {
+		return TOKEN_VERIFICATION_ERR_INVALID_COSE_TAG;
+	}
+
+	QCBORDecode_EnterArray(&context, NULL);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	/* Protected header */
+	err = get_claim(&context, cose_sign1_wrapper);
+	if (err != TOKEN_VERIFICATION_ERR_SUCCESS) {
+		return err;
+	}
+
+	/* Unprotected header. The map is always present, but may contain 0
+	 * items.
+	 */
+	QCBORDecode_EnterMap(&context, NULL);
+	RETURN_ON_DECODE_ERROR(&context);
+
+		/* Skip the content for now. */
+
+	QCBORDecode_ExitMap(&context);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	/* Payload */
+	err = get_claim(&context, cose_sign1_wrapper + 1);
+	if (err != TOKEN_VERIFICATION_ERR_SUCCESS) {
+		return err;
+	}
+
+	/* Signature */
+	err = get_claim(&context, cose_sign1_wrapper + 2);
+	if (err != TOKEN_VERIFICATION_ERR_SUCCESS) {
+		return err;
+	}
+
+	QCBORDecode_ExitArray(&context);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	*token_payload = cose_sign1_wrapper[1].buffer_data;
+
+	return TOKEN_VERIFICATION_ERR_SUCCESS;
+}
+
+static int verify_cca_token(struct q_useful_buf_c  token,
+			    struct q_useful_buf_c *platform_token,
+			    struct q_useful_buf_c *realm_token)
+{
+	QCBORDecodeContext context;
+	QCBORItem item;
+	QCBORError err;
+
+	QCBORDecode_Init(&context, token, QCBOR_DECODE_MODE_NORMAL);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	/* ================== Check CCA_TOKEN tag =========================== */
+	QCBORDecode_PeekNext(&context, &item);
+	if (!QCBORDecode_IsTagged(&context, &item, TAG_CCA_TOKEN)) {
+		return TOKEN_VERIFICATION_ERR_INVALID_COSE_TAG;
+	}
+
+	/* ================== Get the the platform token ==================== */
+	QCBORDecode_EnterMap(&context, NULL);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	/*
+	 * First element is the CCA platfrom token which is a
+	 * COSE_Sign1_Tagged object. It has byte stream wrapper.
+	 */
+	QCBORDecode_GetByteStringInMapN(&context, CCA_PLAT_TOKEN,
+					platform_token);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	/* ================== Get the the realm token ======================= */
+	/*
+	 * Second element is the delegated realm token which is a
+	 * COSE_Sign1_Tagged object. It has byte stream wrapper.
+	 */
+	QCBORDecode_GetByteStringInMapN(&context, CCA_REALM_DELEGATED_TOKEN,
+					realm_token);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	QCBORDecode_ExitMap(&context);
+	RETURN_ON_DECODE_ERROR(&context);
+
+	/* Finishing up the decoding of the top-level wrapper */
+	err = QCBORDecode_Finish(&context);
+	if (err != QCBOR_SUCCESS) {
+		printf("QCBOR decode failed with error at %s:%d. err = %d\r\n",
+			__FILE__, (int)__LINE__, (int)err);
+		return TOKEN_VERIFICATION_ERR_QCBOR(err);
+	}
+
+	return TOKEN_VERIFICATION_ERR_SUCCESS;
+}
+
+/*
+ * This function expect two COSE_Sing1_Tagged object wrapped with a tagged map:
+ *
+ * cca-token = #6.44234(cca-token-map) ; 44234 = 0xACCA
+ *
+ * cca-platform-token = COSE_Sign1_Tagged
+ * cca-realm-delegated-token = COSE_Sign1_Tagged
+ *
+ * cca-token-map = {
+ *   0 => cca-platform-token
+ *   1 => cca-realm-delegated-token
+ * }
+ *
+ * COSE_Sign1_Tagged = #6.18(COSE_Sign1)
+ */
+int verify_token(const char *token, size_t size,
+		 struct attestation_claims *attest_claims)
+{
+	/* TODO: do signature check */
+	/* TODO: Add tag check on tokens */
+	struct q_useful_buf_c buf = {token, size};
+	int ret;
+	struct q_useful_buf_c realm_token;
+	struct q_useful_buf_c realm_token_payload;
+	struct q_useful_buf_c platform_token;
+	struct q_useful_buf_c platform_token_payload;
+
+	ret = init_claims(attest_claims);
+	if (ret != TOKEN_VERIFICATION_ERR_SUCCESS) {
+		return ret;
+	}
+
+	/* Verify top-level token map and extract the two sub-tokens */
+	ret = verify_cca_token(buf, &platform_token, &realm_token);
+	if (ret != TOKEN_VERIFICATION_ERR_SUCCESS) {
+		return ret;
+	}
+
+	/* Verify the COSE_Sign1 wrapper of the realm token */
+	ret = verify_token_cose_sign1_wrapping(realm_token,
+		&realm_token_payload,
+		attest_claims->realm_cose_sign1_wrapper);
+	if (ret != TOKEN_VERIFICATION_ERR_SUCCESS) {
+		return ret;
+	}
+	/* Verify the payload of the realm token */
+	ret = verify_realm_token(realm_token_payload, attest_claims);
+	if (ret != TOKEN_VERIFICATION_ERR_SUCCESS) {
+		return ret;
+	}
+
+	/* Verify the COSE_Sign1 wrapper of the platform token */
+	ret = verify_token_cose_sign1_wrapping(platform_token,
+		&platform_token_payload,
+		attest_claims->plat_cose_sign1_wrapper);
+	if (ret != TOKEN_VERIFICATION_ERR_SUCCESS) {
+		return ret;
+	}
+	/* Verify the payload of the platform token */
+	ret = verify_platform_token(platform_token_payload, attest_claims);
+	if (ret != TOKEN_VERIFICATION_ERR_SUCCESS) {
+		return ret;
+	}
+
+	return TOKEN_VERIFICATION_ERR_SUCCESS;
+}
+
diff --git a/lib/token_verifier/token_verifier.h b/lib/token_verifier/token_verifier.h
new file mode 100644
index 00000000..ec3ab9c9
--- /dev/null
+++ b/lib/token_verifier/token_verifier.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+
+#ifndef __TOKEN_VERIFIER_H__
+#define __TOKEN_VERIFIER_H__
+
+#include <qcbor/qcbor_decode.h>
+
+#define TOKEN_VERIFICATION_ERR_SUCCESS                 0
+#define TOKEN_VERIFICATION_ERR_INIT_ERROR              1
+#define TOKEN_VERIFICATION_ERR_MISSING_MANDATORY_CLAIM 2
+#define TOKEN_VERIFICATION_ERR_INVALID_COSE_TAG        3
+#define TOKEN_VERIFICATION_ERR_INVALID_CLAIM_LEN       4
+#define TOKEN_VERIFICATION_ERR_INTERNAL_ERROR          5
+#define TOKEN_VERIFICATION_ERR_QCBOR(qcbor_err)        (1000 + qcbor_err)
+
+/* Number of realm extensible measurements (REM) */
+#define REM_COUNT 4
+
+#define MAX_SW_COMPONENT_COUNT 16
+
+#define CLAIM_COUNT_REALM_TOKEN 6
+#define CLAIM_COUNT_COSE_SIGN1_WRAPPER 3
+#define CLAIM_COUNT_PLATFORM_TOKEN 8
+#define CLAIM_COUNT_REALM_EXTENSIBLE_MEASUREMENTS REM_COUNT
+#define CLAIM_COUNT_SW_COMPONENT 4
+
+/* This tells how the data should be interpreted in the claim_t struct, and not
+ * necessarily is the same as the item's major type in the token.
+ */
+enum claim_data_type {
+	CLAIM_INT64,
+	CLAIM_BOOL,
+	CLAIM_BSTR,
+	CLAIM_TEXT,
+};
+
+struct claim_t {
+	/* 'static' */
+	bool mandatory;
+	enum claim_data_type type;
+	int64_t key;
+	const char *title;
+
+	/* filled during verification */
+	bool present;
+	union {
+		int64_t int_data;
+		bool bool_data;
+		/* Used for text and bytestream as well */
+		/* TODO: Add expected length check as well? */
+		struct q_useful_buf_c buffer_data;
+	};
+};
+
+struct sw_component_t {
+	bool present;
+	struct claim_t claims[CLAIM_COUNT_SW_COMPONENT];
+};
+
+struct attestation_claims {
+	struct claim_t realm_cose_sign1_wrapper[CLAIM_COUNT_COSE_SIGN1_WRAPPER];
+	struct claim_t realm_token_claims[CLAIM_COUNT_REALM_TOKEN];
+	struct claim_t realm_measurement_claims[CLAIM_COUNT_REALM_EXTENSIBLE_MEASUREMENTS];
+	struct claim_t plat_cose_sign1_wrapper[CLAIM_COUNT_COSE_SIGN1_WRAPPER];
+	struct claim_t plat_token_claims[CLAIM_COUNT_PLATFORM_TOKEN];
+	struct sw_component_t sw_component_claims[MAX_SW_COMPONENT_COUNT];
+};
+
+/* Returns TOKEN_VERIFICATION_ERR* */
+int verify_token(const char *token, size_t size,
+	struct attestation_claims *attest_claims);
+
+#endif /* __TOKEN_VERIFIER_H__ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 27/33] arm: realm: add RSI interface for attestation measurements
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (26 preceding siblings ...)
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 26/33] arm: Add a library to verify tokens using the " Suzuki K Poulose
@ 2024-04-12 10:34 ` Suzuki K Poulose
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 28/33] arm: realm: Add helpers to decode RSI return codes Suzuki K Poulose
                   ` (6 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:34 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

Add wrappers for the Attestation and measurement related RSI calls.
These will be later used in the test cases

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm64/asm/rsi.h | 10 +++++++++
 lib/arm64/rsi.c     | 52 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 62 insertions(+)

diff --git a/lib/arm64/asm/rsi.h b/lib/arm64/asm/rsi.h
index 0b726684..2566c000 100644
--- a/lib/arm64/asm/rsi.h
+++ b/lib/arm64/asm/rsi.h
@@ -29,6 +29,16 @@ int rsi_invoke(unsigned int function_id, unsigned long arg0,
 int __rsi_get_version(unsigned long ver, struct smccc_result *res);
 int rsi_get_version(unsigned long ver);
 
+int rsi_attest_token_init(unsigned long *challenge, unsigned long *max_size);
+int rsi_attest_token_continue(phys_addr_t addr,
+			      unsigned long offset,
+			      unsigned long size,
+			      unsigned long *len);
+void rsi_extend_measurement(unsigned int index, unsigned long size,
+			    unsigned long *measurement,
+			    struct smccc_result *res);
+void rsi_read_measurement(unsigned int index, struct smccc_result *res);
+
 static inline bool is_realm(void)
 {
 	return rsi_present;
diff --git a/lib/arm64/rsi.c b/lib/arm64/rsi.c
index e58d9660..8fe672fc 100644
--- a/lib/arm64/rsi.c
+++ b/lib/arm64/rsi.c
@@ -134,3 +134,55 @@ void arm_set_memory_shared(unsigned long start, unsigned long size)
 {
 	arm_set_memory_state(start, size, RIPAS_EMPTY, RSI_CHANGE_DESTROYED);
 }
+
+int rsi_attest_token_init(unsigned long *challenge, unsigned long *max_size)
+{
+	struct smccc_result res;
+
+	rsi_invoke(SMC_RSI_ATTEST_TOKEN_INIT,
+		   challenge[0], challenge[1], challenge[2],
+		   challenge[3], challenge[4], challenge[5],
+		   challenge[6], challenge[7], 0, 0, 0, &res);
+
+	if (max_size)
+		*max_size = res.r1;
+	return res.r0;
+}
+
+int rsi_attest_token_continue(phys_addr_t addr,
+			      unsigned long offset,
+			      unsigned long size,
+			      unsigned long *len)
+{
+	struct smccc_result res = { 0 };
+
+	rsi_invoke(SMC_RSI_ATTEST_TOKEN_CONTINUE, addr, offset, size,
+		   0, 0, 0, 0, 0, 0, 0, 0, &res);
+	switch (res.r0) {
+	case RSI_SUCCESS:
+	case RSI_INCOMPLETE:
+		if (len)
+			*len = res.r1;
+		/* Fall through */
+	default:
+		break;
+	}
+	return res.r0;
+}
+
+void rsi_extend_measurement(unsigned int index, unsigned long size,
+			    unsigned long *measurement, struct smccc_result *res)
+{
+	rsi_invoke(SMC_RSI_MEASUREMENT_EXTEND, index, size,
+		   measurement[0], measurement[1],
+		   measurement[2], measurement[3],
+		   measurement[4], measurement[5],
+		   measurement[6], measurement[7],
+		   0, res);
+}
+
+void rsi_read_measurement(unsigned int index, struct smccc_result *res)
+{
+	rsi_invoke(SMC_RSI_MEASUREMENT_READ, index, 0,
+		   0, 0, 0, 0, 0, 0, 0, 0, 0, res);
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 28/33] arm: realm: Add helpers to decode RSI return codes
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (27 preceding siblings ...)
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 27/33] arm: realm: add RSI interface for attestation measurements Suzuki K Poulose
@ 2024-04-12 10:34 ` Suzuki K Poulose
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 29/33] arm: realm: Add Realm attestation tests Suzuki K Poulose
                   ` (5 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:34 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

RMM encodes error code and index in the result of an operation.
Add helpers to decode this information for use with the attestation
tests.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 lib/arm64/asm/rsi.h | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/lib/arm64/asm/rsi.h b/lib/arm64/asm/rsi.h
index 2566c000..c7d65333 100644
--- a/lib/arm64/asm/rsi.h
+++ b/lib/arm64/asm/rsi.h
@@ -16,6 +16,39 @@
 
 extern bool rsi_present;
 
+/*
+ * Logical representation of return code returned by RMM commands.
+ * Each failure mode of a given command should return a unique return code, so
+ * that the caller can use it to unambiguously identify the failure mode.  To
+ * avoid having a very large list of enumerated values, the return code is
+ * composed of a status which identifies the category of the error (for example,
+ * an address was misaligned), and an index which disambiguates between multiple
+ * similar failure modes (for example, a command may take multiple addresses as
+ * its input; the index identifies _which_ of them was misaligned.)
+ */
+typedef unsigned int status_t;
+typedef struct {
+	status_t status;
+	unsigned int index;
+} return_code_t;
+
+/*
+ * Convenience function for creating a return_code_t.
+ */
+static inline return_code_t make_return_code(unsigned int status,
+					     unsigned int index)
+{
+	return (return_code_t) {status, index};
+}
+
+/*
+ * Unpacks a return code.
+ */
+static inline return_code_t unpack_return_code(unsigned long error_code)
+{
+	return make_return_code(error_code & 0xff, error_code >> 8);
+}
+
 void arm_rsi_init(void);
 
 int rsi_invoke(unsigned int function_id, unsigned long arg0,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 29/33] arm: realm: Add Realm attestation tests
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (28 preceding siblings ...)
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 28/33] arm: realm: Add helpers to decode RSI return codes Suzuki K Poulose
@ 2024-04-12 10:34 ` Suzuki K Poulose
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 30/33] " Suzuki K Poulose
                   ` (4 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:34 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Mate Toth-Pal, Suzuki K Poulose

From: Mate Toth-Pal <mate.toth-pal@arm.com>

Add tests for Attestation and measurement related RSI calls.

Signed-off-by: Mate Toth-Pal <mate.toth-pal@arm.com>
Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
[ Rewrote the test cases, keeping the core testing data/logic
  Added more test scenarios to test the ABI
]
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/Makefile.arm64 |    1 +
 arm/realm-attest.c | 1252 ++++++++++++++++++++++++++++++++++++++++++++
 arm/unittests.cfg  |   50 ++
 lib/libcflat.h     |    1 +
 4 files changed, 1304 insertions(+)
 create mode 100644 arm/realm-attest.c

diff --git a/arm/Makefile.arm64 b/arm/Makefile.arm64
index 79952914..7a56029e 100644
--- a/arm/Makefile.arm64
+++ b/arm/Makefile.arm64
@@ -67,6 +67,7 @@ tests += $(TEST_DIR)/debug.$(exe)
 tests += $(TEST_DIR)/fpu.$(exe)
 tests += $(TEST_DIR)/realm-rsi.$(exe)
 tests += $(TEST_DIR)/realm-sea.$(exe)
+tests += $(TEST_DIR)/realm-attest.$(exe)
 
 include $(SRCDIR)/$(TEST_DIR)/Makefile.common
 
diff --git a/arm/realm-attest.c b/arm/realm-attest.c
new file mode 100644
index 00000000..082d4964
--- /dev/null
+++ b/arm/realm-attest.c
@@ -0,0 +1,1252 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+#include <libcflat.h>
+
+#include <attest_defines.h>
+#include <alloc.h>
+#include <stdlib.h>
+#include <token_dumper.h>
+#include <token_verifier.h>
+
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/rsi.h>
+#include <asm/setup.h>
+#include <asm/smp.h>
+
+
+#define SHA256_SIZE	32
+
+struct challenge {
+	unsigned long words[8];
+};
+
+struct measurement {
+	unsigned long words[8];
+};
+
+static char __attribute__((aligned(SZ_2M))) __attribute__((section(".data")))
+			block_buf_data[SZ_2M * 2];
+
+static char __attribute__((aligned(SZ_2M))) __attribute__((section(".bss")))
+			block_buf_bss[SZ_2M];
+
+static char __attribute__((aligned(SZ_4K))) __attribute__((section(".data")))
+			page_buf_data[SZ_4K];
+
+static char __attribute__((aligned(SZ_4K))) __attribute__((section(".bss")))
+			page_buf_bss[SZ_4K];
+
+/* Page aligned offset within the block mapped buffer */
+#define BLOCK_BUF_OFFSET	(SZ_8K)
+
+static inline void debug_print_raw_token(void *buf, size_t size)
+{
+#ifdef PRINT_RAW_TOKEN
+	print_raw_token(buf, size);
+#endif
+}
+
+static inline void debug_print_token(struct attestation_claims *claim)
+{
+#ifdef PRINT_TOKEN
+	print_token(claim);
+#endif
+}
+
+static bool claims_verify_token(char *token, size_t token_size,
+				struct attestation_claims *claims,
+				bool report_success)
+{
+	int verify_rc = verify_token(token, token_size, claims);
+	int cpu = smp_processor_id();
+
+	if (verify_rc == TOKEN_VERIFICATION_ERR_SUCCESS) {
+		if (report_success)
+			report(true, "CPU%d: Verfication of token passed", cpu);
+		return true;
+	}
+
+	report(false,
+	       "CPU%d: Verification of token failed with error code %d",
+	       cpu, verify_rc);
+
+	return false;
+}
+
+static inline int attest_token_init(struct challenge *ch, unsigned long *max_size)
+{
+	return rsi_attest_token_init(&ch->words[0], max_size);
+}
+
+static inline int attest_token_continue(phys_addr_t addr,
+					unsigned long offset,
+					unsigned long size,
+					unsigned long *len)
+{
+	return rsi_attest_token_continue(addr, offset, size, len);
+}
+
+static int attest_token_complete(phys_addr_t base, size_t buf_size,
+				 size_t *plen)
+{
+	int ret = 0;
+	phys_addr_t ipa = base;
+	size_t offset = 0;
+	size_t len = 0;
+	unsigned long bytes = 0;
+	size_t size = 0;
+
+	len = 0;
+	do {
+		ipa = ALIGN_DOWN(ipa, RSI_GRANULE_SIZE);
+		offset &= RSI_GRANULE_SIZE - 1;
+		size = RSI_GRANULE_SIZE - offset;
+		if ((buf_size - len) < size)
+			size = buf_size - len;
+
+		ret = rsi_attest_token_continue(ipa, offset, size, &bytes);
+		len += bytes;
+		ipa += bytes;
+		offset += bytes;
+	} while (ret == RSI_INCOMPLETE && len < buf_size);
+
+	if (plen)
+		*plen = len;
+	report_info("Found %ldbytes\n", len);
+	return ret;
+}
+
+static int get_attest_token(phys_addr_t ipa,
+			    size_t size,
+			    struct challenge *ch,
+			    unsigned long *len)
+{
+	size_t max_size = 0;
+	int rc;
+
+	rc = attest_token_init(ch, &max_size);
+	if (max_size > size)
+		report_info("Attestation token size (%ld bytes) is greater than the buffer size\n",
+			    max_size);
+	if (rc)
+		return rc;
+
+	rc = attest_token_complete(ipa, size, len);
+	if (len && *len > max_size)
+		report_info("RMM BUG: Token size is greater than the max token size from RSI_ATTEST_TOKEN_INIT\n");
+
+	return rc;
+}
+
+/*
+ * __get_attest_token_claims: Get attestation token and verify the claims.
+ * If @claims is not NULL, token is parsed and the @claims is populated.
+ * All failures reported. Success is only reported if the @report_success is
+ * true.
+ * Returns whether the calls and verification succeeds
+ */
+static bool __get_attest_token_claims(void *buf, size_t buf_size,
+				      struct challenge *ch,
+				      struct attestation_claims *claims,
+				      size_t *ptoken_size, bool report_success)
+{
+	int rc;
+	struct attestation_claims local_claims;
+	struct attestation_claims *claimsp;
+	unsigned long token_size = 0;
+
+	/* Use the local_claims if claims is not supplied */
+	claimsp = claims ? : &local_claims;
+
+	rc = get_attest_token(virt_to_phys(buf), buf_size, ch, &token_size);
+	if (rc != RSI_SUCCESS) {
+		report(false, "Get attestation token with : %d", rc);
+		return false;
+	}
+
+	if (report_success)
+		report(true, "Get attestation token");
+
+	if (ptoken_size)
+		*ptoken_size = token_size;
+
+	return claims_verify_token(buf, token_size, claimsp, report_success);
+}
+
+static bool get_attest_token_claims(void *buf, size_t buf_size,
+				    struct challenge *ch,
+				    struct attestation_claims *claims,
+				    size_t *token_size)
+{
+	return __get_attest_token_claims(buf, buf_size, ch, claims, token_size, false);
+}
+
+static void get_verify_attest_token(void *buf, size_t buf_size,
+				    struct challenge *ch, const char *desc)
+{
+	report_prefix_push(desc);
+	__get_attest_token_claims(buf, buf_size, ch, NULL, NULL, true);
+	report_prefix_pop();
+}
+
+static void get_verify_attest_token_verbose(void *buf, size_t buf_size,
+					    struct challenge *ch,
+					    const char *desc)
+{
+	size_t token_size;
+	struct attestation_claims claims;
+
+	report_prefix_push(desc);
+	if (__get_attest_token_claims(buf, buf_size, ch, &claims, &token_size, true)) {
+		debug_print_raw_token(buf, token_size);
+		debug_print_token(&claims);
+	}
+	report_prefix_pop();
+}
+
+static void test_get_attest_token(void)
+{
+	char stack_buf[SZ_4K]__attribute__((aligned(SZ_4K)));
+	char *heap_buf;
+	struct challenge ch;
+
+	memset(&ch, 0xAB, sizeof(ch));
+
+	/* Heap buffer */
+	heap_buf = memalign(SZ_4K, SZ_4K);
+	if (heap_buf) {
+		get_verify_attest_token(heap_buf, SZ_4K, &ch, "heap buffer");
+		free(heap_buf);
+	} else {
+		report_skip("heap buffer: Failed to allocate");
+	}
+
+	/* Stack buffer */
+	get_verify_attest_token(stack_buf, SZ_4K, &ch, "stack buffer");
+	/* Page aligned buffer .data segment */
+	get_verify_attest_token(page_buf_data, SZ_4K, &ch, ".data segment buffer");
+	/* Page aligned buffer .bss segment */
+	get_verify_attest_token(page_buf_bss, SZ_4K, &ch, ".bss segment buffer");
+	/* Block mapped buffer in .data segment */
+	get_verify_attest_token(&block_buf_data[BLOCK_BUF_OFFSET], SZ_8K, &ch,
+				"block mapped .data segment buffer");
+	/* Block mapped buffer in .bss segment */
+	get_verify_attest_token_verbose(&block_buf_bss[BLOCK_BUF_OFFSET], SZ_8K,
+					 &ch, "block mapped .bss segment buffer");
+}
+
+static void get_attest_token_check_fail(phys_addr_t ipa,
+					struct challenge *ch,
+					return_code_t exp,
+					const char *buf_desc)
+{
+	return_code_t rc;
+
+	report_prefix_push(buf_desc);
+	rc = unpack_return_code(get_attest_token(ipa, SZ_4K, ch, NULL));
+	if (rc.status != exp.status) {
+		report(false, "Get attestation token "
+			      "got (%d) expected (%d)",
+			      rc.status, exp.status);
+	} else {
+		report(true, "Get attestation token fails as expected");
+	}
+	report_prefix_pop();
+}
+
+static void test_get_attest_token_bad_input(void)
+{
+	struct challenge ch;
+	return_code_t exp;
+
+	memset(page_buf_data, 0, sizeof(page_buf_data));
+	memset(&ch, 0xAB, sizeof(ch));
+	exp = make_return_code(RSI_ERROR_INPUT, 0);
+
+	get_attest_token_check_fail(__phys_end + SZ_512M, &ch, exp,
+				    "buffer outside PAR");
+}
+
+static void test_get_attest_token_abi_misuse(void)
+{
+	int ret;
+	size_t len = 0;
+	struct challenge ch;
+	phys_addr_t ipa = virt_to_phys(page_buf_data);
+	return_code_t rc;
+
+	memset(&ch, 0xAB, sizeof(ch));
+
+	/*
+	 * Testcase 1 - Miss call to RSI_ATTEST_TOKEN_INIT
+	 *
+	 * step1. Execute a successful test to reset the state machine.
+	 */
+	report_prefix_push("miss token init");
+	ret = get_attest_token(ipa, SZ_4K, &ch, &len);
+	if (ret) {
+		report(false, "Get attestation failed %d", ret);
+		report_prefix_pop(); /* miss token init */
+		return;
+	}
+	report_info("Received a token of size %ld\n", len);
+
+	/*
+	 * step2. Execute RSI_ATTEST_TOKEN_CONTINUE without an RSI_ATTEST_TOKEN_INIT.
+	 * 	  Expect an error == RSI_ERROR_STATE
+	 */
+	rc = unpack_return_code(attest_token_continue(ipa, 0, 0, &len));
+	if (rc.status != RSI_ERROR_STATE) {
+		report(false, "Unexpected result (%d, %d) vs (%d) expected",
+		       rc.status, rc.index, RSI_ERROR_STATE);
+	} else {
+		report(true, "Subsequent call without RSI_ATTEST_TOKEN_INIT fails as expected");
+	}
+	report_prefix_pop(); /* miss token init */
+
+	/*
+	 * Test case 2 - Calling with invalid input.
+	 * step1. Issue RSI_ATTEST_TOKEN_INIT
+	 */
+	report_prefix_push("invalid input");
+	ret = attest_token_init(&ch, NULL);
+	if (ret) {
+		report(false, "RSI_ATTEST_TOKEN_INIT failed unexpectedly (%d, %d)",
+		       rc.status, rc.index);
+		report_prefix_pop(); /* inconsistent input */
+		return;
+	}
+
+	/*
+	 * step2. Pass invalid IPA and issue RSI_ATTEST_TOKEN_CONTINUE.
+	 * Test : Expect error == (RSI_ERROR_INPUT, 0)
+	 */
+	report_prefix_push("invalid ipa");
+	len = 0;
+	rc = unpack_return_code(attest_token_continue(ipa ^ 0x1UL, 0, RSI_GRANULE_SIZE, &len));
+	if (rc.status != RSI_ERROR_INPUT || len) {
+		report(false, "Attest token continue unexpected results"
+			       " (%d) vs expected (%d), len: %ld vs 0",
+			      rc.status, RSI_ERROR_INPUT, len);
+	} else {
+		report(true, "Attestation token continue failed for invalid IPA\n");
+	}
+	report_prefix_pop();
+
+	/*
+	 * step3. Pass offset == RSI_GRANULE_SIZE + 1 for RSI_ATTEST_TOKEN_CONTINUE.
+	 *	  triggering (offset + size) > RSI_GRANULE_SIZE
+	 * Test : Expect error == (RSI_ERROR_INPUT, 0)
+	 */
+	report_prefix_push("invalid offset");
+	len = 0;
+	rc = unpack_return_code(attest_token_continue(ipa, RSI_GRANULE_SIZE + 1, 0, &len));
+	if (rc.status != RSI_ERROR_INPUT || len) {
+		report(false, "Attest token continue unexpected results"
+			       " (%d) vs expected (%d), len: %ld vs 0",
+			      rc.status, RSI_ERROR_INPUT, len);
+	} else {
+		report(true, "Attestation token continue failed for invalid offset\n");
+	}
+	report_prefix_pop();
+
+	/*
+	 * step4. Pass size == RSI_GRANULE_SIZE + 1 for RSI_ATTEST_TOKEN_CONTINUE.
+	 *	  triggering (offset + size) > RSI_GRANULE_SIZE
+	 * Test : Expect error == (RSI_ERROR_INPUT, 0)
+	 */
+	report_prefix_push("invalid size");
+	len = 0;
+	rc = unpack_return_code(attest_token_continue(ipa, 0, RSI_GRANULE_SIZE + 1, NULL));
+	if (rc.status != RSI_ERROR_INPUT) {
+		report(false, "Attest token continue unexpected results"
+			       " (%d) vs expected (%d)",
+			      rc.status, RSI_ERROR_INPUT);
+	} else {
+		report(true, "Attestation token continue failed for invalid size\n");
+	}
+	report_prefix_pop();
+
+	/*
+	 * step5. Pass offset, size to cause overflow for RSI_ATTEST_TOKEN_CONTINUE.
+	 * Test : Expect error == (RSI_ERROR_INPUT, 0)
+	 */
+	report_prefix_push("size overflow");
+	len = 0;
+	rc = unpack_return_code(attest_token_continue(ipa, 0x100, ~0UL, &len));
+	if (rc.status != RSI_ERROR_INPUT || len) {
+		report(false, "Attest token continue unexpected results"
+			       " (%d) vs expected (%d), len: %ld vs 0",
+			      rc.status, RSI_ERROR_INPUT, len);
+	} else {
+		report(true, "Attestation token continue failed for overflow size\n");
+	}
+	report_prefix_pop();
+
+	/*
+	 * step6. Pass offset, size to cause overflow for RSI_ATTEST_TOKEN_CONTINUE.
+	 * Test : Expect error == (RSI_ERROR_INPUT, 0)
+	 */
+	report_prefix_push("offset overflow");
+	len = 0;
+	rc = unpack_return_code(attest_token_continue(ipa, ~0UL, 0x100, &len));
+	if (rc.status != RSI_ERROR_INPUT || len) {
+		report(false, "Attest token continue unexpected results"
+			       " (%d) vs expected (%d), len: %ld vs 0",
+			      rc.status, RSI_ERROR_INPUT, len);
+	} else {
+		report(true, "Attestation token continue failed for overflow offset\n");
+	}
+	report_prefix_pop();
+
+	/*
+	 * step7. Pass unaligned IPA
+	 * Test : Expect error == (RSI_ERROR_INPUT, 0)
+	 */
+	report_prefix_push("unaligned ipa");
+	len = 0;
+	rc = unpack_return_code(attest_token_continue(ipa + 0x100, 0, 0x200, &len));
+	if (rc.status != RSI_ERROR_INPUT || len) {
+		report(false, "Attest token continue unexpected results"
+			       " (%d) vs expected (%d), len: %ld vs 0",
+			      rc.status, RSI_ERROR_INPUT, len);
+	} else {
+		report(true, "Attestation token continue failed for unaligned ipa\n");
+	}
+	report_prefix_pop();
+
+	/*
+	 * step8. Pass valid offset, and size == 0
+	 * Test : Expect success == { (RSI_INCOMPLETE, 0), len = 0 }
+	 */
+	report_prefix_push("0 size");
+	len = 0;
+	rc = unpack_return_code(attest_token_continue(ipa, 0, 0, &len));
+	if (rc.status != RSI_INCOMPLETE || len) {
+		report(false, "Attest token continue unexpected results"
+			       " (%d) vs expected (%d), len: %ld vs 0",
+			      rc.status, RSI_INCOMPLETE, len);
+	} else {
+		report(true, "Attestation token continue returned 0bytes\n");
+	}
+	report_prefix_pop(); /* invalid input */
+
+
+	/*
+	 * Test case 3
+	 * step1. Complete the token attestation from with proper values.
+	 *        Failures in the Test case 2 should not affect the completion.
+	 */
+	report_prefix_push("valid input after inconsistent input");
+	rc = unpack_return_code(attest_token_complete(ipa, SZ_4K, &len));
+	if (rc.status) {
+		report(false, "Attest token continue failed with (%d, %d)",
+			rc.status, rc.index);
+		return;
+	} else {
+		report(true, "Attest token continue complete with %ld bytes", len);
+	}
+	report_prefix_pop(); /* Valid input after inconsistent input */
+}
+
+static void test_get_attest_token_abi_abort_req(void)
+{
+	int ret;
+	char *p;
+	size_t size;
+	struct attestation_claims claims;
+	struct challenge ch;
+	char stack_buf[SZ_4K] __attribute__((aligned(SZ_4K))) = { 0 };
+	phys_addr_t addr = virt_to_phys(stack_buf);
+
+	/* Set the initial challenge, which will be aborted */
+	memset(&ch, 0xAB, sizeof(ch));
+	ret = attest_token_init(&ch, &size);
+	if (ret) {
+		report(false, "Attest token init failed %d", ret);
+		return;
+	}
+
+	/* Execute one cycles, but not let it complete */
+	report_info("Attest token continue with %lx, 0, 4K\n", addr);
+	ret = attest_token_continue(addr, 0, RSI_GRANULE_SIZE, &size);
+	if (ret != RSI_INCOMPLETE) {
+		if (ret) {
+			report(false, "Attest token continue : unexpected "
+			       "failure %d", ret);
+		} else {
+			report_skip("Attest token finished in one iteration");
+			return;
+		}
+	}
+
+	/* Issue a fresh Attest Token request with updated challenge */
+	memset(&ch, 0xEE, sizeof(ch));
+	ret = get_attest_token(addr, RSI_GRANULE_SIZE, &ch, &size);
+	if (ret) {
+		report(false, "Attest Token failed %d", ret);
+		return;
+	}
+	claims_verify_token(stack_buf, size, &claims, false);
+
+	/*
+	 * TODO: Index of claim in the array depends on the init sequence
+	 * in token_verifier.c: init_claim()
+	 */
+	p = (char*)claims.realm_token_claims[0].buffer_data.ptr;
+	size = claims.realm_token_claims[0].buffer_data.len;
+
+	/* Verify that token contains the updated challenge. */
+	if (size != sizeof(ch)) {
+		report(false, "Attestation token: abort request: "
+				"claim size mismatch : %d", ret);
+		return;
+	}
+	if (memcmp(p, &ch, size)) {
+		report(false, "Attestation token: abort request: "
+			      "claim value mismatch: %d", ret);
+		return;
+	}
+	report(true, "Aborting ongoing request");
+}
+
+static void run_rsi_attest_tests(void)
+{
+	report_prefix_push("attest");
+
+	test_get_attest_token();
+
+	report_prefix_push("bad input");
+	test_get_attest_token_bad_input();
+	report_prefix_pop();
+
+	report_prefix_push("ABI misuse");
+	test_get_attest_token_abi_misuse();
+	report_prefix_pop();
+
+	report_prefix_push("ABI Abort");
+	test_get_attest_token_abi_abort_req();
+	report_prefix_pop();
+
+	report_prefix_pop(); /* attest */
+}
+
+static void run_get_token_times(void *data)
+{
+	char buf[SZ_4K] __attribute__((aligned(SZ_4K)));
+	struct challenge ch;
+	struct attestation_claims claims;
+	unsigned long runs = ((size_t)data);
+	int i, j;
+	int cpu = smp_processor_id();
+
+	report_info("CPU%d: Running get token test %ld times", cpu, runs);
+	for (i = 0; i < runs; i++) {
+		uint8_t pattern = (cpu << 4) | (i & 0xf);
+		size_t token_size;
+		struct claim_t *claim;
+
+		memset(buf, 0, sizeof(buf));
+		memset(&ch, pattern, sizeof(ch));
+
+		if (!get_attest_token_claims(buf, sizeof(buf), &ch, &claims, &token_size))
+			return;
+		claim = claims.realm_token_claims;
+		if (claim->key != CCA_REALM_CHALLENGE ||
+		    claim->buffer_data.len != sizeof(ch)) {
+			report(false, "Invalid challenge size in parsed token:"
+				      " %zu (expected %zu)",
+				      claim->buffer_data.len, sizeof(ch));
+			return;
+		}
+
+		for (j = 0; j < sizeof(ch); j++) {
+			uint8_t byte = ((uint8_t *)claim->buffer_data.ptr)[j];
+			if (byte != pattern) {
+				report(false, "Invalid byte in challenge[%d]: "
+					       " %02x (expected %02x)",
+					       j, byte, pattern);
+				return;
+			}
+		}
+	}
+	report(true, "CPU%d: Completed runs", cpu);
+}
+
+static void run_rsi_attest_smp_test(void)
+{
+	unsigned long runs = 100;
+
+	report_prefix_push("attest_smp");
+	on_cpus(run_get_token_times, (void *)runs);
+	report_prefix_pop();
+}
+
+/*
+ * There are 7 slots for measurements. The first is reserved for initial
+ * content measurement. The rest are meant to store runtime measurements.
+ * Runtime measurements are extended (concatenated and hashed). Reading
+ * them back separately is unsupported. They can be queried in an
+ * attestation token.
+ *
+ * Measurement size is 64bytes maximum to accommodate a SHA512 hash.
+ */
+
+static void measurement_extend(int idx, struct measurement *m, size_t size,
+			       struct smccc_result *res)
+{
+	rsi_extend_measurement(idx, size, &m->words[0], res);
+}
+
+static void test_extend_measurement(void)
+{
+	struct smccc_result result;
+	struct measurement m;
+	return_code_t rc;
+	int idx;
+
+	memset(&m, 0xEE, sizeof(m));
+	/*
+	 * Store Runtime measurements for all possible slots.
+	 */
+	for (idx = 1; idx <= REM_COUNT; idx ++) {
+		measurement_extend(idx, &m, sizeof(m.words), &result);
+		rc = unpack_return_code(result.r0);
+		report(!rc.status, "Extend measurement idx: %d (%d, %d)",
+			idx, rc.status, rc.index);
+	}
+}
+
+static void test_extend_measurement_bad_index(struct measurement *m)
+{
+	struct smccc_result result;
+	return_code_t rc;
+	int indices[] = { 0, REM_COUNT + 1 };
+	const char *idx_descs[] = { "reserved", "out-of-bounds" };
+	int i;
+
+	report_prefix_push("index");
+	for (i = 0; i < ARRAY_SIZE(indices); i++) {
+		report_prefix_push(idx_descs[i]);
+		measurement_extend(indices[i], m, sizeof(m->words), &result);
+		rc = unpack_return_code(result.r0);
+
+		if (rc.status != RSI_ERROR_INPUT)
+			report(false, "Extend measurement index: "
+				      "actual (%d) vs expected (%d)",
+				      rc.status, RSI_ERROR_INPUT);
+		else
+			report(true, "Extend measurement index fails as expected");
+		report_prefix_pop(); /* idx_descs[i] */
+	}
+	report_prefix_pop(); /* index */
+}
+
+static void test_extend_measurement_bad_size(struct measurement *m)
+{
+	struct smccc_result result;
+	return_code_t rc;
+
+	report_prefix_push("size");
+	rsi_extend_measurement(1, 65, &m->words[0], &result);
+	rc = unpack_return_code(result.r0);
+	if (rc.status != RSI_ERROR_INPUT)
+		report(false, "Measurement extend "
+			      "actual (%d) vs expected (%d)",
+			      rc.status, RSI_ERROR_INPUT);
+	else
+		report(true, "Extend measurement fails as expected");
+	report_prefix_pop(); /* size */
+}
+
+static void test_extend_measurement_bad_input(void)
+{
+	struct measurement m;
+
+	report_prefix_push("bad input");
+	memset(&m, 0xEE, sizeof(m));
+	test_extend_measurement_bad_index(&m);
+	test_extend_measurement_bad_size(&m);
+	report_prefix_pop(); /* bad input */
+}
+
+static void run_rsi_extend_tests(void)
+{
+	report_prefix_push("extend");
+	test_extend_measurement();
+	test_extend_measurement_bad_input();
+	report_prefix_pop(); /* extend */
+}
+
+/*
+ * cpu_extend_run - Parameters for the extend measurement SMP run.
+ * @idx		- Pointer to the index
+ * @size	- Size of the measurement data
+ * @m		- Measurement data.
+ */
+struct cpu_extend_run {
+	int *idx;
+	struct measurement *m;
+	size_t size;
+	unsigned long rc;
+};
+
+/*
+ * We get an array of the parameters for the extend measurement.
+ * The cpu number is the index to the array. At the moment we
+ * only support 2 cpus.
+ */
+static void cpu_run_extend_measurement(void *data)
+{
+	struct smccc_result result;
+	struct cpu_extend_run *run;
+	int me = smp_processor_id();
+
+	assert(me >= 0);
+
+	/* Tests for only 2 CPUs */
+	if (me > 1)
+		return;
+	run = (struct cpu_extend_run *)data + me;
+	rsi_extend_measurement(*run->idx, run->size, &run->m->words[0], &result);
+	run->rc = result.r0;
+	if (result.r0 != 0)
+		report(false, "CPU%d: Extend measurement failed for slot %d",
+		       me, *run->idx);
+}
+
+static bool claims_uses_sha256_algo(struct attestation_claims *claims)
+{
+	struct claim_t *claim = claims->realm_token_claims + 2; /* CCA_REALM_HASH_ALGO_ID */
+
+	/* claim->buffer_data.ptr: Not NULL terminated, so using memcmp */
+	return !memcmp(claim->buffer_data.ptr, "sha-256", strlen("sha-256"));
+}
+
+static void test_rsi_extend_smp(void)
+{
+	int slot, m_idx;
+	struct measurement m[2];
+	struct challenge ch;
+	struct attestation_claims claims;
+	size_t token_size;
+
+	/*
+	 * Measurements to extend with
+	 *
+	 * Run		CPU0 data	CPU1 data
+	 *   1:		[31 - 0]	[55 - 24]
+	 *   2:		[39 - 8]	[63 - 32]
+	 *   3:		[47 - 16]	[71 - 40]
+	 */
+	char measure_bytes[] = {
+		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+	};
+
+	/*
+	 * The expected measurement values. Each element in the array contains
+	 * a possible extended measurement value. (Multiple values are possible
+	 * as the extend function might be called in any order by the cores.)
+	 * The array contains results for all the possible orders. The number of
+	 * possibilities can be calculated as here:
+	 * https://math.stackexchange.com/q/1065374
+	 */
+	struct extend_smp_expected {
+		const char *sequence;
+		char measurement[SHA256_SIZE];
+	} expected[] = {
+		{
+			"[ cpu0#0 cpu0#1 cpu0#2 cpu1#0 cpu1#1 cpu1#2 ]",
+			{
+				0xB1, 0xBE, 0x04, 0x25, 0xBB, 0xBC, 0x04, 0x9F,
+				0x98, 0x4F, 0xFB, 0xDE, 0xAA, 0x00, 0xC9, 0xBC,
+				0x41, 0x43, 0xDB, 0x16, 0xBB, 0x2A, 0x5F, 0x4B,
+				0x8B, 0x36, 0xAE, 0x3F, 0xFE, 0x24, 0x23, 0xA4
+			},
+		},
+		{
+			"[ cpu0#0 cpu0#1 cpu1#0 cpu0#2 cpu1#1 cpu1#2 ]",
+			{
+				0x99, 0x00, 0x5E, 0xB7, 0xF8, 0x84, 0xA3, 0x99,
+				0x7E, 0x12, 0xDE, 0xD1, 0x5B, 0xA7, 0x07, 0xF4,
+				0x24, 0x3E, 0x77, 0xED, 0x60, 0xC0, 0xBD, 0x43,
+				0x3B, 0x60, 0x7E, 0x38, 0xDD, 0x58, 0xC7, 0x46
+			},
+		},
+		{
+			"[ cpu0#0 cpu0#1 cpu1#0 cpu1#1 cpu0#2 cpu1#2 ]",
+			{
+				0x0B, 0x5E, 0x31, 0x69, 0xAC, 0xAF, 0xA0, 0x8B,
+				0x4F, 0x90, 0xD1, 0x86, 0xCC, 0x8E, 0x11, 0x42,
+				0x0B, 0x74, 0x49, 0x6C, 0xA1, 0x27, 0x1B, 0x7C,
+				0x52, 0x77, 0x7F, 0x2F, 0x53, 0x2F, 0x9A, 0xC1
+			},
+		},
+		{
+			"[ cpu0#0 cpu0#1 cpu1#0 cpu1#1 cpu1#2 cpu0#2 ]",
+			{
+				0x99, 0xDE, 0xF8, 0x02, 0x27, 0xE9, 0x6F, 0x6F,
+				0xA6, 0x55, 0xFC, 0x56, 0xCC, 0x7A, 0xFC, 0xEF,
+				0x2F, 0x0C, 0x45, 0x3E, 0x01, 0xE0, 0x4B, 0xA1,
+				0x60, 0x96, 0xEE, 0xB1, 0x4A, 0x25, 0x86, 0x89},
+		},
+		{
+			"[ cpu0#0 cpu1#0 cpu0#1 cpu0#2 cpu1#1 cpu1#2 ]",
+			{	0x88, 0x40, 0x05, 0xF5, 0xA6, 0x95, 0xC1, 0xC7,
+				0xD3, 0x69, 0x16, 0x82, 0x0D, 0x79, 0xC1, 0x5B,
+				0x4A, 0x48, 0xCA, 0x7F, 0xA5, 0xF3, 0x77, 0x37,
+				0xBE, 0x0D, 0xAC, 0x2E, 0x42, 0x3E, 0x03, 0x37
+			},
+		},
+		{
+			"[ cpu0#0 cpu1#0 cpu0#1 cpu1#1 cpu0#2 cpu1#2 ]",
+			{
+				0x68, 0x32, 0xC6, 0xAF, 0x8C, 0x86, 0x77, 0x09,
+				0x4A, 0xB9, 0xA1, 0x9E, 0xBB, 0x2B, 0x42, 0x35,
+				0xF8, 0xDE, 0x9A, 0x98, 0x37, 0x7B, 0x3E, 0x82,
+				0x59, 0x0B, 0xC4, 0xAD, 0x1D, 0x01, 0x28, 0xCA
+			},
+		},
+		{
+			"[ cpu0#0 cpu1#0 cpu0#1 cpu1#1 cpu1#2 cpu0#2 ]",
+			{
+				0xF5, 0x96, 0x77, 0x68, 0xD9, 0x6A, 0xA2, 0xFC,
+				0x08, 0x8C, 0xF5, 0xA9, 0x6B, 0xE7, 0x1E, 0x20,
+				0x35, 0xC1, 0x92, 0xCE, 0xBC, 0x3A, 0x75, 0xEA,
+				0xB4, 0xEB, 0x17, 0xE5, 0x77, 0x50, 0x85, 0x40
+			},
+
+		},
+		{
+			"[ cpu0#0 cpu1#0 cpu1#1 cpu0#1 cpu0#2 cpu1#2 ]",
+			{
+				0x4E, 0xA2, 0xD2, 0x79, 0x55, 0x75, 0xCB, 0x86,
+				0x87, 0x34, 0x35, 0xE7, 0x75, 0xDF, 0xD5, 0x59,
+				0x58, 0xDE, 0x74, 0x35, 0x68, 0x2B, 0xDC, 0xC8,
+				0x85, 0x72, 0x97, 0xBE, 0x58, 0xB1, 0x1E, 0xA7
+			},
+
+		},
+		{
+			"[ cpu0#0 cpu1#0 cpu1#1 cpu0#1 cpu1#2 cpu0#2 ]",
+			{
+				0xD1, 0xC2, 0xC8, 0x08, 0x00, 0x64, 0xB8, 0x1F,
+				0xA0, 0xA5, 0x32, 0x20, 0xAA, 0x08, 0xC0, 0x48,
+				0xDB, 0xB1, 0xED, 0xE7, 0xAF, 0x18, 0x2F, 0x7F,
+				0x3C, 0xB8, 0x58, 0x83, 0xEC, 0xF9, 0x38, 0xFD
+			},
+
+		},
+		{
+			"[ cpu0#0 cpu1#0 cpu1#1 cpu1#2 cpu0#1 cpu0#2 ]",
+			{
+				0xD1, 0xB8, 0x31, 0x98, 0x8E, 0xF2, 0xE7, 0xF5,
+				0xBB, 0xD1, 0xE1, 0xC7, 0x3E, 0xB7, 0xA9, 0x18,
+				0x3B, 0xCC, 0x58, 0x98, 0xED, 0x22, 0x1E, 0xE2,
+				0x04, 0x76, 0xA1, 0xB9, 0x92, 0x54, 0xB5, 0x5B
+			},
+
+		},
+		{
+			"[ cpu1#0 cpu0#0 cpu0#1 cpu0#2 cpu1#1 cpu1#2 ]",
+			{
+				0xAB, 0x50, 0x2A, 0x68, 0x28, 0x35, 0x16, 0xA9,
+				0xDE, 0x26, 0x77, 0xAA, 0x99, 0x29, 0x0E, 0x9C,
+				0x67, 0x41, 0x64, 0x28, 0x6E, 0xFF, 0x54, 0x33,
+				0xE5, 0x29, 0xC4, 0xA5, 0x98, 0x40, 0x7E, 0xC9
+			},
+
+		},
+		{
+			"[ cpu1#0 cpu0#0 cpu0#1 cpu1#1 cpu0#2 cpu1#2 ]",
+			{
+				0xA3, 0x4D, 0xB0, 0x28, 0xAB, 0x01, 0x56, 0xBB,
+				0x7D, 0xE5, 0x0E, 0x86, 0x26, 0xBB, 0xBB, 0xDE,
+				0x58, 0x91, 0x88, 0xBB, 0x9F, 0x6A, 0x58, 0x78,
+				0x30, 0x2C, 0x22, 0x2E, 0x85, 0x7F, 0x87, 0xF6
+			},
+
+		},
+		{
+			"[ cpu1#0 cpu0#0 cpu0#1 cpu1#1 cpu1#2 cpu0#2 ]",
+			{
+				0x1A, 0x2E, 0xD2, 0xC2, 0x0C, 0xBD, 0x30, 0xDA,
+				0x4F, 0x37, 0x6B, 0x90, 0xE3, 0x67, 0xFE, 0x61,
+				0x4F, 0x30, 0xBB, 0x29, 0xBC, 0xAA, 0x6E, 0xC5,
+				0x60, 0x6E, 0x13, 0x6B, 0x33, 0x3D, 0xC0, 0x11
+			},
+
+		},
+		{
+			"[ cpu1#0 cpu0#0 cpu1#1 cpu0#1 cpu0#2 cpu1#2 ]",
+			{
+				0x8F, 0xEA, 0xD1, 0x80, 0xE0, 0xBE, 0x27, 0xF7,
+				0x8D, 0x19, 0xBF, 0x65, 0xBE, 0x92, 0x83, 0x7C,
+				0x61, 0x8F, 0xC5, 0x8D, 0x0F, 0xAD, 0x89, 0x1E,
+				0xAE, 0x0A, 0x75, 0xAC, 0x3E, 0x5F, 0xD5, 0x31
+			},
+
+		},
+		{
+			"[ cpu1#0 cpu0#0 cpu1#1 cpu0#1 cpu1#2 cpu0#2 ]",
+			{
+				0x0F, 0x7B, 0xEE, 0xA5, 0x9A, 0xCD, 0xED, 0x8D,
+				0x5A, 0x52, 0xFF, 0xD6, 0x30, 0xF4, 0xD9, 0xE9,
+				0xF4, 0xC1, 0x1A, 0x0C, 0x86, 0x2B, 0x96, 0x2C,
+				0x0E, 0x2D, 0x1A, 0x2A, 0xFE, 0xE6, 0x7C, 0xAD
+			},
+
+		},
+		{
+			"[ cpu1#0 cpu0#0 cpu1#1 cpu1#2 cpu0#1 cpu0#2 ]",
+			{
+				0x4A, 0xBA, 0xFF, 0x0B, 0x0B, 0x06, 0xD1, 0xCE,
+				0x95, 0x91, 0x70, 0x68, 0x20, 0xD6, 0xF2, 0x23,
+				0xC5, 0x6A, 0x63, 0x2B, 0xCA, 0xDF, 0x37, 0xB5,
+				0x0B, 0xDC, 0x64, 0x6A, 0xA3, 0xC9, 0x8F, 0x1E
+			},
+
+		},
+		{
+			"[ cpu1#0 cpu1#1 cpu0#0 cpu0#1 cpu0#2 cpu1#2 ]",
+			{
+				0x3D, 0xB1, 0xE1, 0xBD, 0x85, 0x2C, 0xA0, 0x04,
+				0xE6, 0x43, 0xE8, 0x82, 0xC3, 0x77, 0xF3, 0xCE,
+				0x4D, 0x62, 0x2C, 0xF4, 0x65, 0xF6, 0x29, 0x5F,
+				0x17, 0xDA, 0xD5, 0x79, 0x55, 0xE2, 0x3D, 0x0C
+			},
+
+		},
+		{
+			"[ cpu1#0 cpu1#1 cpu0#0 cpu0#1 cpu1#2 cpu0#2 ]",
+			{
+				0x5B, 0xFE, 0x29, 0xA4, 0xDA, 0x9F, 0xE7, 0x13,
+				0x5F, 0xA2, 0xCE, 0x53, 0x40, 0xC0, 0x38, 0xBC,
+				0x10, 0x7A, 0xF0, 0x29, 0x3C, 0xD6, 0xAF, 0x8A,
+				0x03, 0x40, 0xED, 0xE1, 0xFD, 0x46, 0xB7, 0x06
+			},
+
+		},
+		{
+			"[ cpu1#0 cpu1#1 cpu0#0 cpu1#2 cpu0#1 cpu0#2 ]",
+			{
+				0x66, 0x20, 0xA7, 0xBE, 0xED, 0x90, 0x0A, 0x14,
+				0x95, 0x7A, 0x93, 0x47, 0x1E, 0xA8, 0xDD, 0x6E,
+				0x25, 0xCB, 0x73, 0x18, 0x77, 0x77, 0x91, 0xE9,
+				0xCA, 0x17, 0x26, 0x16, 0xAA, 0xC9, 0x34, 0x7A
+			},
+
+		},
+		{
+			"[ cpu1#0 cpu1#1 cpu1#2 cpu0#0 cpu0#1 cpu0#2 ]",
+			{
+				0x4D, 0xF6, 0xC7, 0x74, 0x37, 0x66, 0x4C, 0x6A,
+				0x40, 0x32, 0x94, 0x01, 0x17, 0xA2, 0xE6, 0x3D,
+				0xA8, 0x00, 0x3E, 0xB7, 0x89, 0x24, 0xF4, 0x04,
+				0x14, 0xA8, 0xA1, 0xD1, 0xCD, 0x5B, 0xC3, 0x60
+			},
+
+		},
+	};
+
+	struct cpu_extend_run cpus[2] = {
+		/* CPU0 */
+		{ .idx = &slot, .m = &m[0], .size = SHA256_SIZE },
+		/* CPU1 */
+		{ .idx = &slot, .m = &m[1], .size = SHA256_SIZE },
+	};
+
+	for (slot = 1; slot <= REM_COUNT; slot++) {
+		for (m_idx = 0; m_idx < 3; m_idx++) {
+			memcpy(m[0].words, &measure_bytes[m_idx * 8], SHA256_SIZE);
+			memcpy(m[1].words, &measure_bytes[24 + m_idx * 8], SHA256_SIZE);
+			on_cpus(cpu_run_extend_measurement, (void *)&cpus[0]);
+			if (cpus[0].rc || cpus[1].rc)
+				return;
+		}
+	}
+
+	/* Get the token and parse the claims */
+	memset(page_buf_data, 0, sizeof(page_buf_data));
+	memset(&ch, 0xAB, sizeof(ch));
+	if (!get_attest_token_claims(page_buf_data, sizeof(page_buf_data),
+				     &ch, &claims, &token_size))
+		return;
+
+	/*
+	 * Hard-coded test data expects sha-256 algorithm, skip the measurement
+	 * value comparison if realm hash algo is different.
+	 */
+	if (!claims_uses_sha256_algo(&claims)) {
+		report_skip("Hash algo is different than sha-256,"
+			    " skip measurement value comparison");
+		return;
+	}
+
+	for (slot = 0; slot < REM_COUNT; slot++) {
+		struct claim_t *claim = &claims.realm_measurement_claims[slot];
+		const char *data = claim->buffer_data.ptr;
+		const size_t len = claim->buffer_data.len;
+
+		if (len != SHA256_SIZE) {
+			report(false, "Realm measurement size mismatch "
+				      "%zu vs %d (expected)", len, SHA256_SIZE);
+			continue;
+		}
+
+		for (m_idx = 0; m_idx < ARRAY_SIZE(expected); m_idx++) {
+			struct extend_smp_expected *em = &expected[m_idx];
+
+			if (memcmp(data, em->measurement, SHA256_SIZE) == 0) {
+				report(true, "Hash found for slot %d: %s",
+					      slot, em->sequence);
+				break;
+			}
+		}
+
+		if (m_idx == ARRAY_SIZE(expected))
+			report(false, "Measurement doesn't match any expected "
+				      "sequence for slot %d", slot);
+	}
+}
+
+static void run_rsi_extend_smp_tests(void)
+{
+	report_prefix_push("extend_smp");
+	test_rsi_extend_smp();
+	report_prefix_pop();
+}
+
+static void test_rsi_extend_and_attest(void)
+{
+	struct challenge ch;
+	struct measurement m;
+	struct attestation_claims claims;
+	size_t token_size;
+	int i, j;
+
+	char measure_bytes[] = {
+		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, /*slot 1*/
+		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, /*slot 2*/
+		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, /*slot 3*/
+		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, /*slot 4*/
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /*slot 5*/
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /*slot 6*/
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+	};
+
+	/* The following expectations assume extending with SHA256 */
+	char expected_measurements[][SHA256_SIZE] = {
+		{
+			0x88, 0x78, 0xb1, 0x5a, 0x7d, 0x6a, 0x3a, 0x4f,
+			0x46, 0x4e, 0x8f, 0x9f, 0x42, 0x59, 0x1d, 0xbc,
+			0x0c, 0xf4, 0xbe, 0xde, 0xa0, 0xec, 0x30, 0x90,
+			0x03, 0xd2, 0xb2, 0xee, 0x53, 0x65, 0x5e, 0xf8
+		},
+		{
+			0x58, 0x32, 0x3b, 0xdf, 0x7a, 0x91, 0xf6, 0x8e,
+			0x80, 0xc7, 0xc8, 0x7f, 0xda, 0x1e, 0x22, 0x6c,
+			0x8b, 0xe7, 0xee, 0xa9, 0xef, 0x64, 0xa5, 0x21,
+			0xdb, 0x2c, 0x09, 0xa7, 0xd7, 0x01, 0x92, 0x05
+		},
+		{
+			0x66, 0xe3, 0x3b, 0x99, 0x49, 0x4d, 0xf4, 0xdd,
+			0xbc, 0x7a, 0x61, 0x7a, 0xa1, 0x56, 0x7b, 0xf8,
+			0x96, 0x3f, 0x0a, 0xf3, 0x1e, 0xab, 0xdd, 0x16,
+			0x37, 0xb0, 0xfb, 0xe0, 0x71, 0x82, 0x66, 0xce
+		},
+		{
+			0x97, 0x5e, 0x9f, 0x64, 0x79, 0x90, 0xa1, 0x51,
+			0xd2, 0x5b, 0x73, 0x75, 0x50, 0x94, 0xeb, 0x54,
+			0x90, 0xbb, 0x1e, 0xf8, 0x3b, 0x2c, 0xb8, 0x3b,
+			0x6f, 0x24, 0xf3, 0x86, 0x07, 0xe0, 0x58, 0x13
+		},
+		{
+			0x68, 0x99, 0x86, 0x64, 0x9b, 0xeb, 0xa2, 0xe4,
+			0x4d, 0x07, 0xbb, 0xb3, 0xa1, 0xd9, 0x2d, 0x07,
+			0x76, 0x7f, 0x86, 0x19, 0xb8, 0x5f, 0x14, 0x48,
+			0x1f, 0x38, 0x4b, 0x87, 0x51, 0xdc, 0x10, 0x31
+		},
+		{
+			0xee, 0x8f, 0xb3, 0xe9, 0xc8, 0xa5, 0xbe, 0x4f,
+			0x12, 0x90, 0x4a, 0x52, 0xb9, 0xc8, 0x62, 0xd1,
+			0x8a, 0x44, 0x31, 0xf7, 0x56, 0x7d, 0x96, 0xda,
+			0x97, 0x7a, 0x9e, 0x96, 0xae, 0x6a, 0x78, 0x43
+		},
+	};
+	int times_to_extend[] = {1, 2, 3, 4, 5, 6};
+
+	memset(page_buf_data, 0, sizeof(page_buf_data));
+	memset(&ch, 0xAB, sizeof(ch));
+	if (!__get_attest_token_claims(page_buf_data, sizeof(page_buf_data),
+				       &ch, &claims, &token_size, true))
+		return;
+
+	for (i = 0; i < REM_COUNT; i++) {
+		struct claim_t c = claims.realm_measurement_claims[i];
+		for (j = 0; j < c.buffer_data.len; j++) {
+			if (((char *)c.buffer_data.ptr)[j])
+				break;
+		}
+	}
+
+	report((i == REM_COUNT), "Initial measurements must be 0");
+
+	/* Extend the possible measurements (i.e., 1 to REM_COUNT) */
+	for (i = 1; i <= REM_COUNT; i++) {
+		memcpy(&m.words[0], &measure_bytes[(i - 1) * 8], SHA256_SIZE);
+		for (j = 0; j < times_to_extend[i - 1]; j++) {
+			struct smccc_result r;
+
+			measurement_extend(i, &m, SHA256_SIZE, &r);
+			if (r.r0) {
+				report(false, "Extend measurment slot %d, iteration %d "
+					      "failed with %ld", i, j, r.r0);
+				return;
+			}
+		}
+	}
+	report(true, "Extend measurement for all slots completed");
+
+	/* Get the attestation token again */
+	if (!__get_attest_token_claims(page_buf_data, sizeof(page_buf_data),
+				       &ch, &claims, &token_size, true))
+		return;
+
+	/*
+	 * Hard-coded test data expects sha-256 algorithm, skip the measurement
+	 * value comparison if realm hash algo is different.
+	 */
+	if (!claims_uses_sha256_algo(&claims))
+		return;
+
+	/* Verify the extended measurements */
+	for (i = 0; i < REM_COUNT; i++) {
+		const char *exp = expected_measurements[i];
+		const char *actual = claims.realm_measurement_claims[i].buffer_data.ptr;
+		const size_t len = claims.realm_measurement_claims[i].buffer_data.len;
+
+		if (len != SHA256_SIZE) {
+			report(false, "Realm measurement: slot: %d, unexpected size "
+				      "actual %ld vs %d expected", i, len,
+				      SHA256_SIZE);
+			return;
+		}
+		if (memcmp(exp, actual, len)) {
+			report(false, "Measurement doesn't match for slot %d", i);
+			printf("Expected:\n");
+			for (j = 0; j < len; j++)
+				printf("0x%2x ", exp[j]);
+			printf("\nActual:\n");
+			for (j = 0; j < len; j++)
+				printf("0x%2x ", actual[j]);
+			printf("\n");
+		} else {
+			report(true, "Extended measurement match expected for "
+				     "slot %d", i);
+
+		}
+	}
+}
+
+static void run_rsi_extend_and_attest_tests(void)
+{
+	report_prefix_push("extend_and_attest");
+	test_rsi_extend_and_attest();
+	report_prefix_pop();
+}
+
+#define MEASUREMENT_MAX_SIZE_LONGS	8
+
+static void test_read_measurement(void)
+{
+	struct smccc_result result;
+	return_code_t rc;
+	unsigned long *m;
+	int i, j;
+
+	/*
+	 * We must be able to read all measurements
+	 * 0 (Initial read-only measurement and the
+	 * realm extendable ones, 1 to REM_COUNT.
+	 */
+	for (i = 0; i <= REM_COUNT; i++) {
+		rsi_read_measurement(i, &result);
+		rc = unpack_return_code(result.r0);
+		if (rc.status) {
+			report(false, "Read measurement failed for slot %d with "
+				      "(%d, %d)", i, rc.status, rc.index);
+			return;
+		}
+		m = &result.r1;
+		printf("Read measurement slot:%d, Hash = ", i);
+		for (j = 0; j < MEASUREMENT_MAX_SIZE_LONGS; j++)
+			printf("%lx", __builtin_bswap64(*m++));
+		printf("\n");
+		report(true, "Read Measurement Slot: %d", i);
+	}
+}
+
+static void test_read_measurement_bad_input(void)
+{
+	struct smccc_result result;
+	return_code_t rc;
+
+	report_prefix_push("out-of-range index");
+	rsi_read_measurement(REM_COUNT + 1, &result);
+	rc = unpack_return_code(result.r0);
+	if (rc.status != RSI_ERROR_INPUT) {
+		report(false, "Read measurement fails, "
+			      "expected (%d), got (%d)",
+			      RSI_ERROR_INPUT, rc.status);
+	} else {
+		report(true, "Read measurement fails as expected");
+	}
+	report_prefix_pop(); /* out-of-range index */
+}
+
+static void run_rsi_read_measurement_tests(void)
+{
+	report_prefix_push("measurement");
+	test_read_measurement();
+	test_read_measurement_bad_input();
+	report_prefix_pop();
+}
+
+int main(int argc, char **argv)
+{
+	int i;
+	report_prefix_push("attestation");
+
+	for (i = 1; i < argc; i++) {
+		if (strcmp(argv[i], "attest") == 0)
+			run_rsi_attest_tests();
+		else if (strcmp(argv[i], "attest_smp") == 0)
+			run_rsi_attest_smp_test();
+		else if (strcmp(argv[i], "extend") == 0)
+			run_rsi_extend_tests();
+		else if (strcmp(argv[i], "extend_smp") == 0)
+			run_rsi_extend_smp_tests();
+		else if (strcmp(argv[i], "extend_and_attest") == 0)
+			run_rsi_extend_and_attest_tests();
+		else if (strcmp(argv[i], "measurement") == 0)
+			run_rsi_read_measurement_tests();
+		else
+			report_info("Unknown subtest '%s'", argv[i]);
+	}
+	return report_summary();
+}
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
index e2821c26..f95fc1ba 100644
--- a/arm/unittests.cfg
+++ b/arm/unittests.cfg
@@ -317,3 +317,53 @@ file = realm-sea.flat
 groups = nodefault realms
 accel = kvm
 arch = arm64
+
+# Realm Attestation realted tests
+[realm-attest]
+file = realm-attest.flat
+groups = nodefault realms
+smp = 1
+extra_params = -m 32 -append 'attest'
+accel = kvm
+arch = arm64
+
+[realm-attest-smp]
+file = realm-attest.flat
+groups = nodefault realms
+smp = 2
+extra_params = -m 32 -append 'attest_smp'
+accel = kvm
+arch = arm64
+
+[realm-extend]
+file = realm-attest.flat
+groups = nodefault realms
+smp = 1
+extra_params = -m 32 -append 'extend'
+accel = kvm
+arch = arm64
+
+[realm-extend-smp]
+file = realm-attest.flat
+groups = nodefault realms
+smp = 2
+extra_params = -m 32 -append 'extend_smp'
+accel = kvm
+arch = arm64
+
+[realm-extend-and-attest]
+file = realm-attest.flat
+groups = nodefault realms
+smp = 1
+extra_params = -m 32 -append 'extend_and_attest'
+accel = kvm
+arch = arm64
+
+
+[realm-measurement]
+file = realm-attest.flat
+groups = nodefault realms
+smp = 1
+extra_params = -m 32 -append 'measurement'
+accel = kvm
+arch = arm64
diff --git a/lib/libcflat.h b/lib/libcflat.h
index 700f4352..1f0eeeed 100644
--- a/lib/libcflat.h
+++ b/lib/libcflat.h
@@ -164,6 +164,7 @@ extern void setup_vm(void);
 #define SZ_128K			(1 << 17)
 #define SZ_1M			(1 << 20)
 #define SZ_2M			(1 << 21)
+#define SZ_512M			(1 << 29)
 #define SZ_1G			(1 << 30)
 #define SZ_2G			(1ul << 31)
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 30/33] arm: realm: Add Realm attestation tests
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (29 preceding siblings ...)
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 29/33] arm: realm: Add Realm attestation tests Suzuki K Poulose
@ 2024-04-12 10:34 ` Suzuki K Poulose
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 31/33] arm: realm: Add a test for shared memory Suzuki K Poulose
                   ` (3 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:34 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose, Mate Toth-Pal

Add tests for Attestation and measurement related RSI calls.

Signed-off-by: Mate Toth-Pal <mate.toth-pal@arm.com>
Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
[ Rewrote the test cases, keeping the core testing data/logic
  Added more test scenarios to test the ABI
]
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/realm-attest.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/arm/realm-attest.c b/arm/realm-attest.c
index 082d4964..6e407bae 100644
--- a/arm/realm-attest.c
+++ b/arm/realm-attest.c
@@ -108,7 +108,7 @@ static int attest_token_complete(phys_addr_t base, size_t buf_size,
 		if ((buf_size - len) < size)
 			size = buf_size - len;
 
-		ret = rsi_attest_token_continue(ipa, offset, size, &bytes);
+		ret = attest_token_continue(ipa, offset, size, &bytes);
 		len += bytes;
 		ipa += bytes;
 		offset += bytes;
@@ -116,7 +116,6 @@ static int attest_token_complete(phys_addr_t base, size_t buf_size,
 
 	if (plen)
 		*plen = len;
-	report_info("Found %ldbytes\n", len);
 	return ret;
 }
 
@@ -130,14 +129,14 @@ static int get_attest_token(phys_addr_t ipa,
 
 	rc = attest_token_init(ch, &max_size);
 	if (max_size > size)
-		report_info("Attestation token size (%ld bytes) is greater than the buffer size\n",
+		report_info("Attestation token size (%ld bytes) is greater than the buffer size",
 			    max_size);
 	if (rc)
 		return rc;
 
 	rc = attest_token_complete(ipa, size, len);
 	if (len && *len > max_size)
-		report_info("RMM BUG: Token size is greater than the max token size from RSI_ATTEST_TOKEN_INIT\n");
+		report_info("RMM BUG: Token size is greater than the max token size from RSI_ATTEST_TOKEN_INIT");
 
 	return rc;
 }
@@ -293,7 +292,7 @@ static void test_get_attest_token_abi_misuse(void)
 		report_prefix_pop(); /* miss token init */
 		return;
 	}
-	report_info("Received a token of size %ld\n", len);
+	report_info("Received a token of size %ld", len);
 
 	/*
 	 * step2. Execute RSI_ATTEST_TOKEN_CONTINUE without an RSI_ATTEST_TOKEN_INIT.
@@ -333,7 +332,7 @@ static void test_get_attest_token_abi_misuse(void)
 			       " (%d) vs expected (%d), len: %ld vs 0",
 			      rc.status, RSI_ERROR_INPUT, len);
 	} else {
-		report(true, "Attestation token continue failed for invalid IPA\n");
+		report(true, "Attestation token continue failed for invalid IPA");
 	}
 	report_prefix_pop();
 
@@ -350,7 +349,7 @@ static void test_get_attest_token_abi_misuse(void)
 			       " (%d) vs expected (%d), len: %ld vs 0",
 			      rc.status, RSI_ERROR_INPUT, len);
 	} else {
-		report(true, "Attestation token continue failed for invalid offset\n");
+		report(true, "Attestation token continue failed for invalid offset");
 	}
 	report_prefix_pop();
 
@@ -367,7 +366,7 @@ static void test_get_attest_token_abi_misuse(void)
 			       " (%d) vs expected (%d)",
 			      rc.status, RSI_ERROR_INPUT);
 	} else {
-		report(true, "Attestation token continue failed for invalid size\n");
+		report(true, "Attestation token continue failed for invalid size");
 	}
 	report_prefix_pop();
 
@@ -383,7 +382,7 @@ static void test_get_attest_token_abi_misuse(void)
 			       " (%d) vs expected (%d), len: %ld vs 0",
 			      rc.status, RSI_ERROR_INPUT, len);
 	} else {
-		report(true, "Attestation token continue failed for overflow size\n");
+		report(true, "Attestation token continue failed for overflow size");
 	}
 	report_prefix_pop();
 
@@ -399,7 +398,7 @@ static void test_get_attest_token_abi_misuse(void)
 			       " (%d) vs expected (%d), len: %ld vs 0",
 			      rc.status, RSI_ERROR_INPUT, len);
 	} else {
-		report(true, "Attestation token continue failed for overflow offset\n");
+		report(true, "Attestation token continue failed for overflow offset");
 	}
 	report_prefix_pop();
 
@@ -415,7 +414,7 @@ static void test_get_attest_token_abi_misuse(void)
 			       " (%d) vs expected (%d), len: %ld vs 0",
 			      rc.status, RSI_ERROR_INPUT, len);
 	} else {
-		report(true, "Attestation token continue failed for unaligned ipa\n");
+		report(true, "Attestation token continue failed for unaligned ipa");
 	}
 	report_prefix_pop();
 
@@ -431,7 +430,7 @@ static void test_get_attest_token_abi_misuse(void)
 			       " (%d) vs expected (%d), len: %ld vs 0",
 			      rc.status, RSI_INCOMPLETE, len);
 	} else {
-		report(true, "Attestation token continue returned 0bytes\n");
+		report(true, "Attestation token continue returned 0bytes");
 	}
 	report_prefix_pop(); /* invalid input */
 
@@ -472,7 +471,7 @@ static void test_get_attest_token_abi_abort_req(void)
 	}
 
 	/* Execute one cycles, but not let it complete */
-	report_info("Attest token continue with %lx, 0, 4K\n", addr);
+	report_info("Attest token continue with %lx, 0, 4K", addr);
 	ret = attest_token_continue(addr, 0, RSI_GRANULE_SIZE, &size);
 	if (ret != RSI_INCOMPLETE) {
 		if (ret) {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 31/33] arm: realm: Add a test for shared memory
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (30 preceding siblings ...)
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 30/33] " Suzuki K Poulose
@ 2024-04-12 10:34 ` Suzuki K Poulose
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 32/33] arm: Add memtest support Suzuki K Poulose
                   ` (2 subsequent siblings)
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:34 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

Do some basic tests that trigger marking a memory region as
RIPAS_EMPTY and accessing the shared memory. Also, convert it back
to RAM and make sure the contents are scrubbed.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/Makefile.arm64    |  1 +
 arm/realm-ns-memory.c | 86 +++++++++++++++++++++++++++++++++++++++++++
 arm/unittests.cfg     |  8 ++++
 3 files changed, 95 insertions(+)
 create mode 100644 arm/realm-ns-memory.c

diff --git a/arm/Makefile.arm64 b/arm/Makefile.arm64
index 7a56029e..bd8c947d 100644
--- a/arm/Makefile.arm64
+++ b/arm/Makefile.arm64
@@ -68,6 +68,7 @@ tests += $(TEST_DIR)/fpu.$(exe)
 tests += $(TEST_DIR)/realm-rsi.$(exe)
 tests += $(TEST_DIR)/realm-sea.$(exe)
 tests += $(TEST_DIR)/realm-attest.$(exe)
+tests += $(TEST_DIR)/realm-ns-memory.$(exe)
 
 include $(SRCDIR)/$(TEST_DIR)/Makefile.common
 
diff --git a/arm/realm-ns-memory.c b/arm/realm-ns-memory.c
new file mode 100644
index 00000000..8360c371
--- /dev/null
+++ b/arm/realm-ns-memory.c
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Arm Limited.
+ * All rights reserved.
+ */
+
+#include <asm/io.h>
+#include <alloc_page.h>
+#include <bitops.h>
+
+#define GRANULE_SIZE 	0x1000
+#define BUF_SIZE	(PAGE_SIZE * 2)
+#define BUF_PAGES	(BUF_SIZE / PAGE_SIZE)
+#define BUF_GRANULES	(BUF_SIZE / GRANULE_SIZE)
+
+static char __attribute__((aligned(PAGE_SIZE))) buffer[BUF_SIZE];
+
+static void static_shared_buffer_test(void)
+{
+	int i;
+
+	set_memory_decrypted((unsigned long)buffer, sizeof(buffer));
+	for (i = 0; i < sizeof(buffer); i += GRANULE_SIZE)
+		buffer[i] = (char)i;
+
+	/*
+	 * Verify the content of the NS buffer
+	 */
+	for (i = 0; i < sizeof(buffer); i += GRANULE_SIZE) {
+		if (buffer[i] != (char)i) {
+			report(false, "Failed to set Non Secure memory");
+			return;
+		}
+	}
+
+	/* Make the buffer back to protected... */
+	set_memory_encrypted((unsigned long)buffer, sizeof(buffer));
+	/* .. and check if the contents were destroyed */
+	for (i = 0; i < sizeof(buffer); i += GRANULE_SIZE) {
+		if (buffer[i] != 0) {
+			report(false, "Failed to scrub protected memory");
+			return;
+		}
+	}
+
+	report(true, "Conversion of protected memory to shared and back");
+}
+
+static void dynamic_shared_buffer_test(void)
+{
+	char *ns_buffer;
+	int i;
+	int order = get_order(BUF_PAGES);
+
+	ns_buffer = alloc_pages_shared(order);
+	assert(ns_buffer);
+	for (i = 0; i < sizeof(buffer); i += GRANULE_SIZE)
+		ns_buffer[i] = (char)i;
+
+	/*
+	 * Verify the content of the NS buffer
+	 */
+	for (i = 0; i < sizeof(buffer); i += GRANULE_SIZE) {
+		if (ns_buffer[i] != (char)i) {
+			report(false, "Failed to set Non Secure memory");
+			return;
+		}
+	}
+	free_pages_shared(ns_buffer);
+	report(true, "Dynamic allocation and free of shared memory\n");
+}
+
+static void ns_test(void)
+{
+	static_shared_buffer_test();
+	dynamic_shared_buffer_test();
+}
+
+int main(int argc, char **argv)
+{
+	report_prefix_pushf("ns-memory");
+	ns_test();
+	report_prefix_pop();
+
+	return report_summary();
+}
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
index f95fc1ba..55a17f2b 100644
--- a/arm/unittests.cfg
+++ b/arm/unittests.cfg
@@ -367,3 +367,11 @@ smp = 1
 extra_params = -m 32 -append 'measurement'
 accel = kvm
 arch = arm64
+
+[realm-ns-memory]
+file=realm-ns-memory.flat
+groups = nodefault realms
+smp = 1
+extra_params = -m 32
+accel = kvm
+arch = arm64
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 32/33] arm: Add memtest support
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (31 preceding siblings ...)
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 31/33] arm: realm: Add a test for shared memory Suzuki K Poulose
@ 2024-04-12 10:34 ` Suzuki K Poulose
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 33/33] NOT-FOR-MERGING: add run-realm-tests Suzuki K Poulose
  2024-04-16 14:28 ` [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Jean-Philippe Brucker
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:34 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Joey Gouly <joey.gouly@arm.com>

Add a test to touch all memory allocated to the guest.
Provides options to allocate in block, shared mode etc.
Also adds a "memstress" variant which would test all the
combinations in order.

PS: The memory allocator fragments the available memory
on page allocation and doesn't allow merging them for a
higher order allocation. Hence, all the block alloc tests
are run one after the other, before any page allocation
tests are run

Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/selftest.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 122 insertions(+), 1 deletion(-)

diff --git a/arm/selftest.c b/arm/selftest.c
index 7bc5fb76..d9fd9750 100644
--- a/arm/selftest.c
+++ b/arm/selftest.c
@@ -9,6 +9,7 @@
 #include <util.h>
 #include <devicetree.h>
 #include <memregions.h>
+#include <alloc_page.h>
 #include <vmalloc.h>
 #include <asm/setup.h>
 #include <asm/ptrace.h>
@@ -435,6 +436,123 @@ static void cpu_report(void *data __unused)
 	report_info("CPU%3d: MPIDR=%010" PRIx64, cpu, mpidr);
 }
 
+
+/*
+ * do_memtest: Accepts the following paramters.
+ *
+ * shared[=0/1] - Use shared page for the memtests.
+ * block[=0/1]  - Use SZ_2M allocation/free.
+ * nofree	- Do not free the pages after the test.
+ */
+static void do_memtest(int argc, char *argv[])
+{
+	int i;
+	int npages = 0;
+	bool result = true;
+	const char pattern = 0xFB;
+	void *prev_page = NULL;
+	uintptr_t *page_to_free = NULL;
+	int size;
+	void* (*alloc_order_fn)(unsigned int);
+	void (*free_order_fn)(void *, unsigned int);
+	bool shared = false;
+	bool block = false;
+	bool nofree = false;
+	int order = 0;
+
+	for (i = 2; i < argc; i++) {
+		long val, len;
+
+		len = parse_keyval(argv[i], &val);
+		if (len == -1) {
+			if (!strcmp(argv[i], "shared")) {
+				shared = true;
+				continue;
+			} else if (!strcmp(argv[i], "nofree")) {
+				nofree = true;
+				continue;
+			} else if (!strcmp(argv[i], "block")) {
+				block = true;
+			} else {
+				printf("Unknown options %s\n", argv[i]);
+				abort();
+			}
+		} else if (!strncmp(argv[i], "block", len)) {
+			block = !!val;
+		} else if (!strncmp(argv[i], "shared", len)) {
+			shared = !!val;
+		}
+	}
+
+	/* Block mapping is 2MB */
+	if (block)
+		order = (21 - PAGE_SHIFT);
+
+	size = (1 << order) * PAGE_SIZE;
+	if (shared) {
+		alloc_order_fn = &alloc_pages_shared;
+		free_order_fn = &free_pages_shared_by_order;
+	} else {
+		alloc_order_fn = &alloc_pages;
+		free_order_fn = &free_pages_by_order;
+	}
+
+	report_info("Running %smemtest with size %dK%s, order=%d",
+		    shared ? "shared " : "",
+		    size >> 10,
+		    nofree ? " with no freeing" :"",
+		    order);
+
+	while (1) {
+		void *page = alloc_order_fn(order);
+
+		if (!page)
+			break;
+		npages += 1;
+
+		memset(page, pattern, size);
+
+		for (i = 0; i < size; i += 1) {
+			if (((char *)page)[i] != pattern) {
+				result = false;
+				report(false, "Failed to find the pattern in page %p, expected: %d, got: %d\n",
+					page, pattern, ((char *)page)[i]);
+				goto exit;
+			}
+		}
+
+		/*
+		 * Save a pointer to the allocated page so that it can be
+		 * free'd at the end of the test.
+		*/
+		*(uintptr_t *)page = (uintptr_t)prev_page;
+		prev_page = page;
+	}
+
+	page_to_free = prev_page;
+	while (!nofree && page_to_free) {
+		prev_page = (uintptr_t *)(*page_to_free);
+		free_order_fn(page_to_free, order);
+		page_to_free = prev_page;
+	}
+
+exit:
+	report(result, "Tested with %dKB", (npages  * size) >> 10);
+}
+
+static void do_memstress(void)
+{
+	char shared[16] = "shared";
+	char block[16] = "block";
+	char nofree[16] = "nofree";
+	char null[4] = "";
+
+	do_memtest(4, &((char *[]){ null, null, shared, block })[0]);
+	do_memtest(3, &((char *[]){ null, null, block })[0]);
+	do_memtest(3, &((char *[]){ null, null, shared })[0]);
+	do_memtest(3, &((char *[]){ null, null, nofree })[0]);
+}
+
 int main(int argc, char **argv)
 {
 	report_prefix_push("selftest");
@@ -466,7 +584,10 @@ int main(int argc, char **argv)
 		smp_rmb();		/* Paired with wmb in cpu_report(). */
 		report(cpumask_full(&valid), "MPIDR test on all CPUs");
 		report_info("%d CPUs reported back", nr_cpus);
-
+	} else if (strcmp(argv[1], "memtest") == 0) {
+		do_memtest(argc, argv);
+	} else if (strcmp(argv[1], "memstress") == 0) {
+		do_memstress();
 	} else {
 		printf("Unknown subtest\n");
 		abort();
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [kvm-unit-tests PATCH 33/33] NOT-FOR-MERGING: add run-realm-tests
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (32 preceding siblings ...)
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 32/33] arm: Add memtest support Suzuki K Poulose
@ 2024-04-12 10:34 ` Suzuki K Poulose
  2024-04-16 14:28 ` [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Jean-Philippe Brucker
  34 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-12 10:34 UTC (permalink / raw)
  To: kvmarm
  Cc: kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger, Suzuki K Poulose

From: Alexandru Elisei <alexandru.elisei@arm.com>

Until we add support for KVMTOOL to run the tests using the
scripts, provide a temporary script to run all the Realm tests.

Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com>
Co-developed-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arm/run-realm-tests | 112 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)
 create mode 100755 arm/run-realm-tests

diff --git a/arm/run-realm-tests b/arm/run-realm-tests
new file mode 100755
index 00000000..839f2bfc
--- /dev/null
+++ b/arm/run-realm-tests
@@ -0,0 +1,112 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2023, Arm Ltd
+# All rights reserved
+#
+
+TASKSET=${TASKSET:-taskset}
+LKVM=${LKVM:-lkvm}
+ARGS="--realm --restricted_mem --irqchip=gicv3 --console=serial --network mode=none --nodefaults --loglevel error"
+PMU_ARGS="--pmu --pmu-counters=8"
+
+TESTDIR="."
+while getopts "d:" option; do
+	case "${option}" in
+		d) TESTDIR=${OPTARG};;
+		?)
+			exit 1
+			;;
+	esac
+done
+if [[ ! -d ${TESTDIR} ]]; then
+	echo "Invalid directory: ${TESTDIR}"
+	exit 1
+fi
+
+function set_all_cpu_except {
+	cpu_except=$1
+	pushd /sys/devices/system/cpu
+	for c in $(find . -maxdepth 1 -type d -name "cpu[0-9]*")
+	do
+		if [ $(basename $c) != "cpu${cpu_except}" ]
+		then
+			echo $2 > $c/online
+		fi
+	done
+	popd
+}
+
+function run_on_cpu0 {
+	# Check if we have TASKSET command available
+	$TASKSET --help &>/dev/null
+	no_taskset=$?
+	if [ $no_taskset -eq 0 ]
+	then
+		cmd_prefix="$TASKSET -c 0 "
+	else
+		set_all_cpu_except 0 0
+		cmd_prefix=""
+	fi
+
+	while read cmd
+	do
+		echo "Running on CPU0: ${cmd}"
+		${cmd_prefix} ${cmd}
+	done
+
+	if [ $no_taskset -ne 0 ]
+	then
+		set_all_cpu_except 0 1
+	fi
+}
+
+function run_tests {
+	DIR="$1"
+
+	$LKVM run $ARGS -c 2 -m 16 -k $DIR/selftest.flat -p "setup smp=2 mem=16"
+	$LKVM run $ARGS -c 2 -m 6 -k $DIR/selftest.flat -p "setup smp=2 sve-vl=128" --sve-vl=128
+	$LKVM run $ARGS -c 2 -m 6 -k $DIR/selftest.flat -p "setup smp=2 sve-vl=256" --sve-vl=256
+	$LKVM run $ARGS -c 1 -m 16 -k $DIR/selftest.flat -p "vectors-kernel"
+	$LKVM run $ARGS -c 1 -m 16 -k $DIR/selftest.flat -p "vectors-user"
+	$LKVM run $ARGS -c 4 -m 32 -k $DIR/selftest.flat -p "smp"
+	$LKVM run $ARGS -c 4 -m 32 -k $DIR/selftest.flat -p "memstress"
+
+	$LKVM run $ARGS -c 1 -m 32 -k $DIR/realm-ns-memory.flat
+
+	$LKVM run $ARGS -c 4 -m 32 -k $DIR/psci.flat
+
+	$LKVM run $ARGS -c 4 -m 32 -k $DIR/gic.flat -p "ipi"
+	$LKVM run $ARGS -c 4 -m 32 -k $DIR/gic.flat -p "active"
+
+	$LKVM run $ARGS -c 1 -m 16 -k $DIR/timer.flat
+	$LKVM run $ARGS $PMU_ARGS -c 2 -m 16 -k $DIR/pmu.flat -p "cycle-counter 0"
+	$LKVM run $ARGS $PMU_ARGS -c 2 -m 16 -k $DIR/pmu.flat -p "pmu-event-introspection"
+	$LKVM run $ARGS $PMU_ARGS -c 2 -m 16 -k $DIR/pmu.flat -p "pmu-event-counter-config"
+	$LKVM run $ARGS $PMU_ARGS -c 2 -m 16 -k $DIR/pmu.flat -p "pmu-basic-event-count"
+	$LKVM run $ARGS $PMU_ARGS -c 2 -m 16 -k $DIR/pmu.flat -p "pmu-mem-access"
+	$LKVM run $ARGS $PMU_ARGS -c 2 -m 16 -k $DIR/pmu.flat -p "pmu-mem-access-reliability"
+	$LKVM run $ARGS $PMU_ARGS -c 2 -m 16 -k $DIR/pmu.flat -p "pmu-sw-incr"
+	$LKVM run $ARGS $PMU_ARGS -c 2 -m 16 -k $DIR/pmu.flat -p "pmu-chained-counters"
+	$LKVM run $ARGS $PMU_ARGS -c 2 -m 16 -k $DIR/pmu.flat -p "pmu-chained-sw-incr"
+	$LKVM run $ARGS $PMU_ARGS -c 2 -m 16 -k $DIR/pmu.flat -p "pmu-chain-promotion"
+	$LKVM run $ARGS $PMU_ARGS -c 2 -m 16 -k $DIR/pmu.flat -p "pmu-overflow-interrupt"
+
+	$LKVM run $ARGS -c 1 -m 16 -k $DIR/realm-rsi.flat -p "version"
+	$LKVM run $ARGS -c 1 -m 16 -k $DIR/realm-rsi.flat -p "host_call hvc"
+	$LKVM run $ARGS -c 1 -m 16 -k $DIR/realm-sea.flat
+
+	$LKVM run $ARGS -c 1 -m 24 -k $DIR/realm-attest.flat -p "attest"
+	$LKVM run $ARGS -c 2 -m 24 -k $DIR/realm-attest.flat -p "attest_smp"
+	$LKVM run $ARGS -c 1 -m 24 -k $DIR/realm-attest.flat -p "extend"
+	$LKVM run $ARGS -c 2 -m 24 -k $DIR/realm-attest.flat -p "extend_smp"
+	$LKVM run $ARGS -c 1 -m 24 -k $DIR/realm-attest.flat -p "extend_and_attest"
+	$LKVM run $ARGS -c 1 -m 24 -k $DIR/realm-attest.flat -p "measurement"
+
+	run_on_cpu0 << EOF
+$LKVM run $ARGS -c 4 -m 64 -k $DIR/fpu.flat
+$LKVM run $ARGS -c 4 -m 64 --sve-vl 128 -k $DIR/fpu.flat
+EOF
+
+}
+
+run_tests "${TESTDIR}"
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture
  2024-04-10 16:17 ` Itaru Kitayama
@ 2024-04-15  8:59   ` Suzuki K Poulose
  0 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-15  8:59 UTC (permalink / raw)
  To: Itaru Kitayama
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger

Hi Itaru

On 10/04/2024 17:17, Itaru Kitayama wrote:
> Hi Suzuki,
> 
> On Fri, Apr 12, 2024 at 11:33:35AM +0100, Suzuki K Poulose wrote:
>> This series adds support for running the kvm-unit-tests in the Arm CCA reference
>> software architecture.
>>
>>

...

>> Changes since rfc:
>>    [ https://lkml.kernel.org/r/20230127114108.10025-1-joey.gouly@arm.com ]
>>    - Add support for RMM-v1.0-EAC5, changes to RSI ABIs
>>    - Some hardening checks (FDT overlapping the BSS sections)
>>    - Selftest for memory stress
>>    - Enable PMU/SVE tests for Realms
>>
>>   [0] https://lkml.kernel.org/r/20240412084056.1733704-1-steven.price@arm.com
>>   [1] https://lkml.kernel.org/r/20210702163122.96110-1-alexandru.elisei@arm.com
>>   [2] https://github.com/laurencelundblade/QCBOR

...

> 
> Thanks for the update! I'll go through the series one by one in the
> coming weeks. Just curious one thing - do you guys wish to add Realm tests to the kvm-unit-test package, but not to kselftests?

Thanks for taking a look. kselftests is in plan but we wanted to make
sure the UABI is a bit more stable before we plumb all of that.

Kind regards
Suzuki

> 
> Thanks,
> Itaru.
> 
>>
>> -- 
>> 2.34.1
>>


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture
  2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
                   ` (33 preceding siblings ...)
  2024-04-12 10:34 ` [kvm-unit-tests PATCH 33/33] NOT-FOR-MERGING: add run-realm-tests Suzuki K Poulose
@ 2024-04-16 14:28 ` Jean-Philippe Brucker
  34 siblings, 0 replies; 48+ messages in thread
From: Jean-Philippe Brucker @ 2024-04-16 14:28 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, alexandru.elisei,
	joey.gouly, steven.price, james.morse, oliver.upton, yuzenghui,
	andrew.jones, eric.auger

Hello,

On Fri, Apr 12, 2024 at 11:33:35AM +0100, Suzuki K Poulose wrote:
> This series adds support for running the kvm-unit-tests in the Arm CCA reference
> software architecture.
> 
> 
> The changes involve enlightening the boot/setup code with the Realm Service Interface
> (RSI). The series also includes new test cases that exercise the RSI calls.
> 
> Currently we only support "kvmtool" as the VMM for running Realms. There was
> an attempt to add support for running the test scripts using with kvmtool here [1],
> which hasn't progressed. It would be good to have that resolved, so that we can
> run all the tests without manually specifying the commandlines for each run.
> 
> For the purposes of running the Realm specific tests, we have added a "temporary"
> script "run-realm-tests" until the kvmtool support is added. We do not expect
> this to be merged.

The tests can also be run with QEMU, which requires one more patch to
share the chr-testdev DMA memory with the host. I pushed this and
additional tests here:
https://git.codelinaro.org/linaro/dcap/kvm-unit-tests

Follow the build instructions for QEMU:
https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/Building+an+RME+stack+for+QEMU

Buildroot supports kvm-unit-tests but as standalone scripts. I prefer the
run_tests.sh script, which also enables comparing Realm measurements
between runs:

	./configure --arch=arm64 --cross-prefix=path/to/buildroot/host/bin/aarch64-buildroot-linux-gnu-
	make -j
	# copy everything to the shared directory, then modify config.mak
	sed -i -e "/PRETTY_PRINT_STACKS/s/yes/no/" \
               -e "/ERRATATXT/s/=.*/=errata.txt/"  \
               -e "/HOST/s/=.*/=aarch64/" \
               -e "/ARCH/s/=.*/=arm64/" \
               config.mak

	# Run all realm tests
	ACCEL=kvm MAX_SMP=8 ./run_tests.sh -v -g realms

Thanks,
Jean

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled Suzuki K Poulose
@ 2024-04-22 11:58   ` Alexandru Elisei
  2024-04-22 12:09     ` Suzuki K Poulose
  2024-04-22 15:38   ` Alexandru Elisei
  1 sibling, 1 reply; 48+ messages in thread
From: Alexandru Elisei @ 2024-04-22 11:58 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, joey.gouly,
	steven.price, james.morse, oliver.upton, yuzenghui, andrew.jones,
	eric.auger

Hi,

On Fri, Apr 12, 2024 at 11:33:43AM +0100, Suzuki K Poulose wrote:
> From: Joey Gouly <joey.gouly@arm.com>
> 
> A Realm must access any emulated I/O mappings with the PTE_NS_SHARED bit set.
> This is modelled as a PTE attribute, but is actually part of the address.
> 
> So, when MMU is disabled, the "physical address" must reflect this bit set. We
> access the UART early before the MMU is enabled. So, make sure the UART is
> accessed always with the bit set.
> 
> Signed-off-by: Joey Gouly <joey.gouly@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>  lib/arm/asm/pgtable.h   |  5 +++++
>  lib/arm/io.c            | 24 +++++++++++++++++++++++-
>  lib/arm64/asm/pgtable.h |  5 +++++
>  3 files changed, 33 insertions(+), 1 deletion(-)
> 
> diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
> index 350039ff..7e85e7c6 100644
> --- a/lib/arm/asm/pgtable.h
> +++ b/lib/arm/asm/pgtable.h
> @@ -112,4 +112,9 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
>  	return pte_offset(pmd, addr);
>  }
>  
> +static inline unsigned long arm_shared_phys_alias(void *x)
> +{
> +	return ((unsigned long)(x) | PTE_NS_SHARED);
> +}

Is it allowed for a realm to run in aarch32 mode?

> +
>  #endif /* _ASMARM_PGTABLE_H_ */
> diff --git a/lib/arm/io.c b/lib/arm/io.c
> index 836fa854..127727e4 100644
> --- a/lib/arm/io.c
> +++ b/lib/arm/io.c
> @@ -15,6 +15,8 @@
>  #include <asm/psci.h>
>  #include <asm/spinlock.h>
>  #include <asm/io.h>
> +#include <asm/mmu-api.h>
> +#include <asm/pgtable.h>
>  
>  #include "io.h"
>  
> @@ -30,6 +32,24 @@ static struct spinlock uart_lock;
>  static volatile u8 *uart0_base = UART_EARLY_BASE;
>  bool is_pl011_uart;
>  
> +static inline volatile u8 *get_uart_base(void)
> +{
> +	/*
> +	 * The address of the UART base may be different
> +	 * based on whether we are running with/without
> +	 * MMU enabled.
> +	 *
> +	 * For realms, we must force to use the shared physical
> +	 * alias with MMU disabled, to make sure the I/O can
> +	 * be emulated.
> +	 * When the MMU is turned ON, the mappings are created
> +	 * appropriately.
> +	 */
> +	if (mmu_enabled())
> +		return uart0_base;
> +	return (u8 *)arm_shared_phys_alias((void *)uart0_base);
> +}
> +
>  static void uart0_init_fdt(void)
>  {
>  	/*
> @@ -109,9 +129,11 @@ void io_init(void)
>  
>  void puts(const char *s)
>  {
> +	volatile u8 *uart_base = get_uart_base();
> +
>  	spin_lock(&uart_lock);
>  	while (*s)
> -		writeb(*s++, uart0_base);
> +		writeb(*s++, uart_base);
>  	spin_unlock(&uart_lock);
>  }
>  
> diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
> index 5b9f40b0..871c03e9 100644
> --- a/lib/arm64/asm/pgtable.h
> +++ b/lib/arm64/asm/pgtable.h
> @@ -28,6 +28,11 @@ extern unsigned long prot_ns_shared;
>  */
>  #define PTE_NS_SHARED		(prot_ns_shared)
>  
> +static inline unsigned long arm_shared_phys_alias(void *addr)
> +{
> +	return ((unsigned long)addr | PTE_NS_SHARED);
> +}

Have you considered specifying the correct UART address at compile time using
./configure --earlycon?

Thanks,
Alex
> +
>  /*
>   * Highest possible physical address supported.
>   */
> -- 
> 2.34.1
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled
  2024-04-22 11:58   ` Alexandru Elisei
@ 2024-04-22 12:09     ` Suzuki K Poulose
  2024-04-22 12:23       ` Alexandru Elisei
  0 siblings, 1 reply; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-22 12:09 UTC (permalink / raw)
  To: Alexandru Elisei
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, joey.gouly,
	steven.price, james.morse, oliver.upton, yuzenghui, andrew.jones,
	eric.auger

Hi Alexandru

On 22/04/2024 12:58, Alexandru Elisei wrote:
> Hi,
> 
> On Fri, Apr 12, 2024 at 11:33:43AM +0100, Suzuki K Poulose wrote:
>> From: Joey Gouly <joey.gouly@arm.com>
>>
>> A Realm must access any emulated I/O mappings with the PTE_NS_SHARED bit set.
>> This is modelled as a PTE attribute, but is actually part of the address.
>>
>> So, when MMU is disabled, the "physical address" must reflect this bit set. We
>> access the UART early before the MMU is enabled. So, make sure the UART is
>> accessed always with the bit set.
>>
>> Signed-off-by: Joey Gouly <joey.gouly@arm.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>>   lib/arm/asm/pgtable.h   |  5 +++++
>>   lib/arm/io.c            | 24 +++++++++++++++++++++++-
>>   lib/arm64/asm/pgtable.h |  5 +++++
>>   3 files changed, 33 insertions(+), 1 deletion(-)
>>
>> diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
>> index 350039ff..7e85e7c6 100644
>> --- a/lib/arm/asm/pgtable.h
>> +++ b/lib/arm/asm/pgtable.h
>> @@ -112,4 +112,9 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
>>   	return pte_offset(pmd, addr);
>>   }
>>   
>> +static inline unsigned long arm_shared_phys_alias(void *x)
>> +{
>> +	return ((unsigned long)(x) | PTE_NS_SHARED);
>> +}
> 
> Is it allowed for a realm to run in aarch32 mode?

No. Realm EL1 must be Aarch64.

> 
>> +
>>   #endif /* _ASMARM_PGTABLE_H_ */
>> diff --git a/lib/arm/io.c b/lib/arm/io.c
>> index 836fa854..127727e4 100644
>> --- a/lib/arm/io.c
>> +++ b/lib/arm/io.c
>> @@ -15,6 +15,8 @@
>>   #include <asm/psci.h>
>>   #include <asm/spinlock.h>
>>   #include <asm/io.h>
>> +#include <asm/mmu-api.h>
>> +#include <asm/pgtable.h>
>>   
>>   #include "io.h"
>>   
>> @@ -30,6 +32,24 @@ static struct spinlock uart_lock;
>>   static volatile u8 *uart0_base = UART_EARLY_BASE;
>>   bool is_pl011_uart;
>>   
>> +static inline volatile u8 *get_uart_base(void)
>> +{
>> +	/*
>> +	 * The address of the UART base may be different
>> +	 * based on whether we are running with/without
>> +	 * MMU enabled.
>> +	 *
>> +	 * For realms, we must force to use the shared physical
>> +	 * alias with MMU disabled, to make sure the I/O can
>> +	 * be emulated.
>> +	 * When the MMU is turned ON, the mappings are created
>> +	 * appropriately.
>> +	 */
>> +	if (mmu_enabled())
>> +		return uart0_base;
>> +	return (u8 *)arm_shared_phys_alias((void *)uart0_base);
>> +}
>> +
>>   static void uart0_init_fdt(void)
>>   {
>>   	/*
>> @@ -109,9 +129,11 @@ void io_init(void)
>>   
>>   void puts(const char *s)
>>   {
>> +	volatile u8 *uart_base = get_uart_base();
>> +
>>   	spin_lock(&uart_lock);
>>   	while (*s)
>> -		writeb(*s++, uart0_base);
>> +		writeb(*s++, uart_base);
>>   	spin_unlock(&uart_lock);
>>   }
>>   
>> diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
>> index 5b9f40b0..871c03e9 100644
>> --- a/lib/arm64/asm/pgtable.h
>> +++ b/lib/arm64/asm/pgtable.h
>> @@ -28,6 +28,11 @@ extern unsigned long prot_ns_shared;
>>   */
>>   #define PTE_NS_SHARED		(prot_ns_shared)
>>   
>> +static inline unsigned long arm_shared_phys_alias(void *addr)
>> +{
>> +	return ((unsigned long)addr | PTE_NS_SHARED);
>> +}
> 
> Have you considered specifying the correct UART address at compile time using
> ./configure --earlycon?

Do you mean

./configure --earlycon=<NS-Alias-normal-UART-Address> for Realms ?

If so, there are multiple issues with that :

1. A payload could be run in a normal VM or a Realm VM. Having the above
restricts using the same payload in different worlds. (e.g., comparison).

2. The IPA width of the Realm and thus the PTE_NS_SHARED is dynamic
and really depends on what the VMM decides to choose the IPA size.
(Could be based on user input).

If any of the above fails, and a wrong earlycon address could result
in "Synchronouse External Abort" into the Realm (if it is in protected
IPA).

Suzuki
> 
> Thanks,
> Alex
>> +
>>   /*
>>    * Highest possible physical address supported.
>>    */
>> -- 
>> 2.34.1
>>


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled
  2024-04-22 12:09     ` Suzuki K Poulose
@ 2024-04-22 12:23       ` Alexandru Elisei
  2024-04-22 12:36         ` Alexandru Elisei
  0 siblings, 1 reply; 48+ messages in thread
From: Alexandru Elisei @ 2024-04-22 12:23 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, joey.gouly,
	steven.price, james.morse, oliver.upton, yuzenghui, andrew.jones,
	eric.auger

Hi,

On Mon, Apr 22, 2024 at 01:09:13PM +0100, Suzuki K Poulose wrote:
> Hi Alexandru
> 
> On 22/04/2024 12:58, Alexandru Elisei wrote:
> > Hi,
> > 
> > On Fri, Apr 12, 2024 at 11:33:43AM +0100, Suzuki K Poulose wrote:
> > > From: Joey Gouly <joey.gouly@arm.com>
> > > 
> > > A Realm must access any emulated I/O mappings with the PTE_NS_SHARED bit set.
> > > This is modelled as a PTE attribute, but is actually part of the address.
> > > 
> > > So, when MMU is disabled, the "physical address" must reflect this bit set. We
> > > access the UART early before the MMU is enabled. So, make sure the UART is
> > > accessed always with the bit set.
> > > 
> > > Signed-off-by: Joey Gouly <joey.gouly@arm.com>
> > > Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> > > ---
> > >   lib/arm/asm/pgtable.h   |  5 +++++
> > >   lib/arm/io.c            | 24 +++++++++++++++++++++++-
> > >   lib/arm64/asm/pgtable.h |  5 +++++
> > >   3 files changed, 33 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
> > > index 350039ff..7e85e7c6 100644
> > > --- a/lib/arm/asm/pgtable.h
> > > +++ b/lib/arm/asm/pgtable.h
> > > @@ -112,4 +112,9 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
> > >   	return pte_offset(pmd, addr);
> > >   }
> > > +static inline unsigned long arm_shared_phys_alias(void *x)
> > > +{
> > > +	return ((unsigned long)(x) | PTE_NS_SHARED);
> > > +}
> > 
> > Is it allowed for a realm to run in aarch32 mode?
> 
> No. Realm EL1 must be Aarch64.

Ok, then can you make the above function return the original address?

> 
> > 
> > > +
> > >   #endif /* _ASMARM_PGTABLE_H_ */
> > > diff --git a/lib/arm/io.c b/lib/arm/io.c
> > > index 836fa854..127727e4 100644
> > > --- a/lib/arm/io.c
> > > +++ b/lib/arm/io.c
> > > @@ -15,6 +15,8 @@
> > >   #include <asm/psci.h>
> > >   #include <asm/spinlock.h>
> > >   #include <asm/io.h>
> > > +#include <asm/mmu-api.h>
> > > +#include <asm/pgtable.h>
> > >   #include "io.h"
> > > @@ -30,6 +32,24 @@ static struct spinlock uart_lock;
> > >   static volatile u8 *uart0_base = UART_EARLY_BASE;
> > >   bool is_pl011_uart;
> > > +static inline volatile u8 *get_uart_base(void)
> > > +{
> > > +	/*
> > > +	 * The address of the UART base may be different
> > > +	 * based on whether we are running with/without
> > > +	 * MMU enabled.
> > > +	 *
> > > +	 * For realms, we must force to use the shared physical
> > > +	 * alias with MMU disabled, to make sure the I/O can
> > > +	 * be emulated.
> > > +	 * When the MMU is turned ON, the mappings are created
> > > +	 * appropriately.
> > > +	 */
> > > +	if (mmu_enabled())
> > > +		return uart0_base;
> > > +	return (u8 *)arm_shared_phys_alias((void *)uart0_base);
> > > +}
> > > +
> > >   static void uart0_init_fdt(void)
> > >   {
> > >   	/*
> > > @@ -109,9 +129,11 @@ void io_init(void)
> > >   void puts(const char *s)
> > >   {
> > > +	volatile u8 *uart_base = get_uart_base();

Is it just my email client or is the indentation missing here?

> > > +
> > >   	spin_lock(&uart_lock);
> > >   	while (*s)
> > > -		writeb(*s++, uart0_base);
> > > +		writeb(*s++, uart_base);
> > >   	spin_unlock(&uart_lock);
> > >   }
> > > diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
> > > index 5b9f40b0..871c03e9 100644
> > > --- a/lib/arm64/asm/pgtable.h
> > > +++ b/lib/arm64/asm/pgtable.h
> > > @@ -28,6 +28,11 @@ extern unsigned long prot_ns_shared;
> > >   */
> > >   #define PTE_NS_SHARED		(prot_ns_shared)
> > > +static inline unsigned long arm_shared_phys_alias(void *addr)
> > > +{
> > > +	return ((unsigned long)addr | PTE_NS_SHARED);
> > > +}
> > 
> > Have you considered specifying the correct UART address at compile time using
> > ./configure --earlycon?
> 
> Do you mean
> 
> ./configure --earlycon=<NS-Alias-normal-UART-Address> for Realms ?
> 
> If so, there are multiple issues with that :
> 
> 1. A payload could be run in a normal VM or a Realm VM. Having the above
> restricts using the same payload in different worlds. (e.g., comparison).
> 
> 2. The IPA width of the Realm and thus the PTE_NS_SHARED is dynamic
> and really depends on what the VMM decides to choose the IPA size.
> (Could be based on user input).

Does it depend on the chosen IPA size, or on the VA size, or on both?
Because uart0_base is used with both the MMU on and off.

Thanks,
Alex

> 
> If any of the above fails, and a wrong earlycon address could result
> in "Synchronouse External Abort" into the Realm (if it is in protected
> IPA).
> 
> Suzuki
> > 
> > Thanks,
> > Alex
> > > +
> > >   /*
> > >    * Highest possible physical address supported.
> > >    */
> > > -- 
> > > 2.34.1
> > > 
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled
  2024-04-22 12:23       ` Alexandru Elisei
@ 2024-04-22 12:36         ` Alexandru Elisei
  2024-04-22 13:09           ` Suzuki K Poulose
  0 siblings, 1 reply; 48+ messages in thread
From: Alexandru Elisei @ 2024-04-22 12:36 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, joey.gouly,
	steven.price, james.morse, oliver.upton, yuzenghui, andrew.jones,
	eric.auger

Hi,

On Mon, Apr 22, 2024 at 01:23:05PM +0100, Alexandru Elisei wrote:
> Hi,
> 
> On Mon, Apr 22, 2024 at 01:09:13PM +0100, Suzuki K Poulose wrote:
> > Hi Alexandru
> > 
> > On 22/04/2024 12:58, Alexandru Elisei wrote:
> > > Hi,
> > > 
> > > On Fri, Apr 12, 2024 at 11:33:43AM +0100, Suzuki K Poulose wrote:
> > > > From: Joey Gouly <joey.gouly@arm.com>
> > > > 
> > > > A Realm must access any emulated I/O mappings with the PTE_NS_SHARED bit set.
> > > > This is modelled as a PTE attribute, but is actually part of the address.
> > > > 
> > > > So, when MMU is disabled, the "physical address" must reflect this bit set. We
> > > > access the UART early before the MMU is enabled. So, make sure the UART is
> > > > accessed always with the bit set.
> > > > 
> > > > Signed-off-by: Joey Gouly <joey.gouly@arm.com>
> > > > Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> > > > ---
> > > >   lib/arm/asm/pgtable.h   |  5 +++++
> > > >   lib/arm/io.c            | 24 +++++++++++++++++++++++-
> > > >   lib/arm64/asm/pgtable.h |  5 +++++
> > > >   3 files changed, 33 insertions(+), 1 deletion(-)
> > > > 
> > > > diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
> > > > index 350039ff..7e85e7c6 100644
> > > > --- a/lib/arm/asm/pgtable.h
> > > > +++ b/lib/arm/asm/pgtable.h
> > > > @@ -112,4 +112,9 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
> > > >   	return pte_offset(pmd, addr);
> > > >   }
> > > > +static inline unsigned long arm_shared_phys_alias(void *x)
> > > > +{
> > > > +	return ((unsigned long)(x) | PTE_NS_SHARED);
> > > > +}
> > > 
> > > Is it allowed for a realm to run in aarch32 mode?
> > 
> > No. Realm EL1 must be Aarch64.
> 
> Ok, then can you make the above function return the original address?
> 
> > 
> > > 
> > > > +
> > > >   #endif /* _ASMARM_PGTABLE_H_ */
> > > > diff --git a/lib/arm/io.c b/lib/arm/io.c
> > > > index 836fa854..127727e4 100644
> > > > --- a/lib/arm/io.c
> > > > +++ b/lib/arm/io.c
> > > > @@ -15,6 +15,8 @@
> > > >   #include <asm/psci.h>
> > > >   #include <asm/spinlock.h>
> > > >   #include <asm/io.h>
> > > > +#include <asm/mmu-api.h>
> > > > +#include <asm/pgtable.h>
> > > >   #include "io.h"
> > > > @@ -30,6 +32,24 @@ static struct spinlock uart_lock;
> > > >   static volatile u8 *uart0_base = UART_EARLY_BASE;
> > > >   bool is_pl011_uart;
> > > > +static inline volatile u8 *get_uart_base(void)
> > > > +{
> > > > +	/*
> > > > +	 * The address of the UART base may be different
> > > > +	 * based on whether we are running with/without
> > > > +	 * MMU enabled.
> > > > +	 *
> > > > +	 * For realms, we must force to use the shared physical
> > > > +	 * alias with MMU disabled, to make sure the I/O can
> > > > +	 * be emulated.
> > > > +	 * When the MMU is turned ON, the mappings are created
> > > > +	 * appropriately.
> > > > +	 */
> > > > +	if (mmu_enabled())
> > > > +		return uart0_base;
> > > > +	return (u8 *)arm_shared_phys_alias((void *)uart0_base);
> > > > +}
> > > > +
> > > >   static void uart0_init_fdt(void)
> > > >   {
> > > >   	/*
> > > > @@ -109,9 +129,11 @@ void io_init(void)
> > > >   void puts(const char *s)
> > > >   {
> > > > +	volatile u8 *uart_base = get_uart_base();
> 
> Is it just my email client or is the indentation missing here?
> 
> > > > +
> > > >   	spin_lock(&uart_lock);
> > > >   	while (*s)
> > > > -		writeb(*s++, uart0_base);
> > > > +		writeb(*s++, uart_base);
> > > >   	spin_unlock(&uart_lock);
> > > >   }
> > > > diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
> > > > index 5b9f40b0..871c03e9 100644
> > > > --- a/lib/arm64/asm/pgtable.h
> > > > +++ b/lib/arm64/asm/pgtable.h
> > > > @@ -28,6 +28,11 @@ extern unsigned long prot_ns_shared;
> > > >   */
> > > >   #define PTE_NS_SHARED		(prot_ns_shared)
> > > > +static inline unsigned long arm_shared_phys_alias(void *addr)
> > > > +{
> > > > +	return ((unsigned long)addr | PTE_NS_SHARED);
> > > > +}
> > > 
> > > Have you considered specifying the correct UART address at compile time using
> > > ./configure --earlycon?
> > 
> > Do you mean
> > 
> > ./configure --earlycon=<NS-Alias-normal-UART-Address> for Realms ?
> > 
> > If so, there are multiple issues with that :
> > 
> > 1. A payload could be run in a normal VM or a Realm VM. Having the above
> > restricts using the same payload in different worlds. (e.g., comparison).
> > 
> > 2. The IPA width of the Realm and thus the PTE_NS_SHARED is dynamic
> > and really depends on what the VMM decides to choose the IPA size.
> > (Could be based on user input).
> 
> Does it depend on the chosen IPA size, or on the VA size, or on both?
> Because uart0_base is used with both the MMU on and off.

Oh, ok, my bad, I hadn't realized that the uart address is modified only if the
MMU is not enabled. So it's actually only the IPA that must have the bit set.

I have to say that a PTE attribute, as it is called in the commit message, which
has a variable bit position that depends on the output address size is quite
surprising. I was under the impression that the bit position is constant when I
made the suggestion.

Yeah, it makes sense to have a helper to set the bit based on whether or not the
MMU is enabled.

Thanks,
Alex

> 
> Thanks,
> Alex
> 
> > 
> > If any of the above fails, and a wrong earlycon address could result
> > in "Synchronouse External Abort" into the Realm (if it is in protected
> > IPA).
> > 
> > Suzuki
> > > 
> > > Thanks,
> > > Alex
> > > > +
> > > >   /*
> > > >    * Highest possible physical address supported.
> > > >    */
> > > > -- 
> > > > 2.34.1
> > > > 
> > 
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled
  2024-04-22 12:36         ` Alexandru Elisei
@ 2024-04-22 13:09           ` Suzuki K Poulose
  0 siblings, 0 replies; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-22 13:09 UTC (permalink / raw)
  To: Alexandru Elisei
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, joey.gouly,
	steven.price, james.morse, oliver.upton, yuzenghui, andrew.jones,
	eric.auger

Hi Alex

On 22/04/2024 13:36, Alexandru Elisei wrote:
> Hi,
> 
> On Mon, Apr 22, 2024 at 01:23:05PM +0100, Alexandru Elisei wrote:
>> Hi,
>>
>> On Mon, Apr 22, 2024 at 01:09:13PM +0100, Suzuki K Poulose wrote:
>>> Hi Alexandru
>>>
>>> On 22/04/2024 12:58, Alexandru Elisei wrote:
>>>> Hi,
>>>>
>>>> On Fri, Apr 12, 2024 at 11:33:43AM +0100, Suzuki K Poulose wrote:
>>>>> From: Joey Gouly <joey.gouly@arm.com>
>>>>>
>>>>> A Realm must access any emulated I/O mappings with the PTE_NS_SHARED bit set.
>>>>> This is modelled as a PTE attribute, but is actually part of the address.
>>>>>
>>>>> So, when MMU is disabled, the "physical address" must reflect this bit set. We
>>>>> access the UART early before the MMU is enabled. So, make sure the UART is
>>>>> accessed always with the bit set.
>>>>>
>>>>> Signed-off-by: Joey Gouly <joey.gouly@arm.com>
>>>>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>>>>> ---
>>>>>    lib/arm/asm/pgtable.h   |  5 +++++
>>>>>    lib/arm/io.c            | 24 +++++++++++++++++++++++-
>>>>>    lib/arm64/asm/pgtable.h |  5 +++++
>>>>>    3 files changed, 33 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
>>>>> index 350039ff..7e85e7c6 100644
>>>>> --- a/lib/arm/asm/pgtable.h
>>>>> +++ b/lib/arm/asm/pgtable.h
>>>>> @@ -112,4 +112,9 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
>>>>>    	return pte_offset(pmd, addr);
>>>>>    }
>>>>> +static inline unsigned long arm_shared_phys_alias(void *x)
>>>>> +{
>>>>> +	return ((unsigned long)(x) | PTE_NS_SHARED);
>>>>> +}
>>>>
>>>> Is it allowed for a realm to run in aarch32 mode?
>>>
>>> No. Realm EL1 must be Aarch64.
>>
>> Ok, then can you make the above function return the original address?
>>
>>>
>>>>
>>>>> +
>>>>>    #endif /* _ASMARM_PGTABLE_H_ */
>>>>> diff --git a/lib/arm/io.c b/lib/arm/io.c
>>>>> index 836fa854..127727e4 100644
>>>>> --- a/lib/arm/io.c
>>>>> +++ b/lib/arm/io.c
>>>>> @@ -15,6 +15,8 @@
>>>>>    #include <asm/psci.h>
>>>>>    #include <asm/spinlock.h>
>>>>>    #include <asm/io.h>
>>>>> +#include <asm/mmu-api.h>
>>>>> +#include <asm/pgtable.h>
>>>>>    #include "io.h"
>>>>> @@ -30,6 +32,24 @@ static struct spinlock uart_lock;
>>>>>    static volatile u8 *uart0_base = UART_EARLY_BASE;
>>>>>    bool is_pl011_uart;
>>>>> +static inline volatile u8 *get_uart_base(void)
>>>>> +{
>>>>> +	/*
>>>>> +	 * The address of the UART base may be different
>>>>> +	 * based on whether we are running with/without
>>>>> +	 * MMU enabled.
>>>>> +	 *
>>>>> +	 * For realms, we must force to use the shared physical
>>>>> +	 * alias with MMU disabled, to make sure the I/O can
>>>>> +	 * be emulated.
>>>>> +	 * When the MMU is turned ON, the mappings are created
>>>>> +	 * appropriately.
>>>>> +	 */
>>>>> +	if (mmu_enabled())
>>>>> +		return uart0_base;
>>>>> +	return (u8 *)arm_shared_phys_alias((void *)uart0_base);
>>>>> +}
>>>>> +
>>>>>    static void uart0_init_fdt(void)
>>>>>    {
>>>>>    	/*
>>>>> @@ -109,9 +129,11 @@ void io_init(void)
>>>>>    void puts(const char *s)
>>>>>    {
>>>>> +	volatile u8 *uart_base = get_uart_base();
>>
>> Is it just my email client or is the indentation missing here?

I can confirm that the original patch/email is correctly indented
with TAB.

>>
>>>>> +
>>>>>    	spin_lock(&uart_lock);
>>>>>    	while (*s)
>>>>> -		writeb(*s++, uart0_base);
>>>>> +		writeb(*s++, uart_base);
>>>>>    	spin_unlock(&uart_lock);
>>>>>    }
>>>>> diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
>>>>> index 5b9f40b0..871c03e9 100644
>>>>> --- a/lib/arm64/asm/pgtable.h
>>>>> +++ b/lib/arm64/asm/pgtable.h
>>>>> @@ -28,6 +28,11 @@ extern unsigned long prot_ns_shared;
>>>>>    */
>>>>>    #define PTE_NS_SHARED		(prot_ns_shared)
>>>>> +static inline unsigned long arm_shared_phys_alias(void *addr)
>>>>> +{
>>>>> +	return ((unsigned long)addr | PTE_NS_SHARED);
>>>>> +}
>>>>
>>>> Have you considered specifying the correct UART address at compile time using
>>>> ./configure --earlycon?
>>>
>>> Do you mean
>>>
>>> ./configure --earlycon=<NS-Alias-normal-UART-Address> for Realms ?
>>>
>>> If so, there are multiple issues with that :
>>>
>>> 1. A payload could be run in a normal VM or a Realm VM. Having the above
>>> restricts using the same payload in different worlds. (e.g., comparison).
>>>
>>> 2. The IPA width of the Realm and thus the PTE_NS_SHARED is dynamic
>>> and really depends on what the VMM decides to choose the IPA size.
>>> (Could be based on user input).
>>
>> Does it depend on the chosen IPA size, or on the VA size, or on both?
>> Because uart0_base is used with both the MMU on and off.
> 
> Oh, ok, my bad, I hadn't realized that the uart address is modified only if the
> MMU is not enabled. So it's actually only the IPA that must have the bit set.
> 
> I have to say that a PTE attribute, as it is called in the commit message, which
> has a variable bit position that depends on the output address size is quite
> surprising. I was under the impression that the bit position is constant when I
> made the suggestion.

I understand, may be we could make that clear in the commit description.

> 
> Yeah, it makes sense to have a helper to set the bit based on whether or not the
> MMU is enabled.

Thanks

Suzuki


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled Suzuki K Poulose
  2024-04-22 11:58   ` Alexandru Elisei
@ 2024-04-22 15:38   ` Alexandru Elisei
  2024-04-22 16:05     ` Suzuki K Poulose
  1 sibling, 1 reply; 48+ messages in thread
From: Alexandru Elisei @ 2024-04-22 15:38 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, joey.gouly,
	steven.price, james.morse, oliver.upton, yuzenghui, andrew.jones,
	eric.auger

Hi,

On Fri, Apr 12, 2024 at 11:33:43AM +0100, Suzuki K Poulose wrote:
> From: Joey Gouly <joey.gouly@arm.com>
> 
> A Realm must access any emulated I/O mappings with the PTE_NS_SHARED bit set.

What entity requires that a realm must access I/O mappings with the
PTE_NS_SHARED bit set? Is that an architectural requirement? Or is it an
implementation choice made by the VMM and/or KVM?

Thanks,
Alex

> This is modelled as a PTE attribute, but is actually part of the address.
> 
> So, when MMU is disabled, the "physical address" must reflect this bit set. We
> access the UART early before the MMU is enabled. So, make sure the UART is
> accessed always with the bit set.
> 
> Signed-off-by: Joey Gouly <joey.gouly@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>  lib/arm/asm/pgtable.h   |  5 +++++
>  lib/arm/io.c            | 24 +++++++++++++++++++++++-
>  lib/arm64/asm/pgtable.h |  5 +++++
>  3 files changed, 33 insertions(+), 1 deletion(-)
> 
> diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
> index 350039ff..7e85e7c6 100644
> --- a/lib/arm/asm/pgtable.h
> +++ b/lib/arm/asm/pgtable.h
> @@ -112,4 +112,9 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
>  	return pte_offset(pmd, addr);
>  }
>  
> +static inline unsigned long arm_shared_phys_alias(void *x)
> +{
> +	return ((unsigned long)(x) | PTE_NS_SHARED);
> +}
> +
>  #endif /* _ASMARM_PGTABLE_H_ */
> diff --git a/lib/arm/io.c b/lib/arm/io.c
> index 836fa854..127727e4 100644
> --- a/lib/arm/io.c
> +++ b/lib/arm/io.c
> @@ -15,6 +15,8 @@
>  #include <asm/psci.h>
>  #include <asm/spinlock.h>
>  #include <asm/io.h>
> +#include <asm/mmu-api.h>
> +#include <asm/pgtable.h>
>  
>  #include "io.h"
>  
> @@ -30,6 +32,24 @@ static struct spinlock uart_lock;
>  static volatile u8 *uart0_base = UART_EARLY_BASE;
>  bool is_pl011_uart;
>  
> +static inline volatile u8 *get_uart_base(void)
> +{
> +	/*
> +	 * The address of the UART base may be different
> +	 * based on whether we are running with/without
> +	 * MMU enabled.
> +	 *
> +	 * For realms, we must force to use the shared physical
> +	 * alias with MMU disabled, to make sure the I/O can
> +	 * be emulated.
> +	 * When the MMU is turned ON, the mappings are created
> +	 * appropriately.
> +	 */
> +	if (mmu_enabled())
> +		return uart0_base;
> +	return (u8 *)arm_shared_phys_alias((void *)uart0_base);
> +}
> +
>  static void uart0_init_fdt(void)
>  {
>  	/*
> @@ -109,9 +129,11 @@ void io_init(void)
>  
>  void puts(const char *s)
>  {
> +	volatile u8 *uart_base = get_uart_base();
> +
>  	spin_lock(&uart_lock);
>  	while (*s)
> -		writeb(*s++, uart0_base);
> +		writeb(*s++, uart_base);
>  	spin_unlock(&uart_lock);
>  }
>  
> diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
> index 5b9f40b0..871c03e9 100644
> --- a/lib/arm64/asm/pgtable.h
> +++ b/lib/arm64/asm/pgtable.h
> @@ -28,6 +28,11 @@ extern unsigned long prot_ns_shared;
>  */
>  #define PTE_NS_SHARED		(prot_ns_shared)
>  
> +static inline unsigned long arm_shared_phys_alias(void *addr)
> +{
> +	return ((unsigned long)addr | PTE_NS_SHARED);
> +}
> +
>  /*
>   * Highest possible physical address supported.
>   */
> -- 
> 2.34.1
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 14/33] arm: selftest: realm: skip pabt test when running in a realm
  2024-04-12 10:33 ` [kvm-unit-tests PATCH 14/33] arm: selftest: realm: skip pabt test when running in a realm Suzuki K Poulose
@ 2024-04-22 15:48   ` Alexandru Elisei
  0 siblings, 0 replies; 48+ messages in thread
From: Alexandru Elisei @ 2024-04-22 15:48 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, joey.gouly,
	steven.price, james.morse, oliver.upton, yuzenghui, andrew.jones,
	eric.auger

Hi,

On Fri, Apr 12, 2024 at 11:33:49AM +0100, Suzuki K Poulose wrote:
> From: Alexandru Elisei <alexandru.elisei@arm.com>
> 
> The realm manager treats instruction aborts as fatal errors, skip this
> test.

I wrote the commit message, and now I don't remember if this was a
limitation of the (at the time) implementation for the realm manager that I
used for developing the patch or if this was specified in the architecture
:(

Would you mind clearing that up?

Thanks,
Alex

> 
> Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com>
> Signed-off-by: Joey Gouly <joey.gouly@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>  arm/selftest.c | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/arm/selftest.c b/arm/selftest.c
> index 1553ed8e..8caadad3 100644
> --- a/arm/selftest.c
> +++ b/arm/selftest.c
> @@ -19,6 +19,7 @@
>  #include <asm/smp.h>
>  #include <asm/mmu.h>
>  #include <asm/barrier.h>
> +#include <asm/rsi.h>
>  
>  static cpumask_t ready, valid;
>  
> @@ -393,11 +394,17 @@ static void check_vectors(void *arg __unused)
>  					  user_psci_system_off);
>  #endif
>  	} else {
> +		if (is_realm()) {
> +			report_skip("pabt test not supported in a realm");
> +			goto out;
> +		}
> +
>  		if (!check_pabt_init())
>  			report_skip("Couldn't guess an invalid physical address");
>  		else
>  			report(check_pabt(), "pabt");
>  	}
> +out:
>  	exit(report_summary());
>  }
>  
> -- 
> 2.34.1
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled
  2024-04-22 15:38   ` Alexandru Elisei
@ 2024-04-22 16:05     ` Suzuki K Poulose
  2024-04-22 16:15       ` Alexandru Elisei
  0 siblings, 1 reply; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-22 16:05 UTC (permalink / raw)
  To: Alexandru Elisei
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, joey.gouly,
	steven.price, james.morse, oliver.upton, yuzenghui, andrew.jones,
	eric.auger

On 22/04/2024 16:38, Alexandru Elisei wrote:
> Hi,
> 
> On Fri, Apr 12, 2024 at 11:33:43AM +0100, Suzuki K Poulose wrote:
>> From: Joey Gouly <joey.gouly@arm.com>
>>
>> A Realm must access any emulated I/O mappings with the PTE_NS_SHARED bit set.
> 
> What entity requires that a realm must access I/O mappings with the
> PTE_NS_SHARED bit set? Is that an architectural requirement? Or is it an
> implementation choice made by the VMM and/or KVM?

RMM spec. An MMIO access in the Protected IPA must be emulated by Realm
world. If an MMIO access must be emulated by NS Host, it must be in the 
Unprotected IPA.

Technically, a VMM could create a memory map, where the NS emulated I/O
are kept in the unprotected (upper) half.

Or the VMM retains the current model and expects the Realm to use the 
"Unprotected" alias.


Either way, applying the PTE_NS_SHARED attribute doesn't change anything 
and works for both the models, as far as the Realm is concerned.


Suzuki




> 
> Thanks,
> Alex
> 
>> This is modelled as a PTE attribute, but is actually part of the address.
>>
>> So, when MMU is disabled, the "physical address" must reflect this bit set. We
>> access the UART early before the MMU is enabled. So, make sure the UART is
>> accessed always with the bit set.
>>
>> Signed-off-by: Joey Gouly <joey.gouly@arm.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>>   lib/arm/asm/pgtable.h   |  5 +++++
>>   lib/arm/io.c            | 24 +++++++++++++++++++++++-
>>   lib/arm64/asm/pgtable.h |  5 +++++
>>   3 files changed, 33 insertions(+), 1 deletion(-)
>>
>> diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
>> index 350039ff..7e85e7c6 100644
>> --- a/lib/arm/asm/pgtable.h
>> +++ b/lib/arm/asm/pgtable.h
>> @@ -112,4 +112,9 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
>>   	return pte_offset(pmd, addr);
>>   }
>>   
>> +static inline unsigned long arm_shared_phys_alias(void *x)
>> +{
>> +	return ((unsigned long)(x) | PTE_NS_SHARED);
>> +}
>> +
>>   #endif /* _ASMARM_PGTABLE_H_ */
>> diff --git a/lib/arm/io.c b/lib/arm/io.c
>> index 836fa854..127727e4 100644
>> --- a/lib/arm/io.c
>> +++ b/lib/arm/io.c
>> @@ -15,6 +15,8 @@
>>   #include <asm/psci.h>
>>   #include <asm/spinlock.h>
>>   #include <asm/io.h>
>> +#include <asm/mmu-api.h>
>> +#include <asm/pgtable.h>
>>   
>>   #include "io.h"
>>   
>> @@ -30,6 +32,24 @@ static struct spinlock uart_lock;
>>   static volatile u8 *uart0_base = UART_EARLY_BASE;
>>   bool is_pl011_uart;
>>   
>> +static inline volatile u8 *get_uart_base(void)
>> +{
>> +	/*
>> +	 * The address of the UART base may be different
>> +	 * based on whether we are running with/without
>> +	 * MMU enabled.
>> +	 *
>> +	 * For realms, we must force to use the shared physical
>> +	 * alias with MMU disabled, to make sure the I/O can
>> +	 * be emulated.
>> +	 * When the MMU is turned ON, the mappings are created
>> +	 * appropriately.
>> +	 */
>> +	if (mmu_enabled())
>> +		return uart0_base;
>> +	return (u8 *)arm_shared_phys_alias((void *)uart0_base);
>> +}
>> +
>>   static void uart0_init_fdt(void)
>>   {
>>   	/*
>> @@ -109,9 +129,11 @@ void io_init(void)
>>   
>>   void puts(const char *s)
>>   {
>> +	volatile u8 *uart_base = get_uart_base();
>> +
>>   	spin_lock(&uart_lock);
>>   	while (*s)
>> -		writeb(*s++, uart0_base);
>> +		writeb(*s++, uart_base);
>>   	spin_unlock(&uart_lock);
>>   }
>>   
>> diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
>> index 5b9f40b0..871c03e9 100644
>> --- a/lib/arm64/asm/pgtable.h
>> +++ b/lib/arm64/asm/pgtable.h
>> @@ -28,6 +28,11 @@ extern unsigned long prot_ns_shared;
>>   */
>>   #define PTE_NS_SHARED		(prot_ns_shared)
>>   
>> +static inline unsigned long arm_shared_phys_alias(void *addr)
>> +{
>> +	return ((unsigned long)addr | PTE_NS_SHARED);
>> +}
>> +
>>   /*
>>    * Highest possible physical address supported.
>>    */
>> -- 
>> 2.34.1
>>


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled
  2024-04-22 16:05     ` Suzuki K Poulose
@ 2024-04-22 16:15       ` Alexandru Elisei
  2024-04-26 11:15         ` Suzuki K Poulose
  0 siblings, 1 reply; 48+ messages in thread
From: Alexandru Elisei @ 2024-04-22 16:15 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, joey.gouly,
	steven.price, james.morse, oliver.upton, yuzenghui, andrew.jones,
	eric.auger

Hi,

On Mon, Apr 22, 2024 at 05:05:54PM +0100, Suzuki K Poulose wrote:
> On 22/04/2024 16:38, Alexandru Elisei wrote:
> > Hi,
> > 
> > On Fri, Apr 12, 2024 at 11:33:43AM +0100, Suzuki K Poulose wrote:
> > > From: Joey Gouly <joey.gouly@arm.com>
> > > 
> > > A Realm must access any emulated I/O mappings with the PTE_NS_SHARED bit set.
> > 
> > What entity requires that a realm must access I/O mappings with the
> > PTE_NS_SHARED bit set? Is that an architectural requirement? Or is it an
> > implementation choice made by the VMM and/or KVM?
> 
> RMM spec. An MMIO access in the Protected IPA must be emulated by Realm
> world. If an MMIO access must be emulated by NS Host, it must be in the
> Unprotected IPA.

That's not exactly what I was asking. I was curious to know how a realm
knows if a MMIO access is emulated by the Realm, and thus it must use a
protected address, or it's emulated by the non-secure host, and it must use
an unprotected address.

Thanks,
Alex

> 
> Technically, a VMM could create a memory map, where the NS emulated I/O
> are kept in the unprotected (upper) half.
> 
> Or the VMM retains the current model and expects the Realm to use the
> "Unprotected" alias.
> 
> 
> Either way, applying the PTE_NS_SHARED attribute doesn't change anything and
> works for both the models, as far as the Realm is concerned.
> 
> 
> Suzuki
> 
> 
> 
> 
> > 
> > Thanks,
> > Alex
> > 
> > > This is modelled as a PTE attribute, but is actually part of the address.
> > > 
> > > So, when MMU is disabled, the "physical address" must reflect this bit set. We
> > > access the UART early before the MMU is enabled. So, make sure the UART is
> > > accessed always with the bit set.
> > > 
> > > Signed-off-by: Joey Gouly <joey.gouly@arm.com>
> > > Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> > > ---
> > >   lib/arm/asm/pgtable.h   |  5 +++++
> > >   lib/arm/io.c            | 24 +++++++++++++++++++++++-
> > >   lib/arm64/asm/pgtable.h |  5 +++++
> > >   3 files changed, 33 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
> > > index 350039ff..7e85e7c6 100644
> > > --- a/lib/arm/asm/pgtable.h
> > > +++ b/lib/arm/asm/pgtable.h
> > > @@ -112,4 +112,9 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
> > >   	return pte_offset(pmd, addr);
> > >   }
> > > +static inline unsigned long arm_shared_phys_alias(void *x)
> > > +{
> > > +	return ((unsigned long)(x) | PTE_NS_SHARED);
> > > +}
> > > +
> > >   #endif /* _ASMARM_PGTABLE_H_ */
> > > diff --git a/lib/arm/io.c b/lib/arm/io.c
> > > index 836fa854..127727e4 100644
> > > --- a/lib/arm/io.c
> > > +++ b/lib/arm/io.c
> > > @@ -15,6 +15,8 @@
> > >   #include <asm/psci.h>
> > >   #include <asm/spinlock.h>
> > >   #include <asm/io.h>
> > > +#include <asm/mmu-api.h>
> > > +#include <asm/pgtable.h>
> > >   #include "io.h"
> > > @@ -30,6 +32,24 @@ static struct spinlock uart_lock;
> > >   static volatile u8 *uart0_base = UART_EARLY_BASE;
> > >   bool is_pl011_uart;
> > > +static inline volatile u8 *get_uart_base(void)
> > > +{
> > > +	/*
> > > +	 * The address of the UART base may be different
> > > +	 * based on whether we are running with/without
> > > +	 * MMU enabled.
> > > +	 *
> > > +	 * For realms, we must force to use the shared physical
> > > +	 * alias with MMU disabled, to make sure the I/O can
> > > +	 * be emulated.
> > > +	 * When the MMU is turned ON, the mappings are created
> > > +	 * appropriately.
> > > +	 */
> > > +	if (mmu_enabled())
> > > +		return uart0_base;
> > > +	return (u8 *)arm_shared_phys_alias((void *)uart0_base);
> > > +}
> > > +
> > >   static void uart0_init_fdt(void)
> > >   {
> > >   	/*
> > > @@ -109,9 +129,11 @@ void io_init(void)
> > >   void puts(const char *s)
> > >   {
> > > +	volatile u8 *uart_base = get_uart_base();
> > > +
> > >   	spin_lock(&uart_lock);
> > >   	while (*s)
> > > -		writeb(*s++, uart0_base);
> > > +		writeb(*s++, uart_base);
> > >   	spin_unlock(&uart_lock);
> > >   }
> > > diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
> > > index 5b9f40b0..871c03e9 100644
> > > --- a/lib/arm64/asm/pgtable.h
> > > +++ b/lib/arm64/asm/pgtable.h
> > > @@ -28,6 +28,11 @@ extern unsigned long prot_ns_shared;
> > >   */
> > >   #define PTE_NS_SHARED		(prot_ns_shared)
> > > +static inline unsigned long arm_shared_phys_alias(void *addr)
> > > +{
> > > +	return ((unsigned long)addr | PTE_NS_SHARED);
> > > +}
> > > +
> > >   /*
> > >    * Highest possible physical address supported.
> > >    */
> > > -- 
> > > 2.34.1
> > > 
> 
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled
  2024-04-22 16:15       ` Alexandru Elisei
@ 2024-04-26 11:15         ` Suzuki K Poulose
  2024-04-26 13:51           ` Alexandru Elisei
  0 siblings, 1 reply; 48+ messages in thread
From: Suzuki K Poulose @ 2024-04-26 11:15 UTC (permalink / raw)
  To: Alexandru Elisei
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, joey.gouly,
	steven.price, james.morse, oliver.upton, yuzenghui, andrew.jones,
	eric.auger

On 22/04/2024 17:15, Alexandru Elisei wrote:
> Hi,
> 
> On Mon, Apr 22, 2024 at 05:05:54PM +0100, Suzuki K Poulose wrote:
>> On 22/04/2024 16:38, Alexandru Elisei wrote:
>>> Hi,
>>>
>>> On Fri, Apr 12, 2024 at 11:33:43AM +0100, Suzuki K Poulose wrote:
>>>> From: Joey Gouly <joey.gouly@arm.com>
>>>>
>>>> A Realm must access any emulated I/O mappings with the PTE_NS_SHARED bit set.
>>>
>>> What entity requires that a realm must access I/O mappings with the
>>> PTE_NS_SHARED bit set? Is that an architectural requirement? Or is it an
>>> implementation choice made by the VMM and/or KVM?
>>
>> RMM spec. An MMIO access in the Protected IPA must be emulated by Realm
>> world. If an MMIO access must be emulated by NS Host, it must be in the
>> Unprotected IPA.
> 
> That's not exactly what I was asking. I was curious to know how a realm
> knows if a MMIO access is emulated by the Realm, and thus it must use a
> protected address, or it's emulated by the non-secure host, and it must use
> an unprotected address.

With Arm CCA-1.0, there cannot be an emulation in the Realm world. 
Everything is by the NS Host. It may change in the future, but the
RSI version would make the Realm aware of the difference.


Suzuki



> 
> Thanks,
> Alex
> 
>>
>> Technically, a VMM could create a memory map, where the NS emulated I/O
>> are kept in the unprotected (upper) half.
>>
>> Or the VMM retains the current model and expects the Realm to use the
>> "Unprotected" alias.
>>
>>
>> Either way, applying the PTE_NS_SHARED attribute doesn't change anything and
>> works for both the models, as far as the Realm is concerned.
>>
>>
>> Suzuki
>>
>>
>>
>>
>>>
>>> Thanks,
>>> Alex
>>>
>>>> This is modelled as a PTE attribute, but is actually part of the address.
>>>>
>>>> So, when MMU is disabled, the "physical address" must reflect this bit set. We
>>>> access the UART early before the MMU is enabled. So, make sure the UART is
>>>> accessed always with the bit set.
>>>>
>>>> Signed-off-by: Joey Gouly <joey.gouly@arm.com>
>>>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>>>> ---
>>>>    lib/arm/asm/pgtable.h   |  5 +++++
>>>>    lib/arm/io.c            | 24 +++++++++++++++++++++++-
>>>>    lib/arm64/asm/pgtable.h |  5 +++++
>>>>    3 files changed, 33 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
>>>> index 350039ff..7e85e7c6 100644
>>>> --- a/lib/arm/asm/pgtable.h
>>>> +++ b/lib/arm/asm/pgtable.h
>>>> @@ -112,4 +112,9 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
>>>>    	return pte_offset(pmd, addr);
>>>>    }
>>>> +static inline unsigned long arm_shared_phys_alias(void *x)
>>>> +{
>>>> +	return ((unsigned long)(x) | PTE_NS_SHARED);
>>>> +}
>>>> +
>>>>    #endif /* _ASMARM_PGTABLE_H_ */
>>>> diff --git a/lib/arm/io.c b/lib/arm/io.c
>>>> index 836fa854..127727e4 100644
>>>> --- a/lib/arm/io.c
>>>> +++ b/lib/arm/io.c
>>>> @@ -15,6 +15,8 @@
>>>>    #include <asm/psci.h>
>>>>    #include <asm/spinlock.h>
>>>>    #include <asm/io.h>
>>>> +#include <asm/mmu-api.h>
>>>> +#include <asm/pgtable.h>
>>>>    #include "io.h"
>>>> @@ -30,6 +32,24 @@ static struct spinlock uart_lock;
>>>>    static volatile u8 *uart0_base = UART_EARLY_BASE;
>>>>    bool is_pl011_uart;
>>>> +static inline volatile u8 *get_uart_base(void)
>>>> +{
>>>> +	/*
>>>> +	 * The address of the UART base may be different
>>>> +	 * based on whether we are running with/without
>>>> +	 * MMU enabled.
>>>> +	 *
>>>> +	 * For realms, we must force to use the shared physical
>>>> +	 * alias with MMU disabled, to make sure the I/O can
>>>> +	 * be emulated.
>>>> +	 * When the MMU is turned ON, the mappings are created
>>>> +	 * appropriately.
>>>> +	 */
>>>> +	if (mmu_enabled())
>>>> +		return uart0_base;
>>>> +	return (u8 *)arm_shared_phys_alias((void *)uart0_base);
>>>> +}
>>>> +
>>>>    static void uart0_init_fdt(void)
>>>>    {
>>>>    	/*
>>>> @@ -109,9 +129,11 @@ void io_init(void)
>>>>    void puts(const char *s)
>>>>    {
>>>> +	volatile u8 *uart_base = get_uart_base();
>>>> +
>>>>    	spin_lock(&uart_lock);
>>>>    	while (*s)
>>>> -		writeb(*s++, uart0_base);
>>>> +		writeb(*s++, uart_base);
>>>>    	spin_unlock(&uart_lock);
>>>>    }
>>>> diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
>>>> index 5b9f40b0..871c03e9 100644
>>>> --- a/lib/arm64/asm/pgtable.h
>>>> +++ b/lib/arm64/asm/pgtable.h
>>>> @@ -28,6 +28,11 @@ extern unsigned long prot_ns_shared;
>>>>    */
>>>>    #define PTE_NS_SHARED		(prot_ns_shared)
>>>> +static inline unsigned long arm_shared_phys_alias(void *addr)
>>>> +{
>>>> +	return ((unsigned long)addr | PTE_NS_SHARED);
>>>> +}
>>>> +
>>>>    /*
>>>>     * Highest possible physical address supported.
>>>>     */
>>>> -- 
>>>> 2.34.1
>>>>
>>
>>


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled
  2024-04-26 11:15         ` Suzuki K Poulose
@ 2024-04-26 13:51           ` Alexandru Elisei
  0 siblings, 0 replies; 48+ messages in thread
From: Alexandru Elisei @ 2024-04-26 13:51 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvmarm, kvm, linux-coco, linux-arm-kernel, maz, joey.gouly,
	steven.price, james.morse, oliver.upton, yuzenghui, andrew.jones,
	eric.auger

Hi,

On Fri, Apr 26, 2024 at 12:15:53PM +0100, Suzuki K Poulose wrote:
> On 22/04/2024 17:15, Alexandru Elisei wrote:
> > Hi,
> > 
> > On Mon, Apr 22, 2024 at 05:05:54PM +0100, Suzuki K Poulose wrote:
> > > On 22/04/2024 16:38, Alexandru Elisei wrote:
> > > > Hi,
> > > > 
> > > > On Fri, Apr 12, 2024 at 11:33:43AM +0100, Suzuki K Poulose wrote:
> > > > > From: Joey Gouly <joey.gouly@arm.com>
> > > > > 
> > > > > A Realm must access any emulated I/O mappings with the PTE_NS_SHARED bit set.
> > > > 
> > > > What entity requires that a realm must access I/O mappings with the
> > > > PTE_NS_SHARED bit set? Is that an architectural requirement? Or is it an
> > > > implementation choice made by the VMM and/or KVM?
> > > 
> > > RMM spec. An MMIO access in the Protected IPA must be emulated by Realm
> > > world. If an MMIO access must be emulated by NS Host, it must be in the
> > > Unprotected IPA.
> > 
> > That's not exactly what I was asking. I was curious to know how a realm
> > knows if a MMIO access is emulated by the Realm, and thus it must use a
> > protected address, or it's emulated by the non-secure host, and it must use
> > an unprotected address.
> 
> With Arm CCA-1.0, there cannot be an emulation in the Realm world.
> Everything is by the NS Host. It may change in the future, but the
> RSI version would make the Realm aware of the difference.

Wouldn't that be a breaking change for existing software, though? For
example, existing software running in a realm assumes that all devices use
unprotected addresses, and when the VMM/KVM/realm manager gets updated to
implement the newer spec, that ceases to be the case.

Also, would you mind pointing me to the section in the existing spec where
that is specified? I'm curious about the exact wording.

Thanks,
Alex

> 
> 
> Suzuki
> 
> 
> 
> > 
> > Thanks,
> > Alex
> > 
> > > 
> > > Technically, a VMM could create a memory map, where the NS emulated I/O
> > > are kept in the unprotected (upper) half.
> > > 
> > > Or the VMM retains the current model and expects the Realm to use the
> > > "Unprotected" alias.
> > > 
> > > 
> > > Either way, applying the PTE_NS_SHARED attribute doesn't change anything and
> > > works for both the models, as far as the Realm is concerned.
> > > 
> > > 
> > > Suzuki
> > > 
> > > 
> > > 
> > > 
> > > > 
> > > > Thanks,
> > > > Alex
> > > > 
> > > > > This is modelled as a PTE attribute, but is actually part of the address.
> > > > > 
> > > > > So, when MMU is disabled, the "physical address" must reflect this bit set. We
> > > > > access the UART early before the MMU is enabled. So, make sure the UART is
> > > > > accessed always with the bit set.
> > > > > 
> > > > > Signed-off-by: Joey Gouly <joey.gouly@arm.com>
> > > > > Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> > > > > ---
> > > > >    lib/arm/asm/pgtable.h   |  5 +++++
> > > > >    lib/arm/io.c            | 24 +++++++++++++++++++++++-
> > > > >    lib/arm64/asm/pgtable.h |  5 +++++
> > > > >    3 files changed, 33 insertions(+), 1 deletion(-)
> > > > > 
> > > > > diff --git a/lib/arm/asm/pgtable.h b/lib/arm/asm/pgtable.h
> > > > > index 350039ff..7e85e7c6 100644
> > > > > --- a/lib/arm/asm/pgtable.h
> > > > > +++ b/lib/arm/asm/pgtable.h
> > > > > @@ -112,4 +112,9 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
> > > > >    	return pte_offset(pmd, addr);
> > > > >    }
> > > > > +static inline unsigned long arm_shared_phys_alias(void *x)
> > > > > +{
> > > > > +	return ((unsigned long)(x) | PTE_NS_SHARED);
> > > > > +}
> > > > > +
> > > > >    #endif /* _ASMARM_PGTABLE_H_ */
> > > > > diff --git a/lib/arm/io.c b/lib/arm/io.c
> > > > > index 836fa854..127727e4 100644
> > > > > --- a/lib/arm/io.c
> > > > > +++ b/lib/arm/io.c
> > > > > @@ -15,6 +15,8 @@
> > > > >    #include <asm/psci.h>
> > > > >    #include <asm/spinlock.h>
> > > > >    #include <asm/io.h>
> > > > > +#include <asm/mmu-api.h>
> > > > > +#include <asm/pgtable.h>
> > > > >    #include "io.h"
> > > > > @@ -30,6 +32,24 @@ static struct spinlock uart_lock;
> > > > >    static volatile u8 *uart0_base = UART_EARLY_BASE;
> > > > >    bool is_pl011_uart;
> > > > > +static inline volatile u8 *get_uart_base(void)
> > > > > +{
> > > > > +	/*
> > > > > +	 * The address of the UART base may be different
> > > > > +	 * based on whether we are running with/without
> > > > > +	 * MMU enabled.
> > > > > +	 *
> > > > > +	 * For realms, we must force to use the shared physical
> > > > > +	 * alias with MMU disabled, to make sure the I/O can
> > > > > +	 * be emulated.
> > > > > +	 * When the MMU is turned ON, the mappings are created
> > > > > +	 * appropriately.
> > > > > +	 */
> > > > > +	if (mmu_enabled())
> > > > > +		return uart0_base;
> > > > > +	return (u8 *)arm_shared_phys_alias((void *)uart0_base);
> > > > > +}
> > > > > +
> > > > >    static void uart0_init_fdt(void)
> > > > >    {
> > > > >    	/*
> > > > > @@ -109,9 +129,11 @@ void io_init(void)
> > > > >    void puts(const char *s)
> > > > >    {
> > > > > +	volatile u8 *uart_base = get_uart_base();
> > > > > +
> > > > >    	spin_lock(&uart_lock);
> > > > >    	while (*s)
> > > > > -		writeb(*s++, uart0_base);
> > > > > +		writeb(*s++, uart_base);
> > > > >    	spin_unlock(&uart_lock);
> > > > >    }
> > > > > diff --git a/lib/arm64/asm/pgtable.h b/lib/arm64/asm/pgtable.h
> > > > > index 5b9f40b0..871c03e9 100644
> > > > > --- a/lib/arm64/asm/pgtable.h
> > > > > +++ b/lib/arm64/asm/pgtable.h
> > > > > @@ -28,6 +28,11 @@ extern unsigned long prot_ns_shared;
> > > > >    */
> > > > >    #define PTE_NS_SHARED		(prot_ns_shared)
> > > > > +static inline unsigned long arm_shared_phys_alias(void *addr)
> > > > > +{
> > > > > +	return ((unsigned long)addr | PTE_NS_SHARED);
> > > > > +}
> > > > > +
> > > > >    /*
> > > > >     * Highest possible physical address supported.
> > > > >     */
> > > > > -- 
> > > > > 2.34.1
> > > > > 
> > > 
> > > 
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

end of thread, other threads:[~2024-04-26 13:52 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-12 10:33 [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Suzuki K Poulose
2024-04-10 16:17 ` Itaru Kitayama
2024-04-15  8:59   ` Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 01/33] arm: Add necessary header files in asm/pgtable.h Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 02/33] arm: Detect FDT overlap with uninitialised data Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 03/33] arm64: Expand SMCCC arguments and return values Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 04/33] arm: Make physical address mask dynamic Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 05/33] arm64: Introduce NS_SHARED PTE attribute Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 06/33] arm: Move io_init after vm initialization Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 07/33] arm: realm: Add RSI interface header Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 08/33] arm: realm: Make uart available before MMU is enabled Suzuki K Poulose
2024-04-22 11:58   ` Alexandru Elisei
2024-04-22 12:09     ` Suzuki K Poulose
2024-04-22 12:23       ` Alexandru Elisei
2024-04-22 12:36         ` Alexandru Elisei
2024-04-22 13:09           ` Suzuki K Poulose
2024-04-22 15:38   ` Alexandru Elisei
2024-04-22 16:05     ` Suzuki K Poulose
2024-04-22 16:15       ` Alexandru Elisei
2024-04-26 11:15         ` Suzuki K Poulose
2024-04-26 13:51           ` Alexandru Elisei
2024-04-12 10:33 ` [kvm-unit-tests PATCH 09/33] arm: realm: Realm initialisation Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 10/33] arm: realm: Add support for changing the state of memory Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 11/33] arm: realm: Set RIPAS state for RAM Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 12/33] arm: realm: Early memory setup Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 13/33] arm: realm: Add RSI version test Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 14/33] arm: selftest: realm: skip pabt test when running in a realm Suzuki K Poulose
2024-04-22 15:48   ` Alexandru Elisei
2024-04-12 10:33 ` [kvm-unit-tests PATCH 15/33] arm: realm: add hvc and RSI_HOST_CALL tests Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 16/33] arm64: add ESR_ELx EC.SVE Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 17/33] arm64: enable SVE at startup Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 18/33] arm: realm: Add test for FPU/SIMD context save/restore Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 19/33] arm64: selftest: add realm SVE VL test Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 20/33] arm: realm: Add tests for in realm SEA Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 21/33] lib/alloc_page: Add shared page allocation support Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 22/33] arm: gic-v3-its: Use shared pages wherever needed Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 23/33] arm: realm: Enable memory encryption Suzuki K Poulose
2024-04-12 10:33 ` [kvm-unit-tests PATCH 24/33] qcbor: Add QCBOR as a submodule Suzuki K Poulose
2024-04-12 10:34 ` [kvm-unit-tests PATCH 25/33] arm: Add build steps for QCBOR library Suzuki K Poulose
2024-04-12 10:34 ` [kvm-unit-tests PATCH 26/33] arm: Add a library to verify tokens using the " Suzuki K Poulose
2024-04-12 10:34 ` [kvm-unit-tests PATCH 27/33] arm: realm: add RSI interface for attestation measurements Suzuki K Poulose
2024-04-12 10:34 ` [kvm-unit-tests PATCH 28/33] arm: realm: Add helpers to decode RSI return codes Suzuki K Poulose
2024-04-12 10:34 ` [kvm-unit-tests PATCH 29/33] arm: realm: Add Realm attestation tests Suzuki K Poulose
2024-04-12 10:34 ` [kvm-unit-tests PATCH 30/33] " Suzuki K Poulose
2024-04-12 10:34 ` [kvm-unit-tests PATCH 31/33] arm: realm: Add a test for shared memory Suzuki K Poulose
2024-04-12 10:34 ` [kvm-unit-tests PATCH 32/33] arm: Add memtest support Suzuki K Poulose
2024-04-12 10:34 ` [kvm-unit-tests PATCH 33/33] NOT-FOR-MERGING: add run-realm-tests Suzuki K Poulose
2024-04-16 14:28 ` [kvm-unit-tests PATCH 00/33] Support for Arm Confidential Compute Architecture Jean-Philippe Brucker

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).